diff --git a/.github/workflows/submit.yml b/.github/workflows/submit.yml index 382c4e20872..ebe72381738 100644 --- a/.github/workflows/submit.yml +++ b/.github/workflows/submit.yml @@ -9,7 +9,7 @@ on: platforms: description: "Platform(s) to execute on" required: true - default: "Linux x64, Windows x64, macOS x64" + default: "Linux x64, Linux x86, Windows x64, macOS x64" jobs: prerequisites: @@ -18,6 +18,7 @@ jobs: outputs: should_run: ${{ steps.check_submit.outputs.should_run }} bundle_id: ${{ steps.check_bundle_id.outputs.bundle_id }} + platform_linux_x86: ${{ steps.check_platforms.outputs.platform_linux_x86 }} platform_linux_x64: ${{ steps.check_platforms.outputs.platform_linux_x64 }} platform_windows_x64: ${{ steps.check_platforms.outputs.platform_windows_x64 }} platform_macos_x64: ${{ steps.check_platforms.outputs.platform_macos_x64 }} @@ -32,11 +33,13 @@ jobs: id: check_platforms run: | echo "::set-output name=platform_linux_x64::${{ contains(github.event.inputs.platforms, 'linux x64') || (github.event.inputs.platforms == '' && (secrets.JDK_SUBMIT_PLATFORMS == '' || contains(secrets.JDK_SUBMIT_PLATFORMS, 'linux x64'))) }}" + echo "::set-output name=platform_linux_x86::${{ contains(github.event.inputs.platforms, 'linux x86') || (github.event.inputs.platforms == '' && (secrets.JDK_SUBMIT_PLATFORMS == '' || contains(secrets.JDK_SUBMIT_PLATFORMS, 'linux x86'))) }}" echo "::set-output name=platform_windows_x64::${{ contains(github.event.inputs.platforms, 'windows x64') || (github.event.inputs.platforms == '' && (secrets.JDK_SUBMIT_PLATFORMS == '' || contains(secrets.JDK_SUBMIT_PLATFORMS, 'windows x64'))) }}" echo "::set-output name=platform_macos_x64::${{ contains(github.event.inputs.platforms, 'macos x64') || (github.event.inputs.platforms == '' && (secrets.JDK_SUBMIT_PLATFORMS == '' || contains(secrets.JDK_SUBMIT_PLATFORMS, 'macos x64'))) }}" if: steps.check_submit.outputs.should_run != 'false' - name: Determine unique bundle identifier + id: check_bundle_id run: echo "::set-output name=bundle_id::${GITHUB_ACTOR}_${GITHUB_SHA:0:8}" if: steps.check_submit.outputs.should_run != 'false' @@ -113,7 +116,7 @@ jobs: flags: --enable-debug artifact: -debug - flavor: build hotspot no-pch - flags: --disable-precompiled-headers + flags: --enable-debug --disable-precompiled-headers build-target: hotspot - flavor: build hotspot zero flags: --enable-debug --disable-precompiled-headers --with-jvm-variants=zero @@ -348,12 +351,326 @@ jobs: if: always() run: echo "logsuffix=`echo ${{ matrix.test }} | sed -e 's!/!_!'g -e 's! !_!'g`" >> $GITHUB_ENV - - name: Persist test logs + - name: Package test results + if: always() + working-directory: build/run-test-prebuilt/test-results/ + run: > + zip -r9 + "$HOME/linux-x64${{ matrix.artifact }}_testresults_${{ env.logsuffix }}.zip" + . + continue-on-error: true + + - name: Package test support + if: always() + working-directory: build/run-test-prebuilt/test-support/ + run: > + zip -r9 + "$HOME/linux-x64${{ matrix.artifact }}_testsupport_${{ env.logsuffix }}.zip" + . + -i *.jtr + -i */hs_err*.log + -i */replay*.log + continue-on-error: true + + - name: Persist test results + if: always() + uses: actions/upload-artifact@v2 + with: + path: ~/linux-x64${{ matrix.artifact }}_testresults_${{ env.logsuffix }}.zip + continue-on-error: true + + - name: Persist test outputs + if: always() + uses: actions/upload-artifact@v2 + with: + path: ~/linux-x64${{ matrix.artifact }}_testsupport_${{ env.logsuffix }}.zip + continue-on-error: true + + linux_x86_build: + name: Linux x86 + runs-on: "ubuntu-latest" + needs: prerequisites + if: needs.prerequisites.outputs.should_run != 'false' && needs.prerequisites.outputs.platform_linux_x86 != 'false' + + strategy: + fail-fast: false + matrix: + flavor: + - build release + - build debug + include: + - flavor: build debug + flags: --enable-debug + artifact: -debug + + # Reduced 32-bit build uses the same boot JDK as 64-bit build + env: + JDK_VERSION: "${{ fromJson(needs.prerequisites.outputs.dependencies).DEFAULT_VERSION_FEATURE }}" + BOOT_JDK_VERSION: "${{ fromJson(needs.prerequisites.outputs.dependencies).BOOT_JDK_VERSION }}" + BOOT_JDK_FILENAME: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_FILENAME }}" + BOOT_JDK_URL: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_URL }}" + BOOT_JDK_SHA256: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_SHA256 }}" + + steps: + - name: Checkout the source + uses: actions/checkout@v2 + with: + path: jdk + + - name: Restore boot JDK from cache + id: bootjdk + uses: actions/cache@v2 + with: + path: ~/bootjdk/${{ env.BOOT_JDK_VERSION }} + key: bootjdk-${{ runner.os }}-${{ env.BOOT_JDK_VERSION }}-${{ env.BOOT_JDK_SHA256 }}-v1 + + - name: Download boot JDK + run: | + mkdir -p "${HOME}/bootjdk/${BOOT_JDK_VERSION}" + wget -O "${HOME}/bootjdk/${BOOT_JDK_FILENAME}" "${BOOT_JDK_URL}" + echo "${BOOT_JDK_SHA256} ${HOME}/bootjdk/${BOOT_JDK_FILENAME}" | sha256sum -c >/dev/null - + tar -xf "${HOME}/bootjdk/${BOOT_JDK_FILENAME}" -C "${HOME}/bootjdk/${BOOT_JDK_VERSION}" + mv "${HOME}/bootjdk/${BOOT_JDK_VERSION}/"*/* "${HOME}/bootjdk/${BOOT_JDK_VERSION}/" + if: steps.bootjdk.outputs.cache-hit != 'true' + + - name: Restore jtreg artifact + id: jtreg_restore + uses: actions/download-artifact@v2 + with: + name: transient_jtreg_${{ needs.prerequisites.outputs.bundle_id }} + path: ~/jtreg/ + continue-on-error: true + + - name: Restore jtreg artifact (retry) + uses: actions/download-artifact@v2 + with: + name: transient_jtreg_${{ needs.prerequisites.outputs.bundle_id }} + path: ~/jtreg/ + if: steps.jtreg_restore.outcome == 'failure' + + - name: Checkout gtest sources + uses: actions/checkout@v2 + with: + repository: "google/googletest" + ref: "release-${{ fromJson(needs.prerequisites.outputs.dependencies).GTEST_VERSION }}" + path: gtest + + # Roll in the multilib environment and its dependencies. + # Some multilib libraries do not have proper inter-dependencies, so we have to + # install their dependencies manually. + - name: Install dependencies + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install gcc-multilib g++-multilib libfreetype6-dev:i386 libxrandr-dev:i386 libxtst-dev:i386 libtiff-dev:i386 libcupsimage2-dev:i386 libcups2-dev:i386 libasound2-dev:i386 + + - name: Configure + run: > + bash configure + --with-conf-name=linux-x86 + --with-target-bits=32 + ${{ matrix.flags }} + --with-version-opt=${GITHUB_ACTOR}-${GITHUB_SHA} + --with-version-build=0 + --with-boot-jdk=${HOME}/bootjdk/${BOOT_JDK_VERSION} + --with-jtreg=${HOME}/jtreg + --with-gtest=${GITHUB_WORKSPACE}/gtest + --with-default-make-target="product-bundles test-bundles" + --with-zlib=system + --enable-jtreg-failure-handler + working-directory: jdk + + - name: Build + run: make CONF_NAME=linux-x86 ${{ matrix.build-target }} + working-directory: jdk + + - name: Persist test bundles + uses: actions/upload-artifact@v2 + with: + name: transient_jdk-linux-x86${{ matrix.artifact }}_${{ needs.prerequisites.outputs.bundle_id }} + path: | + jdk/build/linux-x86/bundles/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x86_bin${{ matrix.artifact }}.tar.gz + jdk/build/linux-x86/bundles/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x86_bin-tests${{ matrix.artifact }}.tar.gz + if: matrix.build-target == false + + linux_x86_test: + name: Linux x86 + runs-on: "ubuntu-latest" + needs: + - prerequisites + - linux_x86_build + + strategy: + fail-fast: false + matrix: + test: + - jdk/tier1 part 1 + - jdk/tier1 part 2 + - jdk/tier1 part 3 + - langtools/tier1 + - hs/tier1 common + - hs/tier1 compiler + - hs/tier1 gc + - hs/tier1 runtime + - hs/tier1 serviceability + include: + - test: jdk/tier1 part 1 + suites: test/jdk/:tier1_part1 + - test: jdk/tier1 part 2 + suites: test/jdk/:tier1_part2 + - test: jdk/tier1 part 3 + suites: test/jdk/:tier1_part3 + - test: langtools/tier1 + suites: test/langtools/:tier1 + - test: hs/tier1 common + suites: test/hotspot/jtreg/:tier1_common + artifact: -debug + - test: hs/tier1 compiler + suites: test/hotspot/jtreg/:tier1_compiler + artifact: -debug + - test: hs/tier1 gc + suites: test/hotspot/jtreg/:tier1_gc + artifact: -debug + - test: hs/tier1 runtime + suites: test/hotspot/jtreg/:tier1_runtime + artifact: -debug + - test: hs/tier1 serviceability + suites: test/hotspot/jtreg/:tier1_serviceability + artifact: -debug + + # Reduced 32-bit build uses the same boot JDK as 64-bit build + env: + JDK_VERSION: "${{ fromJson(needs.prerequisites.outputs.dependencies).DEFAULT_VERSION_FEATURE }}" + BOOT_JDK_VERSION: "${{ fromJson(needs.prerequisites.outputs.dependencies).BOOT_JDK_VERSION }}" + BOOT_JDK_FILENAME: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_FILENAME }}" + BOOT_JDK_URL: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_URL }}" + BOOT_JDK_SHA256: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_SHA256 }}" + + steps: + - name: Checkout the source + uses: actions/checkout@v2 + + - name: Restore boot JDK from cache + id: bootjdk + uses: actions/cache@v2 + with: + path: ~/bootjdk/${{ env.BOOT_JDK_VERSION }} + key: bootjdk-${{ runner.os }}-${{ env.BOOT_JDK_VERSION }}-${{ env.BOOT_JDK_SHA256 }}-v1 + + - name: Download boot JDK + run: | + mkdir -p "${HOME}/bootjdk/${BOOT_JDK_VERSION}" + wget -O "${HOME}/bootjdk/${BOOT_JDK_FILENAME}" "${BOOT_JDK_URL}" + echo "${BOOT_JDK_SHA256} ${HOME}/bootjdk/${BOOT_JDK_FILENAME}" | sha256sum -c >/dev/null - + tar -xf "${HOME}/bootjdk/${BOOT_JDK_FILENAME}" -C "${HOME}/bootjdk/${BOOT_JDK_VERSION}" + mv "${HOME}/bootjdk/${BOOT_JDK_VERSION}/"*/* "${HOME}/bootjdk/${BOOT_JDK_VERSION}/" + if: steps.bootjdk.outputs.cache-hit != 'true' + + - name: Restore jtreg artifact + id: jtreg_restore + uses: actions/download-artifact@v2 + with: + name: transient_jtreg_${{ needs.prerequisites.outputs.bundle_id }} + path: ~/jtreg/ + continue-on-error: true + + - name: Restore jtreg artifact (retry) + uses: actions/download-artifact@v2 + with: + name: transient_jtreg_${{ needs.prerequisites.outputs.bundle_id }} + path: ~/jtreg/ + if: steps.jtreg_restore.outcome == 'failure' + + - name: Restore build artifacts + id: build_restore + uses: actions/download-artifact@v2 + with: + name: transient_jdk-linux-x86${{ matrix.artifact }}_${{ needs.prerequisites.outputs.bundle_id }} + path: ~/jdk-linux-x86${{ matrix.artifact }} + continue-on-error: true + + - name: Restore build artifacts (retry) + uses: actions/download-artifact@v2 + with: + name: transient_jdk-linux-x86${{ matrix.artifact }}_${{ needs.prerequisites.outputs.bundle_id }} + path: ~/jdk-linux-x86${{ matrix.artifact }} + if: steps.build_restore.outcome == 'failure' + + - name: Unpack jdk + run: | + mkdir -p "${HOME}/jdk-linux-x86${{ matrix.artifact }}/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x86_bin${{ matrix.artifact }}" + tar -xf "${HOME}/jdk-linux-x86${{ matrix.artifact }}/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x86_bin${{ matrix.artifact }}.tar.gz" -C "${HOME}/jdk-linux-x86${{ matrix.artifact }}/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x86_bin${{ matrix.artifact }}" + + - name: Unpack tests + run: | + mkdir -p "${HOME}/jdk-linux-x86${{ matrix.artifact }}/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x86_bin-tests${{ matrix.artifact }}" + tar -xf "${HOME}/jdk-linux-x86${{ matrix.artifact }}/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x86_bin-tests${{ matrix.artifact }}.tar.gz" -C "${HOME}/jdk-linux-x86${{ matrix.artifact }}/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x86_bin-tests${{ matrix.artifact }}" + + - name: Find root of jdk image dir + run: | + imageroot=`find ${HOME}/jdk-linux-x86${{ matrix.artifact }}/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x86_bin${{ matrix.artifact }} -name release -type f` + echo "imageroot=`dirname ${imageroot}`" >> $GITHUB_ENV + + - name: Run tests + run: > + JDK_IMAGE_DIR=${{ env.imageroot }} + TEST_IMAGE_DIR=${HOME}/jdk-linux-x86${{ matrix.artifact }}/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x86_bin-tests${{ matrix.artifact }} + BOOT_JDK=${HOME}/bootjdk/${BOOT_JDK_VERSION} + JT_HOME=${HOME}/jtreg + make test-prebuilt + CONF_NAME=run-test-prebuilt + LOG_CMDLINES=true + JTREG_VERBOSE=fail,error,time + TEST="${{ matrix.suites }}" + TEST_OPTS_JAVA_OPTIONS= + JTREG_KEYWORDS="!headful" + JTREG="JAVA_OPTIONS=-XX:-CreateCoredumpOnCrash" + + - name: Check that all tests executed successfully + if: always() + run: > + if ! grep --include=test-summary.txt -lqr build/*/test-results -e "TEST SUCCESS" ; then + cat build/*/test-results/*/text/newfailures.txt ; + exit 1 ; + fi + + - name: Create suitable test log artifact name + if: always() + run: echo "logsuffix=`echo ${{ matrix.test }} | sed -e 's!/!_!'g -e 's! !_!'g`" >> $GITHUB_ENV + + - name: Package test results + if: always() + working-directory: build/run-test-prebuilt/test-results/ + run: > + zip -r9 + "$HOME/linux-x86${{ matrix.artifact }}_testresults_${{ env.logsuffix }}.zip" + . + continue-on-error: true + + - name: Package test support + if: always() + working-directory: build/run-test-prebuilt/test-support/ + run: > + zip -r9 + "$HOME/linux-x86${{ matrix.artifact }}_testsupport_${{ env.logsuffix }}.zip" + . + -i *.jtr + -i */hs_err*.log + -i */replay*.log + continue-on-error: true + + - name: Persist test results if: always() uses: actions/upload-artifact@v2 with: - name: linux-x64${{ matrix.artifact }}_testlogs_${{ env.logsuffix }} - path: build/*/test-results + path: ~/linux-x86${{ matrix.artifact }}_testresults_${{ env.logsuffix }}.zip + continue-on-error: true + + - name: Persist test outputs + if: always() + uses: actions/upload-artifact@v2 + with: + path: ~/linux-x86${{ matrix.artifact }}_testsupport_${{ env.logsuffix }}.zip continue-on-error: true windows_x64_build: @@ -635,12 +952,41 @@ jobs: if: always() run: echo ("logsuffix=" + ("${{ matrix.test }}" -replace "/", "_" -replace " ", "_")) | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 - - name: Persist test logs + - name: Package test results + if: always() + working-directory: build/run-test-prebuilt/test-results/ + run: > + $env:Path = "$HOME\cygwin\cygwin64\bin;$env:Path" ; + zip -r9 + "$HOME/windows-x64${{ matrix.artifact }}_testresults_${{ env.logsuffix }}.zip" + . + continue-on-error: true + + - name: Package test support + if: always() + working-directory: build/run-test-prebuilt/test-support/ + run: > + $env:Path = "$HOME\cygwin\cygwin64\bin;$env:Path" ; + zip -r9 + "$HOME/windows-x64${{ matrix.artifact }}_testsupport_${{ env.logsuffix }}.zip" + . + -i *.jtr + -i */hs_err*.log + -i */replay*.log + continue-on-error: true + + - name: Persist test results if: always() uses: actions/upload-artifact@v2 with: - name: windows-x64${{ matrix.artifact }}_testlogs_${{ env.logsuffix }} - path: build/*/test-results + path: ~/windows-x64${{ matrix.artifact }}_testresults_${{ env.logsuffix }}.zip + continue-on-error: true + + - name: Persist test outputs + if: always() + uses: actions/upload-artifact@v2 + with: + path: ~/windows-x64${{ matrix.artifact }}_testsupport_${{ env.logsuffix }}.zip continue-on-error: true macos_x64_build: @@ -890,12 +1236,39 @@ jobs: if: always() run: echo "logsuffix=`echo ${{ matrix.test }} | sed -e 's!/!_!'g -e 's! !_!'g`" >> $GITHUB_ENV - - name: Persist test logs + - name: Package test results + if: always() + working-directory: build/run-test-prebuilt/test-results/ + run: > + zip -r9 + "$HOME/macos-x64${{ matrix.artifact }}_testresults_${{ env.logsuffix }}.zip" + . + continue-on-error: true + + - name: Package test support + if: always() + working-directory: build/run-test-prebuilt/test-support/ + run: > + zip -r9 + "$HOME/macos-x64${{ matrix.artifact }}_testsupport_${{ env.logsuffix }}.zip" + . + -i *.jtr + -i */hs_err*.log + -i */replay*.log + continue-on-error: true + + - name: Persist test results + if: always() + uses: actions/upload-artifact@v2 + with: + path: ~/macos-x64${{ matrix.artifact }}_testresults_${{ env.logsuffix }}.zip + continue-on-error: true + + - name: Persist test outputs if: always() uses: actions/upload-artifact@v2 with: - name: macos-x64${{ matrix.artifact }}_testlogs_${{ env.logsuffix }} - path: build/*/test-results + path: ~/macos-x64${{ matrix.artifact }}_testsupport_${{ env.logsuffix }}.zip continue-on-error: true artifacts: @@ -904,7 +1277,9 @@ jobs: if: always() continue-on-error: true needs: + - prerequisites - linux_x64_test + - linux_x86_test - windows_x64_test - macos_x64_test diff --git a/.gitignore b/.gitignore index c34d27c8470..cf21c8919cd 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /dist/ /.idea/ /.vscode/ +/nbproject/ nbproject/private/ /webrev /.src-rev @@ -14,3 +15,4 @@ test/nashorn/lib NashornProfile.txt **/JTreport/** **/JTwork/** +/src/utils/LogCompilation/target/ diff --git a/.hgignore b/.hgignore deleted file mode 100644 index 312ce62a641..00000000000 --- a/.hgignore +++ /dev/null @@ -1,18 +0,0 @@ -^build/ -^dist/ -^.idea/ -^.vscode/ -nbproject/private/ -^webrev -^.src-rev$ -^.jib/ -(^|/)\.DS_Store -(^|/)\.metadata/ -(^|/)\.recommenders/ -test/nashorn/script/external -test/nashorn/lib -NashornProfile.txt -(^|/)JTreport/ -(^|/)JTwork/ -(^|/)\.git/ -^src/utils/hsdis/build/ \ No newline at end of file diff --git a/doc/building.html b/doc/building.html index 5f615f9d4ef..318a24aa840 100644 --- a/doc/building.html +++ b/doc/building.html @@ -78,6 +78,7 @@

Building the JDK

  • Native Libraries
  • Creating And Using Sysroots With qemu-deboostrap
  • Building for ARM/aarch64
  • +
  • Building for musl
  • Verifying the Build
  • Build Performance

    The build does not create new files in that chroot, so it can be reused for multiple builds without additional cleanup.

    Architectures that are known to successfully cross-compile like this are:

    @@ -688,6 +706,15 @@

    Creating And Using Sys

    Additional architectures might be supported by Debian/Ubuntu Ports.

    Building for ARM/aarch64

    A common cross-compilation target is the ARM CPU. When building for ARM, it is useful to set the ABI profile. A number of pre-defined ABI profiles are available using --with-abi-profile: arm-vfp-sflt, arm-vfp-hflt, arm-sflt, armv5-vfp-sflt, armv6-vfp-hflt. Note that soft-float ABIs are no longer properly supported by the JDK.

    +

    Building for musl

    +

    Just like it's possible to cross-compile for a different CPU, it's possible to cross-compile for musl libc on a glibc-based build system. A devkit suitable for most target CPU architectures can be obtained from musl.cc. After installing the required packages in the sysroot, configure the build with --openjdk-target:

    +
    sh ./configure --with-jvm-variants=server \
    +--with-boot-jdk=$BOOT_JDK \
    +--with-build-jdk=$BUILD_JDK \
    +--openjdk-target=x86_64-unknown-linux-musl \
    +--with-devkit=$DEVKIT \
    +--with-sysroot=$SYSROOT
    +

    and run make normally.

    Verifying the Build

    The build will end up in a directory named like build/linux-arm-normal-server-release.

    Inside this build output directory, the images/jdk will contain the newly built JDK, for your target system.

    diff --git a/doc/building.md b/doc/building.md index 47fa445998d..e0ac5c7b6c7 100644 --- a/doc/building.md +++ b/doc/building.md @@ -273,6 +273,13 @@ For rpm-based distributions (Fedora, Red Hat, etc), try this: sudo yum groupinstall "Development Tools" ``` +For Alpine Linux, aside from basic tooling, install the GNU versions of some +programs: + +``` +sudo apk add build-base bash grep zip +``` + ### AIX Please consult the AIX section of the [Supported Build Platforms]( @@ -431,6 +438,7 @@ rather than bundling the JDK's own copy. libfreetype6-dev`. * To install on an rpm-based Linux, try running `sudo yum install freetype-devel`. + * To install on Alpine Linux, try running `sudo apk add freetype-dev`. Use `--with-freetype-include=` and `--with-freetype-lib=` if `configure` does not automatically locate the platform FreeType files. @@ -445,6 +453,7 @@ your operating system. libcups2-dev`. * To install on an rpm-based Linux, try running `sudo yum install cups-devel`. + * To install on Alpine Linux, try running `sudo apk add cups-dev`. Use `--with-cups=` if `configure` does not properly locate your CUPS files. @@ -458,6 +467,8 @@ Linux. libx11-dev libxext-dev libxrender-dev libxrandr-dev libxtst-dev libxt-dev`. * To install on an rpm-based Linux, try running `sudo yum install libXtst-devel libXt-devel libXrender-devel libXrandr-devel libXi-devel`. + * To install on Alpine Linux, try running `sudo apk add libx11-dev + libxext-dev libxrender-dev libxrandr-dev libxtst-dev libxt-dev`. Use `--with-x=` if `configure` does not properly locate your X11 files. @@ -470,6 +481,7 @@ required on Linux. At least version 0.9.1 of ALSA is required. libasound2-dev`. * To install on an rpm-based Linux, try running `sudo yum install alsa-lib-devel`. + * To install on Alpine Linux, try running `sudo apk add alsa-lib-dev`. Use `--with-alsa=` if `configure` does not properly locate your ALSA files. @@ -484,6 +496,7 @@ Hotspot. libffi-dev`. * To install on an rpm-based Linux, try running `sudo yum install libffi-devel`. + * To install on Alpine Linux, try running `sudo apk add libffi-dev`. Use `--with-libffi=` if `configure` does not properly locate your libffi files. @@ -499,6 +512,7 @@ platforms. At least version 2.69 is required. autoconf`. * To install on an rpm-based Linux, try running `sudo yum install autoconf`. + * To install on Alpine Linux, try running `sudo apk add autoconf`. * To install on macOS, try running `brew install autoconf`. * To install on Windows, try running `/setup-x86_64 -q -P autoconf`. @@ -1072,23 +1086,39 @@ for foreign architectures with native compilation speed. For example, cross-compiling to AArch64 from x86_64 could be done like this: * Install cross-compiler on the *build* system: -``` -apt install g++-aarch64-linux-gnu gcc-aarch64-linux-gnu -``` + ``` + apt install g++-aarch64-linux-gnu gcc-aarch64-linux-gnu + ``` * Create chroot on the *build* system, configuring it for *target* system: -``` -sudo qemu-debootstrap --arch=arm64 --verbose \ - --include=fakeroot,build-essential,libx11-dev,libxext-dev,libxrender-dev,libxrandr-dev,libxtst-dev,libxt-dev,libcups2-dev,libfontconfig1-dev,libasound2-dev,libfreetype6-dev,libpng12-dev \ - --resolve-deps jessie /chroots/arm64 http://httpredir.debian.org/debian/ -``` + ``` + sudo qemu-debootstrap \ + --arch=arm64 \ + --verbose \ + --include=fakeroot,symlinks,build-essential,libx11-dev,libxext-dev,libxrender-dev,libxrandr-dev,libxtst-dev,libxt-dev,libcups2-dev,libfontconfig1-dev,libasound2-dev,libfreetype6-dev,libpng-dev \ + --resolve-deps \ + buster \ + ~/sysroot-arm64 \ + http://httpredir.debian.org/debian/ + ``` + + * Make sure the symlinks inside the newly created chroot point to proper locations: + ``` + sudo chroot ~/sysroot-arm64 symlinks -cr . + ``` * Configure and build with newly created chroot as sysroot/toolchain-path: -``` -CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ sh ./configure --openjdk-target=aarch64-linux-gnu --with-sysroot=/chroots/arm64/ --with-toolchain-path=/chroots/arm64/ -make images -ls build/linux-aarch64-normal-server-release/ -``` + ``` + CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ sh ./configure \ + --openjdk-target=aarch64-linux-gnu \ + --with-sysroot=~/sysroot-arm64 \ + --with-toolchain-path=~/sysroot-arm64 \ + --with-freetype-lib=~/sysroot-arm64/usr/lib/aarch64-linux-gnu/ \ + --with-freetype-include=~/sysroot-arm64/usr/include/freetype2/ \ + --x-libraries=~/sysroot-arm64/usr/lib/aarch64-linux-gnu/ + make images + ls build/linux-aarch64-server-release/ + ``` The build does not create new files in that chroot, so it can be reused for multiple builds without additional cleanup. @@ -1113,6 +1143,25 @@ available using `--with-abi-profile`: arm-vfp-sflt, arm-vfp-hflt, arm-sflt, armv5-vfp-sflt, armv6-vfp-hflt. Note that soft-float ABIs are no longer properly supported by the JDK. +### Building for musl + +Just like it's possible to cross-compile for a different CPU, it's possible to +cross-compile for musl libc on a glibc-based *build* system. +A devkit suitable for most target CPU architectures can be obtained from +[musl.cc](https://musl.cc). After installing the required packages in the +sysroot, configure the build with `--openjdk-target`: + +``` +sh ./configure --with-jvm-variants=server \ +--with-boot-jdk=$BOOT_JDK \ +--with-build-jdk=$BUILD_JDK \ +--openjdk-target=x86_64-unknown-linux-musl \ +--with-devkit=$DEVKIT \ +--with-sysroot=$SYSROOT +``` + +and run `make` normally. + ### Verifying the Build The build will end up in a directory named like diff --git a/make/Bundles.gmk b/make/Bundles.gmk index b7c8ddbfbe7..b52b5720772 100644 --- a/make/Bundles.gmk +++ b/make/Bundles.gmk @@ -410,17 +410,43 @@ endif ################################################################################ -ifneq ($(filter docs-bundles, $(MAKECMDGOALS)), ) - DOCS_BUNDLE_FILES := $(call FindFiles, $(DOCS_IMAGE_DIR)) +ifneq ($(filter docs-jdk-bundles, $(MAKECMDGOALS)), ) + DOCS_JDK_BUNDLE_FILES := $(call FindFiles, $(DOCS_JDK_IMAGE_DIR)) - $(eval $(call SetupBundleFile, BUILD_DOCS_BUNDLE, \ - BUNDLE_NAME := $(DOCS_BUNDLE_NAME), \ - FILES := $(DOCS_BUNDLE_FILES), \ - BASE_DIRS := $(DOCS_IMAGE_DIR), \ + $(eval $(call SetupBundleFile, BUILD_DOCS_JDK_BUNDLE, \ + BUNDLE_NAME := $(DOCS_JDK_BUNDLE_NAME), \ + FILES := $(DOCS_JDK_BUNDLE_FILES), \ + BASE_DIRS := $(DOCS_JDK_IMAGE_DIR), \ SUBDIR := docs, \ )) - DOCS_TARGETS += $(BUILD_DOCS_BUNDLE) + DOCS_JDK_TARGETS += $(BUILD_DOCS_JDK_BUNDLE) +endif + +ifneq ($(filter docs-javase-bundles, $(MAKECMDGOALS)), ) + DOCS_JAVASE_BUNDLE_FILES := $(call FindFiles, $(DOCS_JAVASE_IMAGE_DIR)) + + $(eval $(call SetupBundleFile, BUILD_DOCS_JAVASE_BUNDLE, \ + BUNDLE_NAME := $(DOCS_JAVASE_BUNDLE_NAME), \ + FILES := $(DOCS_JAVASE_BUNDLE_FILES), \ + BASE_DIRS := $(DOCS_JAVASE_IMAGE_DIR), \ + SUBDIR := docs-javase, \ + )) + + DOCS_JAVASE_TARGETS += $(BUILD_DOCS_JAVASE_BUNDLE) +endif + +ifneq ($(filter docs-reference-bundles, $(MAKECMDGOALS)), ) + DOCS_REFERENCE_BUNDLE_FILES := $(call FindFiles, $(DOCS_REFERENCE_IMAGE_DIR)) + + $(eval $(call SetupBundleFile, BUILD_DOCS_REFERENCE_BUNDLE, \ + BUNDLE_NAME := $(DOCS_REFERENCE_BUNDLE_NAME), \ + FILES := $(DOCS_REFERENCE_BUNDLE_FILES), \ + BASE_DIRS := $(DOCS_REFERENCE_IMAGE_DIR), \ + SUBDIR := docs-reference, \ + )) + + DOCS_REFERENCE_TARGETS += $(BUILD_DOCS_REFERENCE_BUNDLE) endif ################################################################################ @@ -469,9 +495,12 @@ $(eval $(call IncludeCustomExtension, Bundles.gmk)) product-bundles: $(PRODUCT_TARGETS) legacy-bundles: $(LEGACY_TARGETS) test-bundles: $(TEST_TARGETS) -docs-bundles: $(DOCS_TARGETS) +docs-jdk-bundles: $(DOCS_JDK_TARGETS) +docs-javase-bundles: $(DOCS_JAVASE_TARGETS) +docs-reference-bundles: $(DOCS_REFERENCE_TARGETS) static-libs-bundles: $(STATIC_LIBS_TARGETS) jcov-bundles: $(JCOV_TARGETS) -.PHONY: all default product-bundles test-bundles docs-bundles \ +.PHONY: all default product-bundles test-bundles \ + docs-jdk-bundles docs-javase-bundles docs-reference-bundles \ static-libs-bundles jcov-bundles diff --git a/make/CompileJavaModules.gmk b/make/CompileJavaModules.gmk index c4d25c90122..e8997e0da83 100644 --- a/make/CompileJavaModules.gmk +++ b/make/CompileJavaModules.gmk @@ -184,10 +184,6 @@ ifeq ($(call isTargetOs, windows), true) java.desktop_EXCLUDES += com/sun/java/swing/plaf/gtk endif -ifdef BUILD_HEADLESS_ONLY - java.desktop_EXCLUDES += sun/applet -endif - ifeq ($(call isTargetOs, windows macosx), false) java.desktop_EXCLUDE_FILES += sun/awt/AWTCharset.java endif @@ -389,11 +385,11 @@ endif ################################################################################ -jdk.incubator.jpackage_COPY += .gif .png .txt .spec .script .prerm .preinst \ +jdk.jpackage_COPY += .gif .png .txt .spec .script .prerm .preinst \ .postrm .postinst .list .sh .desktop .copyright .control .plist .template \ .icns .scpt .wxs .wxl .wxi .ico .bmp .tiff -jdk.incubator.jpackage_CLEAN += .properties +jdk.jpackage_CLEAN += .properties ################################################################################ @@ -546,6 +542,10 @@ jdk.jfr_DISABLED_WARNINGS += exports jdk.jfr_COPY := .xsd .xml .dtd jdk.jfr_JAVAC_FLAGS := -XDstringConcat=inline +################################################################################ + +jdk.incubator.vector_DOCLINT += -Xdoclint:all/protected + ################################################################################ # If this is an imported module that has prebuilt classes, only compile # module-info.java. diff --git a/make/CompileModuleTools.gmk b/make/CompileModuleTools.gmk index c6322e5b36e..18cd42f0612 100644 --- a/make/CompileModuleTools.gmk +++ b/make/CompileModuleTools.gmk @@ -33,8 +33,20 @@ include JavaCompilation.gmk TOOLS_CLASSES_DIR := $(BUILDTOOLS_OUTPUTDIR)/tools_jigsaw_classes +# When using an external BUILDJDK, make it possible to shortcut building of +# these tools using the BUILD_JAVAC instead of having to build the complete +# exploded image first. +ifeq ($(EXTERNAL_BUILDJDK), true) + COMPILER := buildjdk + TARGET_RELEASE := $(TARGET_RELEASE_NEWJDK) +else + COMPILER := interim + TARGET_RELEASE := $(TARGET_RELEASE_NEWJDK_UPGRADED) +endif + $(eval $(call SetupJavaCompilation, BUILD_JIGSAW_TOOLS, \ - TARGET_RELEASE := $(TARGET_RELEASE_NEWJDK_UPGRADED), \ + TARGET_RELEASE := $(TARGET_RELEASE), \ + COMPILER := $(COMPILER), \ SRC := $(TOPDIR)/make/jdk/src/classes, \ INCLUDES := build/tools/deps \ build/tools/docs \ diff --git a/make/CompileToolsJdk.gmk b/make/CompileToolsJdk.gmk index a671f934998..2f09476aa67 100644 --- a/make/CompileToolsJdk.gmk +++ b/make/CompileToolsJdk.gmk @@ -56,7 +56,8 @@ $(eval $(call SetupJavaCompilation, BUILD_TOOLS_JDK, \ DISABLED_WARNINGS := options, \ JAVAC_FLAGS := \ --add-exports java.desktop/sun.awt=ALL-UNNAMED \ - --add-exports java.base/sun.text=ALL-UNNAMED, \ + --add-exports java.base/sun.text=ALL-UNNAMED \ + --add-exports java.base/sun.security.util=ALL-UNNAMED, \ )) TARGETS += $(BUILD_TOOLS_JDK) diff --git a/make/Docs.gmk b/make/Docs.gmk index 19e962b79af..1f7a0caf819 100644 --- a/make/Docs.gmk +++ b/make/Docs.gmk @@ -458,7 +458,7 @@ $(eval $(call SetupApiDocsGeneration, JAVASE_API, \ MODULES := $(JAVASE_MODULES), \ SHORT_NAME := $(JAVASE_SHORT_NAME), \ LONG_NAME := $(JAVASE_LONG_NAME), \ - TARGET_DIR := $(IMAGES_OUTPUTDIR)/javase-docs/api, \ + TARGET_DIR := $(DOCS_JAVASE_IMAGE_DIR)/api, \ )) # Targets generated are returned in JAVASE_API_JAVADOC_TARGETS and @@ -476,7 +476,7 @@ $(eval $(call SetupApiDocsGeneration, REFERENCE_API, \ MODULES := $(JAVASE_MODULES), \ SHORT_NAME := $(JAVASE_SHORT_NAME), \ LONG_NAME := $(JAVASE_LONG_NAME), \ - TARGET_DIR := $(IMAGES_OUTPUTDIR)/reference-docs/api, \ + TARGET_DIR := $(DOCS_REFERENCE_IMAGE_DIR)/api, \ JAVADOC_CMD := $(JAVADOC), \ OPTIONS := $(REFERENCE_OPTIONS), \ TAGS := $(REFERENCE_TAGS), \ diff --git a/make/Main.gmk b/make/Main.gmk index 493b795d35a..cdb4be67c56 100644 --- a/make/Main.gmk +++ b/make/Main.gmk @@ -90,7 +90,6 @@ $(eval $(call SetupTarget, buildtools-jdk, \ $(eval $(call SetupTarget, buildtools-modules, \ MAKEFILE := CompileModuleTools, \ - DEPS := exploded-image-base, \ )) $(eval $(call SetupTarget, buildtools-hotspot, \ @@ -339,7 +338,7 @@ $(eval $(call SetupTarget, test-image-demos-jdk, \ $(eval $(call SetupTarget, generate-summary, \ MAKEFILE := GenerateModuleSummary, \ - DEPS := jmods buildtools-modules, \ + DEPS := jmods buildtools-modules runnable-buildjdk, \ )) ################################################################################ @@ -469,7 +468,7 @@ $(eval $(call SetupTarget, docs-jdk-api-javadoc, \ $(eval $(call SetupTarget, docs-jdk-api-modulegraph, \ MAKEFILE := Docs, \ TARGET := docs-jdk-api-modulegraph, \ - DEPS := exploded-image buildtools-modules, \ + DEPS := buildtools-modules runnable-buildjdk, \ )) $(eval $(call SetupTarget, docs-javase-api-javadoc, \ @@ -480,7 +479,7 @@ $(eval $(call SetupTarget, docs-javase-api-javadoc, \ $(eval $(call SetupTarget, docs-javase-api-modulegraph, \ MAKEFILE := Docs, \ TARGET := docs-javase-api-modulegraph, \ - DEPS := exploded-image buildtools-modules, \ + DEPS := buildtools-modules runnable-buildjdk, \ )) $(eval $(call SetupTarget, docs-reference-api-javadoc, \ @@ -491,7 +490,7 @@ $(eval $(call SetupTarget, docs-reference-api-javadoc, \ $(eval $(call SetupTarget, docs-reference-api-modulegraph, \ MAKEFILE := Docs, \ TARGET := docs-reference-api-modulegraph, \ - DEPS := exploded-image buildtools-modules, \ + DEPS := buildtools-modules runnable-buildjdk, \ )) # The gensrc steps for jdk.jdi create html spec files. @@ -749,12 +748,24 @@ $(eval $(call SetupTarget, test-bundles, \ DEPS := test-image, \ )) -$(eval $(call SetupTarget, docs-bundles, \ +$(eval $(call SetupTarget, docs-jdk-bundles, \ MAKEFILE := Bundles, \ - TARGET := docs-bundles, \ + TARGET := docs-jdk-bundles, \ DEPS := docs-image, \ )) +$(eval $(call SetupTarget, docs-javase-bundles, \ + MAKEFILE := Bundles, \ + TARGET := docs-javase-bundles, \ + DEPS := docs-javase-image, \ +)) + +$(eval $(call SetupTarget, docs-reference-bundles, \ + MAKEFILE := Bundles, \ + TARGET := docs-reference-bundles, \ + DEPS := docs-reference-image, \ +)) + $(eval $(call SetupTarget, static-libs-bundles, \ MAKEFILE := Bundles, \ TARGET := static-libs-bundles, \ @@ -945,10 +956,13 @@ else $(JMOD_TARGETS) $(INTERIM_JMOD_TARGETS): java.base-libs java.base-copy \ java.base-gendata jdk.jlink-launchers java endif - else - # The normal non cross compilation case uses needs to wait for the full + else ifeq ($(EXTERNAL_BUILDJDK), false) + # The normal non cross compilation usecase needs to wait for the full # exploded-image to avoid a race with the optimize target. $(JMOD_TARGETS) $(INTERIM_JMOD_TARGETS): exploded-image + # The buildtools-modules are used for the exploded-image-optimize target, + # but can be built either using the exploded-image or an external BUILDJDK. + buildtools-modules: exploded-image-base endif # All modules include the main license files from java.base. @@ -1069,6 +1083,18 @@ ifneq ($(COMPILE_TYPE), cross) exploded-image: exploded-image-optimize endif +# The runnable-buildjdk target guarantees that the buildjdk is done +# building and ready to be used. The exact set of dependencies it needs +# depends on what kind of buildjdk is used for the current configuration. +runnable-buildjdk: +ifeq ($(CREATE_BUILDJDK), true) + ifneq ($(CREATING_BUILDJDK), true) + runnable-buildjdk: create-buildjdk + endif +else ifeq ($(EXTERNAL_BUILDJDK), false) + runnable-buildjdk: exploded-image +endif + create-buildjdk: create-buildjdk-interim-image docs-jdk-api: docs-jdk-api-javadoc @@ -1122,8 +1148,16 @@ ifeq ($(call isTargetOs, macosx), true) legacy-images: mac-legacy-jre-bundle endif -# This target builds the documentation image -docs-image: docs-jdk +# These targets build the various documentation images +docs-jdk-image: docs-jdk +docs-javase-image: docs-javase +docs-reference-image: docs-reference +# The docs-jdk-image is what most users expect to be built +docs-image: docs-jdk-image +all-docs-images: docs-jdk-image docs-javase-image docs-reference-image + +docs-bundles: docs-jdk-bundles +all-docs-bundles: docs-jdk-bundles docs-javase-bundles docs-reference-bundles # This target builds the test image test-image: prepare-test-image test-image-jdk-jtreg-native \ @@ -1156,7 +1190,7 @@ endif ################################################################################ # all-images builds all our deliverables as images. -all-images: product-images test-image docs-image +all-images: product-images test-image all-docs-images # all-bundles packages all our deliverables as tar.gz bundles. all-bundles: product-bundles test-bundles docs-bundles static-libs-bundles @@ -1164,10 +1198,11 @@ all-bundles: product-bundles test-bundles docs-bundles static-libs-bundles ALL_TARGETS += buildtools hotspot hotspot-libs hotspot-gensrc gensrc gendata \ copy java libs static-libs launchers jmods \ jdk.jdwp.agent-gensrc $(ALL_MODULES) demos \ - exploded-image-base exploded-image \ + exploded-image-base exploded-image runnable-buildjdk \ create-buildjdk docs-jdk-api docs-javase-api docs-reference-api docs-jdk \ docs-javase docs-reference docs-javadoc mac-bundles product-images legacy-images \ - docs-image test-image all-images \ + docs-image docs-javase-image docs-reference-image all-docs-images \ + docs-bundles all-docs-bundles test-image all-images \ all-bundles ################################################################################ diff --git a/make/ReleaseFile.gmk b/make/ReleaseFile.gmk index 14ebc9c32ae..0424e2fb623 100644 --- a/make/ReleaseFile.gmk +++ b/make/ReleaseFile.gmk @@ -53,6 +53,7 @@ define create-info-file $(call info-file-item, "JAVA_VERSION_DATE", "$(VERSION_DATE)") $(call info-file-item, "OS_NAME", "$(RELEASE_FILE_OS_NAME)") $(call info-file-item, "OS_ARCH", "$(RELEASE_FILE_OS_ARCH)") + $(call info-file-item, "LIBC", "$(RELEASE_FILE_LIBC)") endef # Param 1 - The file containing the MODULES list diff --git a/make/ToolsJdk.gmk b/make/ToolsJdk.gmk index 296411559a9..45a0cc8c64e 100644 --- a/make/ToolsJdk.gmk +++ b/make/ToolsJdk.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -68,6 +68,7 @@ TOOL_TZDB = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ build.tools.tzdb.TzdbZoneRulesCompiler TOOL_BLACKLISTED_CERTS = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ + --add-exports java.base/sun.security.util=ALL-UNNAMED \ build.tools.blacklistedcertsconverter.BlacklistedCertsConverter TOOL_MAKEJAVASECURITY = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ diff --git a/make/autoconf/build-aux/config.guess b/make/autoconf/build-aux/config.guess index b650b5109d0..14f21a25e8f 100644 --- a/make/autoconf/build-aux/config.guess +++ b/make/autoconf/build-aux/config.guess @@ -30,6 +30,17 @@ DIR=`dirname $0` OUT=`. $DIR/autoconf-config.guess` +# Detect C library. +# Use '-gnu' suffix on systems that use glibc. +# Use '-musl' suffix on systems that use the musl libc. +echo $OUT | grep -- -linux- > /dev/null 2> /dev/null +if test $? = 0; then + libc_vendor=`ldd --version 2>&1 | sed -n '1s/.*\(musl\).*/\1/p'` + if [ x"${libc_vendor}" = x"musl" ]; then + OUT=`echo $OUT | sed 's/-gnu/-musl/'` + fi +fi + # Test and fix cygwin on x86_64 echo $OUT | grep 86-pc-cygwin > /dev/null 2> /dev/null if test $? != 0; then diff --git a/make/autoconf/build-aux/config.sub b/make/autoconf/build-aux/config.sub index a36e6690728..d0dd001abdf 100644 --- a/make/autoconf/build-aux/config.sub +++ b/make/autoconf/build-aux/config.sub @@ -29,6 +29,11 @@ DIR=`dirname $0` +if echo $* | grep linux-musl >/dev/null ; then + echo $* + exit +fi + # Allow wsl if echo $* | grep x86_64-pc-wsl >/dev/null ; then echo $* diff --git a/make/autoconf/buildjdk-spec.gmk.in b/make/autoconf/buildjdk-spec.gmk.in index 7134e34bcee..524f35f417c 100644 --- a/make/autoconf/buildjdk-spec.gmk.in +++ b/make/autoconf/buildjdk-spec.gmk.in @@ -54,11 +54,13 @@ IMAGES_OUTPUTDIR := $(patsubst $(OUTPUTDIR)%,$(BUILDJDK_OUTPUTDIR)%,$(IMAGES_OUT OPENJDK_BUILD_CPU_LEGACY := @OPENJDK_BUILD_CPU_LEGACY@ OPENJDK_BUILD_CPU_LEGACY_LIB := @OPENJDK_BUILD_CPU_LEGACY_LIB@ +OPENJDK_BUILD_LIBC := @OPENJDK_BUILD_LIBC@ OPENJDK_TARGET_CPU := @OPENJDK_BUILD_CPU@ OPENJDK_TARGET_CPU_ARCH := @OPENJDK_BUILD_CPU_ARCH@ OPENJDK_TARGET_CPU_BITS := @OPENJDK_BUILD_CPU_BITS@ OPENJDK_TARGET_CPU_ENDIAN := @OPENJDK_BUILD_CPU_ENDIAN@ OPENJDK_TARGET_CPU_LEGACY := @OPENJDK_BUILD_CPU_LEGACY@ +OPENJDK_TARGET_LIBC := @OPENJDK_BUILD_LIBC@ OPENJDK_TARGET_OS_INCLUDE_SUBDIR := @OPENJDK_BUILD_OS_INCLUDE_SUBDIR@ HOTSPOT_TARGET_OS := @HOTSPOT_BUILD_OS@ @@ -66,6 +68,7 @@ HOTSPOT_TARGET_OS_TYPE := @HOTSPOT_BUILD_OS_TYPE@ HOTSPOT_TARGET_CPU := @HOTSPOT_BUILD_CPU@ HOTSPOT_TARGET_CPU_ARCH := @HOTSPOT_BUILD_CPU_ARCH@ HOTSPOT_TARGET_CPU_DEFINE := @HOTSPOT_BUILD_CPU_DEFINE@ +HOTSPOT_TARGET_LIBC := @HOTSPOT_BUILD_LIBC@ CFLAGS_JDKLIB := @OPENJDK_BUILD_CFLAGS_JDKLIB@ CXXFLAGS_JDKLIB := @OPENJDK_BUILD_CXXFLAGS_JDKLIB@ diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index 588df7f0011..d4738ad6837 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -231,8 +231,14 @@ AC_DEFUN([FLAGS_SETUP_OPTIMIZATION], # -D_FORTIFY_SOURCE=2 hardening option needs optimization (at least -O1) enabled # set for lower O-levels -U_FORTIFY_SOURCE to overwrite previous settings if test "x$OPENJDK_TARGET_OS" = xlinux -a "x$DEBUG_LEVEL" = "xfastdebug"; then - ENABLE_FORTIFY_CFLAGS="-D_FORTIFY_SOURCE=2" DISABLE_FORTIFY_CFLAGS="-U_FORTIFY_SOURCE" + # ASan doesn't work well with _FORTIFY_SOURCE + # See https://github.com/google/sanitizers/wiki/AddressSanitizer#faq + if test "x$ASAN_ENABLED" = xyes; then + ENABLE_FORTIFY_CFLAGS="${DISABLE_FORTIFY_CFLAGS}" + else + ENABLE_FORTIFY_CFLAGS="-D_FORTIFY_SOURCE=2" + fi C_O_FLAG_HIGHEST_JVM="${C_O_FLAG_HIGHEST_JVM} ${ENABLE_FORTIFY_CFLAGS}" C_O_FLAG_HIGHEST="${C_O_FLAG_HIGHEST} ${ENABLE_FORTIFY_CFLAGS}" C_O_FLAG_HI="${C_O_FLAG_HI} ${ENABLE_FORTIFY_CFLAGS}" @@ -558,6 +564,11 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_HELPER], fi fi + OS_CFLAGS="$OS_CFLAGS -DLIBC=$OPENJDK_TARGET_LIBC" + if test "x$OPENJDK_TARGET_LIBC" = xmusl; then + OS_CFLAGS="$OS_CFLAGS -DMUSL_LIBC" + fi + # Where does this really belong?? if test "x$TOOLCHAIN_TYPE" = xgcc || test "x$TOOLCHAIN_TYPE" = xclang; then PICFLAG="-fPIC" @@ -652,16 +663,10 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_CPU_DEP], $1_DEFINES_CPU_JDK="${$1_DEFINES_CPU_JDK} -DARCH='\"$FLAGS_CPU_LEGACY\"' \ -D$FLAGS_CPU_LEGACY" - if test "x$FLAGS_CPU_BITS" = x64; then - # -D_LP64=1 is only set on linux and mac. Setting on windows causes diff in - # unpack200.exe. - if test "x$FLAGS_OS" = xlinux || test "x$FLAGS_OS" = xmacosx; then - $1_DEFINES_CPU_JDK="${$1_DEFINES_CPU_JDK} -D_LP64=1" - fi - if test "x$FLAGS_OS" != xaix; then - # xlc on AIX defines _LP64=1 by default and issues a warning if we redefine it. - $1_DEFINES_CPU_JVM="${$1_DEFINES_CPU_JVM} -D_LP64=1" - fi + if test "x$FLAGS_CPU_BITS" = x64 && test "x$FLAGS_OS" != xaix; then + # xlc on AIX defines _LP64=1 by default and issues a warning if we redefine it. + $1_DEFINES_CPU_JDK="${$1_DEFINES_CPU_JDK} -D_LP64=1" + $1_DEFINES_CPU_JVM="${$1_DEFINES_CPU_JVM} -D_LP64=1" fi # toolchain dependend, per-cpu diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index 8f58db17d4a..a112a78d624 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -423,7 +423,10 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_ADDRESS_SANITIZER], fi ], IF_ENABLED: [ - ASAN_CFLAGS="-fsanitize=address -fno-omit-frame-pointer" + # ASan is simply incompatible with gcc -Wstringop-truncation. See + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85650 + # It's harmless to be suppressed in clang as well. + ASAN_CFLAGS="-fsanitize=address -Wno-stringop-truncation -fno-omit-frame-pointer" ASAN_LDFLAGS="-fsanitize=address" JVM_CFLAGS="$JVM_CFLAGS $ASAN_CFLAGS" JVM_LDFLAGS="$JVM_LDFLAGS $ASAN_LDFLAGS" diff --git a/make/autoconf/jvm-features.m4 b/make/autoconf/jvm-features.m4 index 04ca7b4e909..5ad791795a7 100644 --- a/make/autoconf/jvm-features.m4 +++ b/make/autoconf/jvm-features.m4 @@ -306,7 +306,7 @@ AC_DEFUN_ONCE([JVM_FEATURES_CHECK_GRAAL], # Graal is only available where JVMCI is available since it requires JVMCI. if test "x$OPENJDK_TARGET_CPU" = "xx86_64"; then AC_MSG_RESULT([yes]) - elif test "x$OPENJDK_TARGET_OS-$OPENJDK_TARGET_CPU" = "xlinux-aarch64"; then + elif test "x$OPENJDK_TARGET_CPU" = "xaarch64"; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no, $OPENJDK_TARGET_CPU]) @@ -340,7 +340,7 @@ AC_DEFUN_ONCE([JVM_FEATURES_CHECK_JVMCI], AC_MSG_CHECKING([if platform is supported by JVMCI]) if test "x$OPENJDK_TARGET_CPU" = "xx86_64"; then AC_MSG_RESULT([yes]) - elif test "x$OPENJDK_TARGET_OS-$OPENJDK_TARGET_CPU" = "xlinux-aarch64"; then + elif test "x$OPENJDK_TARGET_CPU" = "xaarch64"; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no, $OPENJDK_TARGET_CPU]) diff --git a/make/autoconf/libraries.m4 b/make/autoconf/libraries.m4 index 5120918aed2..e6aafe01550 100644 --- a/make/autoconf/libraries.m4 +++ b/make/autoconf/libraries.m4 @@ -43,9 +43,11 @@ AC_DEFUN_ONCE([LIB_DETERMINE_DEPENDENCIES], if test "x$OPENJDK_TARGET_OS" = xwindows || test "x$OPENJDK_TARGET_OS" = xmacosx; then # No X11 support on windows or macosx NEEDS_LIB_X11=false + elif test "x$ENABLE_HEADLESS_ONLY" = xtrue; then + # No X11 support needed when building headless only + NEEDS_LIB_X11=false else - # All other instances need X11, even if building headless only, libawt still - # needs X11 headers. + # All other instances need X11 NEEDS_LIB_X11=true fi diff --git a/make/autoconf/platform.m4 b/make/autoconf/platform.m4 index c0f2446dbd7..2f39d2b0ca7 100644 --- a/make/autoconf/platform.m4 +++ b/make/autoconf/platform.m4 @@ -220,6 +220,24 @@ AC_DEFUN([PLATFORM_EXTRACT_VARS_FROM_OS], esac ]) +# Support macro for PLATFORM_EXTRACT_TARGET_AND_BUILD. +# Converts autoconf style OS name to OpenJDK style, into +# VAR_LIBC. +AC_DEFUN([PLATFORM_EXTRACT_VARS_FROM_LIBC], +[ + case "$1" in + *linux*-musl) + VAR_LIBC=musl + ;; + *linux*-gnu) + VAR_LIBC=gnu + ;; + *) + VAR_LIBC=default + ;; + esac +]) + # Expects $host_os $host_cpu $build_os and $build_cpu # and $with_target_bits to have been setup! # @@ -237,9 +255,10 @@ AC_DEFUN([PLATFORM_EXTRACT_TARGET_AND_BUILD], AC_SUBST(OPENJDK_TARGET_AUTOCONF_NAME) AC_SUBST(OPENJDK_BUILD_AUTOCONF_NAME) - # Convert the autoconf OS/CPU value to our own data, into the VAR_OS/CPU variables. + # Convert the autoconf OS/CPU value to our own data, into the VAR_OS/CPU/LIBC variables. PLATFORM_EXTRACT_VARS_FROM_OS($build_os) PLATFORM_EXTRACT_VARS_FROM_CPU($build_cpu) + PLATFORM_EXTRACT_VARS_FROM_LIBC($build_os) # ..and setup our own variables. (Do this explicitly to facilitate searching) OPENJDK_BUILD_OS="$VAR_OS" if test "x$VAR_OS_TYPE" != x; then @@ -256,6 +275,7 @@ AC_DEFUN([PLATFORM_EXTRACT_TARGET_AND_BUILD], OPENJDK_BUILD_CPU_ARCH="$VAR_CPU_ARCH" OPENJDK_BUILD_CPU_BITS="$VAR_CPU_BITS" OPENJDK_BUILD_CPU_ENDIAN="$VAR_CPU_ENDIAN" + OPENJDK_BUILD_LIBC="$VAR_LIBC" AC_SUBST(OPENJDK_BUILD_OS) AC_SUBST(OPENJDK_BUILD_OS_TYPE) AC_SUBST(OPENJDK_BUILD_OS_ENV) @@ -263,13 +283,20 @@ AC_DEFUN([PLATFORM_EXTRACT_TARGET_AND_BUILD], AC_SUBST(OPENJDK_BUILD_CPU_ARCH) AC_SUBST(OPENJDK_BUILD_CPU_BITS) AC_SUBST(OPENJDK_BUILD_CPU_ENDIAN) + AC_SUBST(OPENJDK_BUILD_LIBC) AC_MSG_CHECKING([openjdk-build os-cpu]) AC_MSG_RESULT([$OPENJDK_BUILD_OS-$OPENJDK_BUILD_CPU]) - # Convert the autoconf OS/CPU value to our own data, into the VAR_OS/CPU variables. + if test "x$OPENJDK_BUILD_OS" = "xlinux"; then + AC_MSG_CHECKING([openjdk-build C library]) + AC_MSG_RESULT([$OPENJDK_BUILD_LIBC]) + fi + + # Convert the autoconf OS/CPU value to our own data, into the VAR_OS/CPU/LIBC variables. PLATFORM_EXTRACT_VARS_FROM_OS($host_os) PLATFORM_EXTRACT_VARS_FROM_CPU($host_cpu) + PLATFORM_EXTRACT_VARS_FROM_LIBC($host_os) # ... and setup our own variables. (Do this explicitly to facilitate searching) OPENJDK_TARGET_OS="$VAR_OS" if test "x$VAR_OS_TYPE" != x; then @@ -287,6 +314,7 @@ AC_DEFUN([PLATFORM_EXTRACT_TARGET_AND_BUILD], OPENJDK_TARGET_CPU_BITS="$VAR_CPU_BITS" OPENJDK_TARGET_CPU_ENDIAN="$VAR_CPU_ENDIAN" OPENJDK_TARGET_OS_UPPERCASE=`$ECHO $OPENJDK_TARGET_OS | $TR 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + OPENJDK_TARGET_LIBC="$VAR_LIBC" AC_SUBST(OPENJDK_TARGET_OS) AC_SUBST(OPENJDK_TARGET_OS_TYPE) @@ -296,9 +324,15 @@ AC_DEFUN([PLATFORM_EXTRACT_TARGET_AND_BUILD], AC_SUBST(OPENJDK_TARGET_CPU_ARCH) AC_SUBST(OPENJDK_TARGET_CPU_BITS) AC_SUBST(OPENJDK_TARGET_CPU_ENDIAN) + AC_SUBST(OPENJDK_TARGET_LIBC) AC_MSG_CHECKING([openjdk-target os-cpu]) AC_MSG_RESULT([$OPENJDK_TARGET_OS-$OPENJDK_TARGET_CPU]) + + if test "x$OPENJDK_TARGET_OS" = "xlinux"; then + AC_MSG_CHECKING([openjdk-target C library]) + AC_MSG_RESULT([$OPENJDK_TARGET_LIBC]) + fi ]) # Check if a reduced build (32-bit on 64-bit platforms) is requested, and modify behaviour @@ -420,7 +454,13 @@ AC_DEFUN([PLATFORM_SETUP_LEGACY_VARS_HELPER], else OPENJDK_$1_CPU_BUNDLE="$OPENJDK_$1_CPU" fi - OPENJDK_$1_BUNDLE_PLATFORM="${OPENJDK_$1_OS_BUNDLE}-${OPENJDK_$1_CPU_BUNDLE}" + + OPENJDK_$1_LIBC_BUNDLE="" + if test "x$OPENJDK_$1_LIBC" = "xmusl"; then + OPENJDK_$1_LIBC_BUNDLE="-$OPENJDK_$1_LIBC" + fi + + OPENJDK_$1_BUNDLE_PLATFORM="${OPENJDK_$1_OS_BUNDLE}-${OPENJDK_$1_CPU_BUNDLE}${OPENJDK_$1_LIBC_BUNDLE}" AC_SUBST(OPENJDK_$1_BUNDLE_PLATFORM) if test "x$COMPILE_TYPE" = "xcross"; then @@ -493,6 +533,9 @@ AC_DEFUN([PLATFORM_SETUP_LEGACY_VARS_HELPER], fi AC_SUBST(HOTSPOT_$1_CPU_DEFINE) + HOTSPOT_$1_LIBC=$OPENJDK_$1_LIBC + AC_SUBST(HOTSPOT_$1_LIBC) + # For historical reasons, the OS include directories have odd names. OPENJDK_$1_OS_INCLUDE_SUBDIR="$OPENJDK_TARGET_OS" if test "x$OPENJDK_TARGET_OS" = "xwindows"; then @@ -518,9 +561,11 @@ AC_DEFUN([PLATFORM_SET_RELEASE_FILE_OS_VALUES], RELEASE_FILE_OS_NAME="AIX" fi RELEASE_FILE_OS_ARCH=${OPENJDK_TARGET_CPU} + RELEASE_FILE_LIBC=${OPENJDK_TARGET_LIBC} AC_SUBST(RELEASE_FILE_OS_NAME) AC_SUBST(RELEASE_FILE_OS_ARCH) + AC_SUBST(RELEASE_FILE_LIBC) ]) AC_DEFUN([PLATFORM_SET_MODULE_TARGET_OS_VALUES], diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in index 14d7a18a0e8..63dc9a5767d 100644 --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in @@ -80,6 +80,8 @@ OPENJDK_TARGET_CPU_ARCH:=@OPENJDK_TARGET_CPU_ARCH@ OPENJDK_TARGET_CPU_BITS:=@OPENJDK_TARGET_CPU_BITS@ OPENJDK_TARGET_CPU_ENDIAN:=@OPENJDK_TARGET_CPU_ENDIAN@ +OPENJDK_TARGET_LIBC:=@OPENJDK_TARGET_LIBC@ + COMPILE_TYPE:=@COMPILE_TYPE@ # Legacy support @@ -95,6 +97,8 @@ HOTSPOT_TARGET_CPU := @HOTSPOT_TARGET_CPU@ HOTSPOT_TARGET_CPU_ARCH := @HOTSPOT_TARGET_CPU_ARCH@ HOTSPOT_TARGET_CPU_DEFINE := @HOTSPOT_TARGET_CPU_DEFINE@ +HOTSPOT_TARGET_LIBC := @HOTSPOT_TARGET_LIBC@ + OPENJDK_TARGET_BUNDLE_PLATFORM:=@OPENJDK_TARGET_BUNDLE_PLATFORM@ JDK_ARCH_ABI_PROP_NAME := @JDK_ARCH_ABI_PROP_NAME@ @@ -109,6 +113,8 @@ OPENJDK_BUILD_CPU_ARCH:=@OPENJDK_BUILD_CPU_ARCH@ OPENJDK_BUILD_CPU_BITS:=@OPENJDK_BUILD_CPU_BITS@ OPENJDK_BUILD_CPU_ENDIAN:=@OPENJDK_BUILD_CPU_ENDIAN@ +OPENJDK_BUILD_LIBC:=@OPENJDK_BUILD_LIBC@ + OPENJDK_BUILD_OS_INCLUDE_SUBDIR:=@OPENJDK_TARGET_OS_INCLUDE_SUBDIR@ # Target platform value in ModuleTarget class file attribute. @@ -117,6 +123,7 @@ OPENJDK_MODULE_TARGET_PLATFORM:=@OPENJDK_MODULE_TARGET_PLATFORM@ # OS_* properties in release file RELEASE_FILE_OS_NAME:=@RELEASE_FILE_OS_NAME@ RELEASE_FILE_OS_ARCH:=@RELEASE_FILE_OS_ARCH@ +RELEASE_FILE_LIBC:=@RELEASE_FILE_LIBC@ SOURCE_DATE := @SOURCE_DATE@ ENABLE_REPRODUCIBLE_BUILD := @ENABLE_REPRODUCIBLE_BUILD@ @@ -637,6 +644,7 @@ JARSIGNER=@FIXPATH@ $(JARSIGNER_CMD) BUILD_JAVA_FLAGS := @BOOTCYCLE_JVM_ARGS_BIG@ BUILD_JAVA=@FIXPATH@ $(BUILD_JDK)/bin/java $(BUILD_JAVA_FLAGS) +BUILD_JAVAC=@FIXPATH@ $(BUILD_JDK)/bin/javac BUILD_JAR=@FIXPATH@ $(BUILD_JDK)/bin/jar # Interim langtools modules and arguments @@ -751,7 +759,6 @@ TAR_SUPPORTS_TRANSFORM:=@TAR_SUPPORTS_TRANSFORM@ # Build setup ENABLE_AOT:=@ENABLE_AOT@ -ENABLE_INTREE_EC:=@ENABLE_INTREE_EC@ USE_EXTERNAL_LIBJPEG:=@USE_EXTERNAL_LIBJPEG@ USE_EXTERNAL_LIBGIF:=@USE_EXTERNAL_LIBGIF@ USE_EXTERNAL_LIBZ:=@USE_EXTERNAL_LIBZ@ @@ -869,10 +876,14 @@ INTERIM_JMODS_DIR := $(SUPPORT_OUTPUTDIR)/interim-jmods INTERIM_IMAGE_DIR := $(SUPPORT_OUTPUTDIR)/interim-image # Docs image -DOCS_IMAGE_SUBDIR := docs -DOCS_IMAGE_DIR = $(IMAGES_OUTPUTDIR)/$(DOCS_IMAGE_SUBDIR) +DOCS_JDK_IMAGE_SUBDIR := docs +DOCS_JDK_IMAGE_DIR = $(IMAGES_OUTPUTDIR)/$(DOCS_JDK_IMAGE_SUBDIR) +DOCS_JAVASE_IMAGE_SUBDIR := docs-javase +DOCS_JAVASE_IMAGE_DIR = $(IMAGES_OUTPUTDIR)/$(DOCS_JAVASE_IMAGE_SUBDIR) +DOCS_REFERENCE_IMAGE_SUBDIR := docs-reference +DOCS_REFERENCE_IMAGE_DIR = $(IMAGES_OUTPUTDIR)/$(DOCS_REFERENCE_IMAGE_SUBDIR) # Output docs directly into image -DOCS_OUTPUTDIR := $(DOCS_IMAGE_DIR) +DOCS_OUTPUTDIR := $(DOCS_JDK_IMAGE_DIR) # Static libs image STATIC_LIBS_IMAGE_SUBDIR := static-libs @@ -915,7 +926,9 @@ JRE_BUNDLE_NAME := jre-$(BASE_NAME)_bin$(DEBUG_PART).$(JDK_BUNDLE_EXTENSION) JDK_SYMBOLS_BUNDLE_NAME := jdk-$(BASE_NAME)_bin$(DEBUG_PART)-symbols.tar.gz TEST_DEMOS_BUNDLE_NAME := jdk-$(BASE_NAME)_bin-tests-demos$(DEBUG_PART).tar.gz TEST_BUNDLE_NAME := jdk-$(BASE_NAME)_bin-tests$(DEBUG_PART).tar.gz -DOCS_BUNDLE_NAME := jdk-$(BASE_NAME)_doc-api-spec$(DEBUG_PART).tar.gz +DOCS_JDK_BUNDLE_NAME := jdk-$(BASE_NAME)_doc-api-spec$(DEBUG_PART).tar.gz +DOCS_JAVASE_BUNDLE_NAME := javase-$(BASE_NAME)_doc-api-spec$(DEBUG_PART).tar.gz +DOCS_REFERENCE_BUNDLE_NAME := jdk-reference-$(BASE_NAME)_doc-api-spec$(DEBUG_PART).tar.gz STATIC_LIBS_BUNDLE_NAME := jdk-$(BASE_NAME)_bin-static-libs$(DEBUG_PART).tar.gz JCOV_BUNDLE_NAME := jdk-jcov-$(BASE_NAME)_bin$(DEBUG_PART).$(JDK_BUNDLE_EXTENSION) @@ -924,7 +937,9 @@ JRE_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(JRE_BUNDLE_NAME) JDK_SYMBOLS_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(JDK_SYMBOLS_BUNDLE_NAME) TEST_DEMOS_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(TEST_DEMOS_BUNDLE_NAME) TEST_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(TEST_BUNDLE_NAME) -DOCS_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(DOCS_BUNDLE_NAME) +DOCS_JDK_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(DOCS_JDK_BUNDLE_NAME) +DOCS_JAVASE_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(DOCS_JAVASE_BUNDLE_NAME) +DOCS_REFERENCE_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(DOCS_REFERENCE_BUNDLE_NAME) JCOV_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(JCOV_BUNDLE_NAME) # This macro is called to allow inclusion of closed source counterparts. diff --git a/make/common/JavaCompilation.gmk b/make/common/JavaCompilation.gmk index e0b1fb003d4..bedb971115b 100644 --- a/make/common/JavaCompilation.gmk +++ b/make/common/JavaCompilation.gmk @@ -202,6 +202,13 @@ define SetupJavaCompilationBody # If unspecified, default to the new jdk we're building $1_TARGET_RELEASE := $$(TARGET_RELEASE_BOOTJDK) endif + else ifeq ($$($1_COMPILER), buildjdk) + $1_JAVAC_CMD := $$(BUILD_JAVAC) + + ifeq ($$($1_TARGET_RELEASE), ) + # If unspecified, default to the new jdk we're building + $1_TARGET_RELEASE := $$(TARGET_RELEASE_NEWJDK) + endif else ifeq ($$($1_COMPILER), interim) # Use java server if it is enabled, and the user does not want a specialized # class path. @@ -304,9 +311,11 @@ define SetupJavaCompilationBody ifneq ($$($1_KEEP_DUPS), true) # Remove duplicate source files by keeping the first found of each duplicate. # This allows for automatic overrides with custom or platform specific versions - # source files. + # source files. Need to call DoubleDollar as we have java classes with '$' in + # their names. $1_SRCS := $$(strip $$(foreach s, $$($1_SRCS), \ - $$(eval relative_src := $$(call remove-prefixes, $$($1_SRC), $$(s))) \ + $$(eval relative_src := $$(call remove-prefixes, $$($1_SRC), \ + $$(call DoubleDollar, $$(s)))) \ $$(if $$($1_$$(relative_src)), \ , \ $$(eval $1_$$(relative_src) := 1) $$(s)))) diff --git a/make/common/JdkNativeCompilation.gmk b/make/common/JdkNativeCompilation.gmk index 21134966dc0..6a963ac2c49 100644 --- a/make/common/JdkNativeCompilation.gmk +++ b/make/common/JdkNativeCompilation.gmk @@ -77,8 +77,10 @@ ifeq ($(STATIC_LIBS), true) FindStaticLib = endif +# Returns the module specific java header dir if it exists. +# Param 1 - module name GetJavaHeaderDir = \ - $(wildcard $(SUPPORT_OUTPUTDIR)/headers/$(strip $1)) + $(if $(strip $1),$(wildcard $(SUPPORT_OUTPUTDIR)/headers/$(strip $1))) # Process a dir description such as "java.base:headers" into a set of proper absolute paths. ProcessDir = \ @@ -123,15 +125,27 @@ JDK_RCFLAGS=$(RCFLAGS) \ SetupJdkLibrary = $(NamedParamsMacroTemplate) define SetupJdkLibraryBody ifeq ($$($1_OUTPUT_DIR), ) - $1_OUTPUT_DIR := $$(call FindLibDirForModule, $$(MODULE)) + ifneq ($$(MODULE), ) + $1_OUTPUT_DIR := $$(call FindLibDirForModule, $$(MODULE)) + else + $$(error Must specify OUTPUT_DIR in a MODULE free context) + endif endif ifeq ($$($1_OBJECT_DIR), ) - $1_OBJECT_DIR := $$(SUPPORT_OUTPUTDIR)/native/$$(MODULE)/lib$$($1_NAME) + ifneq ($$(MODULE), ) + $1_OBJECT_DIR := $$(SUPPORT_OUTPUTDIR)/native/$$(MODULE)/lib$$($1_NAME) + else + $$(error Must specify OBJECT_DIR in a MODULE free context) + endif endif ifeq ($$($1_SRC), ) - $1_SRC := $$(call FindSrcDirsForLib, $$(MODULE), $$($1_NAME)) + ifneq ($$(MODULE), ) + $1_SRC := $$(call FindSrcDirsForLib, $$(MODULE), $$($1_NAME)) + else + $$(error Must specify SRC in a MODULE free context) + endif else $1_SRC := $$(foreach dir, $$($1_SRC), $$(call ProcessDir, $$(dir))) endif @@ -165,7 +179,8 @@ define SetupJdkLibraryBody ifneq ($$($1_HEADERS_FROM_SRC), false) $1_SRC_HEADER_FLAGS := $$(addprefix -I, $$(wildcard $$($1_SRC))) endif - # Always add the java header dir + + # Add the module specific java header dir $1_SRC_HEADER_FLAGS += $$(addprefix -I, $$(call GetJavaHeaderDir, $$(MODULE))) ifneq ($$($1_EXTRA_HEADER_DIRS), ) @@ -203,11 +218,19 @@ define SetupJdkExecutableBody $1_TYPE := EXECUTABLE ifeq ($$($1_OUTPUT_DIR), ) - $1_OUTPUT_DIR := $$(call FindExecutableDirForModule, $$(MODULE)) + ifneq ($$(MODULE), ) + $1_OUTPUT_DIR := $$(call FindExecutableDirForModule, $$(MODULE)) + else + $$(error Must specify OUTPUT_DIR in a MODULE free context) + endif endif ifeq ($$($1_OBJECT_DIR), ) - $1_OBJECT_DIR := $$(SUPPORT_OUTPUTDIR)/native/$$(MODULE)/$$($1_NAME) + ifneq ($$(MODULE), ) + $1_OBJECT_DIR := $$(SUPPORT_OUTPUTDIR)/native/$$(MODULE)/$$($1_NAME) + else + $$(error Must specify OBJECT_DIR in a MODULE free context) + endif endif ifeq ($$($1_VERSIONINFO_RESOURCE), ) diff --git a/make/common/Modules.gmk b/make/common/Modules.gmk index 72e19840501..10aacff4726 100644 --- a/make/common/Modules.gmk +++ b/make/common/Modules.gmk @@ -59,6 +59,7 @@ BOOT_MODULES += \ java.security.sasl \ java.xml \ jdk.incubator.foreign \ + jdk.incubator.vector \ jdk.internal.vm.ci \ jdk.jfr \ jdk.management \ @@ -124,7 +125,7 @@ endif JRE_TOOL_MODULES += \ jdk.jdwp.agent \ - jdk.incubator.jpackage \ + jdk.jpackage \ # ################################################################################ @@ -144,7 +145,8 @@ DOCS_MODULES += \ jdk.editpad \ jdk.hotspot.agent \ jdk.httpserver \ - jdk.incubator.jpackage \ + jdk.jpackage \ + jdk.incubator.vector \ jdk.jartool \ jdk.javadoc \ jdk.jcmd \ @@ -226,7 +228,7 @@ endif # jpackage is only on windows, macosx, and linux ifeq ($(call isTargetOs, windows macosx linux), false) - MODULES_FILTER += jdk.incubator.jpackage + MODULES_FILTER += jdk.jpackage endif ################################################################################ diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index e70d6617745..36460fee4b4 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -251,6 +251,8 @@ var getJibProfilesCommon = function (input, data) { configure_args: concat("--enable-jtreg-failure-handler", "--with-exclude-translations=de,es,fr,it,ko,pt_BR,sv,ca,tr,cs,sk,ja_JP_A,ja_JP_HA,ja_JP_HI,ja_JP_I,zh_TW,zh_HK", "--disable-manpages", + "--disable-jvm-feature-aot", + "--disable-jvm-feature-graal", "--disable-jvm-feature-shenandoahgc", versionArgs(input, common)) }; @@ -404,12 +406,11 @@ var getJibProfilesProfiles = function (input, common, data) { "linux-x64": { target_os: "linux", target_cpu: "x64", - dependencies: ["devkit", "gtest", "graphviz", "pandoc", "graalunit_lib"], + dependencies: ["devkit", "gtest", "graphviz", "pandoc"], configure_args: concat(common.configure_args_64bit, - "--enable-full-docs", "--with-zlib=system", + "--with-zlib=system", "--disable-dtrace", (isWsl(input) ? [ "--host=x86_64-unknown-linux-gnu", "--build=x86_64-unknown-linux-gnu" ] : [])), - default_make_targets: ["docs-bundles"], }, "linux-x86": { @@ -424,7 +425,7 @@ var getJibProfilesProfiles = function (input, common, data) { "macosx-x64": { target_os: "macosx", target_cpu: "x64", - dependencies: ["devkit", "gtest", "pandoc", "graalunit_lib"], + dependencies: ["devkit", "gtest", "pandoc"], configure_args: concat(common.configure_args_64bit, "--with-zlib=system", "--with-macosx-version-max=10.9.0", // Use system SetFile instead of the one in the devkit as the @@ -435,7 +436,7 @@ var getJibProfilesProfiles = function (input, common, data) { "windows-x64": { target_os: "windows", target_cpu: "x64", - dependencies: ["devkit", "gtest", "pandoc", "graalunit_lib"], + dependencies: ["devkit", "gtest", "pandoc"], configure_args: concat(common.configure_args_64bit), }, @@ -455,8 +456,6 @@ var getJibProfilesProfiles = function (input, common, data) { configure_args: [ "--openjdk-target=aarch64-linux-gnu", "--disable-jvm-feature-jvmci", - "--disable-jvm-feature-graal", - "--disable-jvm-feature-aot", ], }, @@ -680,20 +679,47 @@ var getJibProfilesProfiles = function (input, common, data) { common.debug_profile_artifacts(artifactData[name])); }); - profilesArtifacts = { - "linux-x64": { + buildJdkDep = input.build_os + "-" + input.build_cpu + ".jdk"; + docsProfiles = { + "docs": { + target_os: input.build_os, + target_cpu: input.build_cpu, + dependencies: [ + "boot_jdk", "devkit", "graphviz", "pandoc", buildJdkDep, + ], + configure_args: concat( + "--enable-full-docs", + versionArgs(input, common), + "--with-build-jdk=" + input.get(buildJdkDep, "home_path") + + (input.build_os == "macosx" ? "/Contents/Home" : "") + ), + default_make_targets: ["all-docs-bundles"], artifacts: { doc_api_spec: { - local: "bundles/\\(jdk.*doc-api-spec.tar.gz\\)", + local: "bundles/\\(jdk-" + data.version + ".*doc-api-spec.tar.gz\\)", remote: [ "bundles/common/jdk-" + data.version + "_doc-api-spec.tar.gz", "bundles/common/\\1" ], }, + javase_doc_api_spec: { + local: "bundles/\\(javase-" + data.version + ".*doc-api-spec.tar.gz\\)", + remote: [ + "bundles/common/javase-" + data.version + "_doc-api-spec.tar.gz", + "bundles/common/\\1" + ], + }, + reference_doc_api_spec: { + local: "bundles/\\(jdk-reference-" + data.version + ".*doc-api-spec.tar.gz\\)", + remote: [ + "bundles/common/jdk-reference-" + data.version + "_doc-api-spec.tar.gz", + "bundles/common/\\1" + ], + }, } } }; - profiles = concatObjects(profiles, profilesArtifacts); + profiles = concatObjects(profiles, docsProfiles); // Generate open only profiles for all the main and debug profiles. // Rewrite artifact remote paths by adding "openjdk/GPL". @@ -960,7 +986,7 @@ var getJibProfilesDependencies = function (input, common) { var devkit_platform_revisions = { linux_x64: "gcc10.2.0-OL6.4+1.0", - macosx_x64: "Xcode11.3.1-MacOSX10.15+1.0", + macosx_x64: "Xcode11.3.1-MacOSX10.15+1.1", windows_x64: "VS2019-16.7.2+1.0", linux_aarch64: "gcc10.2.0-OL7.6+1.0", linux_arm: "gcc8.2.0-Fedora27+1.0", @@ -1126,15 +1152,6 @@ var getJibProfilesDependencies = function (input, common) { configure_args: "", }, - graalunit_lib: { - organization: common.organization, - ext: "zip", - revision: "619_Apr_12_2018", - module: "graalunit-lib", - configure_args: "--with-graalunit-lib=" + input.get("graalunit_lib", "install_path"), - environment_name: "GRAALUNIT_LIB" - }, - gtest: { organization: common.organization, ext: "tar.gz", diff --git a/make/data/blacklistedcertsconverter/blacklisted.certs.pem b/make/data/blacklistedcertsconverter/blacklisted.certs.pem index 191e94e12a5..688becbc493 100644 --- a/make/data/blacklistedcertsconverter/blacklisted.certs.pem +++ b/make/data/blacklistedcertsconverter/blacklisted.certs.pem @@ -1,8 +1,7 @@ #! java BlacklistedCertsConverter SHA-256 -# The line above must be the first line of the blacklisted.certs.pem -# file inside src/share/lib/security/. It will be ignored if added in -# src/closed/share/lib/security/blacklisted.certs.pem. +# The line above must be the first line of this file. Do not +# remove it. // Subject: CN=Digisign Server ID (Enrich), // OU=457608-K, diff --git a/make/data/tzdata/VERSION b/make/data/tzdata/VERSION index e96a6d78497..94ba7462f2e 100644 --- a/make/data/tzdata/VERSION +++ b/make/data/tzdata/VERSION @@ -21,4 +21,4 @@ # or visit www.oracle.com if you need additional information or have any # questions. # -tzdata2020a +tzdata2020d diff --git a/make/data/tzdata/africa b/make/data/tzdata/africa index 7947bc55b00..e1c3d8929e6 100644 --- a/make/data/tzdata/africa +++ b/make/data/tzdata/africa @@ -87,7 +87,7 @@ # Corrections are welcome. # Algeria -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Algeria 1916 only - Jun 14 23:00s 1:00 S Rule Algeria 1916 1919 - Oct Sun>=1 23:00s 0 - Rule Algeria 1917 only - Mar 24 23:00s 1:00 S @@ -110,10 +110,9 @@ Rule Algeria 1978 only - Mar 24 1:00 1:00 S Rule Algeria 1978 only - Sep 22 3:00 0 - Rule Algeria 1980 only - Apr 25 0:00 1:00 S Rule Algeria 1980 only - Oct 31 2:00 0 - -# Shanks & Pottenger give 0:09:20 for Paris Mean Time; go with Howse's -# more precise 0:09:21. +# See Europe/Paris for PMT-related transitions. # Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone Africa/Algiers 0:12:12 - LMT 1891 Mar 15 0:01 +Zone Africa/Algiers 0:12:12 - LMT 1891 Mar 16 0:09:21 - PMT 1911 Mar 11 # Paris Mean Time 0:00 Algeria WE%sT 1940 Feb 25 2:00 1:00 Algeria CE%sT 1946 Oct 7 @@ -199,7 +198,7 @@ Link Africa/Abidjan Atlantic/St_Helena # St Helena # Egypt was mean noon at the Great Pyramid, 2:04:30.5, but apparently this # did not apply to Cairo, Alexandria, or Port Said. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Egypt 1940 only - Jul 15 0:00 1:00 S Rule Egypt 1940 only - Oct 1 0:00 0 - Rule Egypt 1941 only - Apr 15 0:00 1:00 S @@ -434,7 +433,7 @@ Zone Africa/Cairo 2:05:09 - LMT 1900 Oct # now Ghana observed different DST regimes in different years. For # lack of better info, use Shanks except treat the minus sign as a # typo, and assume DST started in 1920 not 1936. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Ghana 1920 1942 - Sep 1 0:00 0:20 - Rule Ghana 1920 1942 - Dec 31 0:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] @@ -524,7 +523,7 @@ Zone Africa/Monrovia -0:43:08 - LMT 1882 # From Paul Eggert (2013-10-25): # For now, assume they're reverting to the pre-2012 rules of permanent UT +02. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Libya 1951 only - Oct 14 2:00 1:00 S Rule Libya 1952 only - Jan 1 0:00 0 - Rule Libya 1953 only - Oct 9 2:00 1:00 S @@ -647,7 +646,7 @@ Zone Africa/Tripoli 0:52:44 - LMT 1920 # "The trial ended on March 29, 2009, when the clocks moved back by one hour # at 2am (or 02:00) local time..." -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Mauritius 1982 only - Oct 10 0:00 1:00 - Rule Mauritius 1983 only - Mar 21 0:00 0 - Rule Mauritius 2008 only - Oct lastSun 2:00 1:00 - @@ -898,17 +897,30 @@ Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis # https://maroc-diplomatique.net/maroc-le-retour-a-lheure-gmt-est-prevu-dimanche-prochain/ # http://aujourdhui.ma/actualite/gmt1-retour-a-lheure-normale-dimanche-prochain-1 # -# From Paul Eggert (2020-04-14): +# From Milamber (2020-05-31) +# In Morocco (where I live), the end of Ramadan (Arabic month) is followed by +# the Eid al-Fitr, and concretely it's 1 or 2 day offs for the people (with +# traditional visiting of family, big lunches/dinners, etc.). So for this +# year the astronomical calculations don't include the following 2 days off in +# the calc. These 2 days fall in a Sunday/Monday, so it's not acceptable by +# people to have a time shift during these 2 days off. Perhaps you can modify +# the (predicted) rules for next years: if the end of Ramadan is a (probable) +# Friday or Saturday (and so the 2 days off are on a weekend), the next time +# shift will be the next weekend. +# +# From Paul Eggert (2020-05-31): # For now, guess that in the future Morocco will fall back at 03:00 # the last Sunday before Ramadan, and spring forward at 02:00 the -# first Sunday after the day after Ramadan. To implement this, -# transition dates for 2021 through 2087 were determined by running -# the following program under GNU Emacs 26.3. -# (let ((islamic-year 1442)) +# first Sunday after two days after Ramadan. To implement this, +# transition dates and times for 2019 through 2087 were determined by +# running the following program under GNU Emacs 26.3. (This algorithm +# also produces the correct transition dates for 2016 through 2018, +# though the times differ due to Morocco's time zone change in 2018.) +# (let ((islamic-year 1440)) # (require 'cal-islam) # (while (< islamic-year 1511) # (let ((a (calendar-islamic-to-absolute (list 9 1 islamic-year))) -# (b (1+ (calendar-islamic-to-absolute (list 10 1 islamic-year)))) +# (b (+ 2 (calendar-islamic-to-absolute (list 10 1 islamic-year)))) # (sunday 0)) # (while (/= sunday (mod (setq a (1- a)) 7))) # (while (/= sunday (mod b 7)) @@ -923,7 +935,7 @@ Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis # (car (cdr (cdr b))) (calendar-month-name (car b) t) (car (cdr b))))) # (setq islamic-year (+ 1 islamic-year)))) -# RULE NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Morocco 1939 only - Sep 12 0:00 1:00 - Rule Morocco 1939 only - Nov 19 0:00 0 - Rule Morocco 1940 only - Feb 25 0:00 1:00 - @@ -974,7 +986,7 @@ Rule Morocco 2021 only - May 16 2:00 0 - Rule Morocco 2022 only - Mar 27 3:00 -1:00 - Rule Morocco 2022 only - May 8 2:00 0 - Rule Morocco 2023 only - Mar 19 3:00 -1:00 - -Rule Morocco 2023 only - Apr 23 2:00 0 - +Rule Morocco 2023 only - Apr 30 2:00 0 - Rule Morocco 2024 only - Mar 10 3:00 -1:00 - Rule Morocco 2024 only - Apr 14 2:00 0 - Rule Morocco 2025 only - Feb 23 3:00 -1:00 - @@ -990,7 +1002,7 @@ Rule Morocco 2029 only - Feb 18 2:00 0 - Rule Morocco 2029 only - Dec 30 3:00 -1:00 - Rule Morocco 2030 only - Feb 10 2:00 0 - Rule Morocco 2030 only - Dec 22 3:00 -1:00 - -Rule Morocco 2031 only - Jan 26 2:00 0 - +Rule Morocco 2031 only - Feb 2 2:00 0 - Rule Morocco 2031 only - Dec 14 3:00 -1:00 - Rule Morocco 2032 only - Jan 18 2:00 0 - Rule Morocco 2032 only - Nov 28 3:00 -1:00 - @@ -1006,7 +1018,7 @@ Rule Morocco 2036 only - Nov 23 2:00 0 - Rule Morocco 2037 only - Oct 4 3:00 -1:00 - Rule Morocco 2037 only - Nov 15 2:00 0 - Rule Morocco 2038 only - Sep 26 3:00 -1:00 - -Rule Morocco 2038 only - Oct 31 2:00 0 - +Rule Morocco 2038 only - Nov 7 2:00 0 - Rule Morocco 2039 only - Sep 18 3:00 -1:00 - Rule Morocco 2039 only - Oct 23 2:00 0 - Rule Morocco 2040 only - Sep 2 3:00 -1:00 - @@ -1022,7 +1034,7 @@ Rule Morocco 2044 only - Aug 28 2:00 0 - Rule Morocco 2045 only - Jul 9 3:00 -1:00 - Rule Morocco 2045 only - Aug 20 2:00 0 - Rule Morocco 2046 only - Jul 1 3:00 -1:00 - -Rule Morocco 2046 only - Aug 5 2:00 0 - +Rule Morocco 2046 only - Aug 12 2:00 0 - Rule Morocco 2047 only - Jun 23 3:00 -1:00 - Rule Morocco 2047 only - Jul 28 2:00 0 - Rule Morocco 2048 only - Jun 7 3:00 -1:00 - @@ -1038,7 +1050,7 @@ Rule Morocco 2052 only - Jun 2 2:00 0 - Rule Morocco 2053 only - Apr 13 3:00 -1:00 - Rule Morocco 2053 only - May 25 2:00 0 - Rule Morocco 2054 only - Apr 5 3:00 -1:00 - -Rule Morocco 2054 only - May 10 2:00 0 - +Rule Morocco 2054 only - May 17 2:00 0 - Rule Morocco 2055 only - Mar 28 3:00 -1:00 - Rule Morocco 2055 only - May 2 2:00 0 - Rule Morocco 2056 only - Mar 12 3:00 -1:00 - @@ -1054,7 +1066,7 @@ Rule Morocco 2060 only - Mar 7 2:00 0 - Rule Morocco 2061 only - Jan 16 3:00 -1:00 - Rule Morocco 2061 only - Feb 27 2:00 0 - Rule Morocco 2062 only - Jan 8 3:00 -1:00 - -Rule Morocco 2062 only - Feb 12 2:00 0 - +Rule Morocco 2062 only - Feb 19 2:00 0 - Rule Morocco 2062 only - Dec 31 3:00 -1:00 - Rule Morocco 2063 only - Feb 4 2:00 0 - Rule Morocco 2063 only - Dec 16 3:00 -1:00 - @@ -1070,7 +1082,7 @@ Rule Morocco 2067 only - Dec 11 2:00 0 - Rule Morocco 2068 only - Oct 21 3:00 -1:00 - Rule Morocco 2068 only - Dec 2 2:00 0 - Rule Morocco 2069 only - Oct 13 3:00 -1:00 - -Rule Morocco 2069 only - Nov 17 2:00 0 - +Rule Morocco 2069 only - Nov 24 2:00 0 - Rule Morocco 2070 only - Oct 5 3:00 -1:00 - Rule Morocco 2070 only - Nov 9 2:00 0 - Rule Morocco 2071 only - Sep 20 3:00 -1:00 - @@ -1086,7 +1098,7 @@ Rule Morocco 2075 only - Sep 15 2:00 0 - Rule Morocco 2076 only - Jul 26 3:00 -1:00 - Rule Morocco 2076 only - Sep 6 2:00 0 - Rule Morocco 2077 only - Jul 18 3:00 -1:00 - -Rule Morocco 2077 only - Aug 22 2:00 0 - +Rule Morocco 2077 only - Aug 29 2:00 0 - Rule Morocco 2078 only - Jul 10 3:00 -1:00 - Rule Morocco 2078 only - Aug 14 2:00 0 - Rule Morocco 2079 only - Jun 25 3:00 -1:00 - @@ -1096,13 +1108,13 @@ Rule Morocco 2080 only - Jul 21 2:00 0 - Rule Morocco 2081 only - Jun 1 3:00 -1:00 - Rule Morocco 2081 only - Jul 13 2:00 0 - Rule Morocco 2082 only - May 24 3:00 -1:00 - -Rule Morocco 2082 only - Jun 28 2:00 0 - +Rule Morocco 2082 only - Jul 5 2:00 0 - Rule Morocco 2083 only - May 16 3:00 -1:00 - Rule Morocco 2083 only - Jun 20 2:00 0 - Rule Morocco 2084 only - Apr 30 3:00 -1:00 - Rule Morocco 2084 only - Jun 11 2:00 0 - Rule Morocco 2085 only - Apr 22 3:00 -1:00 - -Rule Morocco 2085 only - May 27 2:00 0 - +Rule Morocco 2085 only - Jun 3 2:00 0 - Rule Morocco 2086 only - Apr 14 3:00 -1:00 - Rule Morocco 2086 only - May 19 2:00 0 - Rule Morocco 2087 only - Mar 30 3:00 -1:00 - @@ -1203,7 +1215,7 @@ Link Africa/Maputo Africa/Lusaka # Zambia # Use plain "WAT" and "CAT" for the time zone abbreviations, to be compatible # with Namibia's neighbors. -# RULE NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S # Vanguard section, for zic and other parsers that support negative DST. Rule Namibia 1994 only - Mar 21 0:00 -1:00 WAT Rule Namibia 1994 2017 - Sep Sun>=1 2:00 0 CAT @@ -1326,7 +1338,7 @@ Zone Indian/Mahe 3:41:48 - LMT 1906 Jun # Victoria # See Africa/Nairobi. # South Africa -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule SA 1942 1943 - Sep Sun>=15 2:00 1:00 - Rule SA 1943 1944 - Mar Sun>=15 2:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] @@ -1359,7 +1371,7 @@ Link Africa/Johannesburg Africa/Mbabane # Eswatini # Abdalla of NTC, archived at: # https://mm.icann.org/pipermail/tz/2017-October/025333.html -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Sudan 1970 only - May 1 0:00 1:00 S Rule Sudan 1970 1985 - Oct 15 0:00 0 - Rule Sudan 1971 only - Apr 30 0:00 1:00 S @@ -1447,7 +1459,7 @@ Zone Africa/Juba 2:06:28 - LMT 1931 # http://www.almadenahnews.com/newss/news.php?c=118&id=38036 # http://www.worldtimezone.com/dst_news/dst_news_tunis02.html -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Tunisia 1939 only - Apr 15 23:00s 1:00 S Rule Tunisia 1939 only - Nov 18 23:00s 0 - Rule Tunisia 1940 only - Feb 25 23:00s 1:00 S @@ -1474,9 +1486,7 @@ Rule Tunisia 2005 only - Sep 30 1:00s 0 - Rule Tunisia 2006 2008 - Mar lastSun 2:00s 1:00 S Rule Tunisia 2006 2008 - Oct lastSun 2:00s 0 - -# Shanks & Pottenger give 0:09:20 for Paris Mean Time; go with Howse's -# more precise 0:09:21. -# Shanks & Pottenger say the 1911 switch was on Mar 9; go with Howse's Mar 11. +# See Europe/Paris for PMT-related transitions. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Africa/Tunis 0:40:44 - LMT 1881 May 12 0:09:21 - PMT 1911 Mar 11 # Paris Mean Time diff --git a/make/data/tzdata/antarctica b/make/data/tzdata/antarctica index 6239f837e9f..509fadc29a9 100644 --- a/make/data/tzdata/antarctica +++ b/make/data/tzdata/antarctica @@ -93,15 +93,30 @@ # Australian Antarctica Division informed us that Casey changed time # zone to UTC+11 in "the morning of 22nd October 2016". +# From Steffen Thorsen (2020-10-02, as corrected): +# Based on information we have received from the Australian Antarctic +# Division, Casey station and Macquarie Island station will move to Tasmanian +# daylight savings time on Sunday 4 October. This will take effect from 0001 +# hrs on Sunday 4 October 2020 and will mean Casey and Macquarie Island will +# be on the same time zone as Hobart. Some past dates too for this 3 hour +# time change back and forth between UTC+8 and UTC+11 for Casey: +# - 2018 Oct 7 4:00 - 2019 Mar 17 3:00 - 2019 Oct 4 3:00 - 2020 Mar 8 3:00 +# and now - 2020 Oct 4 0:01 + # Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone Antarctica/Casey 0 - -00 1969 - 8:00 - +08 2009 Oct 18 2:00 +Zone Antarctica/Casey 0 - -00 1969 + 8:00 - +08 2009 Oct 18 2:00 11:00 - +11 2010 Mar 5 2:00 - 8:00 - +08 2011 Oct 28 2:00 + 8:00 - +08 2011 Oct 28 2:00 11:00 - +11 2012 Feb 21 17:00u - 8:00 - +08 2016 Oct 22 + 8:00 - +08 2016 Oct 22 11:00 - +11 2018 Mar 11 4:00 - 8:00 - +08 + 8:00 - +08 2018 Oct 7 4:00 + 11:00 - +11 2019 Mar 17 3:00 + 8:00 - +08 2019 Oct 4 3:00 + 11:00 - +11 2020 Mar 8 3:00 + 8:00 - +08 2020 Oct 4 0:01 + 11:00 - +11 Zone Antarctica/Davis 0 - -00 1957 Jan 13 7:00 - +07 1964 Nov 0 - -00 1969 Feb @@ -247,7 +262,7 @@ Zone Antarctica/Syowa 0 - -00 1957 Jan 29 # suggested by Bengt-Inge Larsson comment them out for now, and approximate # with only UTC and CEST. Uncomment them when 2014b is more prevalent. # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S #Rule Troll 2005 max - Mar 1 1:00u 1:00 +01 Rule Troll 2005 max - Mar lastSun 1:00u 2:00 +02 #Rule Troll 2005 max - Oct lastSun 1:00u 1:00 +01 diff --git a/make/data/tzdata/asia b/make/data/tzdata/asia index 0700aa46b41..acca6554fa2 100644 --- a/make/data/tzdata/asia +++ b/make/data/tzdata/asia @@ -93,7 +93,7 @@ ############################################################################### # These rules are stolen from the 'europe' file. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule EUAsia 1981 max - Mar lastSun 1:00u 1:00 S Rule EUAsia 1979 1995 - Sep lastSun 1:00u 0 - Rule EUAsia 1996 max - Oct lastSun 1:00u 0 - @@ -137,7 +137,7 @@ Zone Asia/Kabul 4:36:48 - LMT 1890 # or # (brief) # http://www.worldtimezone.com/dst_news/dst_news_armenia03.html -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Armenia 2011 only - Mar lastSun 2:00s 1:00 - Rule Armenia 2011 only - Oct lastSun 2:00s 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] @@ -163,7 +163,7 @@ Zone Asia/Yerevan 2:58:00 - LMT 1924 May 2 # http://vestnikkavkaza.net/news/Azerbaijani-Cabinet-of-Ministers-cancels-daylight-saving-time.html # http://en.apa.az/xeber_azerbaijan_abolishes_daylight_savings_ti_240862.html -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Azer 1997 2015 - Mar lastSun 4:00 1:00 - Rule Azer 1997 2015 - Oct lastSun 5:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] @@ -250,7 +250,7 @@ Zone Asia/Baku 3:19:24 - LMT 1924 May 2 # http://www.thedailystar.net/newDesign/latest_news.php?nid=22817 # http://www.worldtimezone.com/dst_news/dst_news_bangladesh06.html -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Dhaka 2009 only - Jun 19 23:00 1:00 - Rule Dhaka 2009 only - Dec 31 24:00 0 - @@ -326,7 +326,7 @@ Zone Asia/Yangon 6:24:47 - LMT 1880 # or Rangoon # generally esteemed a success, it was announced early in 1920 that it would # not be repeated." # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Shang 1919 only - Apr 12 24:00 1:00 D Rule Shang 1919 only - Sep 30 24:00 0 S @@ -422,7 +422,7 @@ Rule Shang 1919 only - Sep 30 24:00 0 S # the Yangtze river delta area during that period of time although the scope # of such use will need to be investigated to determine. # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Shang 1940 only - Jun 1 0:00 1:00 D Rule Shang 1940 only - Oct 12 24:00 0 S Rule Shang 1941 only - Mar 15 0:00 1:00 D @@ -485,7 +485,7 @@ Rule Shang 1948 1949 - Sep 30 24:00 0 S #plan # to begin on 17 April. # http://data.people.com.cn/pic/101p/1988/04/1988041201.jpg -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule PRC 1986 only - May 4 2:00 1:00 D Rule PRC 1986 1991 - Sep Sun>=11 2:00 0 S Rule PRC 1987 1991 - Apr Sun>=11 2:00 1:00 D @@ -869,7 +869,7 @@ Zone Asia/Urumqi 5:50:20 - LMT 1928 # or dates for the 1942 and 1945 transitions. # The Japanese occupation of Hong Kong began 1941-12-25. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule HK 1946 only - Apr 21 0:00 1:00 S Rule HK 1946 only - Dec 1 3:30s 0 - Rule HK 1947 only - Apr 13 3:30s 1:00 S @@ -996,7 +996,7 @@ Zone Asia/Hong_Kong 7:36:42 - LMT 1904 Oct 30 0:36:42 # until 1945-09-21 at 01:00, overriding Shanks & Pottenger. # Likewise, use Yu-Cheng Chuang's data for DST in Taiwan. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Taiwan 1946 only - May 15 0:00 1:00 D Rule Taiwan 1946 only - Oct 1 0:00 0 S Rule Taiwan 1947 only - Apr 15 0:00 1:00 D @@ -1122,7 +1122,7 @@ Zone Asia/Taipei 8:06:00 - LMT 1896 Jan 1 # The 1904 decree says that Macau changed from the meridian of # Fortaleza do Monte, presumably the basis for the 7:34:10 for LMT. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Macau 1942 1943 - Apr 30 23:00 1:00 - Rule Macau 1942 only - Nov 17 23:00 0 - Rule Macau 1943 only - Sep 30 23:00 0 S @@ -1180,7 +1180,7 @@ Zone Asia/Macau 7:34:10 - LMT 1904 Oct 30 # Cyprus to remain united in time. Cyprus Mail 2017-10-17. # https://cyprus-mail.com/2017/10/17/cyprus-remain-united-time/ -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Cyprus 1975 only - Apr 13 0:00 1:00 S Rule Cyprus 1975 only - Oct 12 0:00 0 - Rule Cyprus 1976 only - May 15 0:00 1:00 S @@ -1557,7 +1557,7 @@ Zone Asia/Jayapura 9:22:48 - LMT 1932 Nov # be changed back to its previous state on the 24 hours of the # thirtieth day of Shahrivar. # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Iran 1978 1980 - Mar 20 24:00 1:00 - Rule Iran 1978 only - Oct 20 24:00 0 - Rule Iran 1979 only - Sep 18 24:00 0 - @@ -1699,7 +1699,7 @@ Zone Asia/Tehran 3:25:44 - LMT 1916 # We have published a short article in English about the change: # https://www.timeanddate.com/news/time/iraq-dumps-daylight-saving.html -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Iraq 1982 only - May 1 0:00 1:00 - Rule Iraq 1982 1984 - Oct 1 0:00 0 - Rule Iraq 1983 only - Mar 31 0:00 1:00 - @@ -1722,6 +1722,10 @@ Zone Asia/Baghdad 2:57:40 - LMT 1890 # Israel +# For more info about the motivation for DST in Israel, see: +# Barak Y. Israel's Daylight Saving Time controversy. Israel Affairs. +# 2020-08-11. https://doi.org/10.1080/13537121.2020.1806564 + # From Ephraim Silverberg (2001-01-11): # # I coined "IST/IDT" circa 1988. Until then there were three @@ -1743,7 +1747,7 @@ Zone Asia/Baghdad 2:57:40 - LMT 1890 # family is from India). # From Shanks & Pottenger: -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Zion 1940 only - Jun 1 0:00 1:00 D Rule Zion 1942 1944 - Nov 1 0:00 0 S Rule Zion 1943 only - Apr 1 2:00 1:00 D @@ -1835,7 +1839,7 @@ Rule Zion 1988 only - Sep 4 0:00 0 S # (except in 2002) is three nights before Yom Kippur [Day of Atonement] # (the eve of the 7th of Tishrei in the lunar Hebrew calendar). -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Zion 1989 only - Apr 30 0:00 1:00 D Rule Zion 1989 only - Sep 3 0:00 0 S Rule Zion 1990 only - Mar 25 0:00 1:00 D @@ -1851,7 +1855,7 @@ Rule Zion 1993 only - Sep 5 0:00 0 S # Ministry of Interior, Jerusalem, Israel. The spokeswoman can be reached by # calling the office directly at 972-2-6701447 or 972-2-6701448. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Zion 1994 only - Apr 1 0:00 1:00 D Rule Zion 1994 only - Aug 28 0:00 0 S Rule Zion 1995 only - Mar 31 0:00 1:00 D @@ -1871,7 +1875,7 @@ Rule Zion 1995 only - Sep 3 0:00 0 S # # where YYYY is the relevant year. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Zion 1996 only - Mar 15 0:00 1:00 D Rule Zion 1996 only - Sep 16 0:00 0 S Rule Zion 1997 only - Mar 21 0:00 1:00 D @@ -1894,7 +1898,7 @@ Rule Zion 1999 only - Sep 3 2:00 0 S # # ftp://ftp.cs.huji.ac.il/pub/tz/announcements/2000-2004.ps.gz -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Zion 2000 only - Apr 14 2:00 1:00 D Rule Zion 2000 only - Oct 6 1:00 0 S Rule Zion 2001 only - Apr 9 1:00 1:00 D @@ -1916,7 +1920,7 @@ Rule Zion 2004 only - Sep 22 1:00 0 S # # ftp://ftp.cs.huji.ac.il/pub/tz/announcements/2005+beyond.ps -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Zion 2005 2012 - Apr Fri<=1 2:00 1:00 D Rule Zion 2005 only - Oct 9 2:00 0 S Rule Zion 2006 only - Oct 1 2:00 0 S @@ -1936,7 +1940,7 @@ Rule Zion 2012 only - Sep 23 2:00 0 S # As of 2013, DST starts at 02:00 on the Friday before the last Sunday # in March. DST ends at 02:00 on the last Sunday of October. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Zion 2013 max - Mar Fri>=23 2:00 1:00 D Rule Zion 2013 max - Oct lastSun 2:00 0 S @@ -2036,7 +2040,7 @@ Zone Asia/Jerusalem 2:20:54 - LMT 1880 # do in any POSIX or C platform. The "25:00" assumes zic from 2007 or later, # which should be safe now. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Japan 1948 only - May Sat>=1 24:00 1:00 D Rule Japan 1948 1951 - Sep Sat>=8 25:00 0 S Rule Japan 1949 only - Apr Sat>=1 24:00 1:00 D @@ -2113,7 +2117,7 @@ Zone Asia/Tokyo 9:18:59 - LMT 1887 Dec 31 15:00u # From Paul Eggert (2013-12-11): # As Steffen suggested, consider the past 21-month experiment to be DST. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Jordan 1973 only - Jun 6 0:00 1:00 S Rule Jordan 1973 1975 - Oct 1 0:00 0 - Rule Jordan 1974 1977 - May 1 0:00 1:00 S @@ -2439,7 +2443,7 @@ Zone Asia/Oral 3:25:24 - LMT 1924 May 2 # or Ural'sk # Our government cancels daylight saving time 6th of August 2005. # From 2005-08-12 our GMT-offset is +6, w/o any daylight saving. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Kyrgyz 1992 1996 - Apr Sun>=7 0:00s 1:00 - Rule Kyrgyz 1992 1996 - Sep lastSun 0:00 0 - Rule Kyrgyz 1997 2005 - Mar lastSun 2:30 1:00 - @@ -2495,7 +2499,7 @@ Zone Asia/Bishkek 4:58:24 - LMT 1924 May 2 # follow and continued to use GMT+9:00 for interoperability. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule ROK 1948 only - Jun 1 0:00 1:00 D Rule ROK 1948 only - Sep 12 24:00 0 S Rule ROK 1949 only - Apr 3 0:00 1:00 D @@ -2583,7 +2587,7 @@ Zone Asia/Pyongyang 8:23:00 - LMT 1908 Apr 1 # Lebanon -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Lebanon 1920 only - Mar 28 0:00 1:00 S Rule Lebanon 1920 only - Oct 25 0:00 0 - Rule Lebanon 1921 only - Apr 3 0:00 1:00 S @@ -2613,7 +2617,7 @@ Zone Asia/Beirut 2:22:00 - LMT 1880 2:00 Lebanon EE%sT # Malaysia -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule NBorneo 1935 1941 - Sep 14 0:00 0:20 - Rule NBorneo 1935 1941 - Dec 14 0:00 0 - # @@ -2758,7 +2762,7 @@ Zone Indian/Maldives 4:54:00 - LMT 1880 # Malé # September daylight saving time ends. Source: # http://zasag.mn/news/view/8969 -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Mongol 1983 1984 - Apr 1 0:00 1:00 - Rule Mongol 1983 only - Oct 1 0:00 0 - # Shanks & Pottenger and IATA SSIM say 1990s switches occurred at 00:00, @@ -2946,7 +2950,7 @@ Zone Asia/Kathmandu 5:41:16 - LMT 1920 # "People laud PM's announcement to end DST" # http://www.app.com.pk/en_/index.php?option=com_content&task=view&id=99374&Itemid=2 -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Pakistan 2002 only - Apr Sun>=2 0:00 1:00 S Rule Pakistan 2002 only - Oct Sun>=2 0:00 0 - Rule Pakistan 2008 only - Jun 1 0:00 1:00 S @@ -3240,15 +3244,42 @@ Zone Asia/Karachi 4:28:12 - LMT 1907 # From Sharef Mustafa (2019-10-18): # Palestine summer time will end on midnight Oct 26th 2019 ... -# http://www.palestinecabinet.gov.ps/website/ar/ViewDetails?ID=43948 # -# From Paul Eggert (2019-04-10): -# For now, guess spring-ahead transitions are March's last Friday at 00:00. +# From Steffen Thorsen (2020-10-20): +# Some sources such as these say, and display on clocks, that DST ended at +# midnight last year... +# https://www.amad.ps/ar/post/320006 +# +# From Tim Parenti (2020-10-20): +# The report of the Palestinian Cabinet meeting of 2019-10-14 confirms +# a decision on (translated): "The start of the winter time in Palestine, by +# delaying the clock by sixty minutes, starting from midnight on Friday / +# Saturday corresponding to 26/10/2019." +# http://www.palestinecabinet.gov.ps/portal/meeting/details/43948 + +# From Sharef Mustafa (2020-10-20): +# As per the palestinian cabinet announcement yesterday , the day light saving +# shall [end] on Oct 24th 2020 at 01:00AM by delaying the clock by 60 minutes. +# http://www.palestinecabinet.gov.ps/portal/Meeting/Details/51584 + +# From Tim Parenti (2020-10-20): +# Predict future fall transitions at 01:00 on the Saturday preceding October's +# last Sunday (i.e., Sat>=24). This is consistent with our predictions since +# 2016, although the time of the change differed slightly in 2019. + +# From Pierre Cashon (2020-10-20): +# The summer time this year started on March 28 at 00:00. +# https://wafa.ps/ar_page.aspx?id=GveQNZa872839351758aGveQNZ +# http://www.palestinecabinet.gov.ps/portal/meeting/details/50284 +# The winter time in 2015 started on October 23 at 01:00. +# https://wafa.ps/ar_page.aspx?id=CgpCdYa670694628582aCgpCdY +# http://www.palestinecabinet.gov.ps/portal/meeting/details/27583 # -# From Tim Parenti (2016-10-19): -# Predict fall transitions on October's last Saturday at 01:00 from now on. +# From Paul Eggert (2019-04-10): +# For now, guess spring-ahead transitions are at 00:00 on the Saturday +# preceding March's last Sunday (i.e., Sat>=24). -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule EgyptAsia 1957 only - May 10 0:00 1:00 S Rule EgyptAsia 1957 1958 - Oct 1 0:00 0 - Rule EgyptAsia 1958 only - May 1 0:00 1:00 S @@ -3262,10 +3293,10 @@ Rule Palestine 2004 only - Oct 1 1:00 0 - Rule Palestine 2005 only - Oct 4 2:00 0 - Rule Palestine 2006 2007 - Apr 1 0:00 1:00 S Rule Palestine 2006 only - Sep 22 0:00 0 - -Rule Palestine 2007 only - Sep Thu>=8 2:00 0 - +Rule Palestine 2007 only - Sep 13 2:00 0 - Rule Palestine 2008 2009 - Mar lastFri 0:00 1:00 S Rule Palestine 2008 only - Sep 1 0:00 0 - -Rule Palestine 2009 only - Sep Fri>=1 1:00 0 - +Rule Palestine 2009 only - Sep 4 1:00 0 - Rule Palestine 2010 only - Mar 26 0:00 1:00 S Rule Palestine 2010 only - Aug 11 0:00 0 - Rule Palestine 2011 only - Apr 1 0:01 1:00 S @@ -3274,12 +3305,16 @@ Rule Palestine 2011 only - Aug 30 0:00 1:00 S Rule Palestine 2011 only - Sep 30 0:00 0 - Rule Palestine 2012 2014 - Mar lastThu 24:00 1:00 S Rule Palestine 2012 only - Sep 21 1:00 0 - -Rule Palestine 2013 only - Sep Fri>=21 0:00 0 - -Rule Palestine 2014 2015 - Oct Fri>=21 0:00 0 - -Rule Palestine 2015 only - Mar lastFri 24:00 1:00 S +Rule Palestine 2013 only - Sep 27 0:00 0 - +Rule Palestine 2014 only - Oct 24 0:00 0 - +Rule Palestine 2015 only - Mar 28 0:00 1:00 S +Rule Palestine 2015 only - Oct 23 1:00 0 - Rule Palestine 2016 2018 - Mar Sat>=24 1:00 1:00 S -Rule Palestine 2016 max - Oct lastSat 1:00 0 - -Rule Palestine 2019 max - Mar lastFri 0:00 1:00 S +Rule Palestine 2016 2018 - Oct Sat>=24 1:00 0 - +Rule Palestine 2019 only - Mar 29 0:00 1:00 S +Rule Palestine 2019 only - Oct Sat>=24 0:00 0 - +Rule Palestine 2020 max - Mar Sat>=24 0:00 1:00 S +Rule Palestine 2020 max - Oct Sat>=24 1:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Gaza 2:17:52 - LMT 1900 Oct @@ -3348,7 +3383,7 @@ Zone Asia/Hebron 2:20:23 - LMT 1900 Oct # influence of the sources. There is no current abbreviation for DST, # so use "PDT", the usual American style. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Phil 1936 only - Nov 1 0:00 1:00 D Rule Phil 1937 only - Feb 1 0:00 0 S Rule Phil 1954 only - Apr 12 0:00 1:00 D @@ -3496,7 +3531,7 @@ Zone Asia/Colombo 5:19:24 - LMT 1880 5:30 - +0530 # Syria -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Syria 1920 1923 - Apr Sun>=15 2:00 1:00 S Rule Syria 1920 1923 - Oct Sun>=1 2:00 0 - Rule Syria 1962 only - Apr 29 2:00 1:00 S diff --git a/make/data/tzdata/australasia b/make/data/tzdata/australasia index e66d5ca4d79..1f0fd47959f 100644 --- a/make/data/tzdata/australasia +++ b/make/data/tzdata/australasia @@ -36,7 +36,7 @@ # Please see the notes below for the controversy about "EST" versus "AEST" etc. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Aus 1917 only - Jan 1 0:01 1:00 D Rule Aus 1917 only - Mar 25 2:00 0 S Rule Aus 1942 only - Jan 1 2:00 1:00 D @@ -55,7 +55,7 @@ Zone Australia/Darwin 8:43:20 - LMT 1895 Feb 9:30 Aus AC%sT # Western Australia # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule AW 1974 only - Oct lastSun 2:00s 1:00 D Rule AW 1975 only - Mar Sun>=1 2:00s 0 S Rule AW 1983 only - Oct lastSun 2:00s 1:00 D @@ -93,7 +93,7 @@ Zone Australia/Eucla 8:35:28 - LMT 1895 Dec # applies to all of the Whitsundays. # http://www.australia.gov.au/about-australia/australian-story/austn-islands # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule AQ 1971 only - Oct lastSun 2:00s 1:00 D Rule AQ 1972 only - Feb lastSun 2:00s 0 S Rule AQ 1989 1991 - Oct lastSun 2:00s 1:00 D @@ -109,7 +109,7 @@ Zone Australia/Lindeman 9:55:56 - LMT 1895 10:00 Holiday AE%sT # South Australia -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule AS 1971 1985 - Oct lastSun 2:00s 1:00 D Rule AS 1986 only - Oct 19 2:00s 1:00 D Rule AS 1987 2007 - Oct lastSun 2:00s 1:00 D @@ -137,7 +137,7 @@ Zone Australia/Adelaide 9:14:20 - LMT 1895 Feb # http://www.bom.gov.au/climate/averages/tables/dst_times.shtml # says King Island didn't observe DST from WWII until late 1971. # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule AT 1967 only - Oct Sun>=1 2:00s 1:00 D Rule AT 1968 only - Mar lastSun 2:00s 0 S Rule AT 1968 1985 - Oct lastSun 2:00s 1:00 D @@ -170,7 +170,7 @@ Zone Australia/Currie 9:35:28 - LMT 1895 Sep 10:00 AT AE%sT # Victoria -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule AV 1971 1985 - Oct lastSun 2:00s 1:00 D Rule AV 1972 only - Feb lastSun 2:00s 0 S Rule AV 1973 1985 - Mar Sun>=1 2:00s 0 S @@ -191,7 +191,7 @@ Zone Australia/Melbourne 9:39:52 - LMT 1895 Feb 10:00 AV AE%sT # New South Wales -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule AN 1971 1985 - Oct lastSun 2:00s 1:00 D Rule AN 1972 only - Feb 27 2:00s 0 S Rule AN 1973 1981 - Mar Sun>=1 2:00s 0 S @@ -220,7 +220,7 @@ Zone Australia/Broken_Hill 9:25:48 - LMT 1895 Feb 9:30 AS AC%sT # Lord Howe Island -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule LH 1981 1984 - Oct lastSun 2:00 1:00 - Rule LH 1982 1985 - Mar Sun>=1 2:00 0 - Rule LH 1985 only - Oct lastSun 2:00 0:30 - @@ -275,8 +275,9 @@ Zone Antarctica/Macquarie 0 - -00 1899 Nov 10:00 Aus AE%sT 1919 Apr 1 0:00s 0 - -00 1948 Mar 25 10:00 Aus AE%sT 1967 - 10:00 AT AE%sT 2010 Apr 4 3:00 - 11:00 - +11 + 10:00 AT AE%sT 2010 + 10:00 1:00 AEDT 2011 + 10:00 AT AE%sT # Christmas # Zone NAME STDOFF RULES FORMAT [UNTIL] @@ -403,7 +404,20 @@ Zone Indian/Cocos 6:27:40 - LMT 1900 # From Michael Deckers (2019-08-06): # https://www.laws.gov.fj/LawsAsMade/downloadfile/848 -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# From Raymond Kumar (2020-10-08): +# [DST in Fiji] is from December 20th 2020, till 17th January 2021. +# From Alan Mintz (2020-10-08): +# https://www.laws.gov.fj/LawsAsMade/GetFile/1071 +# From Tim Parenti (2020-10-08): +# https://www.fijivillage.com/news/Daylight-saving-from-Dec-20th-this-year-to-Jan-17th-2021-8rf4x5/ +# "Minister for Employment, Parveen Bala says they had never thought of +# stopping daylight saving. He says it was just to decide on when it should +# start and end. Bala says it is a short period..." +# Since the end date is still in line with our ongoing predictions, assume for +# now that the later-than-usual start date is a one-time departure from the +# recent second Sunday in November pattern. + +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Fiji 1998 1999 - Nov Sun>=1 2:00 1:00 - Rule Fiji 1999 2000 - Feb lastSun 3:00 0 - Rule Fiji 2009 only - Nov 29 2:00 1:00 - @@ -414,7 +428,9 @@ Rule Fiji 2012 2013 - Jan Sun>=18 3:00 0 - Rule Fiji 2014 only - Jan Sun>=18 2:00 0 - Rule Fiji 2014 2018 - Nov Sun>=1 2:00 1:00 - Rule Fiji 2015 max - Jan Sun>=12 3:00 0 - -Rule Fiji 2019 max - Nov Sun>=8 2:00 1:00 - +Rule Fiji 2019 only - Nov Sun>=8 2:00 1:00 - +Rule Fiji 2020 only - Dec 20 2:00 1:00 - +Rule Fiji 2021 max - Nov Sun>=8 2:00 1:00 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Fiji 11:55:44 - LMT 1915 Oct 26 # Suva 12:00 Fiji +12/+13 @@ -432,7 +448,7 @@ Zone Pacific/Tahiti -9:58:16 - LMT 1912 Oct # Papeete # Guam -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S # http://guamlegislature.com/Public_Laws_5th/PL05-025.pdf # http://documents.guam.gov/wp-content/uploads/E.O.-59-7-Guam-Daylight-Savings-Time-May-6-1959.pdf Rule Guam 1959 only - Jun 27 2:00 1:00 D @@ -543,7 +559,7 @@ Zone Pacific/Nauru 11:07:40 - LMT 1921 Jan 15 # Uaobe 12:00 - +12 # New Caledonia -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule NC 1977 1978 - Dec Sun>=1 0:00 1:00 - Rule NC 1978 1979 - Feb 27 0:00 0 - Rule NC 1996 only - Dec 1 2:00s 1:00 - @@ -558,7 +574,7 @@ Zone Pacific/Noumea 11:05:48 - LMT 1912 Jan 13 # Nouméa # New Zealand -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule NZ 1927 only - Nov 6 2:00 1:00 S Rule NZ 1928 only - Mar 4 2:00 0 M Rule NZ 1928 1933 - Oct Sun>=8 2:00 0:30 S @@ -610,7 +626,7 @@ Link Pacific/Auckland Antarctica/McMurdo # Cook Is # From Shanks & Pottenger: -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Cook 1978 only - Nov 12 0:00 0:30 - Rule Cook 1979 1991 - Mar Sun>=1 0:00 0 - Rule Cook 1979 1990 - Oct lastSun 0:00 0:30 - @@ -755,7 +771,7 @@ Link Pacific/Pago_Pago Pacific/Midway # in US minor outlying islands # That web page currently lists transitions for 2012/3 and 2013/4. # Assume the pattern instituted in 2012 will continue indefinitely. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule WS 2010 only - Sep lastSun 0:00 1 - Rule WS 2011 only - Apr Sat>=1 4:00 0 - Rule WS 2011 only - Sep lastSat 3:00 1 - @@ -799,7 +815,7 @@ Zone Pacific/Fakaofo -11:24:56 - LMT 1901 13:00 - +13 # Tonga -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Tonga 1999 only - Oct 7 2:00s 1:00 - Rule Tonga 2000 only - Mar 19 2:00s 0 - Rule Tonga 2000 2001 - Nov Sun>=1 2:00 1:00 - @@ -880,7 +896,7 @@ Zone Pacific/Wake 11:06:28 - LMT 1901 # Vanuatu -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Vanuatu 1983 only - Sep 25 0:00 1:00 - Rule Vanuatu 1984 1991 - Mar Sun>=23 0:00 0 - Rule Vanuatu 1984 only - Oct 23 0:00 1:00 - diff --git a/make/data/tzdata/europe b/make/data/tzdata/europe index 8fed2cf5e98..adb260624dc 100644 --- a/make/data/tzdata/europe +++ b/make/data/tzdata/europe @@ -411,7 +411,7 @@ # http://www.irishstatutebook.ie/eli/1926/sro/919/made/en/print # http://www.irishstatutebook.ie/eli/1947/sro/71/made/en/print -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S # Summer Time Act, 1916 Rule GB-Eire 1916 only - May 21 2:00s 1:00 BST Rule GB-Eire 1916 only - Oct 1 2:00s 0 GMT @@ -552,7 +552,7 @@ Link Europe/London Europe/Isle_of_Man # The following is like GB-Eire and EU, except with standard time in # summer and negative daylight saving time in winter. It is for when # negative SAVE values are used. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Eire 1971 only - Oct 31 2:00u -1:00 - Rule Eire 1972 1980 - Mar Sun>=16 2:00u 0 - Rule Eire 1972 1980 - Oct Sun>=23 2:00u -1:00 - @@ -589,7 +589,7 @@ Zone Europe/Dublin -0:25:00 - LMT 1880 Aug 2 # predecessor organization, the European Communities. # For brevity they are called "EU rules" elsewhere in this file. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule EU 1977 1980 - Apr Sun>=1 1:00u 1:00 S Rule EU 1977 only - Sep lastSun 1:00u 0 - Rule EU 1978 only - Oct 1 1:00u 0 - @@ -629,13 +629,13 @@ Rule C-Eur 1944 only - Oct 2 2:00s 0 - # corrected in version 2008d). The circumstantial evidence is simply the # tz database itself, as seen below: # -# Zone Europe/Paris 0:09:21 - LMT 1891 Mar 15 0:01 +# Zone Europe/Paris ... # 0:00 France WE%sT 1945 Sep 16 3:00 # -# Zone Europe/Monaco 0:29:32 - LMT 1891 Mar 15 +# Zone Europe/Monaco ... # 0:00 France WE%sT 1945 Sep 16 3:00 # -# Zone Europe/Belgrade 1:22:00 - LMT 1884 +# Zone Europe/Belgrade ... # 1:00 1:00 CEST 1945 Sep 16 2:00s # # Rule France 1945 only - Sep 16 3:00 0 - @@ -681,7 +681,7 @@ Rule E-Eur 1996 max - Oct lastSun 0:00 0 - # # The 1917-1921 decree URLs are from Alexander Belopolsky (2016-08-23). -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Russia 1917 only - Jul 1 23:00 1:00 MST # Moscow Summer Time # # Decree No. 142 (1917-12-22) http://istmat.info/node/28137 @@ -795,7 +795,7 @@ Zone EET 2:00 EU EE%sT # Albania -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Albania 1940 only - Jun 16 0:00 1:00 S Rule Albania 1942 only - Nov 2 3:00 0 - Rule Albania 1943 only - Mar 29 2:00 1:00 S @@ -849,7 +849,7 @@ Zone Europe/Andorra 0:06:04 - LMT 1901 # In 1946 the end of DST was on Monday, 7 October 1946, at 3:00 am. # Shanks had this right. Source: Die Weltpresse, 5. Oktober 1946, page 5. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Austria 1920 only - Apr 5 2:00s 1:00 S Rule Austria 1920 only - Sep 13 2:00s 0 - Rule Austria 1946 only - Apr 14 2:00s 1:00 S @@ -936,7 +936,7 @@ Zone Europe/Minsk 1:50:16 - LMT 1880 # The 1918 rules are listed for completeness; they apply to unoccupied Belgium. # Assume Brussels switched to WET in 1918 when the armistice took effect. # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Belgium 1918 only - Mar 9 0:00s 1:00 S Rule Belgium 1918 1919 - Oct Sat>=1 23:00s 0 - Rule Belgium 1919 only - Mar 1 23:00s 1:00 S @@ -996,7 +996,7 @@ Zone Europe/Brussels 0:17:30 - LMT 1880 # EET -> EETDST is in 03:00 Local time in last Sunday of March ... # EETDST -> EET is in 04:00 Local time in last Sunday of October # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Bulg 1979 only - Mar 31 23:00 1:00 S Rule Bulg 1979 only - Oct 1 1:00 0 - Rule Bulg 1980 1982 - Apr Sat>=1 23:00 1:00 S @@ -1028,7 +1028,7 @@ Zone Europe/Sofia 1:33:16 - LMT 1880 # We know of no English-language name for historical Czech winter time; # abbreviate it as "GMT", as it happened to be GMT. # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Czech 1945 only - Apr Mon>=1 2:00s 1:00 S Rule Czech 1945 only - Oct 1 2:00s 0 - Rule Czech 1946 only - May 6 2:00s 1:00 S @@ -1052,17 +1052,16 @@ Zone Europe/Prague 0:57:44 - LMT 1850 # Denmark, Faroe Islands, and Greenland # From Jesper Nørgaard Welen (2005-04-26): -# http://www.hum.aau.dk/~poe/tid/tine/DanskTid.htm says that the law -# [introducing standard time] was in effect from 1894-01-01.... -# The page http://www.retsinfo.dk/_GETDOCI_/ACCN/A18930008330-REGL +# the law [introducing standard time] was in effect from 1894-01-01.... +# The page https://www.retsinformation.dk/eli/lta/1893/83 # confirms this, and states that the law was put forth 1893-03-29. # # The EU [actually, EEC and Euratom] treaty with effect from 1973: -# http://www.retsinfo.dk/_GETDOCI_/ACCN/A19722110030-REGL +# https://www.retsinformation.dk/eli/lta/1972/21100 # # This provoked a new law from 1974 to make possible summer time changes # in subsequent decrees with the law -# http://www.retsinfo.dk/_GETDOCI_/ACCN/A19740022330-REGL +# https://www.retsinformation.dk/eli/lta/1974/223 # # It seems however that no decree was set forward until 1980. I have # not found any decree, but in another related law, the effecting DST @@ -1074,7 +1073,7 @@ Zone Europe/Prague 0:57:44 - LMT 1850 # The law is about the management of the extra hour, concerning # working hours reported and effect on obligatory-rest rules (which # was suspended on that night): -# http://www.retsinfo.dk/_GETDOCI_/ACCN/C19801120554-REGL +# https://web.archive.org/web/20140104053304/https://www.retsinformation.dk/Forms/R0710.aspx?id=60267 # From Jesper Nørgaard Welen (2005-06-11): # The Herning Folkeblad (1980-09-26) reported that the night between @@ -1084,7 +1083,7 @@ Zone Europe/Prague 0:57:44 - LMT 1850 # Hence the "02:00" of the 1980 law refers to standard time, not # wall-clock time, and so the EU rules were in effect in 1980. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Denmark 1916 only - May 14 23:00 1:00 S Rule Denmark 1916 only - Sep 30 23:00 0 - Rule Denmark 1940 only - May 15 0:00 1:00 S @@ -1186,7 +1185,7 @@ Zone Atlantic/Faroe -0:27:04 - LMT 1908 Jan 11 # Tórshavn # http://naalakkersuisut.gl/~/media/Nanoq/Files/Attached%20Files/Engelske-tekster/Legislation/Executive%20Order%20National%20Park.rtf # It is their only National Park. # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Thule 1991 1992 - Mar lastSun 2:00 1:00 D Rule Thule 1991 1992 - Sep lastSun 2:00 0 S Rule Thule 1993 2006 - Apr Sun>=1 2:00 1:00 D @@ -1317,7 +1316,7 @@ Zone Europe/Tallinn 1:39:00 - LMT 1880 # From Paul Eggert (2014-06-14): # Go with Oja over Shanks. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Finland 1942 only - Apr 2 24:00 1:00 S Rule Finland 1942 only - Oct 4 1:00 0 - Rule Finland 1981 1982 - Mar lastSun 2:00 1:00 S @@ -1349,10 +1348,58 @@ Link Europe/Helsinki Europe/Mariehamn # Françoise Gauquelin, Problèmes de l'heure résolus en astrologie, # Guy Trédaniel, Paris 1987 +# From Michael Deckers (2020-06-11): +# the law of 1891 +# was published on 1891-03-15, so it could only take force on 1891-03-16. + +# From Michael Deckers (2020-06-10): +# Le Gaulois, 1911-03-11, page 1/6, online at +# https://www.retronews.fr/societe/echo-de-presse/2018/01/29/1911-change-lheure-de-paris +# ... [ Instantly, all pressure driven clock dials halted... Nine minutes and +# twenty-one seconds later the hands resumed their circular motion. ] +# There are also precise reports about how the change was prepared in train +# stations: all the publicly visible clocks stopped at midnight railway time +# (or were covered), only the chief of service had a watch, labeled +# "Heure ancienne", that he kept running until it reached 00:04:21, when +# he announced "Heure nouvelle". See the "Le Petit Journal 1911-03-11". +# https://gallica.bnf.fr/ark:/12148/bpt6k6192911/f1.item.zoom +# +# From Michael Deckers (2020-06-12): +# That "all French clocks stopped" for 00:09:21 is a misreading of French +# newspapers; this sort of adjustment applies only to certain +# remote-controlled clocks ("pendules pneumatiques", of which there existed +# perhaps a dozen in Paris, and which simply could not be set back remotely), +# but not to all the clocks in all French towns and villages. For instance, +# the following story in the "Courrier de Saône-et-Loire" 1911-03-11, page 2: +# only works if legal time was stepped back (was not monotone): ... +# [One can observe that children who had been born at midnight less 5 +# minutes and who had died at midnight of the old time, would turn out to +# be dead before being born, time having been set back and having +# suppressed 9 minutes and 25 seconds of their existence, that is, more +# than they could spend.] +# +# From Paul Eggert (2020-06-12): +# French time in railway stations was legally five minutes behind civil time, +# which explains why railway "old time" ran to 00:04:21 instead of to 00:09:21. +# The law's text (which Michael Deckers noted is at +# ) says only that +# at 1911-03-11 00:00 legal time was that of Paris mean time delayed by +# nine minutes and twenty-one seconds, and does not say how the +# transition from Paris mean time was to occur. +# +# tzdb has no way to represent stopped clocks. As the railway practice +# was to keep a watch running on "old time" to decide when to restart +# the other clocks, this could be modeled as a transition for "old time" at +# 00:09:21. However, since the law was ambiguous and clocks outside railway +# stations were probably done haphazardly with the popular impression being +# that the transition was done at 00:00 "old time", simply leave the time +# blank; this causes zic to default to 00:00 "old time" which is good enough. +# Do something similar for the 1891-03-16 transition. There are similar +# problems in Algiers, Monaco and Tunis. # # Shank & Pottenger seem to use '24:00' ambiguously; resolve it with Whitman. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule France 1916 only - Jun 14 23:00s 1:00 S Rule France 1916 1919 - Oct Sun>=1 23:00s 0 - Rule France 1917 only - Mar 24 23:00s 1:00 S @@ -1412,13 +1459,11 @@ Rule France 1945 only - Sep 16 3:00 0 - # go with Excoffier's 28/3/76 0hUT and 25/9/76 23hUT. Rule France 1976 only - Mar 28 1:00 1:00 S Rule France 1976 only - Sep 26 1:00 0 - -# Shanks & Pottenger give 0:09:20 for Paris Mean Time, and Whitman 0:09:05, -# but Howse quotes the actual French legislation as saying 0:09:21. -# Go with Howse. Howse writes that the time in France was officially based +# Howse writes that the time in France was officially based # on PMT-0:09:21 until 1978-08-09, when the time base finally switched to UTC. # Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone Europe/Paris 0:09:21 - LMT 1891 Mar 15 0:01 - 0:09:21 - PMT 1911 Mar 11 0:01 # Paris MT +Zone Europe/Paris 0:09:21 - LMT 1891 Mar 16 + 0:09:21 - PMT 1911 Mar 11 # Paris Mean Time # Shanks & Pottenger give 1940 Jun 14 0:00; go with Excoffier and Le Corre. 0:00 France WE%sT 1940 Jun 14 23:00 # Le Corre says Paris stuck with occupied-France time after the liberation; @@ -1447,7 +1492,7 @@ Zone Europe/Paris 0:09:21 - LMT 1891 Mar 15 0:01 # this was equivalent to UT +03, not +04. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Germany 1946 only - Apr 14 2:00s 1:00 S Rule Germany 1946 only - Oct 7 2:00s 0 - Rule Germany 1947 1949 - Oct Sun>=1 2:00s 0 - @@ -1499,7 +1544,7 @@ Zone Europe/Gibraltar -0:21:24 - LMT 1880 Aug 2 0:00s 1:00 EU CE%sT # Greece -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S # Whitman gives 1932 Jul 5 - Nov 1; go with Shanks & Pottenger. Rule Greece 1932 only - Jul 7 0:00 1:00 S Rule Greece 1932 only - Sep 1 0:00 0 - @@ -1534,38 +1579,73 @@ Zone Europe/Athens 1:34:52 - LMT 1895 Sep 14 2:00 EU EE%sT # Hungary -# From Paul Eggert (2014-07-15): -# Dates for 1916-1945 are taken from: -# Oross A. Jelen a múlt jövője: a nyári időszámítás Magyarországon 1916-1945. -# National Archives of Hungary (2012-10-29). -# http://mnl.gov.hu/a_het_dokumentuma/a_nyari_idoszamitas_magyarorszagon_19161945.html -# This source does not always give times, which are taken from Shanks -# & Pottenger (which disagree about the dates). -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Hungary 1918 only - Apr 1 3:00 1:00 S -Rule Hungary 1918 only - Sep 16 3:00 0 - -Rule Hungary 1919 only - Apr 15 3:00 1:00 S -Rule Hungary 1919 only - Nov 24 3:00 0 - + +# From Michael Deckers (2020-06-09): +# an Austrian encyclopedia of railroads of 1913, online at +# http://www.zeno.org/Roell-1912/A/Eisenbahnzeit +# says that the switch [to CET] happened on 1890-11-01. + +# From Géza Nyáry (2020-06-07): +# Data for 1918-1983 are based on the archive database of Library Hungaricana. +# The dates are collected from original, scanned governmental orders, +# bulletins, instructions and public press. +# [See URLs below.] + +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S +# https://library.hungaricana.hu/hu/view/OGYK_RT_1918/?pg=238 +# https://library.hungaricana.hu/hu/view/OGYK_RT_1919/?pg=808 +# https://library.hungaricana.hu/hu/view/OGYK_RT_1920/?pg=201 +Rule Hungary 1918 1919 - Apr 15 2:00 1:00 S +Rule Hungary 1918 1920 - Sep Mon>=15 3:00 0 - +Rule Hungary 1920 only - Apr 5 2:00 1:00 S +# https://library.hungaricana.hu/hu/view/OGYK_RT_1945/?pg=882 Rule Hungary 1945 only - May 1 23:00 1:00 S -Rule Hungary 1945 only - Nov 1 0:00 0 - +Rule Hungary 1945 only - Nov 1 1:00 0 - +# https://library.hungaricana.hu/hu/view/Delmagyarorszag_1946_03/?pg=49 Rule Hungary 1946 only - Mar 31 2:00s 1:00 S -Rule Hungary 1946 1949 - Oct Sun>=1 2:00s 0 - +# https://library.hungaricana.hu/hu/view/Delmagyarorszag_1946_09/?pg=54 +Rule Hungary 1946 only - Oct 7 2:00 0 - +# https://library.hungaricana.hu/hu/view/KulfBelfHirek_1947_04_1__001-123/?pg=90 +# https://library.hungaricana.hu/hu/view/DunantuliNaplo_1947_09/?pg=128 +# https://library.hungaricana.hu/hu/view/KulfBelfHirek_1948_03_3__001-123/?pg=304 +# https://library.hungaricana.hu/hu/view/Zala_1948_09/?pg=64 +# https://library.hungaricana.hu/hu/view/SatoraljaujhelyiLeveltar_ZempleniNepujsag_1948/?pg=53 +# https://library.hungaricana.hu/hu/view/SatoraljaujhelyiLeveltar_ZempleniNepujsag_1948/?pg=160 +# https://library.hungaricana.hu/hu/view/UjSzo_1949_01-04/?pg=102 +# https://library.hungaricana.hu/hu/view/KeletMagyarorszag_1949_03/?pg=96 +# https://library.hungaricana.hu/hu/view/Delmagyarorszag_1949_09/?pg=94 Rule Hungary 1947 1949 - Apr Sun>=4 2:00s 1:00 S -Rule Hungary 1950 only - Apr 17 2:00s 1:00 S -Rule Hungary 1950 only - Oct 23 2:00s 0 - -Rule Hungary 1954 1955 - May 23 0:00 1:00 S -Rule Hungary 1954 1955 - Oct 3 0:00 0 - -Rule Hungary 1956 only - Jun Sun>=1 0:00 1:00 S -Rule Hungary 1956 only - Sep lastSun 0:00 0 - -Rule Hungary 1957 only - Jun Sun>=1 1:00 1:00 S -Rule Hungary 1957 only - Sep lastSun 3:00 0 - -Rule Hungary 1980 only - Apr 6 1:00 1:00 S +Rule Hungary 1947 1949 - Oct Sun>=1 2:00s 0 - +# https://library.hungaricana.hu/hu/view/DTT_KOZL_TanacsokKozlonye_1954/?pg=513 +Rule Hungary 1954 only - May 23 0:00 1:00 S +Rule Hungary 1954 only - Oct 3 0:00 0 - +# https://library.hungaricana.hu/hu/view/DTT_KOZL_TanacsokKozlonye_1955/?pg=398 +Rule Hungary 1955 only - May 22 2:00 1:00 S +Rule Hungary 1955 only - Oct 2 3:00 0 - +# https://library.hungaricana.hu/hu/view/HevesMegyeiNepujsag_1956_06/?pg=0 +# https://library.hungaricana.hu/hu/view/EszakMagyarorszag_1956_06/?pg=6 +# https://library.hungaricana.hu/hu/view/SzolnokMegyeiNeplap_1957_04/?pg=120 +# https://library.hungaricana.hu/hu/view/PestMegyeiHirlap_1957_09/?pg=143 +Rule Hungary 1956 1957 - Jun Sun>=1 2:00 1:00 S +Rule Hungary 1956 1957 - Sep lastSun 3:00 0 - +# https://library.hungaricana.hu/hu/view/DTT_KOZL_TanacsokKozlonye_1980/?pg=189 +Rule Hungary 1980 only - Apr 6 0:00 1:00 S +Rule Hungary 1980 only - Sep 28 1:00 0 - +# https://library.hungaricana.hu/hu/view/DTT_KOZL_TanacsokKozlonye_1980/?pg=1227 +# https://library.hungaricana.hu/hu/view/Delmagyarorszag_1981_01/?pg=79 +# https://library.hungaricana.hu/hu/view/DTT_KOZL_TanacsokKozlonye_1982/?pg=115 +# https://library.hungaricana.hu/hu/view/DTT_KOZL_TanacsokKozlonye_1983/?pg=85 +Rule Hungary 1981 1983 - Mar lastSun 0:00 1:00 S +Rule Hungary 1981 1983 - Sep lastSun 1:00 0 - +# # Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone Europe/Budapest 1:16:20 - LMT 1890 Oct +Zone Europe/Budapest 1:16:20 - LMT 1890 Nov 1 1:00 C-Eur CE%sT 1918 - 1:00 Hungary CE%sT 1941 Apr 8 +# https://library.hungaricana.hu/hu/view/OGYK_RT_1941/?pg=1204 +# https://library.hungaricana.hu/hu/view/OGYK_RT_1942/?pg=3955 + 1:00 Hungary CE%sT 1941 Apr 7 23:00 1:00 C-Eur CE%sT 1945 - 1:00 Hungary CE%sT 1980 Sep 28 2:00s + 1:00 Hungary CE%sT 1984 1:00 EU CE%sT # Iceland @@ -1601,7 +1681,7 @@ Zone Europe/Budapest 1:16:20 - LMT 1890 Oct # The information below is taken from the 1988 Almanak; see # http://www.almanak.hi.is/klukkan.html # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Iceland 1917 1919 - Feb 19 23:00 1:00 - Rule Iceland 1917 only - Oct 21 1:00 0 - Rule Iceland 1918 1919 - Nov 16 1:00 0 - @@ -1693,7 +1773,7 @@ Zone Atlantic/Reykjavik -1:28 - LMT 1908 # to 1944-06-04; although Rome was an open city during this period, it # was effectively controlled by Germany. # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Italy 1916 only - Jun 3 24:00 1:00 S Rule Italy 1916 1917 - Sep 30 24:00 0 - Rule Italy 1917 only - Mar 31 24:00 1:00 S @@ -1803,7 +1883,7 @@ Link Europe/Rome Europe/San_Marino # urged Lithuania and Estonia to adopt a similar time policy, but it # appears that they will not do so.... -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Latvia 1989 1996 - Mar lastSun 2:00s 1:00 S Rule Latvia 1989 1996 - Sep lastSun 2:00s 0 - @@ -1896,7 +1976,7 @@ Zone Europe/Vilnius 1:41:16 - LMT 1880 # Luxembourg # Whitman disagrees with most of these dates in minor ways; # go with Shanks & Pottenger. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Lux 1916 only - May 14 23:00 1:00 S Rule Lux 1916 only - Oct 1 1:00 0 - Rule Lux 1917 only - Apr 28 23:00 1:00 S @@ -1937,7 +2017,7 @@ Zone Europe/Luxembourg 0:24:36 - LMT 1904 Jun # From Paul Eggert (2016-10-21): # Assume 1900-1972 was like Rome, overriding Shanks. # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Malta 1973 only - Mar 31 0:00s 1:00 S Rule Malta 1973 only - Sep 29 0:00s 0 - Rule Malta 1974 only - Apr 21 0:00s 1:00 S @@ -2010,7 +2090,7 @@ Zone Europe/Malta 0:58:04 - LMT 1893 Nov 2 0:00s # Valletta # says the 2014-03-30 spring-forward transition was at 02:00 local time. # Guess that since 1997 Moldova has switched one hour before the EU. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Moldova 1997 max - Mar lastSun 2:00 1:00 S Rule Moldova 1997 max - Oct lastSun 3:00 0 - @@ -2028,11 +2108,24 @@ Zone Europe/Chisinau 1:55:20 - LMT 1880 2:00 Moldova EE%sT # Monaco -# Shanks & Pottenger give 0:09:20 for Paris Mean Time; go with Howse's -# more precise 0:09:21. +# +# From Michael Deckers (2020-06-12): +# In the "Journal de Monaco" of 1892-05-24, online at +# https://journaldemonaco.gouv.mc/var/jdm/storage/original/application/b1c67c12c5af11b41ea888fb048e4fe8.pdf +# we read: ... +# [In virtue of a Sovereign Ordinance of the May 13 of the current [year], +# legal time in the Principality will be set to, from the date of June 1, +# 1892 onwards, to the meridian of Paris, as in France.] +# In the "Journal de Monaco" of 1911-03-28, online at +# https://journaldemonaco.gouv.mc/var/jdm/storage/original/application/de74ffb7db53d4f599059fe8f0ed482a.pdf +# we read an ordinance of 1911-03-16: ... +# [Legal time in the Principality will be set, from the date of promulgation +# of the present ordinance, to legal time in France.... Consequently, legal +# time will be retarded by 9 minutes and 21 seconds.] +# # Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone Europe/Monaco 0:29:32 - LMT 1891 Mar 15 - 0:09:21 - PMT 1911 Mar 11 # Paris Mean Time +Zone Europe/Monaco 0:29:32 - LMT 1892 Jun 1 + 0:09:21 - PMT 1911 Mar 29 # Paris Mean Time 0:00 France WE%sT 1945 Sep 16 3:00 1:00 France CE%sT 1977 1:00 EU CE%sT @@ -2080,7 +2173,7 @@ Zone Europe/Monaco 0:29:32 - LMT 1891 Mar 15 # The data entries before 1945 are taken from # https://www.staff.science.uu.nl/~gent0113/wettijd/wettijd.htm -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Neth 1916 only - May 1 0:00 1:00 NST # Netherlands Summer Time Rule Neth 1916 only - Oct 1 0:00 0 AMT # Amsterdam Mean Time Rule Neth 1917 only - Apr 16 2:00s 1:00 NST @@ -2117,7 +2210,7 @@ Zone Europe/Amsterdam 0:19:32 - LMT 1835 # Norway # http://met.no/met/met_lex/q_u/sommertid.html (2004-01) agrees with Shanks & # Pottenger. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Norway 1916 only - May 22 1:00 1:00 S Rule Norway 1916 only - Sep 30 0:00 0 - Rule Norway 1945 only - Apr 2 2:00s 1:00 S @@ -2186,7 +2279,7 @@ Link Europe/Oslo Arctic/Longyearbyen # The 1919 dates and times can be found in Tygodnik Urzędowy nr 1 (1919-03-20), # pp 1-2. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Poland 1918 1919 - Sep 16 2:00s 0 - Rule Poland 1919 only - Apr 15 2:00s 1:00 S Rule Poland 1944 only - Apr 3 2:00s 1:00 S @@ -2257,7 +2350,7 @@ Zone Europe/Warsaw 1:24:00 - LMT 1880 # Guess that the Azores changed to EU rules in 1992 (since that's when Portugal # harmonized with EU rules), and that they stayed +0:00 that winter. # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S # DSH writes that despite Decree 1,469 (1915), the change to the clocks was not # done every year, depending on what Spain did, because of railroad schedules. # Go with Shanks & Pottenger. @@ -2370,7 +2463,7 @@ Zone Atlantic/Madeira -1:07:36 - LMT 1884 # Funchal # assume that Romania and Moldova switched to EU rules in 1997, # the same year as Bulgaria. # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Romania 1932 only - May 21 0:00s 1:00 S Rule Romania 1932 1939 - Oct Sun>=1 0:00s 0 - Rule Romania 1933 1939 - Apr Sun>=2 0:00s 1:00 S @@ -3468,14 +3561,14 @@ Link Europe/Prague Europe/Bratislava # fallback transition from the next day's 00:59... to 00:00. # From Michael Deckers (2016-12-15): -# The Royal Decree of 1900-06-26 quoted by Planesas, online at +# The Royal Decree of 1900-07-26 quoted by Planesas, online at # https://www.boe.es/datos/pdfs/BOE//1900/209/A00383-00384.pdf # says in its article 5 (my translation): # These dispositions will enter into force beginning with the # instant at which, according to the time indicated in article 1, # the 1st day of January of 1901 will begin. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Spain 1918 only - Apr 15 23:00 1:00 S Rule Spain 1918 1919 - Oct 6 24:00s 0 - Rule Spain 1919 only - Apr 6 23:00 1:00 S @@ -3612,7 +3705,7 @@ Zone Europe/Stockholm 1:12:12 - LMT 1879 Jan 1 # By the end of the 18th century clocks and watches became commonplace # and their performance improved enormously. Communities began to keep # mean time in preference to apparent time - Geneva from 1780 .... -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S # From Whitman (who writes "Midnight?"): # Rule Swiss 1940 only - Nov 2 0:00 1:00 S # Rule Swiss 1940 only - Dec 31 0:00 0 - @@ -3699,7 +3792,7 @@ Zone Europe/Stockholm 1:12:12 - LMT 1879 Jan 1 # 1853-07-16, though it probably occurred at some other date in Zurich, and # legal civil time probably changed at still some other transition date. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Swiss 1941 1942 - May Mon>=1 1:00 1:00 S Rule Swiss 1941 1942 - Oct Mon>=1 2:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] @@ -3848,7 +3941,7 @@ Zone Europe/Zurich 0:34:08 - LMT 1853 Jul 16 # See above comment. # Although Google Translate misfires on that source, it looks like # Turkey reversed last month's decision, and so will stay at +03. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Turkey 1916 only - May 1 0:00 1:00 S Rule Turkey 1916 only - Oct 1 0:00 0 - Rule Turkey 1920 only - Mar 28 0:00 1:00 S @@ -4006,7 +4099,7 @@ Zone Europe/Kiev 2:02:04 - LMT 1880 2:00 1:00 EEST 1991 Sep 29 3:00 2:00 E-Eur EE%sT 1995 2:00 EU EE%sT -# Ruthenia used CET 1990/1991. +# Transcarpathia used CET 1990/1991. # "Uzhhorod" is the transliteration of the Rusyn/Ukrainian pronunciation, but # "Uzhgorod" is more common in English. Zone Europe/Uzhgorod 1:29:12 - LMT 1890 Oct diff --git a/make/data/tzdata/leapseconds b/make/data/tzdata/leapseconds index fe8e170ed26..e00b297baed 100644 --- a/make/data/tzdata/leapseconds +++ b/make/data/tzdata/leapseconds @@ -91,11 +91,11 @@ Leap 2016 Dec 31 23:59:60 + S # Any additional leap seconds will come after this. # This Expires line is commented out for now, # so that pre-2020a zic implementations do not reject this file. -#Expires 2020 Dec 28 00:00:00 +#Expires 2021 Jun 28 00:00:00 # POSIX timestamps for the data in this file: #updated 1467936000 (2016-07-08 00:00:00 UTC) -#expires 1609113600 (2020-12-28 00:00:00 UTC) +#expires 1624838400 (2021-06-28 00:00:00 UTC) -# Updated through IERS Bulletin C59 -# File expires on: 28 December 2020 +# Updated through IERS Bulletin C60 +# File expires on: 28 June 2021 diff --git a/make/data/tzdata/northamerica b/make/data/tzdata/northamerica index 60c7addef09..9a70e313c78 100644 --- a/make/data/tzdata/northamerica +++ b/make/data/tzdata/northamerica @@ -193,7 +193,7 @@ # U.S. government action. So even though the "US" rules have changed # in the latest release, other countries won't be affected. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule US 1918 1919 - Mar lastSun 2:00 1:00 D Rule US 1918 1919 - Oct lastSun 2:00 0 S Rule US 1942 only - Feb 9 2:00 1:00 W # War @@ -370,7 +370,7 @@ Zone PST8PDT -8:00 US P%sT # Eastern time (i.e., -4:56:01.6) just before the 1883 switch. Round to the # nearest second. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER +# Rule NAME FROM TO - IN ON AT SAVE LETTER Rule NYC 1920 only - Mar lastSun 2:00 1:00 D Rule NYC 1920 only - Oct lastSun 2:00 0 S Rule NYC 1921 1966 - Apr lastSun 2:00 1:00 D @@ -454,7 +454,7 @@ Zone America/New_York -4:56:02 - LMT 1883 Nov 18 12:03:58 # The Tennessean 2007-05-11, republished 2015-04-06. # https://www.tennessean.com/story/insider/extras/2015/04/06/archives-seigenthaler-for-100-years-the-tennessean-had-it-covered/25348545/ -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER +# Rule NAME FROM TO - IN ON AT SAVE LETTER Rule Chicago 1920 only - Jun 13 2:00 1:00 D Rule Chicago 1920 1921 - Oct lastSun 2:00 0 S Rule Chicago 1921 only - Mar lastSun 2:00 1:00 D @@ -523,7 +523,7 @@ Zone America/North_Dakota/Beulah -6:47:07 - LMT 1883 Nov 18 12:12:53 # El Paso Times. 2018-10-24 06:40 -06. # https://www.elpasotimes.com/story/news/local/el-paso/2018/10/24/el-pasoans-were-time-rebels-fought-stay-mountain-zone/1744509002/ # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER +# Rule NAME FROM TO - IN ON AT SAVE LETTER Rule Denver 1920 1921 - Mar lastSun 2:00 1:00 D Rule Denver 1920 only - Oct lastSun 2:00 0 S Rule Denver 1921 only - May 22 2:00 0 S @@ -576,7 +576,7 @@ Zone America/Denver -6:59:56 - LMT 1883 Nov 18 12:00:04 # https://repository.uchastings.edu/cgi/viewcontent.cgi?article=1501&context=ca_ballot_props # https://repository.uchastings.edu/cgi/viewcontent.cgi?article=1636&context=ca_ballot_props # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER +# Rule NAME FROM TO - IN ON AT SAVE LETTER Rule CA 1948 only - Mar 14 2:01 1:00 D Rule CA 1949 only - Jan 1 2:00 0 S Rule CA 1950 1966 - Apr lastSun 1:00 1:00 D @@ -934,7 +934,7 @@ Zone America/Boise -7:44:49 - LMT 1883 Nov 18 12:15:11 # going to switch from Central to Eastern Time on March 11, 2007.... # http://www.indystar.com/apps/pbcs.dll/article?AID=/20070207/LOCAL190108/702070524/0/LOCAL -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER +# Rule NAME FROM TO - IN ON AT SAVE LETTER Rule Indianapolis 1941 only - Jun 22 2:00 1:00 D Rule Indianapolis 1941 1954 - Sep lastSun 2:00 0 S Rule Indianapolis 1946 1954 - Apr lastSun 2:00 1:00 D @@ -953,7 +953,7 @@ Zone America/Indiana/Indianapolis -5:44:38 - LMT 1883 Nov 18 12:15:22 # # Eastern Crawford County, Indiana, left its clocks alone in 1974, # as well as from 1976 through 2005. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER +# Rule NAME FROM TO - IN ON AT SAVE LETTER Rule Marengo 1951 only - Apr lastSun 2:00 1:00 D Rule Marengo 1951 only - Sep lastSun 2:00 0 S Rule Marengo 1954 1960 - Apr lastSun 2:00 1:00 D @@ -972,7 +972,7 @@ Zone America/Indiana/Marengo -5:45:23 - LMT 1883 Nov 18 12:14:37 # Daviess, Dubois, Knox, and Martin Counties, Indiana, # switched from eastern to central time in April 2006, then switched back # in November 2007. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER +# Rule NAME FROM TO - IN ON AT SAVE LETTER Rule Vincennes 1946 only - Apr lastSun 2:00 1:00 D Rule Vincennes 1946 only - Sep lastSun 2:00 0 S Rule Vincennes 1953 1954 - Apr lastSun 2:00 1:00 D @@ -997,7 +997,7 @@ Zone America/Indiana/Vincennes -5:50:07 - LMT 1883 Nov 18 12:09:53 # The Indianapolis News, Friday 27 October 1967 states that Perry County # returned to CST. It went again to EST on 27 April 1969, as documented by the # Indianapolis star of Saturday 26 April. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER +# Rule NAME FROM TO - IN ON AT SAVE LETTER Rule Perry 1955 only - May 1 0:00 1:00 D Rule Perry 1955 1960 - Sep lastSun 2:00 0 S Rule Perry 1956 1963 - Apr lastSun 2:00 1:00 D @@ -1014,7 +1014,7 @@ Zone America/Indiana/Tell_City -5:47:03 - LMT 1883 Nov 18 12:12:57 # # Pike County, Indiana moved from central to eastern time in 1977, # then switched back in 2006, then switched back again in 2007. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER +# Rule NAME FROM TO - IN ON AT SAVE LETTER Rule Pike 1955 only - May 1 0:00 1:00 D Rule Pike 1955 1960 - Sep lastSun 2:00 0 S Rule Pike 1956 1964 - Apr lastSun 2:00 1:00 D @@ -1035,7 +1035,7 @@ Zone America/Indiana/Petersburg -5:49:07 - LMT 1883 Nov 18 12:10:53 # An article on page A3 of the Sunday, 1991-10-27 Washington Post # notes that Starke County switched from Central time to Eastern time as of # 1991-10-27. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER +# Rule NAME FROM TO - IN ON AT SAVE LETTER Rule Starke 1947 1961 - Apr lastSun 2:00 1:00 D Rule Starke 1947 1954 - Sep lastSun 2:00 0 S Rule Starke 1955 1956 - Oct lastSun 2:00 0 S @@ -1052,7 +1052,7 @@ Zone America/Indiana/Knox -5:46:30 - LMT 1883 Nov 18 12:13:30 # # Pulaski County, Indiana, switched from eastern to central time in # April 2006 and then switched back in March 2007. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER +# Rule NAME FROM TO - IN ON AT SAVE LETTER Rule Pulaski 1946 1960 - Apr lastSun 2:00 1:00 D Rule Pulaski 1946 1954 - Sep lastSun 2:00 0 S Rule Pulaski 1955 1956 - Oct lastSun 2:00 0 S @@ -1094,7 +1094,7 @@ Zone America/Indiana/Vevay -5:40:16 - LMT 1883 Nov 18 12:19:44 # # Part of Kentucky left its clocks alone in 1974. # This also includes Clark, Floyd, and Harrison counties in Indiana. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER +# Rule NAME FROM TO - IN ON AT SAVE LETTER Rule Louisville 1921 only - May 1 2:00 1:00 D Rule Louisville 1921 only - Sep 1 2:00 0 S Rule Louisville 1941 only - Apr lastSun 2:00 1:00 D @@ -1208,7 +1208,7 @@ Zone America/Kentucky/Monticello -5:39:24 - LMT 1883 Nov 18 12:20:36 # election Michigan voters narrowly repealed DST, effective 1969. # # Most of Michigan observed DST from 1973 on, but was a bit late in 1975. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER +# Rule NAME FROM TO - IN ON AT SAVE LETTER Rule Detroit 1948 only - Apr lastSun 2:00 1:00 D Rule Detroit 1948 only - Sep lastSun 2:00 0 S # Zone NAME STDOFF RULES FORMAT [UNTIL] @@ -1225,7 +1225,7 @@ Zone America/Detroit -5:32:11 - LMT 1905 # # Dickinson, Gogebic, Iron, and Menominee Counties, Michigan, # switched from EST to CST/CDT in 1973. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER +# Rule NAME FROM TO - IN ON AT SAVE LETTER Rule Menominee 1946 only - Apr lastSun 2:00 1:00 D Rule Menominee 1946 only - Sep lastSun 2:00 0 S Rule Menominee 1966 only - Apr lastSun 2:00 1:00 D @@ -1395,7 +1395,7 @@ Zone America/Menominee -5:50:27 - LMT 1885 Sep 18 12:00 # Oct 31, to Oct 27, 1918 (and Sunday is a more likely transition day # than Thursday) in all Canadian rulesets. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Canada 1918 only - Apr 14 2:00 1:00 D Rule Canada 1918 only - Oct 27 2:00 0 S Rule Canada 1942 only - Feb 9 2:00 1:00 W # War @@ -1418,7 +1418,7 @@ Rule Canada 2007 max - Nov Sun>=1 2:00 0 S # that follows the rules is the southeast corner, including Port Hope # Simpson and Mary's Harbour, but excluding, say, Black Tickle. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule StJohns 1917 only - Apr 8 2:00 1:00 D Rule StJohns 1917 only - Sep 17 2:00 0 S # Whitman gives 1919 Apr 5 and 1920 Apr 5; go with Shanks & Pottenger. @@ -1520,7 +1520,7 @@ Zone America/Goose_Bay -4:01:40 - LMT 1884 # Happy Valley-Goose Bay # bill say that it is "accommodating the customs and practices" of those # regions, which suggests that they have always been in-line with Halifax. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Halifax 1916 only - Apr 1 0:00 1:00 D Rule Halifax 1916 only - Oct 1 0:00 0 S Rule Halifax 1920 only - May 9 0:00 1:00 D @@ -1586,7 +1586,7 @@ Zone America/Glace_Bay -3:59:48 - LMT 1902 Jun 15 # clear that this was the case since at least 1993. # For now, assume it started in 1993. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Moncton 1933 1935 - Jun Sun>=8 1:00 1:00 D Rule Moncton 1933 1935 - Sep Sun>=8 1:00 0 S Rule Moncton 1936 1938 - Jun Sun>=1 1:00 1:00 D @@ -1795,7 +1795,7 @@ Zone America/Blanc-Sablon -3:48:28 - LMT 1884 # With some exceptions, the use of daylight saving may be said to be limited # to those cities and towns lying between Quebec city and Windsor, Ont. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Toronto 1919 only - Mar 30 23:30 1:00 D Rule Toronto 1919 only - Oct 26 0:00 0 S Rule Toronto 1920 only - May 2 2:00 1:00 D @@ -1893,7 +1893,7 @@ Zone America/Atikokan -6:06:28 - LMT 1895 # starting 1966. Since 02:00s is clearly correct for 1967 on, assume # it was also 02:00s in 1966. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Winn 1916 only - Apr 23 0:00 1:00 D Rule Winn 1916 only - Sep 17 0:00 0 S Rule Winn 1918 only - Apr 14 2:00 1:00 D @@ -1984,7 +1984,7 @@ Zone America/Winnipeg -6:28:36 - LMT 1887 Jul 16 # long and rather painful to read. # http://www.qp.gov.sk.ca/documents/English/Statutes/Statutes/T14.pdf -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Regina 1918 only - Apr 14 2:00 1:00 D Rule Regina 1918 only - Oct 27 2:00 0 S Rule Regina 1930 1934 - May Sun>=1 0:00 1:00 D @@ -2034,7 +2034,7 @@ Zone America/Swift_Current -7:11:20 - LMT 1905 Sep # Boyer JP. Forcing Choice: The Risky Reward of Referendums. Dundum. 2017. # ISBN 978-1459739123. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Edm 1918 1919 - Apr Sun>=8 2:00 1:00 D Rule Edm 1918 only - Oct 27 2:00 0 S Rule Edm 1919 only - May 27 2:00 0 S @@ -2143,7 +2143,7 @@ Zone America/Edmonton -7:33:52 - LMT 1906 Sep # https://searcharchives.vancouver.ca/daylight-saving-1918-starts-again-july-7-1941-start-d-s-sept-27-end-of-d-s-1941 # We have no further details, so omit them for now. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Vanc 1918 only - Apr 14 2:00 1:00 D Rule Vanc 1918 only - Oct 27 2:00 0 S Rule Vanc 1942 only - Feb 9 2:00 1:00 W # War @@ -2472,7 +2472,19 @@ Zone America/Creston -7:46:04 - LMT 1884 # consistency with nearby Dawson Creek, Creston, and Fort Nelson. # https://yukon.ca/en/news/yukon-end-seasonal-time-change -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# From Andrew G. Smith (2020-09-24): +# Yukon has completed its regulatory change to be on UTC -7 year-round.... +# http://www.gov.yk.ca/legislation/regs/oic2020_125.pdf +# What we have done is re-defined Yukon Standard Time, as we are +# authorized to do under section 33 of our Interpretation Act: +# http://www.gov.yk.ca/legislation/acts/interpretation_c.pdf +# +# From Paul Eggert (2020-09-24): +# tzdb uses the obsolete YST abbreviation for standard time in Yukon through +# about 1970, and uses PST for standard time in Yukon since then. Consistent +# with that, use MST for -07, the new standard time in Yukon effective Nov. 1. + +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule NT_YK 1918 only - Apr 14 2:00 1:00 D Rule NT_YK 1918 only - Oct 27 2:00 0 S Rule NT_YK 1919 only - May 25 2:00 1:00 D @@ -2526,12 +2538,12 @@ Zone America/Inuvik 0 - -00 1953 # Inuvik founded Zone America/Whitehorse -9:00:12 - LMT 1900 Aug 20 -9:00 NT_YK Y%sT 1967 May 28 0:00 -8:00 NT_YK P%sT 1980 - -8:00 Canada P%sT 2020 Mar 8 2:00 + -8:00 Canada P%sT 2020 Nov 1 -7:00 - MST Zone America/Dawson -9:17:40 - LMT 1900 Aug 20 -9:00 NT_YK Y%sT 1973 Oct 28 0:00 -8:00 NT_YK P%sT 1980 - -8:00 Canada P%sT 2020 Mar 8 2:00 + -8:00 Canada P%sT 2020 Nov 1 -7:00 - MST @@ -2746,7 +2758,7 @@ Zone America/Dawson -9:17:40 - LMT 1900 Aug 20 # 5- The islands, reefs and keys shall take their timezone from the # longitude they are located at. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Mexico 1939 only - Feb 5 0:00 1:00 D Rule Mexico 1939 only - Jun 25 0:00 0 S Rule Mexico 1940 only - Dec 9 0:00 1:00 D @@ -2951,7 +2963,7 @@ Zone America/Tijuana -7:48:04 - LMT 1922 Jan 1 0:11:56 # rules to sync with the U.S. starting in 2007.... # http://www.jonesbahamas.com/?c=45&a=10412 -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Bahamas 1964 1975 - Oct lastSun 2:00 0 S Rule Bahamas 1964 1975 - Apr lastSun 2:00 1:00 D # Zone NAME STDOFF RULES FORMAT [UNTIL] @@ -2963,7 +2975,7 @@ Zone America/Nassau -5:09:30 - LMT 1912 Mar 2 # For 1899 Milne gives -3:58:29.2; round that. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Barb 1977 only - Jun 12 2:00 1:00 D Rule Barb 1977 1978 - Oct Sun>=1 2:00 0 S Rule Barb 1978 1980 - Apr Sun>=15 2:00 1:00 D @@ -2976,7 +2988,7 @@ Zone America/Barbados -3:58:29 - LMT 1924 # Bridgetown # Belize # Whitman entirely disagrees with Shanks; go with Shanks & Pottenger. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Belize 1918 1942 - Oct Sun>=2 0:00 0:30 -0530 Rule Belize 1919 1943 - Feb Sun>=9 0:00 0 CST Rule Belize 1973 only - Dec 5 0:00 1:00 CDT @@ -3013,7 +3025,7 @@ Zone Atlantic/Bermuda -4:19:18 - LMT 1930 Jan 1 2:00 # Hamilton # Milne gives -5:36:13.3 as San José mean time; round to nearest. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule CR 1979 1980 - Feb lastSun 0:00 1:00 D Rule CR 1979 1980 - Jun Sun>=1 0:00 0 S Rule CR 1991 1992 - Jan Sat>=15 0:00 1:00 D @@ -3187,7 +3199,7 @@ Zone America/Costa_Rica -5:36:13 - LMT 1890 # San José # From Paul Eggert (2012-11-03): # For now, assume the future rule is first Sunday in November. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Cuba 1928 only - Jun 10 0:00 1:00 D Rule Cuba 1928 only - Oct 10 0:00 0 S Rule Cuba 1940 1942 - Jun Sun>=1 0:00 1:00 D @@ -3256,7 +3268,7 @@ Zone America/Havana -5:29:28 - LMT 1890 # decided to revert. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule DR 1966 only - Oct 30 0:00 1:00 EDT Rule DR 1967 only - Feb 28 0:00 0 EST Rule DR 1969 1973 - Oct lastSun 0:00 0:30 -0430 @@ -3273,7 +3285,7 @@ Zone America/Santo_Domingo -4:39:36 - LMT 1890 # El Salvador -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Salv 1987 1988 - May Sun>=1 0:00 1:00 D Rule Salv 1987 1988 - Sep lastSun 0:00 0 S # There are too many San Salvadors elsewhere, so use America/El_Salvador @@ -3302,7 +3314,7 @@ Zone America/El_Salvador -5:56:48 - LMT 1921 # San Salvador # (2006-04-19), says DST ends at 24:00. See # http://www.sieca.org.gt/Sitio_publico/Energeticos/Doc/Medidas/Cambio_Horario_Nac_190406.pdf -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Guat 1973 only - Nov 25 0:00 1:00 D Rule Guat 1974 only - Feb 24 0:00 0 S Rule Guat 1983 only - May 21 0:00 1:00 D @@ -3383,7 +3395,7 @@ Zone America/Guatemala -6:02:04 - LMT 1918 Oct 5 # I have not been able to find a more authoritative source: # https://www.haitilibre.com/en/news-20319-haiti-notices-time-change-in-haiti.html -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Haiti 1983 only - May 8 0:00 1:00 D Rule Haiti 1984 1987 - Apr lastSun 0:00 1:00 D Rule Haiti 1983 1987 - Oct lastSun 0:00 0 S @@ -3431,7 +3443,7 @@ Zone America/Port-au-Prince -4:49:20 - LMT 1890 # http://www.laprensahn.com/pais_nota.php?id04962=7386 # So it seems that Honduras will not enter DST this year.... -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Hond 1987 1988 - May Sun>=1 0:00 1:00 D Rule Hond 1987 1988 - Sep lastSun 0:00 0 S Rule Hond 2006 only - May Sun>=1 0:00 1:00 D @@ -3522,7 +3534,7 @@ Zone America/Martinique -4:04:20 - LMT 1890 # Fort-de-France # The natural sun time is restored in all the national territory, in that the # time is returned one hour at 01:00 am of October 1 of 2006. # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Nic 1979 1980 - Mar Sun>=16 0:00 1:00 D Rule Nic 1979 1980 - Jun Mon>=23 0:00 0 S Rule Nic 2005 only - Apr 10 0:00 1:00 D diff --git a/make/data/tzdata/pacificnew b/make/data/tzdata/pacificnew deleted file mode 100644 index f19a876372c..00000000000 --- a/make/data/tzdata/pacificnew +++ /dev/null @@ -1,52 +0,0 @@ -# -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# tzdb data for proposed US election time (this file is obsolete) - -# This file is in the public domain, so clarified as of -# 2009-05-17 by Arthur David Olson. - -# From Arthur David Olson (1989-04-05): -# On 1989-04-05, the U. S. House of Representatives passed (238-154) a bill -# establishing "Pacific Presidential Election Time"; it was not acted on -# by the Senate or signed into law by the President. -# You might want to change the "PE" (Presidential Election) below to -# "Q" (Quadrennial) to maintain three-character zone abbreviations. -# If you're really conservative, you might want to change it to "D". -# Avoid "L" (Leap Year), which won't be true in 2100. - -# If Presidential Election Time is ever established, replace "XXXX" below -# with the year the law takes effect and uncomment the "##" lines. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -## Rule Twilite XXXX max - Apr Sun>=1 2:00 1:00 D -## Rule Twilite XXXX max uspres Oct lastSun 2:00 1:00 PE -## Rule Twilite XXXX max uspres Nov Sun>=7 2:00 0 S -## Rule Twilite XXXX max nonpres Oct lastSun 2:00 0 S - -# Zone NAME STDOFF RULES/SAVE FORMAT [UNTIL] -## Zone America/Los_Angeles-PET -8:00 US P%sT XXXX -## -8:00 Twilite P%sT - -# For now... -Link America/Los_Angeles US/Pacific-New ## diff --git a/make/data/tzdata/southamerica b/make/data/tzdata/southamerica index 51795f7621b..566dabfadb4 100644 --- a/make/data/tzdata/southamerica +++ b/make/data/tzdata/southamerica @@ -71,7 +71,7 @@ # I am sending modifications to the Argentine time zone table... # AR was chosen because they are the ISO letters that represent Argentina. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Arg 1930 only - Dec 1 0:00 1:00 - Rule Arg 1931 only - Apr 1 0:00 0 - Rule Arg 1931 only - Oct 15 0:00 1:00 - @@ -792,7 +792,7 @@ Zone America/La_Paz -4:32:36 - LMT 1890 # From Paul Eggert (2013-10-17): # For now, assume western Amazonas will change as well. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S # Decree 20,466 (1931-10-01) # Decree 21,896 (1932-01-10) Rule Brazil 1931 only - Oct 3 11:00 1:00 - @@ -1281,7 +1281,7 @@ Zone America/Rio_Branco -4:31:12 - LMT 1914 # For now, assume that they will not revert, # since they have extended the expiration date once already. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Chile 1927 1931 - Sep 1 0:00 1:00 - Rule Chile 1928 1932 - Apr 1 0:00 0 - Rule Chile 1968 only - Nov 3 4:00u 1:00 - @@ -1381,7 +1381,7 @@ Zone Antarctica/Palmer 0 - -00 1965 # Milne gives 4:56:16.4 for Bogotá time in 1899; round to nearest. He writes, # "A variation of fifteen minutes in the public clocks of Bogota is not rare." -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule CO 1992 only - May 3 0:00 1:00 - Rule CO 1993 only - Apr 4 0:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] @@ -1441,7 +1441,7 @@ Link America/Curacao America/Kralendijk # Caribbean Netherlands # (Not one step back), the clocks went back in 1993 and the experiment was not # repeated. For now, assume transitions were at 00:00 local time country-wide. # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Ecuador 1992 only - Nov 28 0:00 1:00 - Rule Ecuador 1993 only - Feb 5 0:00 0 - # @@ -1535,7 +1535,7 @@ Zone Pacific/Galapagos -5:58:24 - LMT 1931 # Puerto Baquerizo Moreno # For now we will assume permanent -03 for the Falklands # until advised differently (to apply for 2012 and beyond, after the 2011 # experiment was apparently successful.) -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Falk 1937 1938 - Sep lastSun 0:00 1:00 - Rule Falk 1938 1942 - Mar Sun>=19 0:00 0 - Rule Falk 1939 only - Oct 1 0:00 1:00 - @@ -1581,7 +1581,7 @@ Zone America/Guyana -3:52:40 - LMT 1915 Mar # Georgetown # No time of the day is established for the adjustment, so people normally # adjust their clocks at 0 hour of the given dates. # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Para 1975 1988 - Oct 1 0:00 1:00 - Rule Para 1975 1978 - Mar 1 0:00 0 - Rule Para 1979 1991 - Apr 1 0:00 0 - @@ -1674,7 +1674,7 @@ Zone America/Asuncion -3:50:40 - LMT 1890 # From Paul Eggert (2006-03-22): # Shanks & Pottenger don't have this transition. Assume 1986 was like 1987. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Peru 1938 only - Jan 1 0:00 1:00 - Rule Peru 1938 only - Apr 1 0:00 0 - Rule Peru 1938 1939 - Sep lastSun 0:00 1:00 - @@ -1770,7 +1770,7 @@ Link America/Port_of_Spain America/Tortola # Virgin Islands (UK) # https://www.impo.com.uy/diariooficial/1926/03/10/2 # https://www.impo.com.uy/diariooficial/1926/03/18/2 # -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +# Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Uruguay 1923 1925 - Oct 1 0:00 0:30 - Rule Uruguay 1924 1926 - Apr 1 0:00 0 - # From Tim Parenti (2018-02-15): diff --git a/make/data/tzdata/systemv b/make/data/tzdata/systemv deleted file mode 100644 index 9525ec47171..00000000000 --- a/make/data/tzdata/systemv +++ /dev/null @@ -1,62 +0,0 @@ -# -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# tzdb data for System V rules (this file is obsolete) - -# This file is in the public domain, so clarified as of -# 2009-05-17 by Arthur David Olson. - -# Old rules, should the need arise. -# No attempt is made to handle Newfoundland, since it cannot be expressed -# using the System V "TZ" scheme (half-hour offset), or anything outside -# North America (no support for non-standard DST start/end dates), nor -# the changes in the DST rules in the US after 1976 (which occurred after -# the old rules were written). -# -# If you need the old rules, uncomment ## lines. -# Compile this *without* leap second correction for true conformance. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule SystemV min 1973 - Apr lastSun 2:00 1:00 D -Rule SystemV min 1973 - Oct lastSun 2:00 0 S -Rule SystemV 1974 only - Jan 6 2:00 1:00 D -Rule SystemV 1974 only - Nov lastSun 2:00 0 S -Rule SystemV 1975 only - Feb 23 2:00 1:00 D -Rule SystemV 1975 only - Oct lastSun 2:00 0 S -Rule SystemV 1976 max - Apr lastSun 2:00 1:00 D -Rule SystemV 1976 max - Oct lastSun 2:00 0 S - -# Zone NAME STDOFF RULES/SAVE FORMAT [UNTIL] -## Zone SystemV/AST4ADT -4:00 SystemV A%sT -## Zone SystemV/EST5EDT -5:00 SystemV E%sT -## Zone SystemV/CST6CDT -6:00 SystemV C%sT -## Zone SystemV/MST7MDT -7:00 SystemV M%sT -## Zone SystemV/PST8PDT -8:00 SystemV P%sT -## Zone SystemV/YST9YDT -9:00 SystemV Y%sT -## Zone SystemV/AST4 -4:00 - AST -## Zone SystemV/EST5 -5:00 - EST -## Zone SystemV/CST6 -6:00 - CST -## Zone SystemV/MST7 -7:00 - MST -## Zone SystemV/PST8 -8:00 - PST -## Zone SystemV/YST9 -9:00 - YST -## Zone SystemV/HST10 -10:00 - HST diff --git a/make/devkit/createJMHBundle.sh b/make/devkit/createJMHBundle.sh index b56950c41ec..b460ee75311 100644 --- a/make/devkit/createJMHBundle.sh +++ b/make/devkit/createJMHBundle.sh @@ -26,7 +26,7 @@ # Create a bundle in the build directory, containing what's needed to # build and run JMH microbenchmarks from the OpenJDK build. -JMH_VERSION=1.21 +JMH_VERSION=1.26 COMMONS_MATH3_VERSION=3.2 JOPT_SIMPLE_VERSION=4.6 diff --git a/make/devkit/createMacosxDevkit.sh b/make/devkit/createMacosxDevkit.sh index 2a7dfe2037b..cd105823366 100644 --- a/make/devkit/createMacosxDevkit.sh +++ b/make/devkit/createMacosxDevkit.sh @@ -91,7 +91,6 @@ EXCLUDE_DIRS=" \ Platforms/AppleTVSimulator.platform \ Platforms/iPhoneSimulator.platform \ Platforms/WatchSimulator.platform \ - Contents/SharedFrameworks/LLDB.framework \ Contents/SharedFrameworks/ModelIO.framework \ Contents/SharedFrameworks/XCSUI.framework \ Contents/SharedFrameworks/SceneKit.framework \ diff --git a/make/devkit/createWindowsDevkit2017.sh b/make/devkit/createWindowsDevkit2017.sh index 91227259bdf..42c13251293 100644 --- a/make/devkit/createWindowsDevkit2017.sh +++ b/make/devkit/createWindowsDevkit2017.sh @@ -138,8 +138,8 @@ cp -r "$VS_INSTALL_DIR/$REDIST_SUBDIR/x86" $DEVKIT_ROOT/VC/redist/ cp $DEVKIT_ROOT/VC/redist/x86/$MSVCR_DLL $DEVKIT_ROOT/VC/bin/x86 cp $DEVKIT_ROOT/VC/redist/x86/$MSVCP_DLL $DEVKIT_ROOT/VC/bin/x86 cp $DEVKIT_ROOT/VC/redist/x64/$MSVCR_DLL $DEVKIT_ROOT/VC/bin/x64 -cp $DEVKIT_ROOT/VC/redist/x64/$MSVCR_DLL $DEVKIT_ROOT/VC/bin/x64 -cp $DEVKIT_ROOT/VC/redist/arm64/$MSVCP_DLL $DEVKIT_ROOT/VC/bin/arm64 +cp $DEVKIT_ROOT/VC/redist/x64/$MSVCP_DLL $DEVKIT_ROOT/VC/bin/x64 +cp $DEVKIT_ROOT/VC/redist/arm64/$MSVCR_DLL $DEVKIT_ROOT/VC/bin/arm64 cp $DEVKIT_ROOT/VC/redist/arm64/$MSVCP_DLL $DEVKIT_ROOT/VC/bin/arm64 ################################################################################ diff --git a/make/hotspot/gensrc/GensrcAdlc.gmk b/make/hotspot/gensrc/GensrcAdlc.gmk index 733658d5d8b..fb7d48f1e27 100644 --- a/make/hotspot/gensrc/GensrcAdlc.gmk +++ b/make/hotspot/gensrc/GensrcAdlc.gmk @@ -138,6 +138,7 @@ ifeq ($(call check-jvm-feature, compiler2), true) ifeq ($(HOTSPOT_TARGET_CPU_ARCH), aarch64) AD_SRC_FILES += $(call uniq, $(wildcard $(foreach d, $(AD_SRC_ROOTS), \ + $d/cpu/$(HOTSPOT_TARGET_CPU_ARCH)/$(HOTSPOT_TARGET_CPU_ARCH)_neon.ad \ $d/cpu/$(HOTSPOT_TARGET_CPU_ARCH)/$(HOTSPOT_TARGET_CPU_ARCH)_sve.ad \ ))) endif diff --git a/make/hotspot/gensrc/GensrcJvmti.gmk b/make/hotspot/gensrc/GensrcJvmti.gmk index 312c8bc737a..b31a6f52292 100644 --- a/make/hotspot/gensrc/GensrcJvmti.gmk +++ b/make/hotspot/gensrc/GensrcJvmti.gmk @@ -106,17 +106,6 @@ $(eval $(call SetupJvmtiGeneration, jvmti.h, jvmtiH.xsl, \ $(eval $(call SetupJvmtiGeneration, jvmti.html, jvmti.xsl, \ -PARAM majorversion $(VERSION_FEATURE))) -JVMTI_BC_SRCDIR := $(TOPDIR)/src/hotspot/share/interpreter/zero - -ifeq ($(call check-jvm-feature, zero), true) - $(eval $(call SetupXslTransform, bytecodeInterpreterWithChecks.cpp, \ - XML_FILE := $(JVMTI_BC_SRCDIR)/bytecodeInterpreterWithChecks.xml, \ - XSL_FILE := $(JVMTI_BC_SRCDIR)/bytecodeInterpreterWithChecks.xsl, \ - OUTPUT_DIR := $(JVMTI_OUTPUTDIR), \ - DEPS := $(JVMTI_BC_SRCDIR)/bytecodeInterpreter.cpp, \ - )) -endif - ################################################################################ # Copy jvmti.h to include dir diff --git a/make/hotspot/lib/CompileJvm.gmk b/make/hotspot/lib/CompileJvm.gmk index 441c09a3853..65edd047571 100644 --- a/make/hotspot/lib/CompileJvm.gmk +++ b/make/hotspot/lib/CompileJvm.gmk @@ -91,11 +91,11 @@ DISABLED_WARNINGS_clang := tautological-compare \ undefined-var-template sometimes-uninitialized unknown-pragmas \ delete-non-virtual-dtor missing-braces char-subscripts \ ignored-qualifiers missing-field-initializers mismatched-tags \ - shift-negative-value + shift-negative-value misleading-indentation DISABLED_WARNINGS_xlc := tautological-compare shift-negative-value -DISABLED_WARNINGS_microsoft := 4100 4127 4201 4244 4291 4351 \ +DISABLED_WARNINGS_microsoft := 4100 4127 4146 4201 4244 4291 4351 \ 4511 4512 4514 4624 4996 ################################################################################ diff --git a/make/hotspot/lib/JvmFeatures.gmk b/make/hotspot/lib/JvmFeatures.gmk index 3647806e1d7..d96d006c5fc 100644 --- a/make/hotspot/lib/JvmFeatures.gmk +++ b/make/hotspot/lib/JvmFeatures.gmk @@ -126,6 +126,7 @@ ifneq ($(call check-jvm-feature, cds), true) dynamicArchive.cpp \ filemap.cpp \ heapShared.cpp \ + lambdaFormInvokers.cpp \ metaspaceShared.cpp \ metaspaceShared_$(HOTSPOT_TARGET_CPU).cpp \ metaspaceShared_$(HOTSPOT_TARGET_CPU_ARCH).cpp \ diff --git a/make/hotspot/symbols/symbols-aix b/make/hotspot/symbols/symbols-aix index 0efd2dba97f..92703573a5f 100644 --- a/make/hotspot/symbols/symbols-aix +++ b/make/hotspot/symbols/symbols-aix @@ -21,7 +21,7 @@ # questions. # -JVM_handle_linux_signal +JVM_handle_aix_signal numa_error numa_warn sysThreadAvailableStackWithSlack diff --git a/make/hotspot/symbols/symbols-unix b/make/hotspot/symbols/symbols-unix index 97aa40b970b..1781d84ab94 100644 --- a/make/hotspot/symbols/symbols-unix +++ b/make/hotspot/symbols/symbols-unix @@ -143,14 +143,15 @@ JVM_InternString JVM_Interrupt JVM_InvokeMethod JVM_IsArrayClass -JVM_IsDynamicDumpingEnabled -JVM_IsSharingEnabled +JVM_IsCDSDumpingEnabled JVM_IsConstructorIx +JVM_IsDumpingClassList JVM_IsHiddenClass JVM_IsInterface JVM_IsPrimitiveClass JVM_IsRecord JVM_IsSameClassPackage +JVM_IsSharingEnabled JVM_IsSupportedJNIVersion JVM_IsThreadAlive JVM_IsVMGeneratedMethodIx @@ -158,6 +159,7 @@ JVM_LatestUserDefinedLoader JVM_LoadLibrary JVM_LookupDefineClass JVM_LookupLambdaProxyClassFromArchive +JVM_LogLambdaFormInvoker JVM_MaxMemory JVM_MaxObjectInspectionAge JVM_MonitorNotify @@ -169,11 +171,13 @@ JVM_NativePath JVM_NewArray JVM_NewInstanceFromConstructor JVM_NewMultiArray +JVM_PhantomReferenceRefersTo JVM_RaiseSignal JVM_RawMonitorCreate JVM_RawMonitorDestroy JVM_RawMonitorEnter JVM_RawMonitorExit +JVM_ReferenceRefersTo JVM_RegisterLambdaProxyClassForArchiving JVM_RegisterSignal JVM_ReleaseUTF diff --git a/make/jdk/src/classes/build/tools/blacklistedcertsconverter/BlacklistedCertsConverter.java b/make/jdk/src/classes/build/tools/blacklistedcertsconverter/BlacklistedCertsConverter.java index 54c60eb43d1..653a1db10dd 100644 --- a/make/jdk/src/classes/build/tools/blacklistedcertsconverter/BlacklistedCertsConverter.java +++ b/make/jdk/src/classes/build/tools/blacklistedcertsconverter/BlacklistedCertsConverter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,14 +25,24 @@ package build.tools.blacklistedcertsconverter; +import java.io.IOException; +import java.math.BigInteger; import java.security.MessageDigest; +import java.security.PublicKey; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.security.interfaces.ECPublicKey; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.Set; import java.util.TreeSet; +import sun.security.util.DerInputStream; +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; /** * Converts blacklisted.certs.pem from System.in to blacklisted.certs in @@ -75,8 +85,8 @@ public static void main(String[] args) throws Exception { // Output sorted so that it's easy to locate an entry. Set fingerprints = new TreeSet<>(); for (Certificate cert: certs) { - fingerprints.add( - getCertificateFingerPrint(mdAlg, (X509Certificate)cert)); + fingerprints.addAll( + getCertificateFingerPrints(mdAlg, (X509Certificate)cert)); } for (String s: fingerprints) { @@ -97,17 +107,90 @@ private static void byte2hex(byte b, StringBuffer buf) { } /** - * Gets the requested finger print of the certificate. + * Computes the possible fingerprints of the certificate. */ - private static String getCertificateFingerPrint( + private static List getCertificateFingerPrints( String mdAlg, X509Certificate cert) throws Exception { - byte[] encCertInfo = cert.getEncoded(); - MessageDigest md = MessageDigest.getInstance(mdAlg); - byte[] digest = md.digest(encCertInfo); - StringBuffer buf = new StringBuffer(); - for (int i = 0; i < digest.length; i++) { - byte2hex(digest[i], buf); + List fingerprints = new ArrayList<>(); + for (byte[] encoding : altEncodings(cert)) { + MessageDigest md = MessageDigest.getInstance(mdAlg); + byte[] digest = md.digest(encoding); + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < digest.length; i++) { + byte2hex(digest[i], buf); + } + fingerprints.add(buf.toString()); + } + return fingerprints; + } + + private static List altEncodings(X509Certificate c) + throws Exception { + List result = new ArrayList<>(); + + DerValue d = new DerValue(c.getEncoded()); + DerValue[] seq = new DerValue[3]; + // tbsCertificate + seq[0] = d.data.getDerValue(); + // signatureAlgorithm + seq[1] = d.data.getDerValue(); + // signature + seq[2] = d.data.getDerValue(); + + List algIds = Arrays.asList(seq[1], altAlgId(seq[1])); + + List sigs; + PublicKey p = c.getPublicKey(); + if (p instanceof ECPublicKey) { + ECPublicKey ep = (ECPublicKey) p; + BigInteger mod = ep.getParams().getOrder(); + sigs = Arrays.asList(seq[2], altSig(mod, seq[2])); + } else { + sigs = Arrays.asList(seq[2]); + } + + for (DerValue algId : algIds) { + for (DerValue sig : sigs) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putDerValue(seq[0]); + tmp.putDerValue(algId); + tmp.putDerValue(sig); + DerOutputStream tmp2 = new DerOutputStream(); + tmp2.write(DerValue.tag_Sequence, tmp); + result.add(tmp2.toByteArray()); + } + } + return result; + } + + private static DerValue altSig(BigInteger mod, DerValue sig) + throws IOException { + byte[] sigBits = sig.getBitString(); + DerInputStream in = + new DerInputStream(sigBits, 0, sigBits.length, false); + DerValue[] values = in.getSequence(2); + BigInteger r = values[0].getBigInteger(); + BigInteger s = values[1].getBigInteger(); + BigInteger s2 = s.negate().mod(mod); + DerOutputStream out = new DerOutputStream(); + out.putInteger(r); + out.putInteger(s2); + DerOutputStream tmp = new DerOutputStream(); + tmp.putBitString(new DerValue(DerValue.tag_Sequence, + out.toByteArray()).toByteArray()); + return new DerValue(tmp.toByteArray()); + } + + private static DerValue altAlgId(DerValue algId) throws IOException { + DerInputStream in = algId.toDerInputStream(); + DerOutputStream bytes = new DerOutputStream(); + bytes.putOID(in.getOID()); + // encode parameters as NULL if not present or omit if NULL + if (in.available() == 0) { + bytes.putNull(); } - return buf.toString(); + DerOutputStream tmp = new DerOutputStream(); + tmp.write(DerValue.tag_Sequence, bytes); + return new DerValue(tmp.toByteArray()); } } diff --git a/make/jdk/src/classes/build/tools/spp/Spp.java b/make/jdk/src/classes/build/tools/spp/Spp.java index 6921c65667b..2a0cb57bc39 100644 --- a/make/jdk/src/classes/build/tools/spp/Spp.java +++ b/make/jdk/src/classes/build/tools/spp/Spp.java @@ -106,7 +106,7 @@ public static void main(String args[]) throws Exception { static final String LNSEP = System.getProperty("line.separator"); static final String KEY = "([a-zA-Z0-9]+)"; static final String VAR = "([a-zA-Z0-9_\\-]+)"; - static final String TEXT = "([a-zA-Z0-9&;,.<>/#() \\?\\[\\]\\$]+)"; // $ -- hack embedded $var$ + static final String TEXT = "([\\p{Print}&&[^{#:}]]+)"; static final int GN_NOT = 1; static final int GN_KEY = 2; @@ -140,6 +140,10 @@ void append(StringBuffer buf, String ln, } } } + if (repl == null) { + System.err.println("Error: undefined variable in line " + ln); + System.exit(-1); + } vardef.appendReplacement(buf, repl); } vardef.appendTail(buf); diff --git a/make/modules/java.base/Copy.gmk b/make/modules/java.base/Copy.gmk index 9071f4e6e37..040b7588ba1 100644 --- a/make/modules/java.base/Copy.gmk +++ b/make/modules/java.base/Copy.gmk @@ -182,12 +182,16 @@ endif ################################################################################ -$(eval $(call SetupCopyFiles, COPY_NET_PROPERTIES, \ - FILES := $(TOPDIR)/src/java.base/share/conf/net.properties, \ - DEST := $(CONF_DST_DIR), \ -)) +NET_PROPERTIES_SRCS := $(TOPDIR)/src/java.base/share/conf/net.properties \ + $(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/conf/net.properties + +NET_PROPERTIES_DST := $(CONF_DST_DIR)/net.properties + +$(NET_PROPERTIES_DST): $(NET_PROPERTIES_SRCS) + $(call MakeTargetDir) + $(CAT) $(NET_PROPERTIES_SRCS) > $@ -TARGETS += $(COPY_NET_PROPERTIES) +TARGETS += $(NET_PROPERTIES_DST) ifeq ($(call isTargetOs, linux), true) $(eval $(call SetupCopyFiles, COPY_SDP_CONF, \ diff --git a/make/modules/java.base/gendata/GendataTZDB.gmk b/make/modules/java.base/gendata/GendataTZDB.gmk index 54e6582d81d..1352178694f 100644 --- a/make/modules/java.base/gendata/GendataTZDB.gmk +++ b/make/modules/java.base/gendata/GendataTZDB.gmk @@ -29,7 +29,7 @@ GENDATA_TZDB := # Time zone data file creation # TZDATA_DIR := $(TOPDIR)/make/data/tzdata -TZDATA_TZFILE := africa antarctica asia australasia europe northamerica pacificnew southamerica backward etcetera gmt jdk11_backward +TZDATA_TZFILE := africa antarctica asia australasia europe northamerica southamerica backward etcetera gmt jdk11_backward TZDATA_TZFILES := $(addprefix $(TZDATA_DIR)/,$(TZDATA_TZFILE)) GENDATA_TZDB_DAT := $(SUPPORT_OUTPUTDIR)/modules_libs/$(MODULE)/tzdb.dat diff --git a/make/modules/java.base/lib/CoreLibraries.gmk b/make/modules/java.base/lib/CoreLibraries.gmk index f2b94fe717e..1d5fede2aa8 100644 --- a/make/modules/java.base/lib/CoreLibraries.gmk +++ b/make/modules/java.base/lib/CoreLibraries.gmk @@ -49,7 +49,7 @@ $(eval $(call SetupNativeCompilation, BUILD_LIBFDLIBM, \ CFLAGS_windows_debug := -DLOGGING, \ CFLAGS_aix := -qfloat=nomaf, \ DISABLED_WARNINGS_gcc := sign-compare misleading-indentation array-bounds, \ - DISABLED_WARNINGS_clang := sign-compare, \ + DISABLED_WARNINGS_clang := sign-compare misleading-indentation, \ DISABLED_WARNINGS_microsoft := 4146 4244 4018, \ ARFLAGS := $(ARFLAGS), \ OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libfdlibm, \ diff --git a/make/modules/java.desktop/lib/Awt2dLibraries.gmk b/make/modules/java.desktop/lib/Awt2dLibraries.gmk index 7fbd1049f89..3203378d00a 100644 --- a/make/modules/java.desktop/lib/Awt2dLibraries.gmk +++ b/make/modules/java.desktop/lib/Awt2dLibraries.gmk @@ -435,7 +435,6 @@ endif ifeq ($(USE_EXTERNAL_HARFBUZZ), true) LIBHARFBUZZ_LIBS := $(HARFBUZZ_LIBS) else - HARFBUZZ_CFLAGS := -DHAVE_OT -DHAVE_FALLBACK -DHAVE_UCDN -DHAVE_ROUND # This is better than adding EXPORT_ALL_SYMBOLS ifneq ($(filter $(TOOLCHAIN_TYPE), gcc clang), ) @@ -493,7 +492,7 @@ else maybe-uninitialized class-memaccess, \ DISABLED_WARNINGS_clang := unused-value incompatible-pointer-types \ tautological-constant-out-of-range-compare int-to-pointer-cast \ - undef missing-field-initializers, \ + undef missing-field-initializers range-loop-analysis, \ DISABLED_WARNINGS_microsoft := 4267 4244 4090 4146 4334 4819 4101 4068 4805 4138, \ LDFLAGS := $(LDFLAGS_JDKLIB) \ $(call SET_SHARED_LIBRARY_ORIGIN), \ diff --git a/make/modules/jdk.javadoc/Gendata.gmk b/make/modules/jdk.javadoc/Gendata.gmk index 5b4485808c7..0ee146a1e21 100644 --- a/make/modules/jdk.javadoc/Gendata.gmk +++ b/make/modules/jdk.javadoc/Gendata.gmk @@ -54,7 +54,7 @@ $(eval $(call SetupJavaCompilation, COMPILE_CREATE_SYMBOLS, \ SRC := $(TOPDIR)/make/langtools/src/classes \ $(TOPDIR)/src/jdk.jdeps/share/classes, \ INCLUDES := build/tools/symbolgenerator com/sun/tools/classfile, \ - BIN := $(BUILDTOOLS_OUTPUTDIR)/create_symbols, \ + BIN := $(BUILDTOOLS_OUTPUTDIR)/create_symbols_javadoc, \ DISABLED_WARNINGS := options, \ JAVAC_FLAGS := \ $(INTERIM_LANGTOOLS_ARGS) \ @@ -71,7 +71,7 @@ $(SUPPORT_OUTPUTDIR)/javadoc-symbols/symbols: \ $(ECHO) Creating javadoc element list $(JAVA_SMALL) $(INTERIM_LANGTOOLS_ARGS) \ $(COMPILECREATESYMBOLS_ADD_EXPORTS) \ - -classpath $(BUILDTOOLS_OUTPUTDIR)/create_symbols \ + -classpath $(BUILDTOOLS_OUTPUTDIR)/create_symbols_javadoc \ build.tools.symbolgenerator.CreateSymbols \ build-javadoc-data \ $(CT_DATA_DESCRIPTION) \ @@ -79,7 +79,7 @@ $(SUPPORT_OUTPUTDIR)/javadoc-symbols/symbols: \ 11 $(JAVA_SMALL) $(INTERIM_LANGTOOLS_ARGS) \ $(COMPILECREATESYMBOLS_ADD_EXPORTS) \ - -classpath $(BUILDTOOLS_OUTPUTDIR)/create_symbols \ + -classpath $(BUILDTOOLS_OUTPUTDIR)/create_symbols_javadoc \ build.tools.symbolgenerator.JavadocElementList \ $(JDK_OUTPUTDIR)/modules/jdk.javadoc/jdk/javadoc/internal/doclets/toolkit/resources/releases/element-list-$(JDK_SOURCE_TARGET_VERSION).txt \ $(JAVADOC_MODULESOURCEPATH) \ diff --git a/make/modules/jdk.incubator.jpackage/Gensrc.gmk b/make/modules/jdk.jpackage/Gensrc.gmk similarity index 93% rename from make/modules/jdk.incubator.jpackage/Gensrc.gmk rename to make/modules/jdk.jpackage/Gensrc.gmk index 5948a80f120..6f3e8b08119 100644 --- a/make/modules/jdk.incubator.jpackage/Gensrc.gmk +++ b/make/modules/jdk.jpackage/Gensrc.gmk @@ -31,7 +31,7 @@ include GensrcCommonJdk.gmk ifeq ($(call isTargetOs, macosx), true) ENTITLEMENTS_SRC_FILE := $(TOPDIR)/make/data/macosxsigning/java.plist ENTITLEMENTS_TARGET_FILE := \ - $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/jdk/incubator/jpackage/internal/resources/entitlements.plist + $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/jdk/jpackage/internal/resources/entitlements.plist $(ENTITLEMENTS_TARGET_FILE): $(ENTITLEMENTS_SRC_FILE) $(call install-file) diff --git a/make/modules/jdk.incubator.jpackage/Launcher.gmk b/make/modules/jdk.jpackage/Launcher.gmk similarity index 95% rename from make/modules/jdk.incubator.jpackage/Launcher.gmk rename to make/modules/jdk.jpackage/Launcher.gmk index 7a25dae733c..8d553d5c107 100644 --- a/make/modules/jdk.incubator.jpackage/Launcher.gmk +++ b/make/modules/jdk.jpackage/Launcher.gmk @@ -26,5 +26,5 @@ include LauncherCommon.gmk $(eval $(call SetupBuildLauncher, jpackage, \ - MAIN_CLASS := jdk.incubator.jpackage.main.Main, \ + MAIN_CLASS := jdk.jpackage.main.Main, \ )) diff --git a/make/modules/jdk.incubator.jpackage/Lib.gmk b/make/modules/jdk.jpackage/Lib.gmk similarity index 91% rename from make/modules/jdk.incubator.jpackage/Lib.gmk rename to make/modules/jdk.jpackage/Lib.gmk index 7ffef99afe4..7dfb70be5a6 100644 --- a/make/modules/jdk.incubator.jpackage/Lib.gmk +++ b/make/modules/jdk.jpackage/Lib.gmk @@ -29,8 +29,8 @@ include LibCommon.gmk JPACKAGE_APPLAUNCHER_SRC := \ - $(call FindSrcDirsForComponent, jdk.incubator.jpackage, applauncher) \ - $(call FindSrcDirsForComponent, jdk.incubator.jpackage, common) + $(call FindSrcDirsForComponent, jdk.jpackage, applauncher) \ + $(call FindSrcDirsForComponent, jdk.jpackage, common) ifeq ($(call isTargetOs, windows), true) @@ -42,7 +42,7 @@ else endif -JPACKAGE_OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/incubator/jpackage/internal/resources +JPACKAGE_OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/jpackage/internal/resources JPACKAGE_CXXFLAGS_windows := -EHsc -DUNICODE -D_UNICODE # Output app launcher executable in resources dir, and symbols in the object dir @@ -73,7 +73,7 @@ ifeq ($(call isTargetOs, windows), true) $(eval $(call SetupJdkLibrary, BUILD_LIB_JPACKAGE, \ NAME := jpackage, \ OPTIMIZATION := LOW, \ - EXTRA_SRC := jdk.incubator.jpackage:common, \ + EXTRA_SRC := jdk.jpackage:common, \ CXXFLAGS := $(CXXFLAGS_JDKLIB) $(JPACKAGE_CXXFLAGS_windows), \ LDFLAGS := $(LDFLAGS_JDKLIB) $(LDFLAGS_CXX_JDK) \ $(call SET_SHARED_LIBRARY_ORIGIN), \ @@ -99,8 +99,8 @@ ifeq ($(call isTargetOs, windows), true) TARGETS += $(BUILD_LIB_WIXHELPER) JPACKAGE_MSIWRAPPER_SRC := \ - $(call FindSrcDirsForComponent, jdk.incubator.jpackage, msiwrapper) \ - $(call FindSrcDirsForComponent, jdk.incubator.jpackage, common) + $(call FindSrcDirsForComponent, jdk.jpackage, msiwrapper) \ + $(call FindSrcDirsForComponent, jdk.jpackage, common) # Build exe installer wrapper for msi installer $(eval $(call SetupJdkExecutable, BUILD_JPACKAGE_MSIWRAPPER, \ diff --git a/make/scripts/compare.sh b/make/scripts/compare.sh index 25630199a21..5d0e846e755 100644 --- a/make/scripts/compare.sh +++ b/make/scripts/compare.sh @@ -696,7 +696,7 @@ compare_bin_file() { # pdb files. PDB_DIRS="$(ls -d \ {$OTHER,$THIS}/support/modules_{cmds,libs}/{*,*/*} \ - {$OTHER,$THIS}/support/native/jdk.incubator.jpackage/* \ + {$OTHER,$THIS}/support/native/jdk.jpackage/* \ )" export _NT_SYMBOL_PATH="$(echo $PDB_DIRS | tr ' ' ';')" fi diff --git a/make/test/BuildMicrobenchmark.gmk b/make/test/BuildMicrobenchmark.gmk index 3bbbea47b8e..55e5026eb38 100644 --- a/make/test/BuildMicrobenchmark.gmk +++ b/make/test/BuildMicrobenchmark.gmk @@ -90,11 +90,10 @@ $(eval $(call SetupJavaCompilation, BUILD_JDK_MICROBENCHMARK, \ TARGET_RELEASE := $(TARGET_RELEASE_NEWJDK_UPGRADED), \ SMALL_JAVA := false, \ CLASSPATH := $(MICROBENCHMARK_CLASSPATH), \ - DISABLED_WARNINGS := processing rawtypes cast serial preview, \ + DISABLED_WARNINGS := processing rawtypes cast serial, \ SRC := $(MICROBENCHMARK_SRC), \ BIN := $(MICROBENCHMARK_CLASSES), \ JAVA_FLAGS := --add-modules jdk.unsupported --limit-modules java.management, \ - JAVAC_FLAGS := --enable-preview, \ )) $(BUILD_JDK_MICROBENCHMARK): $(JMH_COMPILE_JARS) diff --git a/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/CMMTests.java b/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/CMMTests.java index 210970f6469..3c0f936358c 100644 --- a/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/CMMTests.java +++ b/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/CMMTests.java @@ -73,14 +73,16 @@ public static void init() { ColorSpace.CS_sRGB, ColorSpace.CS_GRAY, ColorSpace.CS_LINEAR_RGB, - ColorSpace.CS_CIEXYZ + ColorSpace.CS_CIEXYZ, + ColorSpace.CS_PYCC }; String[] csNames = new String[]{ "CS_sRGB", "CS_GRAY", "CS_LINEAR_RGB", - "CS_CIEXYZ" + "CS_CIEXYZ", + "CS_PYCC" }; csList = new Option.IntList(cmmOptRoot, diff --git a/src/demo/share/jfc/Notepad/Notepad.java b/src/demo/share/jfc/Notepad/Notepad.java index 3ebe3f07d22..cb4552f94cf 100644 --- a/src/demo/share/jfc/Notepad/Notepad.java +++ b/src/demo/share/jfc/Notepad/Notepad.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -60,7 +60,7 @@ * @author Timothy Prinzing */ @SuppressWarnings("serial") -class Notepad extends JPanel { +public class Notepad extends JPanel { protected static Properties properties; private static ResourceBundle resources; diff --git a/src/hotspot/cpu/aarch64/aarch64-asmtest.py b/src/hotspot/cpu/aarch64/aarch64-asmtest.py index 104104b09a9..615fe5e045f 100644 --- a/src/hotspot/cpu/aarch64/aarch64-asmtest.py +++ b/src/hotspot/cpu/aarch64/aarch64-asmtest.py @@ -1,4 +1,7 @@ +import os import random +import subprocess +import sys AARCH64_AS = "as" AARCH64_OBJDUMP = "objdump" @@ -129,6 +132,8 @@ class OperandFactory: _modes = {'x' : GeneralRegister, 'w' : GeneralRegister, + 'b' : FloatRegister, + 'h' : FloatRegister, 's' : FloatRegister, 'd' : FloatRegister, 'z' : FloatZero, @@ -198,16 +203,16 @@ def __init__(self, name, mode): self.isFloat = (mode == 'd') | (mode == 's') if self.isFloat: self.isWord = mode != 'd' - self.asmRegPrefix = ["d", "s"][self.isWord] + self.asmRegPrefix = ["d", "s"][self.isWord] else: self.isWord = mode != 'x' self.asmRegPrefix = ["x", "w"][self.isWord] - + def name(self): return self._name + (self.mode if self.mode != 'x' else '') - + def aname(self): - return (self._name+mode if (mode == 'b' or mode == 'h') + return (self._name+mode if (mode == 'b' or mode == 'h') else self._name) class ThreeRegInstruction(Instruction): @@ -220,17 +225,17 @@ def generate(self): def cstr(self): return (super(ThreeRegInstruction, self).cstr() - + ('%s, %s, %s' + + ('%s, %s, %s' % (self.reg[0], self.reg[1], self.reg[2]))) - + def astr(self): prefix = self.asmRegPrefix return (super(ThreeRegInstruction, self).astr() - + ('%s, %s, %s' + + ('%s, %s, %s' % (self.reg[0].astr(prefix), self.reg[1].astr(prefix), self.reg[2].astr(prefix)))) - + class FourRegInstruction(ThreeRegInstruction): def generate(self): @@ -241,12 +246,12 @@ def generate(self): def cstr(self): return (super(FourRegInstruction, self).cstr() + (', %s' % self.reg[3])) - + def astr(self): prefix = self.asmRegPrefix return (super(FourRegInstruction, self).astr() + (', %s' % self.reg[3].astr(prefix))) - + class TwoRegInstruction(Instruction): def generate(self): @@ -261,17 +266,17 @@ def cstr(self): def astr(self): prefix = self.asmRegPrefix return (super(TwoRegInstruction, self).astr() - + ('%s, %s' + + ('%s, %s' % (self.reg[0].astr(prefix), self.reg[1].astr(prefix)))) - + class TwoRegImmedInstruction(TwoRegInstruction): def generate(self): super(TwoRegImmedInstruction, self).generate() self.immed = random.randint(0, 1<<11 -1) return self - + def cstr(self): return (super(TwoRegImmedInstruction, self).cstr() + ', %su' % self.immed) @@ -301,9 +306,9 @@ def generate(self): self.kind = ShiftKind().generate() self.distance = random.randint(0, (1<<5)-1 if self.isWord else (1<<6)-1) return self - + def cstr(self): - return ('%s, Assembler::%s, %s);' + return ('%s, Assembler::%s, %s);' % (ThreeRegInstruction.cstr(self), self.kind.cstr(), self.distance)) @@ -314,9 +319,9 @@ def astr(self): self.distance)) class AddSubCarryOp(ThreeRegInstruction): - + def cstr(self): - return ('%s);' + return ('%s);' % (ThreeRegInstruction.cstr(self))) class AddSubExtendedOp(ThreeRegInstruction): @@ -332,76 +337,75 @@ def generate(self): def cstr(self): return (super(AddSubExtendedOp, self).cstr() - + (", ext::" + AddSubExtendedOp.optNames[self.option] + + (", ext::" + AddSubExtendedOp.optNames[self.option] + ", " + str(self.amount) + ");")) - + def astr(self): return (super(AddSubExtendedOp, self).astr() - + (", " + AddSubExtendedOp.optNames[self.option] + + (", " + AddSubExtendedOp.optNames[self.option] + " #" + str(self.amount))) class AddSubImmOp(TwoRegImmedInstruction): def cstr(self): return super(AddSubImmOp, self).cstr() + ");" - + class LogicalImmOp(AddSubImmOp): # These tables are legal immediate logical operands immediates32 \ - = [0x1, 0x3f, 0x1f0, 0x7e0, - 0x1c00, 0x3ff0, 0x8000, 0x1e000, - 0x3e000, 0x78000, 0xe0000, 0x100000, - 0x1fffe0, 0x3fe000, 0x780000, 0x7ffff8, - 0xff8000, 0x1800180, 0x1fffc00, 0x3c003c0, - 0x3ffff00, 0x7c00000, 0x7fffe00, 0xf000f00, - 0xfffe000, 0x18181818, 0x1ffc0000, 0x1ffffffe, - 0x3f003f00, 0x3fffe000, 0x60006000, 0x7f807f80, - 0x7ffffc00, 0x800001ff, 0x803fffff, 0x9f9f9f9f, - 0xc0000fff, 0xc0c0c0c0, 0xe0000000, 0xe003e003, - 0xe3ffffff, 0xf0000fff, 0xf0f0f0f0, 0xf80000ff, - 0xf83ff83f, 0xfc00007f, 0xfc1fffff, 0xfe0001ff, - 0xfe3fffff, 0xff003fff, 0xff800003, 0xff87ff87, - 0xffc00fff, 0xffe0000f, 0xffefffef, 0xfff1fff1, - 0xfff83fff, 0xfffc0fff, 0xfffe0fff, 0xffff3fff, - 0xffffc007, 0xffffe1ff, 0xfffff80f, 0xfffffe07, + = [0x1, 0x3f, 0x1f0, 0x7e0, + 0x1c00, 0x3ff0, 0x8000, 0x1e000, + 0x3e000, 0x78000, 0xe0000, 0x100000, + 0x1fffe0, 0x3fe000, 0x780000, 0x7ffff8, + 0xff8000, 0x1800180, 0x1fffc00, 0x3c003c0, + 0x3ffff00, 0x7c00000, 0x7fffe00, 0xf000f00, + 0xfffe000, 0x18181818, 0x1ffc0000, 0x1ffffffe, + 0x3f003f00, 0x3fffe000, 0x60006000, 0x7f807f80, + 0x7ffffc00, 0x800001ff, 0x803fffff, 0x9f9f9f9f, + 0xc0000fff, 0xc0c0c0c0, 0xe0000000, 0xe003e003, + 0xe3ffffff, 0xf0000fff, 0xf0f0f0f0, 0xf80000ff, + 0xf83ff83f, 0xfc00007f, 0xfc1fffff, 0xfe0001ff, + 0xfe3fffff, 0xff003fff, 0xff800003, 0xff87ff87, + 0xffc00fff, 0xffe0000f, 0xffefffef, 0xfff1fff1, + 0xfff83fff, 0xfffc0fff, 0xfffe0fff, 0xffff3fff, + 0xffffc007, 0xffffe1ff, 0xfffff80f, 0xfffffe07, 0xffffffbf, 0xfffffffd] immediates \ - = [0x1, 0x1f80, 0x3fff0, 0x3ffffc, - 0x3fe0000, 0x1ffc0000, 0xf8000000, 0x3ffffc000, - 0xffffffe00, 0x3ffffff800, 0xffffc00000, 0x3f000000000, - 0x7fffffff800, 0x1fe000001fe0, 0x3ffffff80000, 0xc00000000000, - 0x1ffc000000000, 0x3ffff0003ffff, 0x7ffffffe00000, 0xfffffffffc000, - 0x1ffffffffffc00, 0x3fffffffffff00, 0x7ffffffffffc00, 0xffffffffff8000, - 0x1ffffffff800000, 0x3fffffc03fffffc, 0x7fffc0000000000, 0xff80ff80ff80ff8, - 0x1c00000000000000, 0x1fffffffffff0000, 0x3fffff803fffff80, 0x7fc000007fc00000, - 0x8000000000000000, 0x803fffff803fffff, 0xc000007fc000007f, 0xe00000000000ffff, - 0xe3ffffffffffffff, 0xf007f007f007f007, 0xf80003ffffffffff, 0xfc000003fc000003, - 0xfe000000007fffff, 0xff00000000007fff, 0xff800000000003ff, 0xffc00000000000ff, - 0xffe00000000003ff, 0xfff0000000003fff, 0xfff80000001fffff, 0xfffc0000fffc0000, - 0xfffe003fffffffff, 0xffff3fffffffffff, 0xffffc0000007ffff, 0xffffe01fffffe01f, - 0xfffff800000007ff, 0xfffffc0fffffffff, 0xffffff00003fffff, 0xffffffc0000007ff, - 0xfffffff0000001ff, 0xfffffffc00003fff, 0xffffffff07ffffff, 0xffffffffe003ffff, + = [0x1, 0x1f80, 0x3fff0, 0x3ffffc, + 0x3fe0000, 0x1ffc0000, 0xf8000000, 0x3ffffc000, + 0xffffffe00, 0x3ffffff800, 0xffffc00000, 0x3f000000000, + 0x7fffffff800, 0x1fe000001fe0, 0x3ffffff80000, 0xc00000000000, + 0x1ffc000000000, 0x3ffff0003ffff, 0x7ffffffe00000, 0xfffffffffc000, + 0x1ffffffffffc00, 0x3fffffffffff00, 0x7ffffffffffc00, 0xffffffffff8000, + 0x1ffffffff800000, 0x3fffffc03fffffc, 0x7fffc0000000000, 0xff80ff80ff80ff8, + 0x1c00000000000000, 0x1fffffffffff0000, 0x3fffff803fffff80, 0x7fc000007fc00000, + 0x8000000000000000, 0x803fffff803fffff, 0xc000007fc000007f, 0xe00000000000ffff, + 0xe3ffffffffffffff, 0xf007f007f007f007, 0xf80003ffffffffff, 0xfc000003fc000003, + 0xfe000000007fffff, 0xff00000000007fff, 0xff800000000003ff, 0xffc00000000000ff, + 0xffe00000000003ff, 0xfff0000000003fff, 0xfff80000001fffff, 0xfffc0000fffc0000, + 0xfffe003fffffffff, 0xffff3fffffffffff, 0xffffc0000007ffff, 0xffffe01fffffe01f, + 0xfffff800000007ff, 0xfffffc0fffffffff, 0xffffff00003fffff, 0xffffffc0000007ff, + 0xfffffff0000001ff, 0xfffffffc00003fff, 0xffffffff07ffffff, 0xffffffffe003ffff, 0xfffffffffc01ffff, 0xffffffffffc00003, 0xfffffffffffc000f, 0xffffffffffffe07f] def generate(self): AddSubImmOp.generate(self) self.immed = \ self.immediates32[random.randint(0, len(self.immediates32)-1)] \ - if self.isWord \ - else \ - self.immediates[random.randint(0, len(self.immediates)-1)] - + if self.isWord else \ + self.immediates[random.randint(0, len(self.immediates)-1)] + return self - + def astr(self): return (super(TwoRegImmedInstruction, self).astr() + ', #0x%x' % self.immed) def cstr(self): return super(AddSubImmOp, self).cstr() + "ll);" - + class MultiOp(): def multipleForms(self): @@ -422,9 +426,9 @@ def astr(self): return Instruction.astr(self) + "%s" class RegAndAbsOp(MultiOp, Instruction): - + def multipleForms(self): - if self.name() == "adrp": + if self.name() == "adrp": # We can only test one form of adrp because anything other # than "adrp ." requires relocs in the assembler output return 1 @@ -434,11 +438,11 @@ def generate(self): Instruction.generate(self) self.reg = GeneralRegister().generate() return self - + def cstr(self): if self.name() == "adrp": return "__ _adrp(" + "%s, %s);" % (self.reg, "%s") - return (super(RegAndAbsOp, self).cstr() + return (super(RegAndAbsOp, self).cstr() + "%s, %s);" % (self.reg, "%s")) def astr(self): @@ -446,14 +450,14 @@ def astr(self): + self.reg.astr(self.asmRegPrefix) + ", %s") class RegImmAbsOp(RegAndAbsOp): - + def cstr(self): return (Instruction.cstr(self) + "%s, %s, %s);" % (self.reg, self.immed, "%s")) def astr(self): return (Instruction.astr(self) - + ("%s, #%s, %s" + + ("%s, #%s, %s" % (self.reg.astr(self.asmRegPrefix), self.immed, "%s"))) def generate(self): @@ -462,7 +466,7 @@ def generate(self): return self class MoveWideImmOp(RegImmAbsOp): - + def multipleForms(self): return 0 @@ -472,8 +476,8 @@ def cstr(self): def astr(self): return (Instruction.astr(self) - + ("%s, #%s, lsl %s" - % (self.reg.astr(self.asmRegPrefix), + + ("%s, #%s, lsl %s" + % (self.reg.astr(self.asmRegPrefix), self.immed, self.shift))) def generate(self): @@ -486,7 +490,7 @@ def generate(self): return self class BitfieldOp(TwoRegInstruction): - + def cstr(self): return (Instruction.cstr(self) + ("%s, %s, %s, %s);" @@ -513,16 +517,16 @@ def generate(self): def cstr(self): return (ThreeRegInstruction.cstr(self) + (", %s);" % self.lsb)) - + def astr(self): return (ThreeRegInstruction.astr(self) + (", #%s" % self.lsb)) - + class CondBranchOp(MultiOp, Instruction): def cstr(self): return "__ br(Assembler::" + self.name() + ", %s);" - + def astr(self): return "b." + self.name() + "\t%s" @@ -530,10 +534,10 @@ class ImmOp(Instruction): def cstr(self): return "%s%s);" % (Instruction.cstr(self), self.immed) - + def astr(self): return Instruction.astr(self) + "#" + str(self.immed) - + def generate(self): self.immed = random.randint(0, 1<<16 -1) return self @@ -542,6 +546,8 @@ class Op(Instruction): def cstr(self): return Instruction.cstr(self) + ");" + def astr(self): + return self.aname(); class SystemOp(Instruction): @@ -573,11 +579,11 @@ def generate(self): return self def cstr(self): - return (super(ConditionalCompareOp, self).cstr() + ", " + return (super(ConditionalCompareOp, self).cstr() + ", " + "Assembler::" + conditionCodes[self.cond] + ");") def astr(self): - return (super(ConditionalCompareOp, self).astr() + + return (super(ConditionalCompareOp, self).astr() + ", " + conditionCodes[self.cond]) class ConditionalCompareImmedOp(Instruction): @@ -596,33 +602,33 @@ def cstr(self): + "Assembler::" + conditionCodes[self.cond] + ");") def astr(self): - return (Instruction.astr(self) - + self.reg.astr(self.asmRegPrefix) + return (Instruction.astr(self) + + self.reg.astr(self.asmRegPrefix) + ", #" + str(self.immed) + ", #" + str(self.immed2) + ", " + conditionCodes[self.cond]) class TwoRegOp(TwoRegInstruction): - + def cstr(self): return TwoRegInstruction.cstr(self) + ");" class ThreeRegOp(ThreeRegInstruction): - + def cstr(self): return ThreeRegInstruction.cstr(self) + ");" class FourRegMulOp(FourRegInstruction): - + def cstr(self): return FourRegInstruction.cstr(self) + ");" def astr(self): isMaddsub = self.name().startswith("madd") | self.name().startswith("msub") midPrefix = self.asmRegPrefix if isMaddsub else "w" - return (Instruction.astr(self) - + self.reg[0].astr(self.asmRegPrefix) - + ", " + self.reg[1].astr(midPrefix) + return (Instruction.astr(self) + + self.reg[0].astr(self.asmRegPrefix) + + ", " + self.reg[1].astr(midPrefix) + ", " + self.reg[2].astr(midPrefix) + ", " + self.reg[3].astr(self.asmRegPrefix)) @@ -638,8 +644,8 @@ def cstr(self): + "Assembler::" + conditionCodes[self.cond] + ");") def astr(self): - return (ThreeRegInstruction.astr(self) - + ", " + conditionCodes[self.cond]) + return (ThreeRegInstruction.astr(self) + + ", " + conditionCodes[self.cond]) class LoadStoreExclusiveOp(InstructionWithModes): @@ -651,7 +657,7 @@ def astr(self): result = self.aname() + '\t' regs = list(self.regs) index = regs.pop() # The last reg is the index register - prefix = ('x' if (self.mode == 'x') + prefix = ('x' if (self.mode == 'x') & ((self.name().startswith("ld")) | (self.name().startswith("stlr"))) # Ewww :-( else 'w') @@ -698,17 +704,17 @@ def aname(self): return self._name class Address(object): - + base_plus_unscaled_offset, pre, post, base_plus_reg, \ base_plus_scaled_offset, pcrel, post_reg, base_only = range(8) - kinds = ["base_plus_unscaled_offset", "pre", "post", "base_plus_reg", + kinds = ["base_plus_unscaled_offset", "pre", "post", "base_plus_reg", "base_plus_scaled_offset", "pcrel", "post_reg", "base_only"] extend_kinds = ["uxtw", "lsl", "sxtw", "sxtx"] @classmethod def kindToStr(cls, i): return cls.kinds[i] - + def generate(self, kind, shift_distance): self.kind = kind self.base = GeneralRegister().generate() @@ -738,7 +744,7 @@ def __str__(self): Address.pcrel: "", Address.base_plus_reg: "Address(%s, %s, Address::%s(%s))" \ % (self.base, self.index, self.extend_kind, self.shift_distance), - Address.base_plus_scaled_offset: + Address.base_plus_scaled_offset: "Address(%s, %s)" % (self.base, self.offset) } [self.kind] if (self.kind == Address.pcrel): result = ["__ pc()", "back", "forth"][self.offset] @@ -758,7 +764,7 @@ def astr(self, prefix): Address.base_only: "[%s]" % (self.base.astr(prefix)), Address.pcrel: "", Address.base_plus_reg: "[%s, %s, %s #%s]" \ - % (self.base.astr(prefix), self.index.astr(extend_prefix), + % (self.base.astr(prefix), self.index.astr(extend_prefix), self.extend_kind, self.shift_distance), Address.base_plus_scaled_offset: \ "[%s, %s]" \ @@ -767,7 +773,7 @@ def astr(self, prefix): if (self.kind == Address.pcrel): result = [".", "back", "forth"][self.offset] return result - + class LoadStoreOp(InstructionWithModes): def __init__(self, args): @@ -822,14 +828,14 @@ def aname(self): class LoadStorePairOp(InstructionWithModes): numRegs = 2 - + def __init__(self, args): name, self.asmname, self.kind, mode = args InstructionWithModes.__init__(self, name, mode) self.offset = random.randint(-1<<4, 1<<4-1) << 4 - + def generate(self): - self.reg = [OperandFactory.create(self.mode).generate() + self.reg = [OperandFactory.create(self.mode).generate() for i in range(self.numRegs)] self.base = OperandFactory.create('x').generate() kindStr = Address.kindToStr(self.kind); @@ -846,8 +852,8 @@ def astr(self): address = ["[%s, #%s]", "[%s, #%s]!", "[%s], #%s"][self.kind] address = address % (self.base.astr('x'), self.offset) result = "%s\t%s, %s, %s" \ - % (self.asmname, - self.reg[0].astr(self.asmRegPrefix), + % (self.asmname, + self.reg[0].astr(self.asmRegPrefix), self.reg[1].astr(self.asmRegPrefix), address) return result @@ -875,7 +881,7 @@ def __init__(self, args): Instruction.__init__(self, name) def generate(self): - self.reg = [OperandFactory.create(self.modes[i]).generate() + self.reg = [OperandFactory.create(self.modes[i]).generate() for i in range(self.numRegs)] return self @@ -884,7 +890,7 @@ def cstr(self): return (formatStr % tuple([Instruction.cstr(self)] + [str(self.reg[i]) for i in range(self.numRegs)])) # Yowza - + def astr(self): formatStr = "%s%s" + ''.join([", %s" for i in range(1, self.numRegs)]) return (formatStr @@ -985,7 +991,7 @@ def astr(self): moreReg + [str(self.reg[2]) + self._width.astr()]) -class LdStSIMDOp(Instruction): +class LdStNEONOp(Instruction): def __init__(self, args): self._name, self.regnum, self.arrangement, self.addresskind = args @@ -1004,7 +1010,7 @@ def generate(self): return self def cstr(self): - buf = super(LdStSIMDOp, self).cstr() + str(self._firstSIMDreg) + buf = super(LdStNEONOp, self).cstr() + str(self._firstSIMDreg) current = self._firstSIMDreg for cnt in range(1, self.regnum): buf = '%s, %s' % (buf, current.nextReg()) @@ -1022,6 +1028,57 @@ def astr(self): def aname(self): return self._name +class NEONReduceInstruction(Instruction): + def __init__(self, args): + self._name, self.insname, self.arrangement = args + + def generate(self): + current = FloatRegister().generate() + self.dstSIMDreg = current + self.srcSIMDreg = current.nextReg() + return self + + def cstr(self): + buf = Instruction.cstr(self) + str(self.dstSIMDreg) + buf = '%s, __ T%s, %s);' % (buf, self.arrangement, self.srcSIMDreg) + return buf + + def astr(self): + buf = '%s\t%s' % (self.insname, self.dstSIMDreg.astr(self.arrangement[-1].lower())) + buf = '%s, %s.%s' % (buf, self.srcSIMDreg, self.arrangement) + return buf + + def aname(self): + return self._name + +class CommonNEONInstruction(Instruction): + def __init__(self, args): + self._name, self.insname, self.arrangement = args + + def generate(self): + self._firstSIMDreg = FloatRegister().generate() + return self + + def cstr(self): + buf = Instruction.cstr(self) + str(self._firstSIMDreg) + buf = '%s, __ T%s' % (buf, self.arrangement) + current = self._firstSIMDreg + for cnt in range(1, self.numRegs): + buf = '%s, %s' % (buf, current.nextReg()) + current = current.nextReg() + return '%s);' % (buf) + + def astr(self): + buf = '%s\t%s.%s' % (self.insname, self._firstSIMDreg, self.arrangement) + current = self._firstSIMDreg + for cnt in range(1, self.numRegs): + buf = '%s, %s.%s' % (buf, current.nextReg(), self.arrangement) + current = current.nextReg() + return buf + + def aname(self): + return self._name + class SHA512SIMDOp(Instruction): def generate(self): @@ -1053,6 +1110,44 @@ def astr(self): + ('\t%s, %s, %s.2D' % (self.reg[0].astr("q"), self.reg[1].astr("q"), self.reg[2].astr("v")))) +class SHA3SIMDOp(Instruction): + + def generate(self): + if ((self._name == 'eor3') or (self._name == 'bcax')): + self.reg = [FloatRegister().generate(), FloatRegister().generate(), + FloatRegister().generate(), FloatRegister().generate()] + else: + self.reg = [FloatRegister().generate(), FloatRegister().generate(), + FloatRegister().generate()] + if (self._name == 'xar'): + self.imm6 = random.randint(0, 63) + return self + + def cstr(self): + if ((self._name == 'eor3') or (self._name == 'bcax')): + return (super(SHA3SIMDOp, self).cstr() + + ('%s, __ T16B, %s, %s, %s);' % (self.reg[0], self.reg[1], self.reg[2], self.reg[3]))) + elif (self._name == 'rax1'): + return (super(SHA3SIMDOp, self).cstr() + + ('%s, __ T2D, %s, %s);' % (self.reg[0], self.reg[1], self.reg[2]))) + else: + return (super(SHA3SIMDOp, self).cstr() + + ('%s, __ T2D, %s, %s, %s);' % (self.reg[0], self.reg[1], self.reg[2], self.imm6))) + + def astr(self): + if ((self._name == 'eor3') or (self._name == 'bcax')): + return (super(SHA3SIMDOp, self).astr() + + ('\t%s.16B, %s.16B, %s.16B, %s.16B' % (self.reg[0].astr("v"), self.reg[1].astr("v"), + self.reg[2].astr("v"), self.reg[3].astr("v")))) + elif (self._name == 'rax1'): + return (super(SHA3SIMDOp, self).astr() + + ('\t%s.2D, %s.2D, %s.2D') % (self.reg[0].astr("v"), self.reg[1].astr("v"), + self.reg[2].astr("v"))) + else: + return (super(SHA3SIMDOp, self).astr() + + ('\t%s.2D, %s.2D, %s.2D, #%s') % (self.reg[0].astr("v"), self.reg[1].astr("v"), + self.reg[2].astr("v"), self.imm6)) + class LSEOp(Instruction): def __init__(self, args): self._name, self.asmname, self.size, self.suffix = args @@ -1097,6 +1192,12 @@ def aname(self): def cname(self): return self._cname +class TwoRegNEONOp(CommonNEONInstruction): + numRegs = 2 + +class ThreeRegNEONOp(TwoRegNEONOp): + numRegs = 3 + class SpecialCases(Instruction): def __init__(self, data): self._name = data[0] @@ -1129,6 +1230,7 @@ def generate(kind, names): outfile = open("aarch64ops.s", "w") +# To minimize the changes of assembler test code random.seed(0) print "// BEGIN Generated code -- do not edit" @@ -1139,18 +1241,18 @@ def generate(kind, names): outfile.write("back:\n") -generate (ArithOp, +generate (ArithOp, [ "add", "sub", "adds", "subs", "addw", "subw", "addsw", "subsw", "and", "orr", "eor", "ands", - "andw", "orrw", "eorw", "andsw", - "bic", "orn", "eon", "bics", + "andw", "orrw", "eorw", "andsw", + "bic", "orn", "eon", "bics", "bicw", "ornw", "eonw", "bicsw" ]) -generate (AddSubImmOp, +generate (AddSubImmOp, [ "addw", "addsw", "subw", "subsw", "add", "adds", "sub", "subs"]) -generate (LogicalImmOp, +generate (LogicalImmOp, [ "andw", "orrw", "eorw", "andsw", "and", "orr", "eor", "ands"]) @@ -1191,26 +1293,26 @@ def generate(kind, names): ["stxp", mode, 4], ["stlxp", mode, 4]]) for kind in range(6): - print "\n// " + Address.kindToStr(kind), + sys.stdout.write("\n// " + Address.kindToStr(kind)) if kind != Address.pcrel: - generate (LoadStoreOp, - [["str", "str", kind, "x"], ["str", "str", kind, "w"], + generate (LoadStoreOp, + [["str", "str", kind, "x"], ["str", "str", kind, "w"], ["str", "strb", kind, "b"], ["str", "strh", kind, "h"], - ["ldr", "ldr", kind, "x"], ["ldr", "ldr", kind, "w"], + ["ldr", "ldr", kind, "x"], ["ldr", "ldr", kind, "w"], ["ldr", "ldrb", kind, "b"], ["ldr", "ldrh", kind, "h"], - ["ldrsb", "ldrsb", kind, "x"], ["ldrsh", "ldrsh", kind, "x"], + ["ldrsb", "ldrsb", kind, "x"], ["ldrsh", "ldrsh", kind, "x"], ["ldrsh", "ldrsh", kind, "w"], ["ldrsw", "ldrsw", kind, "x"], - ["ldr", "ldr", kind, "d"], ["ldr", "ldr", kind, "s"], - ["str", "str", kind, "d"], ["str", "str", kind, "s"], + ["ldr", "ldr", kind, "d"], ["ldr", "ldr", kind, "s"], + ["str", "str", kind, "d"], ["str", "str", kind, "s"], ]) else: - generate (LoadStoreOp, + generate (LoadStoreOp, [["ldr", "ldr", kind, "x"], ["ldr", "ldr", kind, "w"]]) - + for kind in (Address.base_plus_unscaled_offset, Address.pcrel, Address.base_plus_reg, \ Address.base_plus_scaled_offset): - generate (LoadStoreOp, + generate (LoadStoreOp, [["prfm", "prfm\tPLDL1KEEP,", kind, "x"]]) generate(AddSubCarryOp, ["adcw", "adcsw", "sbcw", "sbcsw", "adc", "adcs", "sbc", "sbcs"]) @@ -1219,32 +1321,32 @@ def generate(kind, names): generate(ConditionalCompareOp, ["ccmnw", "ccmpw", "ccmn", "ccmp"]) generate(ConditionalCompareImmedOp, ["ccmnw", "ccmpw", "ccmn", "ccmp"]) -generate(ConditionalSelectOp, +generate(ConditionalSelectOp, ["cselw", "csincw", "csinvw", "csnegw", "csel", "csinc", "csinv", "csneg"]) -generate(TwoRegOp, - ["rbitw", "rev16w", "revw", "clzw", "clsw", "rbit", +generate(TwoRegOp, + ["rbitw", "rev16w", "revw", "clzw", "clsw", "rbit", "rev16", "rev32", "rev", "clz", "cls"]) -generate(ThreeRegOp, - ["udivw", "sdivw", "lslvw", "lsrvw", "asrvw", "rorvw", "udiv", "sdiv", +generate(ThreeRegOp, + ["udivw", "sdivw", "lslvw", "lsrvw", "asrvw", "rorvw", "udiv", "sdiv", "lslv", "lsrv", "asrv", "rorv", "umulh", "smulh"]) -generate(FourRegMulOp, +generate(FourRegMulOp, ["maddw", "msubw", "madd", "msub", "smaddl", "smsubl", "umaddl", "umsubl"]) -generate(ThreeRegFloatOp, - [["fmuls", "sss"], ["fdivs", "sss"], ["fadds", "sss"], ["fsubs", "sss"], +generate(ThreeRegFloatOp, + [["fmuls", "sss"], ["fdivs", "sss"], ["fadds", "sss"], ["fsubs", "sss"], ["fmuls", "sss"], - ["fmuld", "ddd"], ["fdivd", "ddd"], ["faddd", "ddd"], ["fsubd", "ddd"], + ["fmuld", "ddd"], ["fdivd", "ddd"], ["faddd", "ddd"], ["fsubd", "ddd"], ["fmuld", "ddd"]]) -generate(FourRegFloatOp, - [["fmadds", "ssss"], ["fmsubs", "ssss"], ["fnmadds", "ssss"], ["fnmadds", "ssss"], +generate(FourRegFloatOp, + [["fmadds", "ssss"], ["fmsubs", "ssss"], ["fnmadds", "ssss"], ["fnmadds", "ssss"], ["fmaddd", "dddd"], ["fmsubd", "dddd"], ["fnmaddd", "dddd"], ["fnmaddd", "dddd"],]) -generate(TwoRegFloatOp, - [["fmovs", "ss"], ["fabss", "ss"], ["fnegs", "ss"], ["fsqrts", "ss"], +generate(TwoRegFloatOp, + [["fmovs", "ss"], ["fabss", "ss"], ["fnegs", "ss"], ["fsqrts", "ss"], ["fcvts", "ds"], - ["fmovd", "dd"], ["fabsd", "dd"], ["fnegd", "dd"], ["fsqrtd", "dd"], + ["fmovd", "dd"], ["fabsd", "dd"], ["fnegd", "dd"], ["fsqrtd", "dd"], ["fcvtd", "sd"], ]) @@ -1255,18 +1357,18 @@ def generate(kind, names): ["fmovs", "fmov", "ws"], ["fmovd", "fmov", "xd"], ["fmovs", "fmov", "sw"], ["fmovd", "fmov", "dx"]]) -generate(TwoRegFloatOp, [["fcmps", "ss"], ["fcmpd", "dd"], +generate(TwoRegFloatOp, [["fcmps", "ss"], ["fcmpd", "dd"], ["fcmps", "sz"], ["fcmpd", "dz"]]) for kind in range(3): generate(LoadStorePairOp, [["stp", "stp", kind, "w"], ["ldp", "ldp", kind, "w"], - ["ldpsw", "ldpsw", kind, "x"], + ["ldpsw", "ldpsw", kind, "x"], ["stp", "stp", kind, "x"], ["ldp", "ldp", kind, "x"] ]) generate(LoadStorePairOp, [["stnp", "stnp", 0, "w"], ["ldnp", "ldnp", 0, "w"], ["stnp", "stnp", 0, "x"], ["ldnp", "ldnp", 0, "x"]]) -generate(LdStSIMDOp, [["ld1", 1, "8B", Address.base_only], +generate(LdStNEONOp, [["ld1", 1, "8B", Address.base_only], ["ld1", 2, "16B", Address.post], ["ld1", 3, "1D", Address.post_reg], ["ld1", 4, "8H", Address.post], @@ -1290,7 +1392,92 @@ def generate(kind, names): ["ld4r", 4, "2S", Address.post_reg], ]) -generate(SHA512SIMDOp, ["sha512h", "sha512h2", "sha512su0", "sha512su1"]) +generate(NEONReduceInstruction, + [["addv", "addv", "8B"], ["addv", "addv", "16B"], + ["addv", "addv", "4H"], ["addv", "addv", "8H"], + ["addv", "addv", "4S"], + ["smaxv", "smaxv", "8B"], ["smaxv", "smaxv", "16B"], + ["smaxv", "smaxv", "4H"], ["smaxv", "smaxv", "8H"], + ["smaxv", "smaxv", "4S"], ["fmaxv", "fmaxv", "4S"], + ["sminv", "sminv", "8B"], ["sminv", "sminv", "16B"], + ["sminv", "sminv", "4H"], ["sminv", "sminv", "8H"], + ["sminv", "sminv", "4S"], ["fminv", "fminv", "4S"], + ]) + +generate(TwoRegNEONOp, + [["absr", "abs", "8B"], ["absr", "abs", "16B"], + ["absr", "abs", "4H"], ["absr", "abs", "8H"], + ["absr", "abs", "2S"], ["absr", "abs", "4S"], + ["absr", "abs", "2D"], + ["fabs", "fabs", "2S"], ["fabs", "fabs", "4S"], + ["fabs", "fabs", "2D"], + ["fneg", "fneg", "2S"], ["fneg", "fneg", "4S"], + ["fneg", "fneg", "2D"], + ["fsqrt", "fsqrt", "2S"], ["fsqrt", "fsqrt", "4S"], + ["fsqrt", "fsqrt", "2D"], + ["notr", "not", "8B"], ["notr", "not", "16B"], + ]) + +generate(ThreeRegNEONOp, + [["andr", "and", "8B"], ["andr", "and", "16B"], + ["orr", "orr", "8B"], ["orr", "orr", "16B"], + ["eor", "eor", "8B"], ["eor", "eor", "16B"], + ["addv", "add", "8B"], ["addv", "add", "16B"], + ["addv", "add", "4H"], ["addv", "add", "8H"], + ["addv", "add", "2S"], ["addv", "add", "4S"], + ["addv", "add", "2D"], + ["fadd", "fadd", "2S"], ["fadd", "fadd", "4S"], + ["fadd", "fadd", "2D"], + ["subv", "sub", "8B"], ["subv", "sub", "16B"], + ["subv", "sub", "4H"], ["subv", "sub", "8H"], + ["subv", "sub", "2S"], ["subv", "sub", "4S"], + ["subv", "sub", "2D"], + ["fsub", "fsub", "2S"], ["fsub", "fsub", "4S"], + ["fsub", "fsub", "2D"], + ["mulv", "mul", "8B"], ["mulv", "mul", "16B"], + ["mulv", "mul", "4H"], ["mulv", "mul", "8H"], + ["mulv", "mul", "2S"], ["mulv", "mul", "4S"], + ["fmul", "fmul", "2S"], ["fmul", "fmul", "4S"], + ["fmul", "fmul", "2D"], + ["mlav", "mla", "4H"], ["mlav", "mla", "8H"], + ["mlav", "mla", "2S"], ["mlav", "mla", "4S"], + ["fmla", "fmla", "2S"], ["fmla", "fmla", "4S"], + ["fmla", "fmla", "2D"], + ["mlsv", "mls", "4H"], ["mlsv", "mls", "8H"], + ["mlsv", "mls", "2S"], ["mlsv", "mls", "4S"], + ["fmls", "fmls", "2S"], ["fmls", "fmls", "4S"], + ["fmls", "fmls", "2D"], + ["fdiv", "fdiv", "2S"], ["fdiv", "fdiv", "4S"], + ["fdiv", "fdiv", "2D"], + ["maxv", "smax", "8B"], ["maxv", "smax", "16B"], + ["maxv", "smax", "4H"], ["maxv", "smax", "8H"], + ["maxv", "smax", "2S"], ["maxv", "smax", "4S"], + ["fmax", "fmax", "2S"], ["fmax", "fmax", "4S"], + ["fmax", "fmax", "2D"], + ["minv", "smin", "8B"], ["minv", "smin", "16B"], + ["minv", "smin", "4H"], ["minv", "smin", "8H"], + ["minv", "smin", "2S"], ["minv", "smin", "4S"], + ["fmin", "fmin", "2S"], ["fmin", "fmin", "4S"], + ["fmin", "fmin", "2D"], + ["cmeq", "cmeq", "8B"], ["cmeq", "cmeq", "16B"], + ["cmeq", "cmeq", "4H"], ["cmeq", "cmeq", "8H"], + ["cmeq", "cmeq", "2S"], ["cmeq", "cmeq", "4S"], + ["cmeq", "cmeq", "2D"], + ["fcmeq", "fcmeq", "2S"], ["fcmeq", "fcmeq", "4S"], + ["fcmeq", "fcmeq", "2D"], + ["cmgt", "cmgt", "8B"], ["cmgt", "cmgt", "16B"], + ["cmgt", "cmgt", "4H"], ["cmgt", "cmgt", "8H"], + ["cmgt", "cmgt", "2S"], ["cmgt", "cmgt", "4S"], + ["cmgt", "cmgt", "2D"], + ["fcmgt", "fcmgt", "2S"], ["fcmgt", "fcmgt", "4S"], + ["fcmgt", "fcmgt", "2D"], + ["cmge", "cmge", "8B"], ["cmge", "cmge", "16B"], + ["cmge", "cmge", "4H"], ["cmge", "cmge", "8H"], + ["cmge", "cmge", "2S"], ["cmge", "cmge", "4S"], + ["cmge", "cmge", "2D"], + ["fcmge", "fcmge", "2S"], ["fcmge", "fcmge", "4S"], + ["fcmge", "fcmge", "2D"], + ]) generate(SpecialCases, [["ccmn", "__ ccmn(zr, zr, 3u, Assembler::LE);", "ccmn\txzr, xzr, #3, LE"], ["ccmnw", "__ ccmnw(zr, zr, 5u, Assembler::EQ);", "ccmn\twzr, wzr, #5, EQ"], @@ -1344,9 +1531,9 @@ def generate(kind, names): ]) print "\n// FloatImmediateOp" -for float in ("2.0", "2.125", "4.0", "4.25", "8.0", "8.5", "16.0", "17.0", "0.125", - "0.1328125", "0.25", "0.265625", "0.5", "0.53125", "1.0", "1.0625", - "-2.0", "-2.125", "-4.0", "-4.25", "-8.0", "-8.5", "-16.0", "-17.0", +for float in ("2.0", "2.125", "4.0", "4.25", "8.0", "8.5", "16.0", "17.0", "0.125", + "0.1328125", "0.25", "0.265625", "0.5", "0.53125", "1.0", "1.0625", + "-2.0", "-2.125", "-4.0", "-4.25", "-8.0", "-8.5", "-16.0", "-17.0", "-0.125", "-0.1328125", "-0.25", "-0.265625", "-0.5", "-0.53125", "-1.0", "-1.0625"): astr = "fmov d0, #" + float cstr = "__ fmovd(v0, " + float + ");" @@ -1366,6 +1553,11 @@ def generate(kind, names): ["ldumin", "ldumin", size, suffix], ["ldumax", "ldumax", size, suffix]]); +# ARMv8.2A +generate(SHA3SIMDOp, ["bcax", "eor3", "rax1", "xar"]) + +generate(SHA512SIMDOp, ["sha512h", "sha512h2", "sha512su0", "sha512su1"]) + generate(SVEVectorOp, [["add", "ZZZ"], ["sub", "ZZZ"], ["fadd", "ZZZ"], @@ -1414,16 +1606,11 @@ def generate(kind, names): outfile.close() -import subprocess -import sys - -# compile for sve with 8.1 and sha2 because of lse atomics and sha512 crypto extension. -subprocess.check_call([AARCH64_AS, "-march=armv8.1-a+sha2+sve", "aarch64ops.s", "-o", "aarch64ops.o"]) +# compile for sve with 8.2 and sha3 because of SHA3 crypto extension. +subprocess.check_call([AARCH64_AS, "-march=armv8.2-a+sha3+sve", "aarch64ops.s", "-o", "aarch64ops.o"]) print -print "/*", -sys.stdout.flush() -subprocess.check_call([AARCH64_OBJDUMP, "-d", "aarch64ops.o"]) +print "/*" print "*/" subprocess.check_call([AARCH64_OBJCOPY, "-O", "binary", "-j", ".text", "aarch64ops.o", "aarch64ops.bin"]) @@ -1444,4 +1631,7 @@ def generate(kind, names): print "\n };" print "// END Generated code -- do not edit" +infile.close() +for f in ["aarch64ops.s", "aarch64ops.o", "aarch64ops.bin"]: + os.remove(f) diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index ede4040491e..ff82cd08cc1 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -614,9 +614,7 @@ alloc_class chunk3(RFLAGS); // Several register classes are automatically defined based upon information in // this architecture description. // 1) reg_class inline_cache_reg ( /* as def'd in frame section */ ) -// 2) reg_class compiler_method_reg ( /* as def'd in frame section */ ) -// 2) reg_class interpreter_method_reg ( /* as def'd in frame section */ ) -// 3) reg_class stack_slots( /* one chunk of stack-based "registers" */ ) +// 2) reg_class stack_slots( /* one chunk of stack-based "registers" */ ) // // Class for all 32 bit general purpose registers @@ -1755,7 +1753,9 @@ int MachCallDynamicJavaNode::ret_addr_offset() int MachCallRuntimeNode::ret_addr_offset() { // for generated stubs the call will be - // far_call(addr) + // bl(addr) + // or with far branches + // bl(trampoline_stub) // for real runtime callouts it will be six instructions // see aarch64_enc_java_to_runtime // adr(rscratch2, retaddr) @@ -1764,7 +1764,7 @@ int MachCallRuntimeNode::ret_addr_offset() { // blr(rscratch1) CodeBlob *cb = CodeCache::find_blob(_entry_point); if (cb) { - return MacroAssembler::far_branch_size(); + return 1 * NativeInstruction::instruction_size; } else { return 6 * NativeInstruction::instruction_size; } @@ -1966,9 +1966,10 @@ void MachEpilogNode::format(PhaseRegAlloc *ra_, outputStream *st) const { } if (do_polling() && C->is_method_compilation()) { - st->print("# touch polling page\n\t"); - st->print("ldr rscratch1, [rthread],#polling_page_offset\n\t"); - st->print("ldr zr, [rscratch1]"); + st->print("# test polling word\n\t"); + st->print("ldr rscratch1, [rthread],#%d\n\t", in_bytes(JavaThread::polling_word_offset())); + st->print("cmp sp, rscratch1\n\t"); + st->print("bhi #slow_path"); } } #endif @@ -1985,7 +1986,13 @@ void MachEpilogNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { } if (do_polling() && C->is_method_compilation()) { - __ fetch_and_read_polling_page(rscratch1, relocInfo::poll_return_type); + Label dummy_label; + Label* code_stub = &dummy_label; + if (!C->output()->in_scratch_emit_size()) { + code_stub = &C->output()->safepoint_poll_table()->add_safepoint(__ offset()); + } + __ relocate(relocInfo::poll_return_type); + __ safepoint_poll(*code_stub, true /* at_return */, false /* acquire */, true /* in_nmethod */); } } @@ -2403,6 +2410,12 @@ const bool Matcher::match_rule_supported_vector(int opcode, int vlen, BasicType break; case Op_MulVL: return false; + case Op_VectorLoadShuffle: + case Op_VectorRearrange: + if (vlen < 4) { + return false; + } + break; default: break; } @@ -2414,6 +2427,10 @@ const bool Matcher::has_predicated_vectors(void) { return UseSVE > 0; } +bool Matcher::supports_vector_variable_shifts(void) { + return true; +} + const int Matcher::float_pressure(int default_pressure_threshold) { return default_pressure_threshold; } @@ -2459,11 +2476,18 @@ const int Matcher::min_vector_size(const BasicType bt) { if ((UseSVE > 0) && (MaxVectorSize >= 16)) { // Currently vector length less than SVE vector register size is not supported. return max_size; - } else { - // For the moment limit the vector size to 8 bytes with NEON. + } else { // NEON + // Limit the vector size to 8 bytes int size = 8 / type2aelembytes(bt); + if (bt == T_BYTE) { + // To support vector api shuffle/rearrange. + size = 4; + } else if (bt == T_BOOLEAN) { + // To support vector api load/store mask. + size = 2; + } if (size < 2) size = 2; - return size; + return MIN2(size,max_size); } } @@ -2482,6 +2506,9 @@ const uint Matcher::vector_ideal_reg(int len) { return Op_VecA; } switch(len) { + // For 16-bit/32-bit mask vector, reuse VecD. + case 2: + case 4: case 8: return Op_VecD; case 16: return Op_VecX; } @@ -2581,11 +2608,6 @@ const bool Matcher::rematerialize_float_constants = false; // C code as the Java calling convention forces doubles to be aligned. const bool Matcher::misaligned_doubles_ok = true; -// No-op on amd64 -void Matcher::pd_implicit_null_fixup(MachNode *node, uint idx) { - Unimplemented(); -} - // Advertise here if the CPU requires explicit rounding operations to implement strictfp mode. const bool Matcher::strict_fp_requires_explicit_rounding = false; @@ -3124,6 +3146,12 @@ encode %{ // END Non-volatile memory access // Vector loads and stores + enc_class aarch64_enc_ldrvH(vecD dst, memory mem) %{ + FloatRegister dst_reg = as_FloatRegister($dst$$reg); + loadStore(C2_MacroAssembler(&cbuf), &MacroAssembler::ldr, dst_reg, MacroAssembler::H, + $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp); + %} + enc_class aarch64_enc_ldrvS(vecD dst, memory mem) %{ FloatRegister dst_reg = as_FloatRegister($dst$$reg); loadStore(C2_MacroAssembler(&cbuf), &MacroAssembler::ldr, dst_reg, MacroAssembler::S, @@ -3142,6 +3170,12 @@ encode %{ $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp); %} + enc_class aarch64_enc_strvH(vecD src, memory mem) %{ + FloatRegister src_reg = as_FloatRegister($src$$reg); + loadStore(C2_MacroAssembler(&cbuf), &MacroAssembler::str, src_reg, MacroAssembler::H, + $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp); + %} + enc_class aarch64_enc_strvS(vecD src, memory mem) %{ FloatRegister src_reg = as_FloatRegister($src$$reg); loadStore(C2_MacroAssembler(&cbuf), &MacroAssembler::str, src_reg, MacroAssembler::S, @@ -3733,12 +3767,19 @@ encode %{ if (!_method) { // A call to a runtime wrapper, e.g. new, new_typeArray_Java, uncommon_trap. call = __ trampoline_call(Address(addr, relocInfo::runtime_call_type), &cbuf); + if (call == NULL) { + ciEnv::current()->record_failure("CodeCache is full"); + return; + } } else { int method_index = resolved_method_index(cbuf); RelocationHolder rspec = _optimized_virtual ? opt_virtual_call_Relocation::spec(method_index) : static_call_Relocation::spec(method_index); call = __ trampoline_call(Address(addr, rspec), &cbuf); - + if (call == NULL) { + ciEnv::current()->record_failure("CodeCache is full"); + return; + } // Emit stub for static call address stub = CompiledStaticCall::emit_to_interp_stub(cbuf); if (stub == NULL) { @@ -3746,10 +3787,8 @@ encode %{ return; } } - if (call == NULL) { - ciEnv::current()->record_failure("CodeCache is full"); - return; - } else if (UseSVE > 0 && Compile::current()->max_vector_size() >= 16) { + + if (UseSVE > 0 && Compile::current()->max_vector_size() >= 16) { // Only non uncommon_trap calls need to reinitialize ptrue. if (uncommon_trap_request() == 0) { __ reinitialize_ptrue(); @@ -4051,9 +4090,6 @@ frame %{ // Inline Cache Register or Method for I2C. inline_cache_reg(R12); - // Method Register when calling interpreter. - interpreter_method_reg(R12); - // Number of stack slots consumed by locking an object sync_stack_slots(2); @@ -4245,6 +4281,26 @@ operand immI_31() interface(CONST_INTER); %} +operand immI_2() +%{ + predicate(n->get_int() == 2); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immI_4() +%{ + predicate(n->get_int() == 4); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + operand immI_8() %{ predicate(n->get_int() == 8); @@ -5621,16 +5677,6 @@ operand inline_cache_RegP(iRegP reg) interface(REG_INTER); %} -operand interpreter_method_RegP(iRegP reg) -%{ - constraint(ALLOC_IN_RC(method_reg)); // interpreter_method_reg - match(reg); - match(iRegPNoSp); - op_cost(0); - format %{ %} - interface(REG_INTER); -%} - // Thread Register operand thread_RegP(iRegP reg) %{ @@ -11215,6 +11261,7 @@ instruct rShiftL_reg_imm(iRegLNoSp dst, iRegL src1, immI src2) %{ %} // BEGIN This section of the file is automatically generated. Do not edit -------------- +// This section is generated from aarch64_ad.m4 // This pattern is automatically generated from aarch64_ad.m4 @@ -14685,7 +14732,11 @@ instruct clearArray_reg_reg(iRegL_R11 cnt, iRegP_R10 base, Universe dummy, rFlag format %{ "ClearArray $cnt, $base" %} ins_encode %{ - __ zero_words($base$$Register, $cnt$$Register); + address tpc = __ zero_words($base$$Register, $cnt$$Register); + if (tpc == NULL) { + ciEnv::current()->record_failure("CodeCache is full"); + return; + } %} ins_pipe(pipe_class_memory); @@ -15963,8 +16014,8 @@ instruct CallStaticJavaDirect(method meth) format %{ "call,static $meth \t// ==> " %} - ins_encode( aarch64_enc_java_static_call(meth), - aarch64_enc_call_epilog ); + ins_encode(aarch64_enc_java_static_call(meth), + aarch64_enc_call_epilog); ins_pipe(pipe_class_call); %} @@ -15982,8 +16033,8 @@ instruct CallDynamicJavaDirect(method meth) format %{ "CALL,dynamic $meth \t// ==> " %} - ins_encode( aarch64_enc_java_dynamic_call(meth), - aarch64_enc_call_epilog ); + ins_encode(aarch64_enc_java_dynamic_call(meth), + aarch64_enc_call_epilog); ins_pipe(pipe_class_call); %} @@ -16369,15 +16420,16 @@ instruct string_indexof_conUL(iRegP_R1 str1, iRegI_R4 cnt1, iRegP_R3 str2, ins_pipe(pipe_class_memory); %} -instruct string_indexofU_char(iRegP_R1 str1, iRegI_R2 cnt1, iRegI_R3 ch, +instruct string_indexof_char(iRegP_R1 str1, iRegI_R2 cnt1, iRegI_R3 ch, iRegI_R0 result, iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr) %{ match(Set result (StrIndexOfChar (Binary str1 cnt1) ch)); + predicate(((StrIndexOfCharNode*)n)->encoding() == StrIntrinsicNode::U); effect(USE_KILL str1, USE_KILL cnt1, USE_KILL ch, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); - format %{ "String IndexOf char[] $str1,$cnt1,$ch -> $result" %} + format %{ "StringUTF16 IndexOf char[] $str1,$cnt1,$ch -> $result" %} ins_encode %{ __ string_indexof_char($str1$$Register, $cnt1$$Register, $ch$$Register, @@ -16387,6 +16439,25 @@ instruct string_indexofU_char(iRegP_R1 str1, iRegI_R2 cnt1, iRegI_R3 ch, ins_pipe(pipe_class_memory); %} +instruct stringL_indexof_char(iRegP_R1 str1, iRegI_R2 cnt1, iRegI_R3 ch, + iRegI_R0 result, iRegINoSp tmp1, iRegINoSp tmp2, + iRegINoSp tmp3, rFlagsReg cr) +%{ + match(Set result (StrIndexOfChar (Binary str1 cnt1) ch)); + predicate(((StrIndexOfCharNode*)n)->encoding() == StrIntrinsicNode::L); + effect(USE_KILL str1, USE_KILL cnt1, USE_KILL ch, + TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + + format %{ "StringLatin1 IndexOf char[] $str1,$cnt1,$ch -> $result" %} + + ins_encode %{ + __ stringL_indexof_char($str1$$Register, $cnt1$$Register, $ch$$Register, + $result$$Register, $tmp1$$Register, $tmp2$$Register, + $tmp3$$Register); + %} + ins_pipe(pipe_class_memory); +%} + instruct string_equalsL(iRegP_R1 str1, iRegP_R3 str2, iRegI_R4 cnt, iRegI_R0 result, rFlagsReg cr) %{ @@ -16429,10 +16500,14 @@ instruct array_equalsB(iRegP_R1 ary1, iRegP_R2 ary2, iRegI_R0 result, format %{ "Array Equals $ary1,ary2 -> $result // KILL $tmp" %} ins_encode %{ - __ arrays_equals($ary1$$Register, $ary2$$Register, - $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, - $result$$Register, $tmp$$Register, 1); - %} + address tpc = __ arrays_equals($ary1$$Register, $ary2$$Register, + $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, + $result$$Register, $tmp$$Register, 1); + if (tpc == NULL) { + ciEnv::current()->record_failure("CodeCache is full"); + return; + } + %} ins_pipe(pipe_class_memory); %} @@ -16446,9 +16521,13 @@ instruct array_equalsC(iRegP_R1 ary1, iRegP_R2 ary2, iRegI_R0 result, format %{ "Array Equals $ary1,ary2 -> $result // KILL $tmp" %} ins_encode %{ - __ arrays_equals($ary1$$Register, $ary2$$Register, - $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, - $result$$Register, $tmp$$Register, 2); + address tpc = __ arrays_equals($ary1$$Register, $ary2$$Register, + $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, + $result$$Register, $tmp$$Register, 2); + if (tpc == NULL) { + ciEnv::current()->record_failure("CodeCache is full"); + return; + } %} ins_pipe(pipe_class_memory); %} @@ -16459,7 +16538,11 @@ instruct has_negatives(iRegP_R1 ary1, iRegI_R2 len, iRegI_R0 result, rFlagsReg c effect(USE_KILL ary1, USE_KILL len, KILL cr); format %{ "has negatives byte[] $ary1,$len -> $result" %} ins_encode %{ - __ has_negatives($ary1$$Register, $len$$Register, $result$$Register); + address tpc = __ has_negatives($ary1$$Register, $len$$Register, $result$$Register); + if (tpc == NULL) { + ciEnv::current()->record_failure("CodeCache is full"); + return; + } %} ins_pipe( pipe_slow ); %} @@ -16492,8 +16575,13 @@ instruct string_inflate(Universe dummy, iRegP_R0 src, iRegP_R1 dst, iRegI_R2 len format %{ "String Inflate $src,$dst // KILL $tmp1, $tmp2" %} ins_encode %{ - __ byte_array_inflate($src$$Register, $dst$$Register, $len$$Register, - $tmp1$$FloatRegister, $tmp2$$FloatRegister, $tmp3$$FloatRegister, $tmp4$$Register); + address tpc = __ byte_array_inflate($src$$Register, $dst$$Register, $len$$Register, + $tmp1$$FloatRegister, $tmp2$$FloatRegister, + $tmp3$$FloatRegister, $tmp4$$Register); + if (tpc == NULL) { + ciEnv::current()->record_failure("CodeCache is full"); + return; + } %} ins_pipe(pipe_class_memory); %} @@ -16821,6 +16909,7 @@ instruct replicate2D(vecX dst, vRegD src) instruct reduce_add2I(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, iRegINoSp tmp, iRegINoSp tmp2) %{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); match(Set dst (AddReductionVI isrc vsrc)); ins_cost(INSN_COST); effect(TEMP tmp, TEMP tmp2); @@ -16840,6 +16929,7 @@ instruct reduce_add2I(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, iRegINoSp tmp, instruct reduce_add4I(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, vecX vtmp, iRegINoSp itmp) %{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); match(Set dst (AddReductionVI isrc vsrc)); ins_cost(INSN_COST); effect(TEMP vtmp, TEMP itmp); @@ -16858,6 +16948,7 @@ instruct reduce_add4I(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, vecX vtmp, iReg instruct reduce_mul2I(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, iRegINoSp tmp) %{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); match(Set dst (MulReductionVI isrc vsrc)); ins_cost(INSN_COST); effect(TEMP tmp, TEMP dst); @@ -16877,6 +16968,7 @@ instruct reduce_mul2I(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, iRegINoSp tmp) instruct reduce_mul4I(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, vecX vtmp, iRegINoSp itmp) %{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); match(Set dst (MulReductionVI isrc vsrc)); ins_cost(INSN_COST); effect(TEMP vtmp, TEMP itmp, TEMP dst); @@ -17958,8 +18050,7 @@ instruct vabs2F(vecD dst, vecD src) ins_cost(INSN_COST * 3); format %{ "fabs $dst,$src\t# vector (2S)" %} ins_encode %{ - __ fabs(as_FloatRegister($dst$$reg), __ T2S, - as_FloatRegister($src$$reg)); + __ fabs(as_FloatRegister($dst$$reg), __ T2S, as_FloatRegister($src$$reg)); %} ins_pipe(vunop_fp64); %} @@ -17971,8 +18062,7 @@ instruct vabs4F(vecX dst, vecX src) ins_cost(INSN_COST * 3); format %{ "fabs $dst,$src\t# vector (4S)" %} ins_encode %{ - __ fabs(as_FloatRegister($dst$$reg), __ T4S, - as_FloatRegister($src$$reg)); + __ fabs(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($src$$reg)); %} ins_pipe(vunop_fp128); %} @@ -17984,8 +18074,7 @@ instruct vabs2D(vecX dst, vecX src) ins_cost(INSN_COST * 3); format %{ "fabs $dst,$src\t# vector (2D)" %} ins_encode %{ - __ fabs(as_FloatRegister($dst$$reg), __ T2D, - as_FloatRegister($src$$reg)); + __ fabs(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($src$$reg)); %} ins_pipe(vunop_fp128); %} @@ -18126,7 +18215,8 @@ instruct vxor16B(vecX dst, vecX src1, vecX src2) // ------------------------------ Shift --------------------------------------- instruct vshiftcnt8B(vecD dst, iRegIorL2I cnt) %{ - predicate(n->as_Vector()->length_in_bytes() == 8); + predicate(n->as_Vector()->length_in_bytes() == 4 || + n->as_Vector()->length_in_bytes() == 8); match(Set dst (LShiftCntV cnt)); match(Set dst (RShiftCntV cnt)); format %{ "dup $dst, $cnt\t# shift count vector (8B)" %} @@ -18834,6 +18924,216 @@ instruct vsrl2L_imm(vecX dst, vecX src, immI shift) %{ ins_pipe(vshift128_imm); %} +instruct vsraa8B_imm(vecD dst, vecD src, immI shift) %{ + predicate(n->as_Vector()->length() == 8); + match(Set dst (AddVB dst (RShiftVB src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "ssra $dst, $src, $shift\t# vector (8B)" %} + ins_encode %{ + int sh = (int)$shift$$constant; + if (sh >= 8) sh = 7; + __ ssra(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg), sh); + %} + ins_pipe(vshift64_imm); +%} + +instruct vsraa16B_imm(vecX dst, vecX src, immI shift) %{ + predicate(n->as_Vector()->length() == 16); + match(Set dst (AddVB dst (RShiftVB src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "ssra $dst, $src, $shift\t# vector (16B)" %} + ins_encode %{ + int sh = (int)$shift$$constant; + if (sh >= 8) sh = 7; + __ ssra(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg), sh); + %} + ins_pipe(vshift128_imm); +%} + +instruct vsraa4S_imm(vecD dst, vecD src, immI shift) %{ + predicate(n->as_Vector()->length() == 4); + match(Set dst (AddVS dst (RShiftVS src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "ssra $dst, $src, $shift\t# vector (4H)" %} + ins_encode %{ + int sh = (int)$shift$$constant; + if (sh >= 16) sh = 15; + __ ssra(as_FloatRegister($dst$$reg), __ T4H, + as_FloatRegister($src$$reg), sh); + %} + ins_pipe(vshift64_imm); +%} + +instruct vsraa8S_imm(vecX dst, vecX src, immI shift) %{ + predicate(n->as_Vector()->length() == 8); + match(Set dst (AddVS dst (RShiftVS src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "ssra $dst, $src, $shift\t# vector (8H)" %} + ins_encode %{ + int sh = (int)$shift$$constant; + if (sh >= 16) sh = 15; + __ ssra(as_FloatRegister($dst$$reg), __ T8H, + as_FloatRegister($src$$reg), sh); + %} + ins_pipe(vshift128_imm); +%} + +instruct vsraa2I_imm(vecD dst, vecD src, immI shift) %{ + predicate(n->as_Vector()->length() == 2); + match(Set dst (AddVI dst (RShiftVI src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "ssra $dst, $src, $shift\t# vector (2S)" %} + ins_encode %{ + __ ssra(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src$$reg), + (int)$shift$$constant); + %} + ins_pipe(vshift64_imm); +%} + +instruct vsraa4I_imm(vecX dst, vecX src, immI shift) %{ + predicate(n->as_Vector()->length() == 4); + match(Set dst (AddVI dst (RShiftVI src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "ssra $dst, $src, $shift\t# vector (4S)" %} + ins_encode %{ + __ ssra(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src$$reg), + (int)$shift$$constant); + %} + ins_pipe(vshift128_imm); +%} + +instruct vsraa2L_imm(vecX dst, vecX src, immI shift) %{ + predicate(n->as_Vector()->length() == 2); + match(Set dst (AddVL dst (RShiftVL src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "ssra $dst, $src, $shift\t# vector (2D)" %} + ins_encode %{ + __ ssra(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src$$reg), + (int)$shift$$constant); + %} + ins_pipe(vshift128_imm); +%} + +instruct vsrla8B_imm(vecD dst, vecD src, immI shift) %{ + predicate(n->as_Vector()->length() == 8); + match(Set dst (AddVB dst (URShiftVB src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "usra $dst, $src, $shift\t# vector (8B)" %} + ins_encode %{ + int sh = (int)$shift$$constant; + if (sh >= 8) { + __ eor(as_FloatRegister($src$$reg), __ T8B, + as_FloatRegister($src$$reg), + as_FloatRegister($src$$reg)); + } else { + __ usra(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg), sh); + } + %} + ins_pipe(vshift64_imm); +%} + +instruct vsrla16B_imm(vecX dst, vecX src, immI shift) %{ + predicate(n->as_Vector()->length() == 16); + match(Set dst (AddVB dst (URShiftVB src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "usra $dst, $src, $shift\t# vector (16B)" %} + ins_encode %{ + int sh = (int)$shift$$constant; + if (sh >= 8) { + __ eor(as_FloatRegister($src$$reg), __ T16B, + as_FloatRegister($src$$reg), + as_FloatRegister($src$$reg)); + } else { + __ usra(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg), sh); + } + %} + ins_pipe(vshift128_imm); +%} + +instruct vsrla4S_imm(vecD dst, vecD src, immI shift) %{ + predicate(n->as_Vector()->length() == 4); + match(Set dst (AddVS dst (URShiftVS src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "usra $dst, $src, $shift\t# vector (4H)" %} + ins_encode %{ + int sh = (int)$shift$$constant; + if (sh >= 16) { + __ eor(as_FloatRegister($src$$reg), __ T8B, + as_FloatRegister($src$$reg), + as_FloatRegister($src$$reg)); + } else { + __ ushr(as_FloatRegister($dst$$reg), __ T4H, + as_FloatRegister($src$$reg), sh); + } + %} + ins_pipe(vshift64_imm); +%} + +instruct vsrla8S_imm(vecX dst, vecX src, immI shift) %{ + predicate(n->as_Vector()->length() == 8); + match(Set dst (AddVS dst (URShiftVS src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "usra $dst, $src, $shift\t# vector (8H)" %} + ins_encode %{ + int sh = (int)$shift$$constant; + if (sh >= 16) { + __ eor(as_FloatRegister($src$$reg), __ T16B, + as_FloatRegister($src$$reg), + as_FloatRegister($src$$reg)); + } else { + __ usra(as_FloatRegister($dst$$reg), __ T8H, + as_FloatRegister($src$$reg), sh); + } + %} + ins_pipe(vshift128_imm); +%} + +instruct vsrla2I_imm(vecD dst, vecD src, immI shift) %{ + predicate(n->as_Vector()->length() == 2); + match(Set dst (AddVI dst (URShiftVI src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "usra $dst, $src, $shift\t# vector (2S)" %} + ins_encode %{ + __ usra(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src$$reg), + (int)$shift$$constant); + %} + ins_pipe(vshift64_imm); +%} + +instruct vsrla4I_imm(vecX dst, vecX src, immI shift) %{ + predicate(n->as_Vector()->length() == 4); + match(Set dst (AddVI dst (URShiftVI src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "usra $dst, $src, $shift\t# vector (4S)" %} + ins_encode %{ + __ usra(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src$$reg), + (int)$shift$$constant); + %} + ins_pipe(vshift128_imm); +%} + +instruct vsrla2L_imm(vecX dst, vecX src, immI shift) %{ + predicate(n->as_Vector()->length() == 2); + match(Set dst (AddVL dst (URShiftVL src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "usra $dst, $src, $shift\t# vector (2D)" %} + ins_encode %{ + __ usra(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src$$reg), + (int)$shift$$constant); + %} + ins_pipe(vshift128_imm); +%} + instruct vmax2F(vecD dst, vecD src1, vecD src2) %{ predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); @@ -18950,12 +19250,12 @@ instruct vpopcount4I(vecX dst, vecX src) %{ "uaddlp $dst, $dst\t# vector (8H)" %} ins_encode %{ - __ cnt(as_FloatRegister($dst$$reg), __ T16B, - as_FloatRegister($src$$reg)); - __ uaddlp(as_FloatRegister($dst$$reg), __ T16B, - as_FloatRegister($dst$$reg)); - __ uaddlp(as_FloatRegister($dst$$reg), __ T8H, - as_FloatRegister($dst$$reg)); + __ cnt(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg)); + __ uaddlp(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($dst$$reg)); + __ uaddlp(as_FloatRegister($dst$$reg), __ T8H, + as_FloatRegister($dst$$reg)); %} ins_pipe(pipe_class_default); %} @@ -18969,12 +19269,12 @@ instruct vpopcount2I(vecD dst, vecD src) %{ "uaddlp $dst, $dst\t# vector (4H)" %} ins_encode %{ - __ cnt(as_FloatRegister($dst$$reg), __ T8B, - as_FloatRegister($src$$reg)); - __ uaddlp(as_FloatRegister($dst$$reg), __ T8B, - as_FloatRegister($dst$$reg)); - __ uaddlp(as_FloatRegister($dst$$reg), __ T4H, - as_FloatRegister($dst$$reg)); + __ cnt(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg)); + __ uaddlp(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($dst$$reg)); + __ uaddlp(as_FloatRegister($dst$$reg), __ T4H, + as_FloatRegister($dst$$reg)); %} ins_pipe(pipe_class_default); %} diff --git a/src/hotspot/cpu/aarch64/aarch64_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_ad.m4 index 5893f451459..ac1b6dfec65 100644 --- a/src/hotspot/cpu/aarch64/aarch64_ad.m4 +++ b/src/hotspot/cpu/aarch64/aarch64_ad.m4 @@ -1,4 +1,4 @@ -dnl Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. +dnl Copyright (c) 2019, 2020, Red Hat Inc. All rights reserved. dnl DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. dnl dnl This code is free software; you can redistribute it and/or modify it @@ -19,10 +19,14 @@ dnl Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA dnl or visit www.oracle.com if you need additional information or have any dnl questions. dnl -dnl -dnl Process this file with m4 aarch64_ad.m4 to generate the arithmetic -dnl and shift patterns patterns used in aarch64.ad. dnl +dnl Process this file with m4 aarch64_ad.m4 to generate instructions used in +dnl aarch64.ad: +dnl 1. the arithmetic +dnl 2. shift patterns +dnl +// BEGIN This section of the file is automatically generated. Do not edit -------------- +// This section is generated from aarch64_ad.m4 dnl define(`ORL2I', `ifelse($1,I,orL2I)') dnl diff --git a/src/hotspot/cpu/aarch64/aarch64_neon.ad b/src/hotspot/cpu/aarch64/aarch64_neon.ad new file mode 100644 index 00000000000..33b1a869cc3 --- /dev/null +++ b/src/hotspot/cpu/aarch64/aarch64_neon.ad @@ -0,0 +1,3456 @@ +// Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2020, Arm Limited. All rights reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +// or visit www.oracle.com if you need additional information or have any +// questions. +// +// + +// This file is automatically generated by running "m4 aarch64_neon_ad.m4". Do not edit ---- + +// AArch64 NEON Architecture Description File + +// ====================VECTOR INSTRUCTIONS================================== + +// ------------------------------ Load/store/reinterpret ----------------------- + +// Load vector (16 bits) +instruct loadV2(vecD dst, memory mem) +%{ + predicate(n->as_LoadVector()->memory_size() == 2); + match(Set dst (LoadVector mem)); + ins_cost(4 * INSN_COST); + format %{ "ldrh $dst,$mem\t# vector (16 bits)" %} + ins_encode( aarch64_enc_ldrvH(dst, mem) ); + ins_pipe(vload_reg_mem64); +%} + +// Store Vector (16 bits) +instruct storeV2(vecD src, memory mem) +%{ + predicate(n->as_StoreVector()->memory_size() == 2); + match(Set mem (StoreVector mem src)); + ins_cost(4 * INSN_COST); + format %{ "strh $mem,$src\t# vector (16 bits)" %} + ins_encode( aarch64_enc_strvH(src, mem) ); + ins_pipe(vstore_reg_mem64); +%} + +instruct reinterpretD(vecD dst) +%{ + predicate(n->bottom_type()->is_vect()->length_in_bytes() == 8 && + n->in(1)->bottom_type()->is_vect()->length_in_bytes() == 8); + match(Set dst (VectorReinterpret dst)); + ins_cost(0); + format %{ " # reinterpret $dst" %} + ins_encode %{ + // empty + %} + ins_pipe(pipe_class_empty); +%} + +instruct reinterpretX(vecX dst) +%{ + predicate(n->bottom_type()->is_vect()->length_in_bytes() == 16 && + n->in(1)->bottom_type()->is_vect()->length_in_bytes() == 16); + match(Set dst (VectorReinterpret dst)); + ins_cost(0); + format %{ " # reinterpret $dst" %} + ins_encode %{ + // empty + %} + ins_pipe(pipe_class_empty); +%} + +instruct reinterpretD2X(vecX dst, vecD src) +%{ + predicate(n->bottom_type()->is_vect()->length_in_bytes() == 16 && + n->in(1)->bottom_type()->is_vect()->length_in_bytes() == 8); + match(Set dst (VectorReinterpret src)); + ins_cost(INSN_COST); + format %{ " # reinterpret $dst,$src" %} + ins_encode %{ + // If register is the same, then move is not needed. + if (as_FloatRegister($dst$$reg) != as_FloatRegister($src$$reg)) { + __ orr(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg), + as_FloatRegister($src$$reg)); + } + %} + ins_pipe(vlogical64); +%} + +instruct reinterpretX2D(vecD dst, vecX src) +%{ + predicate(n->bottom_type()->is_vect()->length_in_bytes() == 8 && + n->in(1)->bottom_type()->is_vect()->length_in_bytes() == 16); + match(Set dst (VectorReinterpret src)); + ins_cost(INSN_COST); + format %{ " # reinterpret $dst,$src" %} + ins_encode %{ + // If register is the same, then move is not needed. + if (as_FloatRegister($dst$$reg) != as_FloatRegister($src$$reg)) { + __ orr(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg), + as_FloatRegister($src$$reg)); + } + %} + ins_pipe(vlogical64); +%} + +// ------------------------------ Vector cast ------------------------------- + +instruct vcvt4Bto4S(vecD dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorCastB2X src)); + format %{ "sxtl $dst, T8H, $src, T8B\t# convert 4B to 4S vector" %} + ins_encode %{ + __ sxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + %} + ins_pipe(pipe_class_default); +%} + +instruct vcvt8Bto8S(vecX dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 8 && n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorCastB2X src)); + format %{ "sxtl $dst, T8H, $src, T8B\t# convert 8B to 8S vector" %} + ins_encode %{ + __ sxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + %} + ins_pipe(pipe_class_default); +%} + +instruct vcvt4Sto4B(vecD dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorCastS2X src)); + format %{ "xtn $dst, T8B, $src, T8H\t# convert 4S to 4B vector" %} + ins_encode %{ + __ xtn(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($src$$reg), __ T8H); + %} + ins_pipe(pipe_class_default); +%} + +instruct vcvt8Sto8B(vecD dst, vecX src) +%{ + predicate(n->as_Vector()->length() == 8 && n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorCastS2X src)); + format %{ "xtn $dst, T8B, $src, T8H\t# convert 8S to 8B vector" %} + ins_encode %{ + __ xtn(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($src$$reg), __ T8H); + %} + ins_pipe(pipe_class_default); +%} + +instruct vcvt4Sto4I(vecX dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorCastS2X src)); + format %{ "sxtl $dst, T4S, $src, T4H\t# convert 4S to 4I vector" %} + ins_encode %{ + __ sxtl(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($src$$reg), __ T4H); + %} + ins_pipe(pipe_class_default); +%} + +instruct vcvt4Ito4S(vecD dst, vecX src) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorCastI2X src)); + format %{ "xtn $dst, T4H, $src, T4S\t# convert 4I to 4S vector" %} + ins_encode %{ + __ xtn(as_FloatRegister($dst$$reg), __ T4H, as_FloatRegister($src$$reg), __ T4S); + %} + ins_pipe(pipe_class_default); +%} + +instruct vcvt2Ito2L(vecX dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (VectorCastI2X src)); + format %{ "sxtl $dst, T2D, $src, T2S\t# convert 2I to 2L vector" %} + ins_encode %{ + __ sxtl(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($src$$reg), __ T2S); + %} + ins_pipe(pipe_class_default); +%} + +instruct vcvt2Lto2I(vecD dst, vecX src) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorCastL2X src)); + format %{ "xtn $dst, T2S, $src, T2D\t# convert 2L to 2I vector" %} + ins_encode %{ + __ xtn(as_FloatRegister($dst$$reg), __ T2S, as_FloatRegister($src$$reg), __ T2D); + %} + ins_pipe(pipe_class_default); +%} + +instruct vcvt4Bto4I(vecX dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorCastB2X src)); + format %{ "sxtl $dst, T8H, $src, T8B\n\t" + "sxtl $dst, T4S, $dst, T4H\t# convert 4B to 4I vector" + %} + ins_encode %{ + __ sxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + __ sxtl(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($dst$$reg), __ T4H); + %} + ins_pipe(pipe_slow); +%} + +instruct vcvt4Ito4B(vecD dst, vecX src) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorCastI2X src)); + format %{ "xtn $dst, T4H, $src, T4S\n\t" + "xtn $dst, T8B, $dst, T8H\t# convert 4I to 4B vector" + %} + ins_encode %{ + __ xtn(as_FloatRegister($dst$$reg), __ T4H, as_FloatRegister($src$$reg), __ T4S); + __ xtn(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg), __ T8H); + %} + ins_pipe(pipe_slow); +%} + +instruct vcvt4Bto4F(vecX dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorCastB2X src)); + format %{ "sxtl $dst, T8H, $src, T8B\n\t" + "sxtl $dst, T4S, $dst, T4H\n\t" + "scvtfv T4S, $dst, $dst\t# convert 4B to 4F vector" + %} + ins_encode %{ + __ sxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + __ sxtl(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($dst$$reg), __ T4H); + __ scvtfv(__ T4S, as_FloatRegister($dst$$reg), as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vcvt4Sto4F(vecX dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorCastS2X src)); + format %{ "sxtl $dst, T4S, $src, T4H\n\t" + "scvtfv T4S, $dst, $dst\t# convert 4S to 4F vector" + %} + ins_encode %{ + __ sxtl(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($src$$reg), __ T4H); + __ scvtfv(__ T4S, as_FloatRegister($dst$$reg), as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vcvt2Ito2D(vecX dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_DOUBLE); + match(Set dst (VectorCastI2X src)); + format %{ "sxtl $dst, T2D, $src, T2S\n\t" + "scvtfv T2D, $dst, $dst\t# convert 2I to 2D vector" + %} + ins_encode %{ + __ sxtl(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($src$$reg), __ T2S); + __ scvtfv(__ T2D, as_FloatRegister($dst$$reg), as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vcvt2Ito2F(vecD dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorCastI2X src)); + format %{ "scvtfv T2S, $dst, $src\t# convert 2I to 2F vector" %} + ins_encode %{ + __ scvtfv(__ T2S, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); +%} + +instruct vcvt4Ito4F(vecX dst, vecX src) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorCastI2X src)); + format %{ "scvtfv T4S, $dst, $src\t# convert 4I to 4F vector" %} + ins_encode %{ + __ scvtfv(__ T4S, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); +%} + +instruct vcvt2Lto2D(vecX dst, vecX src) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_DOUBLE); + match(Set dst (VectorCastL2X src)); + format %{ "scvtfv T2D, $dst, $src\t# convert 2L to 2D vector" %} + ins_encode %{ + __ scvtfv(__ T2D, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); +%} + +instruct vcvt2Fto2D(vecX dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_DOUBLE); + match(Set dst (VectorCastF2X src)); + format %{ "fcvtl $dst, T2D, $src, T2S\t# convert 2F to 2D vector" %} + ins_encode %{ + __ fcvtl(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($src$$reg), __ T2S); + %} + ins_pipe(pipe_class_default); +%} + +instruct vcvt2Dto2F(vecD dst, vecX src) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorCastD2X src)); + format %{ "fcvtn $dst, T2S, $src, T2D\t# convert 2D to 2F vector" %} + ins_encode %{ + __ fcvtn(as_FloatRegister($dst$$reg), __ T2S, as_FloatRegister($src$$reg), __ T2D); + %} + ins_pipe(pipe_class_default); +%} + +instruct vcvt2Lto2F(vecD dst, vecX src) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorCastL2X src)); + format %{ "scvtfv T2D, $dst, $src\n\t" + "fcvtn $dst, T2S, $dst, T2D\t# convert 2L to 2F vector" + %} + ins_encode %{ + __ scvtfv(__ T2D, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); + __ fcvtn(as_FloatRegister($dst$$reg), __ T2S, as_FloatRegister($dst$$reg), __ T2D); + %} + ins_pipe(pipe_slow); +%} + +// ------------------------------ Reduction ------------------------------- + +instruct reduce_add8B(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, vecD tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (AddReductionVI isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "addv $tmp, T8B, $vsrc\n\t" + "smov $dst, $tmp, B, 0\n\t" + "addw $dst, $dst, $isrc\n\t" + "sxtb $dst, $dst\t# add reduction8B" + %} + ins_encode %{ + __ addv(as_FloatRegister($tmp$$reg), __ T8B, as_FloatRegister($vsrc$$reg)); + __ smov($dst$$Register, as_FloatRegister($tmp$$reg), __ B, 0); + __ addw($dst$$Register, $dst$$Register, $isrc$$Register); + __ sxtb($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_add16B(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, vecX tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (AddReductionVI isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "addv $tmp, T16B, $vsrc\n\t" + "smov $dst, $tmp, B, 0\n\t" + "addw $dst, $dst, $isrc\n\t" + "sxtb $dst, $dst\t# add reduction16B" + %} + ins_encode %{ + __ addv(as_FloatRegister($tmp$$reg), __ T16B, as_FloatRegister($vsrc$$reg)); + __ smov($dst$$Register, as_FloatRegister($tmp$$reg), __ B, 0); + __ addw($dst$$Register, $dst$$Register, $isrc$$Register); + __ sxtb($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_add4S(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, vecD tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (AddReductionVI isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "addv $tmp, T4H, $vsrc\n\t" + "smov $dst, $tmp, H, 0\n\t" + "addw $dst, $dst, $isrc\n\t" + "sxth $dst, $dst\t# add reduction4S" + %} + ins_encode %{ + __ addv(as_FloatRegister($tmp$$reg), __ T4H, as_FloatRegister($vsrc$$reg)); + __ smov($dst$$Register, as_FloatRegister($tmp$$reg), __ H, 0); + __ addw($dst$$Register, $dst$$Register, $isrc$$Register); + __ sxth($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_add8S(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, vecX tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (AddReductionVI isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "addv $tmp, T8H, $vsrc\n\t" + "smov $dst, $tmp, H, 0\n\t" + "addw $dst, $dst, $isrc\n\t" + "sxth $dst, $dst\t# add reduction8S" + %} + ins_encode %{ + __ addv(as_FloatRegister($tmp$$reg), __ T8H, as_FloatRegister($vsrc$$reg)); + __ smov($dst$$Register, as_FloatRegister($tmp$$reg), __ H, 0); + __ addw($dst$$Register, $dst$$Register, $isrc$$Register); + __ sxth($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_add2L(iRegLNoSp dst, iRegL isrc, vecX vsrc, vecX tmp) +%{ + match(Set dst (AddReductionVL isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "addpd $tmp, $vsrc\n\t" + "umov $dst, $tmp, D, 0\n\t" + "add $dst, $isrc, $dst\t# add reduction2L" + %} + ins_encode %{ + __ addpd(as_FloatRegister($tmp$$reg), as_FloatRegister($vsrc$$reg)); + __ umov($dst$$Register, as_FloatRegister($tmp$$reg), __ D, 0); + __ add($dst$$Register, $isrc$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_mul8B(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, vecD vtmp1, vecD vtmp2, iRegINoSp itmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (MulReductionVI isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP vtmp1, TEMP vtmp2, TEMP itmp); + format %{ "ins $vtmp1, S, $vsrc, 0, 1\n\t" + "mulv $vtmp1, T8B, $vtmp1, $vsrc\n\t" + "ins $vtmp2, H, $vtmp1, 0, 1\n\t" + "mulv $vtmp2, T8B, $vtmp2, $vtmp1\n\t" + "umov $itmp, $vtmp2, B, 0\n\t" + "mulw $dst, $itmp, $isrc\n\t" + "sxtb $dst, $dst\n\t" + "umov $itmp, $vtmp2, B, 1\n\t" + "mulw $dst, $itmp, $dst\n\t" + "sxtb $dst, $dst\t# mul reduction8B" + %} + ins_encode %{ + __ ins(as_FloatRegister($vtmp1$$reg), __ S, + as_FloatRegister($vsrc$$reg), 0, 1); + __ mulv(as_FloatRegister($vtmp1$$reg), __ T8B, + as_FloatRegister($vtmp1$$reg), as_FloatRegister($vsrc$$reg)); + __ ins(as_FloatRegister($vtmp2$$reg), __ H, + as_FloatRegister($vtmp1$$reg), 0, 1); + __ mulv(as_FloatRegister($vtmp2$$reg), __ T8B, + as_FloatRegister($vtmp2$$reg), as_FloatRegister($vtmp1$$reg)); + __ umov($itmp$$Register, as_FloatRegister($vtmp2$$reg), __ B, 0); + __ mulw($dst$$Register, $itmp$$Register, $isrc$$Register); + __ sxtb($dst$$Register, $dst$$Register); + __ umov($itmp$$Register, as_FloatRegister($vtmp2$$reg), __ B, 1); + __ mulw($dst$$Register, $itmp$$Register, $dst$$Register); + __ sxtb($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_mul16B(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, vecX vtmp1, vecX vtmp2, iRegINoSp itmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (MulReductionVI isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP vtmp1, TEMP vtmp2, TEMP itmp); + format %{ "ins $vtmp1, D, $vsrc, 0, 1\n\t" + "mulv $vtmp1, T8B, $vtmp1, $vsrc\n\t" + "ins $vtmp2, S, $vtmp1, 0, 1\n\t" + "mulv $vtmp1, T8B, $vtmp2, $vtmp1\n\t" + "ins $vtmp2, H, $vtmp1, 0, 1\n\t" + "mulv $vtmp2, T8B, $vtmp2, $vtmp1\n\t" + "umov $itmp, $vtmp2, B, 0\n\t" + "mulw $dst, $itmp, $isrc\n\t" + "sxtb $dst, $dst\n\t" + "umov $itmp, $vtmp2, B, 1\n\t" + "mulw $dst, $itmp, $dst\n\t" + "sxtb $dst, $dst\t# mul reduction16B" + %} + ins_encode %{ + __ ins(as_FloatRegister($vtmp1$$reg), __ D, + as_FloatRegister($vsrc$$reg), 0, 1); + __ mulv(as_FloatRegister($vtmp1$$reg), __ T8B, + as_FloatRegister($vtmp1$$reg), as_FloatRegister($vsrc$$reg)); + __ ins(as_FloatRegister($vtmp2$$reg), __ S, + as_FloatRegister($vtmp1$$reg), 0, 1); + __ mulv(as_FloatRegister($vtmp1$$reg), __ T8B, + as_FloatRegister($vtmp2$$reg), as_FloatRegister($vtmp1$$reg)); + __ ins(as_FloatRegister($vtmp2$$reg), __ H, + as_FloatRegister($vtmp1$$reg), 0, 1); + __ mulv(as_FloatRegister($vtmp2$$reg), __ T8B, + as_FloatRegister($vtmp2$$reg), as_FloatRegister($vtmp1$$reg)); + __ umov($itmp$$Register, as_FloatRegister($vtmp2$$reg), __ B, 0); + __ mulw($dst$$Register, $itmp$$Register, $isrc$$Register); + __ sxtb($dst$$Register, $dst$$Register); + __ umov($itmp$$Register, as_FloatRegister($vtmp2$$reg), __ B, 1); + __ mulw($dst$$Register, $itmp$$Register, $dst$$Register); + __ sxtb($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_mul4S(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, vecD vtmp, iRegINoSp itmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (MulReductionVI isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP vtmp, TEMP itmp); + format %{ "ins $vtmp, S, $vsrc, 0, 1\n\t" + "mulv $vtmp, T4H, $vtmp, $vsrc\n\t" + "umov $itmp, $vtmp, H, 0\n\t" + "mulw $dst, $itmp, $isrc\n\t" + "sxth $dst, $dst\n\t" + "umov $itmp, $vtmp, H, 1\n\t" + "mulw $dst, $itmp, $dst\n\t" + "sxth $dst, $dst\t# mul reduction4S" + %} + ins_encode %{ + __ ins(as_FloatRegister($vtmp$$reg), __ S, + as_FloatRegister($vsrc$$reg), 0, 1); + __ mulv(as_FloatRegister($vtmp$$reg), __ T4H, + as_FloatRegister($vtmp$$reg), as_FloatRegister($vsrc$$reg)); + __ umov($itmp$$Register, as_FloatRegister($vtmp$$reg), __ H, 0); + __ mulw($dst$$Register, $itmp$$Register, $isrc$$Register); + __ sxth($dst$$Register, $dst$$Register); + __ umov($itmp$$Register, as_FloatRegister($vtmp$$reg), __ H, 1); + __ mulw($dst$$Register, $itmp$$Register, $dst$$Register); + __ sxth($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_mul8S(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, vecX vtmp1, vecX vtmp2, iRegINoSp itmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (MulReductionVI isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP vtmp1, TEMP vtmp2, TEMP itmp); + format %{ "ins $vtmp1, D, $vsrc, 0, 1\n\t" + "mulv $vtmp1, T4H, $vtmp1, $vsrc\n\t" + "ins $vtmp2, S, $vtmp1, 0, 1\n\t" + "mulv $vtmp2, T4H, $vtmp2, $vtmp1\n\t" + "umov $itmp, $vtmp2, H, 0\n\t" + "mulw $dst, $itmp, $isrc\n\t" + "sxth $dst, $dst\n\t" + "umov $itmp, $vtmp2, H, 1\n\t" + "mulw $dst, $itmp, $dst\n\t" + "sxth $dst, $dst\t# mul reduction8S" + %} + ins_encode %{ + __ ins(as_FloatRegister($vtmp1$$reg), __ D, + as_FloatRegister($vsrc$$reg), 0, 1); + __ mulv(as_FloatRegister($vtmp1$$reg), __ T4H, + as_FloatRegister($vtmp1$$reg), as_FloatRegister($vsrc$$reg)); + __ ins(as_FloatRegister($vtmp2$$reg), __ S, + as_FloatRegister($vtmp1$$reg), 0, 1); + __ mulv(as_FloatRegister($vtmp2$$reg), __ T4H, + as_FloatRegister($vtmp2$$reg), as_FloatRegister($vtmp1$$reg)); + __ umov($itmp$$Register, as_FloatRegister($vtmp2$$reg), __ H, 0); + __ mulw($dst$$Register, $itmp$$Register, $isrc$$Register); + __ sxth($dst$$Register, $dst$$Register); + __ umov($itmp$$Register, as_FloatRegister($vtmp2$$reg), __ H, 1); + __ mulw($dst$$Register, $itmp$$Register, $dst$$Register); + __ sxth($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_mul2L(iRegLNoSp dst, iRegL isrc, vecX vsrc, iRegLNoSp tmp) +%{ + match(Set dst (MulReductionVL isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "mul $dst, $isrc, $tmp\n\t" + "umov $tmp, $vsrc, D, 1\n\t" + "mul $dst, $dst, $tmp\t# mul reduction2L" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ mul($dst$$Register, $isrc$$Register, $tmp$$Register); + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ mul($dst$$Register, $dst$$Register, $tmp$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_max8B(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, vecD tmp, rFlagsReg cr) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (MaxReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "smaxv $tmp, T8B, $vsrc\n\t" + "smov $dst, $tmp, B, 0\n\t" + "cmpw $dst, $isrc\n\t" + "cselw $dst, $dst, $isrc GT\t# max reduction8B" + %} + ins_encode %{ + __ smaxv(as_FloatRegister($tmp$$reg), __ T8B, as_FloatRegister($vsrc$$reg)); + __ smov(as_Register($dst$$reg), as_FloatRegister($tmp$$reg), __ B, 0); + __ cmpw(as_Register($dst$$reg), as_Register($isrc$$reg)); + __ cselw(as_Register($dst$$reg), as_Register($dst$$reg), as_Register($isrc$$reg), Assembler::GT); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_max16B(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, vecX tmp, rFlagsReg cr) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (MaxReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "smaxv $tmp, T16B, $vsrc\n\t" + "smov $dst, $tmp, B, 0\n\t" + "cmpw $dst, $isrc\n\t" + "cselw $dst, $dst, $isrc GT\t# max reduction16B" + %} + ins_encode %{ + __ smaxv(as_FloatRegister($tmp$$reg), __ T16B, as_FloatRegister($vsrc$$reg)); + __ smov(as_Register($dst$$reg), as_FloatRegister($tmp$$reg), __ B, 0); + __ cmpw(as_Register($dst$$reg), as_Register($isrc$$reg)); + __ cselw(as_Register($dst$$reg), as_Register($dst$$reg), as_Register($isrc$$reg), Assembler::GT); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_max4S(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, vecD tmp, rFlagsReg cr) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (MaxReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "smaxv $tmp, T4H, $vsrc\n\t" + "smov $dst, $tmp, H, 0\n\t" + "cmpw $dst, $isrc\n\t" + "cselw $dst, $dst, $isrc GT\t# max reduction4S" + %} + ins_encode %{ + __ smaxv(as_FloatRegister($tmp$$reg), __ T4H, as_FloatRegister($vsrc$$reg)); + __ smov(as_Register($dst$$reg), as_FloatRegister($tmp$$reg), __ H, 0); + __ cmpw(as_Register($dst$$reg), as_Register($isrc$$reg)); + __ cselw(as_Register($dst$$reg), as_Register($dst$$reg), as_Register($isrc$$reg), Assembler::GT); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_max8S(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, vecX tmp, rFlagsReg cr) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (MaxReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "smaxv $tmp, T8H, $vsrc\n\t" + "smov $dst, $tmp, H, 0\n\t" + "cmpw $dst, $isrc\n\t" + "cselw $dst, $dst, $isrc GT\t# max reduction8S" + %} + ins_encode %{ + __ smaxv(as_FloatRegister($tmp$$reg), __ T8H, as_FloatRegister($vsrc$$reg)); + __ smov(as_Register($dst$$reg), as_FloatRegister($tmp$$reg), __ H, 0); + __ cmpw(as_Register($dst$$reg), as_Register($isrc$$reg)); + __ cselw(as_Register($dst$$reg), as_Register($dst$$reg), as_Register($isrc$$reg), Assembler::GT); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_max4I(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, vecX tmp, rFlagsReg cr) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (MaxReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "smaxv $tmp, T4S, $vsrc\n\t" + "umov $dst, $tmp, S, 0\n\t" + "cmpw $dst, $isrc\n\t" + "cselw $dst, $dst, $isrc GT\t# max reduction4I" + %} + ins_encode %{ + __ smaxv(as_FloatRegister($tmp$$reg), __ T4S, as_FloatRegister($vsrc$$reg)); + __ umov(as_Register($dst$$reg), as_FloatRegister($tmp$$reg), __ S, 0); + __ cmpw(as_Register($dst$$reg), as_Register($isrc$$reg)); + __ cselw(as_Register($dst$$reg), as_Register($dst$$reg), as_Register($isrc$$reg), Assembler::GT); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_min8B(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, vecD tmp, rFlagsReg cr) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (MinReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "sminv $tmp, T8B, $vsrc\n\t" + "smov $dst, $tmp, B, 0\n\t" + "cmpw $dst, $isrc\n\t" + "cselw $dst, $dst, $isrc LT\t# min reduction8B" + %} + ins_encode %{ + __ sminv(as_FloatRegister($tmp$$reg), __ T8B, as_FloatRegister($vsrc$$reg)); + __ smov(as_Register($dst$$reg), as_FloatRegister($tmp$$reg), __ B, 0); + __ cmpw(as_Register($dst$$reg), as_Register($isrc$$reg)); + __ cselw(as_Register($dst$$reg), as_Register($dst$$reg), as_Register($isrc$$reg), Assembler::LT); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_min16B(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, vecX tmp, rFlagsReg cr) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (MinReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "sminv $tmp, T16B, $vsrc\n\t" + "smov $dst, $tmp, B, 0\n\t" + "cmpw $dst, $isrc\n\t" + "cselw $dst, $dst, $isrc LT\t# min reduction16B" + %} + ins_encode %{ + __ sminv(as_FloatRegister($tmp$$reg), __ T16B, as_FloatRegister($vsrc$$reg)); + __ smov(as_Register($dst$$reg), as_FloatRegister($tmp$$reg), __ B, 0); + __ cmpw(as_Register($dst$$reg), as_Register($isrc$$reg)); + __ cselw(as_Register($dst$$reg), as_Register($dst$$reg), as_Register($isrc$$reg), Assembler::LT); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_min4S(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, vecD tmp, rFlagsReg cr) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (MinReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "sminv $tmp, T4H, $vsrc\n\t" + "smov $dst, $tmp, H, 0\n\t" + "cmpw $dst, $isrc\n\t" + "cselw $dst, $dst, $isrc LT\t# min reduction4S" + %} + ins_encode %{ + __ sminv(as_FloatRegister($tmp$$reg), __ T4H, as_FloatRegister($vsrc$$reg)); + __ smov(as_Register($dst$$reg), as_FloatRegister($tmp$$reg), __ H, 0); + __ cmpw(as_Register($dst$$reg), as_Register($isrc$$reg)); + __ cselw(as_Register($dst$$reg), as_Register($dst$$reg), as_Register($isrc$$reg), Assembler::LT); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_min8S(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, vecX tmp, rFlagsReg cr) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (MinReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "sminv $tmp, T8H, $vsrc\n\t" + "smov $dst, $tmp, H, 0\n\t" + "cmpw $dst, $isrc\n\t" + "cselw $dst, $dst, $isrc LT\t# min reduction8S" + %} + ins_encode %{ + __ sminv(as_FloatRegister($tmp$$reg), __ T8H, as_FloatRegister($vsrc$$reg)); + __ smov(as_Register($dst$$reg), as_FloatRegister($tmp$$reg), __ H, 0); + __ cmpw(as_Register($dst$$reg), as_Register($isrc$$reg)); + __ cselw(as_Register($dst$$reg), as_Register($dst$$reg), as_Register($isrc$$reg), Assembler::LT); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_min4I(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, vecX tmp, rFlagsReg cr) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (MinReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "sminv $tmp, T4S, $vsrc\n\t" + "umov $dst, $tmp, S, 0\n\t" + "cmpw $dst, $isrc\n\t" + "cselw $dst, $dst, $isrc LT\t# min reduction4I" + %} + ins_encode %{ + __ sminv(as_FloatRegister($tmp$$reg), __ T4S, as_FloatRegister($vsrc$$reg)); + __ umov(as_Register($dst$$reg), as_FloatRegister($tmp$$reg), __ S, 0); + __ cmpw(as_Register($dst$$reg), as_Register($isrc$$reg)); + __ cselw(as_Register($dst$$reg), as_Register($dst$$reg), as_Register($isrc$$reg), Assembler::LT); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_max2I(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, vecX tmp, rFlagsReg cr) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (MaxReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "dup $tmp, T2D, $vsrc\n\t" + "smaxv $tmp, T4S, $tmp\n\t" + "umov $dst, $tmp, S, 0\n\t" + "cmpw $dst, $isrc\n\t" + "cselw $dst, $dst, $isrc GT\t# max reduction2I" + %} + ins_encode %{ + __ dup(as_FloatRegister($tmp$$reg), __ T2D, as_FloatRegister($vsrc$$reg)); + __ smaxv(as_FloatRegister($tmp$$reg), __ T4S, as_FloatRegister($tmp$$reg)); + __ umov(as_Register($dst$$reg), as_FloatRegister($tmp$$reg), __ S, 0); + __ cmpw(as_Register($dst$$reg), as_Register($isrc$$reg)); + __ cselw(as_Register($dst$$reg), as_Register($dst$$reg), as_Register($isrc$$reg), Assembler::GT); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_min2I(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, vecX tmp, rFlagsReg cr) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (MinReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "dup $tmp, T2D, $vsrc\n\t" + "sminv $tmp, T4S, $tmp\n\t" + "umov $dst, $tmp, S, 0\n\t" + "cmpw $dst, $isrc\n\t" + "cselw $dst, $dst, $isrc LT\t# min reduction2I" + %} + ins_encode %{ + __ dup(as_FloatRegister($tmp$$reg), __ T2D, as_FloatRegister($vsrc$$reg)); + __ sminv(as_FloatRegister($tmp$$reg), __ T4S, as_FloatRegister($tmp$$reg)); + __ umov(as_Register($dst$$reg), as_FloatRegister($tmp$$reg), __ S, 0); + __ cmpw(as_Register($dst$$reg), as_Register($isrc$$reg)); + __ cselw(as_Register($dst$$reg), as_Register($dst$$reg), as_Register($isrc$$reg), Assembler::LT); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_max2L(iRegLNoSp dst, iRegL isrc, vecX vsrc, iRegLNoSp tmp, rFlagsReg cr) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (MaxReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "cmp $isrc,$tmp\n\t" + "csel $dst, $isrc, $tmp GT\n\t" + "umov $tmp, $vsrc, D, 1\n\t" + "cmp $dst, $tmp\n\t" + "csel $dst, $dst, $tmp GT\t# max reduction2L" + %} + ins_encode %{ + __ umov(as_Register($tmp$$reg), as_FloatRegister($vsrc$$reg), __ D, 0); + __ cmp(as_Register($isrc$$reg), as_Register($tmp$$reg)); + __ csel(as_Register($dst$$reg), as_Register($isrc$$reg), as_Register($tmp$$reg), Assembler::GT); + __ umov(as_Register($tmp$$reg), as_FloatRegister($vsrc$$reg), __ D, 1); + __ cmp(as_Register($dst$$reg), as_Register($tmp$$reg)); + __ csel(as_Register($dst$$reg), as_Register($dst$$reg), as_Register($tmp$$reg), Assembler::GT); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_min2L(iRegLNoSp dst, iRegL isrc, vecX vsrc, iRegLNoSp tmp, rFlagsReg cr) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (MinReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "cmp $isrc,$tmp\n\t" + "csel $dst, $isrc, $tmp LT\n\t" + "umov $tmp, $vsrc, D, 1\n\t" + "cmp $dst, $tmp\n\t" + "csel $dst, $dst, $tmp LT\t# min reduction2L" + %} + ins_encode %{ + __ umov(as_Register($tmp$$reg), as_FloatRegister($vsrc$$reg), __ D, 0); + __ cmp(as_Register($isrc$$reg), as_Register($tmp$$reg)); + __ csel(as_Register($dst$$reg), as_Register($isrc$$reg), as_Register($tmp$$reg), Assembler::LT); + __ umov(as_Register($tmp$$reg), as_FloatRegister($vsrc$$reg), __ D, 1); + __ cmp(as_Register($dst$$reg), as_Register($tmp$$reg)); + __ csel(as_Register($dst$$reg), as_Register($dst$$reg), as_Register($tmp$$reg), Assembler::LT); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_and8B(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (AndReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, S, 0\n\t" + "umov $dst, $vsrc, S, 1\n\t" + "andw $dst, $dst, $tmp\n\t" + "andw $dst, $dst, $dst, LSR #16\n\t" + "andw $dst, $dst, $dst, LSR #8\n\t" + "andw $dst, $isrc, $dst\n\t" + "sxtb $dst, $dst\t# and reduction8B" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ S, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ S, 1); + __ andw($dst$$Register, $dst$$Register, $tmp$$Register); + __ andw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 16); + __ andw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 8); + __ andw($dst$$Register, $isrc$$Register, $dst$$Register); + __ sxtb($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_orr8B(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (OrReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, S, 0\n\t" + "umov $dst, $vsrc, S, 1\n\t" + "orrw $dst, $dst, $tmp\n\t" + "orrw $dst, $dst, $dst, LSR #16\n\t" + "orrw $dst, $dst, $dst, LSR #8\n\t" + "orrw $dst, $isrc, $dst\n\t" + "sxtb $dst, $dst\t# orr reduction8B" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ S, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ S, 1); + __ orrw($dst$$Register, $dst$$Register, $tmp$$Register); + __ orrw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 16); + __ orrw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 8); + __ orrw($dst$$Register, $isrc$$Register, $dst$$Register); + __ sxtb($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_eor8B(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (XorReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, S, 0\n\t" + "umov $dst, $vsrc, S, 1\n\t" + "eorw $dst, $dst, $tmp\n\t" + "eorw $dst, $dst, $dst, LSR #16\n\t" + "eorw $dst, $dst, $dst, LSR #8\n\t" + "eorw $dst, $isrc, $dst\n\t" + "sxtb $dst, $dst\t# eor reduction8B" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ S, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ S, 1); + __ eorw($dst$$Register, $dst$$Register, $tmp$$Register); + __ eorw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 16); + __ eorw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 8); + __ eorw($dst$$Register, $isrc$$Register, $dst$$Register); + __ sxtb($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_and16B(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (AndReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "umov $dst, $vsrc, D, 1\n\t" + "andr $dst, $dst, $tmp\n\t" + "andr $dst, $dst, $dst, LSR #32\n\t" + "andw $dst, $dst, $dst, LSR #16\n\t" + "andw $dst, $dst, $dst, LSR #8\n\t" + "andw $dst, $isrc, $dst\n\t" + "sxtb $dst, $dst\t# and reduction16B" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ andr($dst$$Register, $dst$$Register, $tmp$$Register); + __ andr($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 32); + __ andw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 16); + __ andw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 8); + __ andw($dst$$Register, $isrc$$Register, $dst$$Register); + __ sxtb($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_orr16B(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (OrReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "umov $dst, $vsrc, D, 1\n\t" + "orr $dst, $dst, $tmp\n\t" + "orr $dst, $dst, $dst, LSR #32\n\t" + "orrw $dst, $dst, $dst, LSR #16\n\t" + "orrw $dst, $dst, $dst, LSR #8\n\t" + "orrw $dst, $isrc, $dst\n\t" + "sxtb $dst, $dst\t# orr reduction16B" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ orr ($dst$$Register, $dst$$Register, $tmp$$Register); + __ orr ($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 32); + __ orrw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 16); + __ orrw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 8); + __ orrw($dst$$Register, $isrc$$Register, $dst$$Register); + __ sxtb($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_eor16B(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (XorReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "umov $dst, $vsrc, D, 1\n\t" + "eor $dst, $dst, $tmp\n\t" + "eor $dst, $dst, $dst, LSR #32\n\t" + "eorw $dst, $dst, $dst, LSR #16\n\t" + "eorw $dst, $dst, $dst, LSR #8\n\t" + "eorw $dst, $isrc, $dst\n\t" + "sxtb $dst, $dst\t# eor reduction16B" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ eor ($dst$$Register, $dst$$Register, $tmp$$Register); + __ eor ($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 32); + __ eorw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 16); + __ eorw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 8); + __ eorw($dst$$Register, $isrc$$Register, $dst$$Register); + __ sxtb($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_and4S(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (AndReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, S, 0\n\t" + "umov $dst, $vsrc, S, 1\n\t" + "andw $dst, $dst, $tmp\n\t" + "andw $dst, $dst, $dst, LSR #16\n\t" + "andw $dst, $isrc, $dst\n\t" + "sxth $dst, $dst\t# and reduction4S" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ S, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ S, 1); + __ andw($dst$$Register, $dst$$Register, $tmp$$Register); + __ andw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 16); + __ andw($dst$$Register, $isrc$$Register, $dst$$Register); + __ sxth($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_orr4S(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (OrReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, S, 0\n\t" + "umov $dst, $vsrc, S, 1\n\t" + "orrw $dst, $dst, $tmp\n\t" + "orrw $dst, $dst, $dst, LSR #16\n\t" + "orrw $dst, $isrc, $dst\n\t" + "sxth $dst, $dst\t# orr reduction4S" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ S, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ S, 1); + __ orrw($dst$$Register, $dst$$Register, $tmp$$Register); + __ orrw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 16); + __ orrw($dst$$Register, $isrc$$Register, $dst$$Register); + __ sxth($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_eor4S(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (XorReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, S, 0\n\t" + "umov $dst, $vsrc, S, 1\n\t" + "eorw $dst, $dst, $tmp\n\t" + "eorw $dst, $dst, $dst, LSR #16\n\t" + "eorw $dst, $isrc, $dst\n\t" + "sxth $dst, $dst\t# eor reduction4S" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ S, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ S, 1); + __ eorw($dst$$Register, $dst$$Register, $tmp$$Register); + __ eorw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 16); + __ eorw($dst$$Register, $isrc$$Register, $dst$$Register); + __ sxth($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_and8S(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (AndReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "umov $dst, $vsrc, D, 1\n\t" + "andr $dst, $dst, $tmp\n\t" + "andr $dst, $dst, $dst, LSR #32\n\t" + "andw $dst, $dst, $dst, LSR #16\n\t" + "andw $dst, $isrc, $dst\n\t" + "sxth $dst, $dst\t# and reduction8S" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ andr($dst$$Register, $dst$$Register, $tmp$$Register); + __ andr($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 32); + __ andw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 16); + __ andw($dst$$Register, $isrc$$Register, $dst$$Register); + __ sxth($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_orr8S(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (OrReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "umov $dst, $vsrc, D, 1\n\t" + "orr $dst, $dst, $tmp\n\t" + "orr $dst, $dst, $dst, LSR #32\n\t" + "orrw $dst, $dst, $dst, LSR #16\n\t" + "orrw $dst, $isrc, $dst\n\t" + "sxth $dst, $dst\t# orr reduction8S" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ orr ($dst$$Register, $dst$$Register, $tmp$$Register); + __ orr ($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 32); + __ orrw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 16); + __ orrw($dst$$Register, $isrc$$Register, $dst$$Register); + __ sxth($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_eor8S(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (XorReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "umov $dst, $vsrc, D, 1\n\t" + "eor $dst, $dst, $tmp\n\t" + "eor $dst, $dst, $dst, LSR #32\n\t" + "eorw $dst, $dst, $dst, LSR #16\n\t" + "eorw $dst, $isrc, $dst\n\t" + "sxth $dst, $dst\t# eor reduction8S" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ eor ($dst$$Register, $dst$$Register, $tmp$$Register); + __ eor ($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 32); + __ eorw($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 16); + __ eorw($dst$$Register, $isrc$$Register, $dst$$Register); + __ sxth($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_and2I(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (AndReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, S, 0\n\t" + "andw $dst, $tmp, $isrc\n\t" + "umov $tmp, $vsrc, S, 1\n\t" + "andw $dst, $tmp, $dst\t# and reduction2I" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ S, 0); + __ andw($dst$$Register, $tmp$$Register, $isrc$$Register); + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ S, 1); + __ andw($dst$$Register, $tmp$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_orr2I(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (OrReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, S, 0\n\t" + "orrw $dst, $tmp, $isrc\n\t" + "umov $tmp, $vsrc, S, 1\n\t" + "orrw $dst, $tmp, $dst\t# orr reduction2I" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ S, 0); + __ orrw($dst$$Register, $tmp$$Register, $isrc$$Register); + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ S, 1); + __ orrw($dst$$Register, $tmp$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_eor2I(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (XorReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, S, 0\n\t" + "eorw $dst, $tmp, $isrc\n\t" + "umov $tmp, $vsrc, S, 1\n\t" + "eorw $dst, $tmp, $dst\t# eor reduction2I" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ S, 0); + __ eorw($dst$$Register, $tmp$$Register, $isrc$$Register); + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ S, 1); + __ eorw($dst$$Register, $tmp$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_and4I(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (AndReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "umov $dst, $vsrc, D, 1\n\t" + "andr $dst, $dst, $tmp\n\t" + "andr $dst, $dst, $dst, LSR #32\n\t" + "andw $dst, $isrc, $dst\t# and reduction4I" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ andr($dst$$Register, $dst$$Register, $tmp$$Register); + __ andr($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 32); + __ andw($dst$$Register, $isrc$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_orr4I(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (OrReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "umov $dst, $vsrc, D, 1\n\t" + "orr $dst, $dst, $tmp\n\t" + "orr $dst, $dst, $dst, LSR #32\n\t" + "orrw $dst, $isrc, $dst\t# orr reduction4I" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ orr ($dst$$Register, $dst$$Register, $tmp$$Register); + __ orr ($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 32); + __ orrw($dst$$Register, $isrc$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_eor4I(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (XorReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "umov $dst, $vsrc, D, 1\n\t" + "eor $dst, $dst, $tmp\n\t" + "eor $dst, $dst, $dst, LSR #32\n\t" + "eorw $dst, $isrc, $dst\t# eor reduction4I" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ eor ($dst$$Register, $dst$$Register, $tmp$$Register); + __ eor ($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 32); + __ eorw($dst$$Register, $isrc$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_and2L(iRegLNoSp dst, iRegL isrc, vecX vsrc, iRegLNoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (AndReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "andr $dst, $isrc, $tmp\n\t" + "umov $tmp, $vsrc, D, 1\n\t" + "andr $dst, $dst, $tmp\t# and reduction2L" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ andr($dst$$Register, $isrc$$Register, $tmp$$Register); + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ andr($dst$$Register, $dst$$Register, $tmp$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_orr2L(iRegLNoSp dst, iRegL isrc, vecX vsrc, iRegLNoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (OrReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "orr $dst, $isrc, $tmp\n\t" + "umov $tmp, $vsrc, D, 1\n\t" + "orr $dst, $dst, $tmp\t# orr reduction2L" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ orr ($dst$$Register, $isrc$$Register, $tmp$$Register); + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ orr ($dst$$Register, $dst$$Register, $tmp$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_eor2L(iRegLNoSp dst, iRegL isrc, vecX vsrc, iRegLNoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (XorReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "eor $dst, $isrc, $tmp\n\t" + "umov $tmp, $vsrc, D, 1\n\t" + "eor $dst, $dst, $tmp\t# eor reduction2L" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ eor ($dst$$Register, $isrc$$Register, $tmp$$Register); + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ eor ($dst$$Register, $dst$$Register, $tmp$$Register); + %} + ins_pipe(pipe_slow); +%} + +// ------------------------------ Vector insert --------------------------------- + +instruct insert8B(vecD dst, vecD src, iRegIorL2I val, immI idx) +%{ + predicate(n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorInsert (Binary src val) idx)); + ins_cost(INSN_COST); + format %{ "orr $dst, T8B, $src, $src\n\t" + "mov $dst, T8B, $idx, $val\t# insert into vector(8B)" %} + ins_encode %{ + if (as_FloatRegister($dst$$reg) != as_FloatRegister($src$$reg)) { + __ orr(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); + } + __ mov(as_FloatRegister($dst$$reg), __ T8B, $idx$$constant, $val$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct insert16B(vecX dst, vecX src, iRegIorL2I val, immI idx) +%{ + predicate(n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorInsert (Binary src val) idx)); + ins_cost(INSN_COST); + format %{ "orr $dst, T16B, $src, $src\n\t" + "mov $dst, T16B, $idx, $val\t# insert into vector(16B)" %} + ins_encode %{ + if (as_FloatRegister($dst$$reg) != as_FloatRegister($src$$reg)) { + __ orr(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); + } + __ mov(as_FloatRegister($dst$$reg), __ T16B, $idx$$constant, $val$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct insert4S(vecD dst, vecD src, iRegIorL2I val, immI idx) +%{ + predicate(n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorInsert (Binary src val) idx)); + ins_cost(INSN_COST); + format %{ "orr $dst, T8B, $src, $src\n\t" + "mov $dst, T4H, $idx, $val\t# insert into vector(4S)" %} + ins_encode %{ + if (as_FloatRegister($dst$$reg) != as_FloatRegister($src$$reg)) { + __ orr(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); + } + __ mov(as_FloatRegister($dst$$reg), __ T4H, $idx$$constant, $val$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct insert8S(vecX dst, vecX src, iRegIorL2I val, immI idx) +%{ + predicate(n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorInsert (Binary src val) idx)); + ins_cost(INSN_COST); + format %{ "orr $dst, T16B, $src, $src\n\t" + "mov $dst, T8H, $idx, $val\t# insert into vector(8S)" %} + ins_encode %{ + if (as_FloatRegister($dst$$reg) != as_FloatRegister($src$$reg)) { + __ orr(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); + } + __ mov(as_FloatRegister($dst$$reg), __ T8H, $idx$$constant, $val$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct insert2I(vecD dst, vecD src, iRegIorL2I val, immI idx) +%{ + predicate(n->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorInsert (Binary src val) idx)); + ins_cost(INSN_COST); + format %{ "orr $dst, T8B, $src, $src\n\t" + "mov $dst, T2S, $idx, $val\t# insert into vector(2I)" %} + ins_encode %{ + if (as_FloatRegister($dst$$reg) != as_FloatRegister($src$$reg)) { + __ orr(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); + } + __ mov(as_FloatRegister($dst$$reg), __ T2S, $idx$$constant, $val$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct insert4I(vecX dst, vecX src, iRegIorL2I val, immI idx) +%{ + predicate(n->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorInsert (Binary src val) idx)); + ins_cost(INSN_COST); + format %{ "orr $dst, T16B, $src, $src\n\t" + "mov $dst, T4S, $idx, $val\t# insert into vector(4I)" %} + ins_encode %{ + if (as_FloatRegister($dst$$reg) != as_FloatRegister($src$$reg)) { + __ orr(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); + } + __ mov(as_FloatRegister($dst$$reg), __ T4S, $idx$$constant, $val$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct insert2L(vecX dst, vecX src, iRegL val, immI idx) +%{ + predicate(n->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (VectorInsert (Binary src val) idx)); + ins_cost(INSN_COST); + format %{ "orr $dst, T16B, $src, $src\n\t" + "mov $dst, T2D, $idx, $val\t# insert into vector(2L)" %} + ins_encode %{ + if (as_FloatRegister($dst$$reg) != as_FloatRegister($src$$reg)) { + __ orr(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); + } + __ mov(as_FloatRegister($dst$$reg), __ T2D, $idx$$constant, $val$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct insert2F(vecD dst, vecD src, vRegF val, immI idx) +%{ + predicate(n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorInsert (Binary src val) idx)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst); + format %{ "orr $dst, T8B, $src, $src\n\t" + "ins $dst, S, $val, $idx, 0\t# insert into vector(2F)" %} + ins_encode %{ + __ orr(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); + __ ins(as_FloatRegister($dst$$reg), __ S, + as_FloatRegister($val$$reg), $idx$$constant, 0); + %} + ins_pipe(pipe_slow); +%} + +instruct insert4F(vecX dst, vecX src, vRegF val, immI idx) +%{ + predicate(n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorInsert (Binary src val) idx)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst); + format %{ "orr $dst, T16B, $src, $src\n\t" + "ins $dst, S, $val, $idx, 0\t# insert into vector(4F)" %} + ins_encode %{ + __ orr(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); + __ ins(as_FloatRegister($dst$$reg), __ S, + as_FloatRegister($val$$reg), $idx$$constant, 0); + %} + ins_pipe(pipe_slow); +%} + +instruct insert2D(vecX dst, vecX src, vRegD val, immI idx) +%{ + predicate(n->bottom_type()->is_vect()->element_basic_type() == T_DOUBLE); + match(Set dst (VectorInsert (Binary src val) idx)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst); + format %{ "orr $dst, T16B, $src, $src\n\t" + "ins $dst, D, $val, $idx, 0\t# insert into vector(2D)" %} + ins_encode %{ + __ orr(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); + __ ins(as_FloatRegister($dst$$reg), __ D, + as_FloatRegister($val$$reg), $idx$$constant, 0); + %} + ins_pipe(pipe_slow); +%} + +// ------------------------------ Vector extract --------------------------------- + +instruct extract8B(iRegINoSp dst, vecD src, immI idx) +%{ + predicate(n->in(1)->bottom_type()->is_vect()->length() == 8); + match(Set dst (ExtractB src idx)); + ins_cost(INSN_COST); + format %{ "smov $dst, $src, B, $idx\t# extract from vector(8B)" %} + ins_encode %{ + __ smov($dst$$Register, as_FloatRegister($src$$reg), __ B, $idx$$constant); + %} + ins_pipe(pipe_class_default); +%} + +instruct extract16B(iRegINoSp dst, vecX src, immI idx) +%{ + predicate(n->in(1)->bottom_type()->is_vect()->length() == 16); + match(Set dst (ExtractB src idx)); + ins_cost(INSN_COST); + format %{ "smov $dst, $src, B, $idx\t# extract from vector(16B)" %} + ins_encode %{ + __ smov($dst$$Register, as_FloatRegister($src$$reg), __ B, $idx$$constant); + %} + ins_pipe(pipe_class_default); +%} + +instruct extract4S(iRegINoSp dst, vecD src, immI idx) +%{ + predicate(n->in(1)->bottom_type()->is_vect()->length() == 4); + match(Set dst (ExtractS src idx)); + ins_cost(INSN_COST); + format %{ "smov $dst, $src, H, $idx\t# extract from vector(4S)" %} + ins_encode %{ + __ smov($dst$$Register, as_FloatRegister($src$$reg), __ H, $idx$$constant); + %} + ins_pipe(pipe_class_default); +%} + +instruct extract8S(iRegINoSp dst, vecX src, immI idx) +%{ + predicate(n->in(1)->bottom_type()->is_vect()->length() == 8); + match(Set dst (ExtractS src idx)); + ins_cost(INSN_COST); + format %{ "smov $dst, $src, H, $idx\t# extract from vector(8S)" %} + ins_encode %{ + __ smov($dst$$Register, as_FloatRegister($src$$reg), __ H, $idx$$constant); + %} + ins_pipe(pipe_class_default); +%} + +instruct extract2I(iRegINoSp dst, vecD src, immI idx) +%{ + predicate(n->in(1)->bottom_type()->is_vect()->length() == 2); + match(Set dst (ExtractI src idx)); + ins_cost(INSN_COST); + format %{ "umov $dst, $src, S, $idx\t# extract from vector(2I)" %} + ins_encode %{ + __ umov($dst$$Register, as_FloatRegister($src$$reg), __ S, $idx$$constant); + %} + ins_pipe(pipe_class_default); +%} + +instruct extract4I(iRegINoSp dst, vecX src, immI idx) +%{ + predicate(n->in(1)->bottom_type()->is_vect()->length() == 4); + match(Set dst (ExtractI src idx)); + ins_cost(INSN_COST); + format %{ "umov $dst, $src, S, $idx\t# extract from vector(4I)" %} + ins_encode %{ + __ umov($dst$$Register, as_FloatRegister($src$$reg), __ S, $idx$$constant); + %} + ins_pipe(pipe_class_default); +%} + +instruct extract2L(iRegLNoSp dst, vecX src, immI idx) +%{ + predicate(n->in(1)->bottom_type()->is_vect()->length() == 2); + match(Set dst (ExtractL src idx)); + ins_cost(INSN_COST); + format %{ "umov $dst, $src, D, $idx\t# extract from vector(2L)" %} + ins_encode %{ + __ umov($dst$$Register, as_FloatRegister($src$$reg), __ D, $idx$$constant); + %} + ins_pipe(pipe_class_default); +%} + +instruct extract2F(vRegF dst, vecD src, immI idx) +%{ + predicate(n->in(1)->bottom_type()->is_vect()->length() == 2); + match(Set dst (ExtractF src idx)); + ins_cost(INSN_COST); + format %{ "ins $dst, S, $src, 0, $idx\t# extract from vector(2F)" %} + ins_encode %{ + __ ins(as_FloatRegister($dst$$reg), __ S, + as_FloatRegister($src$$reg), 0, $idx$$constant); + %} + ins_pipe(pipe_class_default); +%} + +instruct extract4F(vRegF dst, vecX src, immI idx) +%{ + predicate(n->in(1)->bottom_type()->is_vect()->length() == 4); + match(Set dst (ExtractF src idx)); + ins_cost(INSN_COST); + format %{ "ins $dst, S, $src, 0, $idx\t# extract from vector(4F)" %} + ins_encode %{ + __ ins(as_FloatRegister($dst$$reg), __ S, + as_FloatRegister($src$$reg), 0, $idx$$constant); + %} + ins_pipe(pipe_class_default); +%} + +instruct extract2D(vRegD dst, vecX src, immI idx) +%{ + predicate(n->in(1)->bottom_type()->is_vect()->length() == 2); + match(Set dst (ExtractD src idx)); + ins_cost(INSN_COST); + format %{ "ins $dst, D, $src, 0, $idx\t# extract from vector(2D)" %} + ins_encode %{ + __ ins(as_FloatRegister($dst$$reg), __ D, + as_FloatRegister($src$$reg), 0, $idx$$constant); + %} + ins_pipe(pipe_class_default); +%} + +// ------------------------------ Vector comparison --------------------------------- + +instruct vcmeq8B(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 8 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::eq && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmeq $dst, $src1, $src2\t# vector cmp (8B)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmeq(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmeq16B(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 16 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::eq && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmeq $dst, $src1, $src2\t# vector cmp (16B)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmeq(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmeq4S(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::eq && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmeq $dst, $src1, $src2\t# vector cmp (4S)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmeq(as_FloatRegister($dst$$reg), __ T4H, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmeq8S(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 8 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::eq && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmeq $dst, $src1, $src2\t# vector cmp (8S)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmeq(as_FloatRegister($dst$$reg), __ T8H, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmeq2I(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::eq && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmeq $dst, $src1, $src2\t# vector cmp (2I)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmeq(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmeq4I(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::eq && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmeq $dst, $src1, $src2\t# vector cmp (4I)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmeq(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmeq2L(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::eq && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmeq $dst, $src1, $src2\t# vector cmp (2L)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmeq(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmeq2F(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::eq && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmeq $dst, $src1, $src2\t# vector cmp (2F)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmeq(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmeq4F(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::eq && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmeq $dst, $src1, $src2\t# vector cmp (4F)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmeq(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmeq2D(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::eq && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_DOUBLE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmeq $dst, $src1, $src2\t# vector cmp (2D)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmeq(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmgt8B(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 8 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::gt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmgt $dst, $src1, $src2\t# vector cmp (8B)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmgt(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmgt16B(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 16 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::gt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmgt $dst, $src1, $src2\t# vector cmp (16B)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmgt(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmgt4S(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::gt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmgt $dst, $src1, $src2\t# vector cmp (4S)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmgt(as_FloatRegister($dst$$reg), __ T4H, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmgt8S(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 8 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::gt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmgt $dst, $src1, $src2\t# vector cmp (8S)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmgt(as_FloatRegister($dst$$reg), __ T8H, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmgt2I(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::gt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmgt $dst, $src1, $src2\t# vector cmp (2I)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmgt(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmgt4I(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::gt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmgt $dst, $src1, $src2\t# vector cmp (4I)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmgt(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmgt2L(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::gt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmgt $dst, $src1, $src2\t# vector cmp (2L)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmgt(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmgt2F(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::gt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmgt $dst, $src1, $src2\t# vector cmp (2F)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmgt(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmgt4F(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::gt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmgt $dst, $src1, $src2\t# vector cmp (4F)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmgt(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmgt2D(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::gt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_DOUBLE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmgt $dst, $src1, $src2\t# vector cmp (2D)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmgt(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmge8B(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 8 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ge && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmge $dst, $src1, $src2\t# vector cmp (8B)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmge(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmge16B(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 16 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ge && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmge $dst, $src1, $src2\t# vector cmp (16B)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmge(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmge4S(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ge && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmge $dst, $src1, $src2\t# vector cmp (4S)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmge(as_FloatRegister($dst$$reg), __ T4H, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmge8S(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 8 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ge && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmge $dst, $src1, $src2\t# vector cmp (8S)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmge(as_FloatRegister($dst$$reg), __ T8H, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmge2I(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ge && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmge $dst, $src1, $src2\t# vector cmp (2I)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmge(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmge4I(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ge && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmge $dst, $src1, $src2\t# vector cmp (4I)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmge(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmge2L(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ge && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmge $dst, $src1, $src2\t# vector cmp (2L)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmge(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmge2F(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ge && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmge $dst, $src1, $src2\t# vector cmp (2F)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmge(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmge4F(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ge && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmge $dst, $src1, $src2\t# vector cmp (4F)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmge(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmge2D(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ge && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_DOUBLE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmge $dst, $src1, $src2\t# vector cmp (2D)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmge(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmne8B(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 8 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ne && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmeq $dst, $src1, $src2\n\t# vector cmp (8B)" + "not $dst, $dst\t" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmeq(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + __ notr(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vcmne16B(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 16 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ne && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmeq $dst, $src1, $src2\n\t# vector cmp (16B)" + "not $dst, $dst\t" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmeq(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + __ notr(as_FloatRegister($dst$$reg), __ T16B, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vcmne4S(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ne && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmeq $dst, $src1, $src2\n\t# vector cmp (4S)" + "not $dst, $dst\t" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmeq(as_FloatRegister($dst$$reg), __ T4H, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + __ notr(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vcmne8S(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 8 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ne && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmeq $dst, $src1, $src2\n\t# vector cmp (8S)" + "not $dst, $dst\t" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmeq(as_FloatRegister($dst$$reg), __ T8H, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + __ notr(as_FloatRegister($dst$$reg), __ T16B, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vcmne2I(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ne && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmeq $dst, $src1, $src2\n\t# vector cmp (2I)" + "not $dst, $dst\t" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmeq(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + __ notr(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vcmne4I(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ne && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmeq $dst, $src1, $src2\n\t# vector cmp (4I)" + "not $dst, $dst\t" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmeq(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + __ notr(as_FloatRegister($dst$$reg), __ T16B, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vcmne2L(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ne && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmeq $dst, $src1, $src2\n\t# vector cmp (2L)" + "not $dst, $dst\t" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmeq(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + __ notr(as_FloatRegister($dst$$reg), __ T16B, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vcmne2F(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ne && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmeq $dst, $src1, $src2\n\t# vector cmp (2F)" + "not $dst, $dst\t" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmeq(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + __ notr(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vcmne4F(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ne && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmeq $dst, $src1, $src2\n\t# vector cmp (4F)" + "not $dst, $dst\t" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmeq(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + __ notr(as_FloatRegister($dst$$reg), __ T16B, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vcmne2D(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ne && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_DOUBLE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmeq $dst, $src1, $src2\n\t# vector cmp (2D)" + "not $dst, $dst\t" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmeq(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + __ notr(as_FloatRegister($dst$$reg), __ T16B, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vcmlt8B(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 8 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::lt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmgt $dst, $src2, $src1\t# vector cmp (8B)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmgt(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmlt16B(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 16 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::lt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmgt $dst, $src2, $src1\t# vector cmp (16B)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmgt(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmlt4S(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::lt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmgt $dst, $src2, $src1\t# vector cmp (4S)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmgt(as_FloatRegister($dst$$reg), __ T4H, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmlt8S(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 8 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::lt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmgt $dst, $src2, $src1\t# vector cmp (8S)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmgt(as_FloatRegister($dst$$reg), __ T8H, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmlt2I(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::lt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmgt $dst, $src2, $src1\t# vector cmp (2I)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmgt(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmlt4I(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::lt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmgt $dst, $src2, $src1\t# vector cmp (4I)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmgt(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmlt2L(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::lt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmgt $dst, $src2, $src1\t# vector cmp (2L)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmgt(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmlt2F(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::lt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmgt $dst, $src2, $src1\t# vector cmp (2F)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmgt(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmlt4F(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::lt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmgt $dst, $src2, $src1\t# vector cmp (4F)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmgt(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmlt2D(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::lt && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_DOUBLE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmgt $dst, $src2, $src1\t# vector cmp (2D)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmgt(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmle8B(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 8 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::le && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmge $dst, $src2, $src1\t# vector cmp (8B)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmge(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmle16B(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 16 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::le && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmge $dst, $src2, $src1\t# vector cmp (16B)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmge(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmle4S(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::le && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmge $dst, $src2, $src1\t# vector cmp (4S)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmge(as_FloatRegister($dst$$reg), __ T4H, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmle8S(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 8 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::le && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmge $dst, $src2, $src1\t# vector cmp (8S)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmge(as_FloatRegister($dst$$reg), __ T8H, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmle2I(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::le && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmge $dst, $src2, $src1\t# vector cmp (2I)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmge(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmle4I(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::le && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmge $dst, $src2, $src1\t# vector cmp (4I)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmge(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmle2L(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::le && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "cmge $dst, $src2, $src1\t# vector cmp (2L)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ cmge(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmle2F(vecD dst, vecD src1, vecD src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::le && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmge $dst, $src2, $src1\t# vector cmp (2F)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmge(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vcmle4F(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 4 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::le && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmge $dst, $src2, $src1\t# vector cmp (4F)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmge(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vcmle2D(vecX dst, vecX src1, vecX src2, immI cond) +%{ + predicate(n->as_Vector()->length() == 2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::le && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_DOUBLE); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "fcmge $dst, $src2, $src1\t# vector cmp (2D)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ fcmge(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop128); +%} + +// ------------------------------ Vector mul ----------------------------------- + +instruct vmul2L(vecX dst, vecX src1, vecX src2, iRegLNoSp tmp1, iRegLNoSp tmp2) +%{ + predicate(n->as_Vector()->length() == 2); + match(Set dst (MulVL src1 src2)); + ins_cost(INSN_COST); + effect(TEMP tmp1, TEMP tmp2); + format %{ "umov $tmp1, $src1, D, 0\n\t" + "umov $tmp2, $src2, D, 0\n\t" + "mul $tmp2, $tmp2, $tmp1\n\t" + "mov $dst, T2D, 0, $tmp2\t# insert into vector(2L)\n\t" + "umov $tmp1, $src1, D, 1\n\t" + "umov $tmp2, $src2, D, 1\n\t" + "mul $tmp2, $tmp2, $tmp1\n\t" + "mov $dst, T2D, 1, $tmp2\t# insert into vector(2L)\n\t" + %} + ins_encode %{ + __ umov($tmp1$$Register, as_FloatRegister($src1$$reg), __ D, 0); + __ umov($tmp2$$Register, as_FloatRegister($src2$$reg), __ D, 0); + __ mul(as_Register($tmp2$$reg), as_Register($tmp2$$reg), as_Register($tmp1$$reg)); + __ mov(as_FloatRegister($dst$$reg), __ T2D, 0, $tmp2$$Register); + __ umov($tmp1$$Register, as_FloatRegister($src1$$reg), __ D, 1); + __ umov($tmp2$$Register, as_FloatRegister($src2$$reg), __ D, 1); + __ mul(as_Register($tmp2$$reg), as_Register($tmp2$$reg), as_Register($tmp1$$reg)); + __ mov(as_FloatRegister($dst$$reg), __ T2D, 1, $tmp2$$Register); + %} + ins_pipe(pipe_slow); +%} + +// --------------------------------- Vector not -------------------------------- + +instruct vnot2I(vecD dst, vecD src, immI_M1 m1) +%{ + predicate(n->as_Vector()->length_in_bytes() == 8); + match(Set dst (XorV src (ReplicateB m1))); + match(Set dst (XorV src (ReplicateS m1))); + match(Set dst (XorV src (ReplicateI m1))); + ins_cost(INSN_COST); + format %{ "not $dst, $src\t# vector (8B)" %} + ins_encode %{ + __ notr(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); +%} + +instruct vnot4I(vecX dst, vecX src, immI_M1 m1) +%{ + predicate(n->as_Vector()->length_in_bytes() == 16); + match(Set dst (XorV src (ReplicateB m1))); + match(Set dst (XorV src (ReplicateS m1))); + match(Set dst (XorV src (ReplicateI m1))); + ins_cost(INSN_COST); + format %{ "not $dst, $src\t# vector (16B)" %} + ins_encode %{ + __ notr(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); +%} + +instruct vnot2L(vecX dst, vecX src, immL_M1 m1) +%{ + predicate(n->as_Vector()->length_in_bytes() == 16); + match(Set dst (XorV src (ReplicateL m1))); + ins_cost(INSN_COST); + format %{ "not $dst, $src\t# vector (16B)" %} + ins_encode %{ + __ notr(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); +%} + +// ------------------------------ Vector max/min ------------------------------- + +instruct vmax8B(vecD dst, vecD src1, vecD src2) +%{ + predicate((n->as_Vector()->length() == 4 || n->as_Vector()->length() == 8) && + n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (MaxV src1 src2)); + ins_cost(INSN_COST); + format %{ "maxv $dst, $src1, $src2\t# vector (8B)" %} + ins_encode %{ + __ maxv(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src1$$reg), + as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vmax16B(vecX dst, vecX src1, vecX src2) +%{ + predicate(n->as_Vector()->length() == 16 && n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (MaxV src1 src2)); + ins_cost(INSN_COST); + format %{ "maxv $dst, $src1, $src2\t# vector (16B)" %} + ins_encode %{ + __ maxv(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src1$$reg), + as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vmax4S(vecD dst, vecD src1, vecD src2) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (MaxV src1 src2)); + ins_cost(INSN_COST); + format %{ "maxv $dst, $src1, $src2\t# vector (4S)" %} + ins_encode %{ + __ maxv(as_FloatRegister($dst$$reg), __ T4H, + as_FloatRegister($src1$$reg), + as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vmax8S(vecX dst, vecX src1, vecX src2) +%{ + predicate(n->as_Vector()->length() == 8 && n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (MaxV src1 src2)); + ins_cost(INSN_COST); + format %{ "maxv $dst, $src1, $src2\t# vector (8S)" %} + ins_encode %{ + __ maxv(as_FloatRegister($dst$$reg), __ T8H, + as_FloatRegister($src1$$reg), + as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vmax2I(vecD dst, vecD src1, vecD src2) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (MaxV src1 src2)); + ins_cost(INSN_COST); + format %{ "maxv $dst, $src1, $src2\t# vector (2I)" %} + ins_encode %{ + __ maxv(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src1$$reg), + as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vmax4I(vecX dst, vecX src1, vecX src2) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (MaxV src1 src2)); + ins_cost(INSN_COST); + format %{ "maxv $dst, $src1, $src2\t# vector (4I)" %} + ins_encode %{ + __ maxv(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src1$$reg), + as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vmin8B(vecD dst, vecD src1, vecD src2) +%{ + predicate((n->as_Vector()->length() == 4 || n->as_Vector()->length() == 8) && + n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (MinV src1 src2)); + ins_cost(INSN_COST); + format %{ "minv $dst, $src1, $src2\t# vector (8B)" %} + ins_encode %{ + __ minv(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src1$$reg), + as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vmin16B(vecX dst, vecX src1, vecX src2) +%{ + predicate(n->as_Vector()->length() == 16 && n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (MinV src1 src2)); + ins_cost(INSN_COST); + format %{ "minv $dst, $src1, $src2\t# vector (16B)" %} + ins_encode %{ + __ minv(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src1$$reg), + as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vmin4S(vecD dst, vecD src1, vecD src2) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (MinV src1 src2)); + ins_cost(INSN_COST); + format %{ "minv $dst, $src1, $src2\t# vector (4S)" %} + ins_encode %{ + __ minv(as_FloatRegister($dst$$reg), __ T4H, + as_FloatRegister($src1$$reg), + as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vmin8S(vecX dst, vecX src1, vecX src2) +%{ + predicate(n->as_Vector()->length() == 8 && n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (MinV src1 src2)); + ins_cost(INSN_COST); + format %{ "minv $dst, $src1, $src2\t# vector (8S)" %} + ins_encode %{ + __ minv(as_FloatRegister($dst$$reg), __ T8H, + as_FloatRegister($src1$$reg), + as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vmin2I(vecD dst, vecD src1, vecD src2) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (MinV src1 src2)); + ins_cost(INSN_COST); + format %{ "minv $dst, $src1, $src2\t# vector (2I)" %} + ins_encode %{ + __ minv(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src1$$reg), + as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop64); +%} + +instruct vmin4I(vecX dst, vecX src1, vecX src2) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (MinV src1 src2)); + ins_cost(INSN_COST); + format %{ "minv $dst, $src1, $src2\t# vector (4I)" %} + ins_encode %{ + __ minv(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src1$$reg), + as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + + +instruct vmax2L(vecX dst, vecX src1, vecX src2) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (MaxV src1 src2)); + ins_cost(INSN_COST); + effect(TEMP dst); + format %{ "cmgt $dst, $src1, $src2\t# vector (2L)\n\t" + "bsl $dst, $src1, $src2\t# vector (16B)" %} + ins_encode %{ + __ cmgt(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + __ bsl(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop128); +%} + +instruct vmin2L(vecX dst, vecX src1, vecX src2) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (MinV src1 src2)); + ins_cost(INSN_COST); + effect(TEMP dst); + format %{ "cmgt $dst, $src1, $src2\t# vector (2L)\n\t" + "bsl $dst, $src2, $src1\t# vector (16B)" %} + ins_encode %{ + __ cmgt(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + __ bsl(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop128); +%} + +// --------------------------------- blend (bsl) ---------------------------- + +instruct vbsl8B(vecD dst, vecD src1, vecD src2) +%{ + predicate(n->as_Vector()->length_in_bytes() == 8); + match(Set dst (VectorBlend (Binary src1 src2) dst)); + ins_cost(INSN_COST); + format %{ "bsl $dst, $src2, $src1\t# vector (8B)" %} + ins_encode %{ + __ bsl(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vlogical64); +%} + +instruct vbsl16B(vecX dst, vecX src1, vecX src2) +%{ + predicate(n->as_Vector()->length_in_bytes() == 16); + match(Set dst (VectorBlend (Binary src1 src2) dst)); + ins_cost(INSN_COST); + format %{ "bsl $dst, $src2, $src1\t# vector (16B)" %} + ins_encode %{ + __ bsl(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vlogical128); +%} + +// --------------------------------- Load/store Mask ---------------------------- + +instruct loadmask8B(vecD dst, vecD src ) +%{ + predicate(n->as_Vector()->length() == 8 && n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorLoadMask src )); + ins_cost(INSN_COST); + format %{ "negr $dst, $src\t# load mask (8B to 8B)" %} + ins_encode %{ + __ negr(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); +%} + +instruct loadmask16B(vecX dst, vecX src ) +%{ + predicate(n->as_Vector()->length() == 16 && n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorLoadMask src )); + ins_cost(INSN_COST); + format %{ "negr $dst, $src\t# load mask (16B to 16B)" %} + ins_encode %{ + __ negr(as_FloatRegister($dst$$reg), __ T16B, as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); +%} + +instruct storemask8B(vecD dst, vecD src , immI_1 size) +%{ + predicate(n->as_Vector()->length() == 8); + match(Set dst (VectorStoreMask src size)); + ins_cost(INSN_COST); + format %{ "negr $dst, $src\t# store mask (8B to 8B)" %} + ins_encode %{ + __ negr(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); +%} + +instruct storemask16B(vecX dst, vecX src , immI_1 size) +%{ + predicate(n->as_Vector()->length() == 16); + match(Set dst (VectorStoreMask src size)); + ins_cost(INSN_COST); + format %{ "negr $dst, $src\t# store mask (16B to 16B)" %} + ins_encode %{ + __ negr(as_FloatRegister($dst$$reg), __ T16B, as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); +%} + +instruct loadmask4S(vecD dst, vecD src ) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorLoadMask src )); + ins_cost(INSN_COST); + format %{ "uxtl $dst, $src\n\t" + "negr $dst, $dst\t# load mask (4B to 4H)" %} + ins_encode %{ + __ uxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + __ negr(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct loadmask8S(vecX dst, vecD src ) +%{ + predicate(n->as_Vector()->length() == 8 && n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorLoadMask src )); + ins_cost(INSN_COST); + format %{ "uxtl $dst, $src\n\t" + "negr $dst, $dst\t# load mask (8B to 8H)" %} + ins_encode %{ + __ uxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + __ negr(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct storemask4S(vecD dst, vecD src , immI_2 size) +%{ + predicate(n->as_Vector()->length() == 4); + match(Set dst (VectorStoreMask src size)); + ins_cost(INSN_COST); + format %{ "xtn $dst, $src\n\t" + "negr $dst, $dst\t# store mask (4H to 4B)" %} + ins_encode %{ + __ xtn(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($src$$reg), __ T8H); + __ negr(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct storemask8S(vecD dst, vecX src , immI_2 size) +%{ + predicate(n->as_Vector()->length() == 8); + match(Set dst (VectorStoreMask src size)); + ins_cost(INSN_COST); + format %{ "xtn $dst, $src\n\t" + "negr $dst, $dst\t# store mask (8H to 8B)" %} + ins_encode %{ + __ xtn(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($src$$reg), __ T8H); + __ negr(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct loadmask2I(vecD dst, vecD src ) +%{ + predicate(n->as_Vector()->length() == 2 && + (n->bottom_type()->is_vect()->element_basic_type() == T_INT || + n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT)); + match(Set dst (VectorLoadMask src )); + ins_cost(INSN_COST); + format %{ "uxtl $dst, $src\t# 2B to 2H\n\t" + "uxtl $dst, $dst\t# 2H to 2S\n\t" + "negr $dst, $dst\t# load mask (2B to 2S)" %} + ins_encode %{ + __ uxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + __ uxtl(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($dst$$reg), __ T4H); + __ negr(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct loadmask4I(vecX dst, vecD src ) +%{ + predicate(n->as_Vector()->length() == 4 && + (n->bottom_type()->is_vect()->element_basic_type() == T_INT || + n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT)); + match(Set dst (VectorLoadMask src )); + ins_cost(INSN_COST); + format %{ "uxtl $dst, $src\t# 4B to 4H\n\t" + "uxtl $dst, $dst\t# 4H to 4S\n\t" + "negr $dst, $dst\t# load mask (4B to 4S)" %} + ins_encode %{ + __ uxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + __ uxtl(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($dst$$reg), __ T4H); + __ negr(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct storemask2I(vecD dst, vecD src , immI_4 size) +%{ + predicate(n->as_Vector()->length() == 2); + match(Set dst (VectorStoreMask src size)); + ins_cost(INSN_COST); + format %{ "xtn $dst, $src\t# 2S to 2H\n\t" + "xtn $dst, $dst\t# 2H to 2B\n\t" + "negr $dst, $dst\t# store mask (2S to 2B)" %} + ins_encode %{ + __ xtn(as_FloatRegister($dst$$reg), __ T4H, as_FloatRegister($src$$reg), __ T4S); + __ xtn(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg), __ T8H); + __ negr(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct storemask4I(vecD dst, vecX src , immI_4 size) +%{ + predicate(n->as_Vector()->length() == 4); + match(Set dst (VectorStoreMask src size)); + ins_cost(INSN_COST); + format %{ "xtn $dst, $src\t# 4S to 4H\n\t" + "xtn $dst, $dst\t# 4H to 4B\n\t" + "negr $dst, $dst\t# store mask (4S to 4B)" %} + ins_encode %{ + __ xtn(as_FloatRegister($dst$$reg), __ T4H, as_FloatRegister($src$$reg), __ T4S); + __ xtn(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg), __ T8H); + __ negr(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct loadmask2L(vecX dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 2 && + (n->bottom_type()->is_vect()->element_basic_type() == T_LONG || + n->bottom_type()->is_vect()->element_basic_type() == T_DOUBLE)); + match(Set dst (VectorLoadMask src)); + ins_cost(INSN_COST); + format %{ "uxtl $dst, $src\t# 2B to 2S\n\t" + "uxtl $dst, $dst\t# 2S to 2I\n\t" + "uxtl $dst, $dst\t# 2I to 2L\n\t" + "neg $dst, $dst\t# load mask (2B to 2L)" %} + ins_encode %{ + __ uxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + __ uxtl(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($dst$$reg), __ T4H); + __ uxtl(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($dst$$reg), __ T2S); + __ negr(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct storemask2L(vecD dst, vecX src, immI_8 size) +%{ + predicate(n->as_Vector()->length() == 2); + match(Set dst (VectorStoreMask src size)); + ins_cost(INSN_COST); + format %{ "xtn $dst, $src\t# 2L to 2I\n\t" + "xtn $dst, $dst\t# 2I to 2S\n\t" + "xtn $dst, $dst\t# 2S to 2B\n\t" + "neg $dst, $dst\t# store mask (2L to 2B)" %} + ins_encode %{ + __ xtn(as_FloatRegister($dst$$reg), __ T2S, as_FloatRegister($src$$reg), __ T2D); + __ xtn(as_FloatRegister($dst$$reg), __ T4H, as_FloatRegister($dst$$reg), __ T4S); + __ xtn(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg), __ T8H); + __ negr(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +//-------------------------------- LOAD_IOTA_INDICES---------------------------------- + +instruct loadcon8B(vecD dst, immI0 src) +%{ + predicate((n->as_Vector()->length() == 2 || n->as_Vector()->length() == 4 || + n->as_Vector()->length() == 8) && + n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorLoadConst src)); + ins_cost(INSN_COST); + format %{ "ldr $dst, CONSTANT_MEMORY\t# load iota indices" %} + ins_encode %{ + __ lea(rscratch1, ExternalAddress(StubRoutines::aarch64::vector_iota_indices())); + __ ldrd(as_FloatRegister($dst$$reg), rscratch1); + %} + ins_pipe(pipe_class_memory); +%} + +instruct loadcon16B(vecX dst, immI0 src) +%{ + predicate(n->as_Vector()->length() == 16 && n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorLoadConst src)); + ins_cost(INSN_COST); + format %{ "ldr $dst, CONSTANT_MEMORY\t# load iota indices" %} + ins_encode %{ + __ lea(rscratch1, ExternalAddress(StubRoutines::aarch64::vector_iota_indices())); + __ ldrq(as_FloatRegister($dst$$reg), rscratch1); + %} + ins_pipe(pipe_class_memory); +%} + +//-------------------------------- LOAD_SHUFFLE ---------------------------------- + +instruct loadshuffle8B(vecD dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 8 && + n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorLoadShuffle src)); + ins_cost(INSN_COST); + format %{ "mov $dst, $src\t# get 8B shuffle" %} + ins_encode %{ + __ orr(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); +%} + +instruct loadshuffle16B(vecX dst, vecX src) +%{ + predicate(n->as_Vector()->length() == 16 && + n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorLoadShuffle src)); + ins_cost(INSN_COST); + format %{ "mov $dst, $src\t# get 16B shuffle" %} + ins_encode %{ + __ orr(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); +%} + +instruct loadshuffle4S(vecD dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 4 && + n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorLoadShuffle src)); + ins_cost(INSN_COST); + format %{ "uxtl $dst, $src\t# 4B to 4H" %} + ins_encode %{ + __ uxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + %} + ins_pipe(pipe_class_default); +%} + +instruct loadshuffle8S(vecX dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 8 && + n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorLoadShuffle src)); + ins_cost(INSN_COST); + format %{ "uxtl $dst, $src\t# 8B to 8H" %} + ins_encode %{ + __ uxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + %} + ins_pipe(pipe_class_default); +%} + +instruct loadshuffle4I(vecX dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 4 && + (n->bottom_type()->is_vect()->element_basic_type() == T_INT || + n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT)); + match(Set dst (VectorLoadShuffle src)); + ins_cost(INSN_COST); + format %{ "uxtl $dst, $src\t# 4B to 4H \n\t" + "uxtl $dst, $dst\t# 4H to 4S" %} + ins_encode %{ + __ uxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + __ uxtl(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($dst$$reg), __ T4H); + %} + ins_pipe(pipe_slow); +%} + +//-------------------------------- Rearrange ------------------------------------- +// Here is an example that rearranges a NEON vector with 4 ints: +// Rearrange V1 int[a0, a1, a2, a3] to V2 int[a2, a3, a0, a1] +// 1. Get the indices of V1 and store them as Vi byte[0, 1, 2, 3]. +// 2. Convert Vi byte[0, 1, 2, 3] to the indices of V2 and also store them as Vi byte[2, 3, 0, 1]. +// 3. Unsigned extend Long Vi from byte[2, 3, 0, 1] to int[2, 3, 0, 1]. +// 4. Multiply Vi int[2, 3, 0, 1] with constant int[0x04040404, 0x04040404, 0x04040404, 0x04040404] +// and get tbl base Vm int[0x08080808, 0x0c0c0c0c, 0x00000000, 0x04040404]. +// 5. Add Vm with constant int[0x03020100, 0x03020100, 0x03020100, 0x03020100] +// and get tbl index Vm int[0x0b0a0908, 0x0f0e0d0c, 0x03020100, 0x07060504] +// 6. Use Vm as index register, and use V1 as table register. +// Then get V2 as the result by tbl NEON instructions. +// Notes: +// Step 1 matches VectorLoadConst. +// Step 3 matches VectorLoadShuffle. +// Step 4, 5, 6 match VectorRearrange. +// For VectorRearrange short/int, the reason why such complex calculation is +// required is because NEON tbl supports bytes table only, so for short/int, we +// need to lookup 2/4 bytes as a group. For VectorRearrange long, we use bsl +// to implement rearrange. + +instruct rearrange8B(vecD dst, vecD src, vecD shuffle) +%{ + predicate(n->as_Vector()->length() == 8 && + n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorRearrange src shuffle)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst); + format %{ "tbl $dst, {$dst}, $shuffle\t# rearrange 8B" %} + ins_encode %{ + __ tbl(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg), 1, as_FloatRegister($shuffle$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct rearrange16B(vecX dst, vecX src, vecX shuffle) +%{ + predicate(n->as_Vector()->length() == 16 && + n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorRearrange src shuffle)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst); + format %{ "tbl $dst, {$dst}, $shuffle\t# rearrange 16B" %} + ins_encode %{ + __ tbl(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg), 1, as_FloatRegister($shuffle$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct rearrange4S(vecD dst, vecD src, vecD shuffle, vecD tmp0, vecD tmp1) +%{ + predicate(n->as_Vector()->length() == 4 && + n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorRearrange src shuffle)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp0, TEMP tmp1); + format %{ "mov $tmp0, CONSTANT\t# constant 0x0202020202020202\n\t" + "mov $tmp1, CONSTANT\t# constant 0x0100010001000100\n\t" + "mulv $dst, T4H, $shuffle, $tmp0\n\t" + "addv $dst, T8B, $dst, $tmp1\n\t" + "tbl $dst, {$src}, $dst\t# rearrange 4S" %} + ins_encode %{ + __ mov(as_FloatRegister($tmp0$$reg), __ T8B, 0x02); + __ mov(as_FloatRegister($tmp1$$reg), __ T4H, 0x0100); + __ mulv(as_FloatRegister($dst$$reg), __ T4H, + as_FloatRegister($shuffle$$reg), as_FloatRegister($tmp0$$reg)); + __ addv(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($dst$$reg), as_FloatRegister($tmp1$$reg)); + __ tbl(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg), 1, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct rearrange8S(vecX dst, vecX src, vecX shuffle, vecX tmp0, vecX tmp1) +%{ + predicate(n->as_Vector()->length() == 8 && + n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorRearrange src shuffle)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp0, TEMP tmp1); + format %{ "mov $tmp0, CONSTANT\t# constant 0x0202020202020202\n\t" + "mov $tmp1, CONSTANT\t# constant 0x0100010001000100\n\t" + "mulv $dst, T8H, $shuffle, $tmp0\n\t" + "addv $dst, T16B, $dst, $tmp1\n\t" + "tbl $dst, {$src}, $dst\t# rearrange 8S" %} + ins_encode %{ + __ mov(as_FloatRegister($tmp0$$reg), __ T16B, 0x02); + __ mov(as_FloatRegister($tmp1$$reg), __ T8H, 0x0100); + __ mulv(as_FloatRegister($dst$$reg), __ T8H, + as_FloatRegister($shuffle$$reg), as_FloatRegister($tmp0$$reg)); + __ addv(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($dst$$reg), as_FloatRegister($tmp1$$reg)); + __ tbl(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg), 1, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct rearrange4I(vecX dst, vecX src, vecX shuffle, vecX tmp0, vecX tmp1) +%{ + predicate(n->as_Vector()->length() == 4 && + (n->bottom_type()->is_vect()->element_basic_type() == T_INT || + n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT)); + match(Set dst (VectorRearrange src shuffle)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp0, TEMP tmp1); + format %{ "mov $tmp0, CONSTANT\t# constant 0x0404040404040404\n\t" + "mov $tmp1, CONSTANT\t# constant 0x0302010003020100\n\t" + "mulv $dst, T8H, $shuffle, $tmp0\n\t" + "addv $dst, T16B, $dst, $tmp1\n\t" + "tbl $dst, {$src}, $dst\t# rearrange 4I" %} + ins_encode %{ + __ mov(as_FloatRegister($tmp0$$reg), __ T16B, 0x04); + __ mov(as_FloatRegister($tmp1$$reg), __ T4S, 0x03020100); + __ mulv(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($shuffle$$reg), as_FloatRegister($tmp0$$reg)); + __ addv(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($dst$$reg), as_FloatRegister($tmp1$$reg)); + __ tbl(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg), 1, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +//-------------------------------- Anytrue/alltrue ----------------------------- + +instruct anytrue_in_mask8B(iRegINoSp dst, vecD src1, vecD src2, vecD tmp, rFlagsReg cr) +%{ + predicate(static_cast(n)->get_predicate() == BoolTest::ne); + match(Set dst (VectorTest src1 src2 )); + ins_cost(INSN_COST); + effect(TEMP tmp, KILL cr); + format %{ "addv $tmp, T8B, $src1\t# src1 and src2 are the same\n\t" + "umov $dst, $tmp, B, 0\n\t" + "cmp $dst, 0\n\t" + "cset $dst" %} + ins_encode %{ + __ addv(as_FloatRegister($tmp$$reg), __ T8B, as_FloatRegister($src1$$reg)); + __ umov($dst$$Register, as_FloatRegister($tmp$$reg), __ B, 0); + __ cmpw($dst$$Register, zr); + __ csetw($dst$$Register, Assembler::NE); + %} + ins_pipe(pipe_slow); +%} + +instruct anytrue_in_mask16B(iRegINoSp dst, vecX src1, vecX src2, vecX tmp, rFlagsReg cr) +%{ + predicate(static_cast(n)->get_predicate() == BoolTest::ne); + match(Set dst (VectorTest src1 src2 )); + ins_cost(INSN_COST); + effect(TEMP tmp, KILL cr); + format %{ "addv $tmp, T16B, $src1\t# src1 and src2 are the same\n\t" + "umov $dst, $tmp, B, 0\n\t" + "cmp $dst, 0\n\t" + "cset $dst" %} + ins_encode %{ + __ addv(as_FloatRegister($tmp$$reg), __ T16B, as_FloatRegister($src1$$reg)); + __ umov($dst$$Register, as_FloatRegister($tmp$$reg), __ B, 0); + __ cmpw($dst$$Register, zr); + __ csetw($dst$$Register, Assembler::NE); + %} + ins_pipe(pipe_slow); +%} + +instruct alltrue_in_mask8B(iRegINoSp dst, vecD src1, vecD src2, vecD tmp, rFlagsReg cr) +%{ + predicate(static_cast(n)->get_predicate() == BoolTest::overflow); + match(Set dst (VectorTest src1 src2 )); + ins_cost(INSN_COST); + effect(TEMP tmp, KILL cr); + format %{ "andr $tmp, T8B, $src1, $src2\t# src2 is maskAllTrue\n\t" + "notr $tmp, T8B, $tmp\n\t" + "addv $tmp, T8B, $tmp\n\t" + "umov $dst, $tmp, B, 0\n\t" + "cmp $dst, 0\n\t" + "cset $dst" %} + ins_encode %{ + __ andr(as_FloatRegister($tmp$$reg), __ T8B, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + __ notr(as_FloatRegister($tmp$$reg), __ T8B, as_FloatRegister($tmp$$reg)); + __ addv(as_FloatRegister($tmp$$reg), __ T8B, as_FloatRegister($tmp$$reg)); + __ umov($dst$$Register, as_FloatRegister($tmp$$reg), __ B, 0); + __ cmpw($dst$$Register, zr); + __ csetw($dst$$Register, Assembler::EQ); + %} + ins_pipe(pipe_slow); +%} + +instruct alltrue_in_mask16B(iRegINoSp dst, vecX src1, vecX src2, vecX tmp, rFlagsReg cr) +%{ + predicate(static_cast(n)->get_predicate() == BoolTest::overflow); + match(Set dst (VectorTest src1 src2 )); + ins_cost(INSN_COST); + effect(TEMP tmp, KILL cr); + format %{ "andr $tmp, T16B, $src1, $src2\t# src2 is maskAllTrue\n\t" + "notr $tmp, T16B, $tmp\n\t" + "addv $tmp, T16B, $tmp\n\t" + "umov $dst, $tmp, B, 0\n\t" + "cmp $dst, 0\n\t" + "cset $dst" %} + ins_encode %{ + __ andr(as_FloatRegister($tmp$$reg), __ T16B, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + __ notr(as_FloatRegister($tmp$$reg), __ T16B, as_FloatRegister($tmp$$reg)); + __ addv(as_FloatRegister($tmp$$reg), __ T16B, as_FloatRegister($tmp$$reg)); + __ umov($dst$$Register, as_FloatRegister($tmp$$reg), __ B, 0); + __ cmpw($dst$$Register, zr); + __ csetw($dst$$Register, Assembler::EQ); + %} + ins_pipe(pipe_slow); +%} diff --git a/src/hotspot/cpu/aarch64/aarch64_neon_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_neon_ad.m4 new file mode 100644 index 00000000000..0b1dc5cb7c6 --- /dev/null +++ b/src/hotspot/cpu/aarch64/aarch64_neon_ad.m4 @@ -0,0 +1,1424 @@ +// Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2020, Arm Limited. All rights reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +// or visit www.oracle.com if you need additional information or have any +// questions. +// +// + +dnl Generate the warning +// This file is automatically generated by running "m4 aarch64_neon_ad.m4". Do not edit ---- +dnl + +// AArch64 NEON Architecture Description File + +dnl +define(`ORL2I', `ifelse($1,I,orL2I)')dnl +dnl +define(`error', `__program__:__file__:__line__: Invalid argument ``$1''m4exit(`1')')dnl +dnl +define(`iTYPE2SIMD', +`ifelse($1, `B', `B', + $1, `S', `H', + $1, `I', `S', + $1, `L', `D', + `error($1)')')dnl +dnl +define(`fTYPE2SIMD', +`ifelse($1, `F', `S', + $1, `D', `D', + `error($1)')')dnl +dnl +define(`TYPE2DATATYPE', +`ifelse($1, `B', `BYTE', + $1, `S', `SHORT', + $1, `I', `INT', + $1, `L', `LONG', + $1, `F', `FLOAT', + $1, `D', `DOUBLE', + `error($1)')')dnl +dnl +// ====================VECTOR INSTRUCTIONS================================== + +// ------------------------------ Load/store/reinterpret ----------------------- + +// Load vector (16 bits) +instruct loadV2(vecD dst, memory mem) +%{ + predicate(n->as_LoadVector()->memory_size() == 2); + match(Set dst (LoadVector mem)); + ins_cost(4 * INSN_COST); + format %{ "ldrh $dst,$mem\t# vector (16 bits)" %} + ins_encode( aarch64_enc_ldrvH(dst, mem) ); + ins_pipe(vload_reg_mem64); +%} + +// Store Vector (16 bits) +instruct storeV2(vecD src, memory mem) +%{ + predicate(n->as_StoreVector()->memory_size() == 2); + match(Set mem (StoreVector mem src)); + ins_cost(4 * INSN_COST); + format %{ "strh $mem,$src\t# vector (16 bits)" %} + ins_encode( aarch64_enc_strvH(src, mem) ); + ins_pipe(vstore_reg_mem64); +%} +dnl +define(`REINTERPRET', ` +instruct reinterpret$1`'(vec$1 dst) +%{ + predicate(n->bottom_type()->is_vect()->length_in_bytes() == $2 && + n->in(1)->bottom_type()->is_vect()->length_in_bytes() == $2); + match(Set dst (VectorReinterpret dst)); + ins_cost(0); + format %{ " # reinterpret $dst" %} + ins_encode %{ + // empty + %} + ins_pipe(pipe_class_empty); +%}')dnl +dnl $1 $2 +REINTERPRET(D, 8) +REINTERPRET(X, 16) +dnl +define(`REINTERPRET_X', ` +instruct reinterpret$1`'2$2`'(vec$2 dst, vec$1 src) +%{ + predicate(n->bottom_type()->is_vect()->length_in_bytes() == $3 && + n->in(1)->bottom_type()->is_vect()->length_in_bytes() == $4); + match(Set dst (VectorReinterpret src)); + ins_cost(INSN_COST); + format %{ " # reinterpret $dst,$src" %} + ins_encode %{ + // If register is the same, then move is not needed. + if (as_FloatRegister($dst$$reg) != as_FloatRegister($src$$reg)) { + __ orr(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg), + as_FloatRegister($src$$reg)); + } + %} + ins_pipe(vlogical64); +%}')dnl +dnl $1 $2 $3 $4 +REINTERPRET_X(D, X, 16, 8) +REINTERPRET_X(X, D, 8, 16) +dnl + +// ------------------------------ Vector cast ------------------------------- +dnl +define(`VECTOR_CAST_I2I', ` +instruct vcvt$1$2to$1$3`'(vec$4 dst, vec$5 src) +%{ + predicate(n->as_Vector()->length() == $1 && n->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($3)); + match(Set dst (VectorCast$2`'2X src)); + format %{ "$6 $dst, T$8, $src, T$7\t# convert $1$2 to $1$3 vector" %} + ins_encode %{ + __ $6(as_FloatRegister($dst$$reg), __ T$8, as_FloatRegister($src$$reg), __ T$7); + %} + ins_pipe(pipe_class_default); +%}')dnl +dnl $1 $2 $3 $4 $5 $6 $7 $8 +VECTOR_CAST_I2I(4, B, S, D, D, sxtl, 8B, 8H) +VECTOR_CAST_I2I(8, B, S, X, D, sxtl, 8B, 8H) +VECTOR_CAST_I2I(4, S, B, D, D, xtn, 8H, 8B) +VECTOR_CAST_I2I(8, S, B, D, X, xtn, 8H, 8B) +VECTOR_CAST_I2I(4, S, I, X, D, sxtl, 4H, 4S) +VECTOR_CAST_I2I(4, I, S, D, X, xtn, 4S, 4H) +VECTOR_CAST_I2I(2, I, L, X, D, sxtl, 2S, 2D) +VECTOR_CAST_I2I(2, L, I, D, X, xtn, 2D, 2S) +dnl +define(`VECTOR_CAST_B2I', ` +instruct vcvt4$1to4$2`'(vec$3 dst, vec$4 src) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($2)); + match(Set dst (VectorCast$1`'2X src)); + format %{ "$5 $dst, T$7, $src, T$6\n\t" + "$5 $dst, T$9, $dst, T$8\t# convert 4$1 to 4$2 vector" + %} + ins_encode %{ + __ $5(as_FloatRegister($dst$$reg), __ T$7, as_FloatRegister($src$$reg), __ T$6); + __ $5(as_FloatRegister($dst$$reg), __ T$9, as_FloatRegister($dst$$reg), __ T$8); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 $4 $5 $6 $7 $8 $9 +VECTOR_CAST_B2I(B, I, X, D, sxtl, 8B, 8H, 4H, 4S) +VECTOR_CAST_B2I(I, B, D, X, xtn, 4S, 4H, 8H, 8B) + +instruct vcvt4Bto4F(vecX dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorCastB2X src)); + format %{ "sxtl $dst, T8H, $src, T8B\n\t" + "sxtl $dst, T4S, $dst, T4H\n\t" + "scvtfv T4S, $dst, $dst\t# convert 4B to 4F vector" + %} + ins_encode %{ + __ sxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + __ sxtl(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($dst$$reg), __ T4H); + __ scvtfv(__ T4S, as_FloatRegister($dst$$reg), as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} +dnl +define(`VECTOR_CAST_I2F_L', ` +instruct vcvt$1$2to$1$3`'(vecX dst, vecD src) +%{ + predicate(n->as_Vector()->length() == $1 && n->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($3)); + match(Set dst (VectorCast$2`'2X src)); + format %{ "sxtl $dst, T$5, $src, T$4\n\t" + "scvtfv T$5, $dst, $dst\t# convert $1$2 to $1$3 vector" + %} + ins_encode %{ + __ sxtl(as_FloatRegister($dst$$reg), __ T$5, as_FloatRegister($src$$reg), __ T$4); + __ scvtfv(__ T$5, as_FloatRegister($dst$$reg), as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 $4 $5 +VECTOR_CAST_I2F_L(4, S, F, 4H, 4S) +VECTOR_CAST_I2F_L(2, I, D, 2S, 2D) +dnl +define(`VECTOR_CAST_I2F', ` +instruct vcvt$1$2to$1$3`'(vec$4 dst, vec$4 src) +%{ + predicate(n->as_Vector()->length() == $1 && n->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($3)); + match(Set dst (VectorCast$2`'2X src)); + format %{ "scvtfv T$5, $dst, $src\t# convert $1$2 to $1$3 vector" %} + ins_encode %{ + __ scvtfv(__ T$5, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); +%}')dnl +dnl $1 $2 $3 $4 $5 +VECTOR_CAST_I2F(2, I, F, D, 2S) +VECTOR_CAST_I2F(4, I, F, X, 4S) +VECTOR_CAST_I2F(2, L, D, X, 2D) +dnl +define(`VECTOR_CAST_F2F', ` +instruct vcvt2$1to2$2`'(vec$3 dst, vec$4 src) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($2)); + match(Set dst (VectorCast$1`'2X src)); + format %{ "$5 $dst, T$7, $src, T$6\t# convert 2$1 to 2$2 vector" %} + ins_encode %{ + __ $5(as_FloatRegister($dst$$reg), __ T$7, as_FloatRegister($src$$reg), __ T$6); + %} + ins_pipe(pipe_class_default); +%}')dnl +dnl $1 $2 $3 $4 $5 $6 $7 +VECTOR_CAST_F2F(F, D, X, D, fcvtl, 2S, 2D) +VECTOR_CAST_F2F(D, F, D, X, fcvtn, 2D, 2S) +dnl + +instruct vcvt2Lto2F(vecD dst, vecX src) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorCastL2X src)); + format %{ "scvtfv T2D, $dst, $src\n\t" + "fcvtn $dst, T2S, $dst, T2D\t# convert 2L to 2F vector" + %} + ins_encode %{ + __ scvtfv(__ T2D, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); + __ fcvtn(as_FloatRegister($dst$$reg), __ T2S, as_FloatRegister($dst$$reg), __ T2D); + %} + ins_pipe(pipe_slow); +%} + +// ------------------------------ Reduction ------------------------------- +dnl +define(`REDUCE_ADD_BORS', ` +instruct reduce_add$1$2`'(iRegINoSp dst, iRegIorL2I isrc, vec$3 vsrc, vec$3 tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($2)); + match(Set dst (AddReductionVI isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "addv $tmp, T$1`'iTYPE2SIMD($2), $vsrc\n\t" + "smov $dst, $tmp, iTYPE2SIMD($2), 0\n\t" + "addw $dst, $dst, $isrc\n\t" + "sxt$4 $dst, $dst\t# add reduction$1$2" + %} + ins_encode %{ + __ addv(as_FloatRegister($tmp$$reg), __ T$1`'iTYPE2SIMD($2), as_FloatRegister($vsrc$$reg)); + __ smov($dst$$Register, as_FloatRegister($tmp$$reg), __ iTYPE2SIMD($2), 0); + __ addw($dst$$Register, $dst$$Register, $isrc$$Register); + __ sxt$4($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 $4 +REDUCE_ADD_BORS(8, B, D, b) +REDUCE_ADD_BORS(16, B, X, b) +REDUCE_ADD_BORS(4, S, D, h) +REDUCE_ADD_BORS(8, S, X, h) +dnl + +instruct reduce_add2L(iRegLNoSp dst, iRegL isrc, vecX vsrc, vecX tmp) +%{ + match(Set dst (AddReductionVL isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "addpd $tmp, $vsrc\n\t" + "umov $dst, $tmp, D, 0\n\t" + "add $dst, $isrc, $dst\t# add reduction2L" + %} + ins_encode %{ + __ addpd(as_FloatRegister($tmp$$reg), as_FloatRegister($vsrc$$reg)); + __ umov($dst$$Register, as_FloatRegister($tmp$$reg), __ D, 0); + __ add($dst$$Register, $isrc$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_mul8B(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, vecD vtmp1, vecD vtmp2, iRegINoSp itmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (MulReductionVI isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP vtmp1, TEMP vtmp2, TEMP itmp); + format %{ "ins $vtmp1, S, $vsrc, 0, 1\n\t" + "mulv $vtmp1, T8B, $vtmp1, $vsrc\n\t" + "ins $vtmp2, H, $vtmp1, 0, 1\n\t" + "mulv $vtmp2, T8B, $vtmp2, $vtmp1\n\t" + "umov $itmp, $vtmp2, B, 0\n\t" + "mulw $dst, $itmp, $isrc\n\t" + "sxtb $dst, $dst\n\t" + "umov $itmp, $vtmp2, B, 1\n\t" + "mulw $dst, $itmp, $dst\n\t" + "sxtb $dst, $dst\t# mul reduction8B" + %} + ins_encode %{ + __ ins(as_FloatRegister($vtmp1$$reg), __ S, + as_FloatRegister($vsrc$$reg), 0, 1); + __ mulv(as_FloatRegister($vtmp1$$reg), __ T8B, + as_FloatRegister($vtmp1$$reg), as_FloatRegister($vsrc$$reg)); + __ ins(as_FloatRegister($vtmp2$$reg), __ H, + as_FloatRegister($vtmp1$$reg), 0, 1); + __ mulv(as_FloatRegister($vtmp2$$reg), __ T8B, + as_FloatRegister($vtmp2$$reg), as_FloatRegister($vtmp1$$reg)); + __ umov($itmp$$Register, as_FloatRegister($vtmp2$$reg), __ B, 0); + __ mulw($dst$$Register, $itmp$$Register, $isrc$$Register); + __ sxtb($dst$$Register, $dst$$Register); + __ umov($itmp$$Register, as_FloatRegister($vtmp2$$reg), __ B, 1); + __ mulw($dst$$Register, $itmp$$Register, $dst$$Register); + __ sxtb($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_mul16B(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, vecX vtmp1, vecX vtmp2, iRegINoSp itmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (MulReductionVI isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP vtmp1, TEMP vtmp2, TEMP itmp); + format %{ "ins $vtmp1, D, $vsrc, 0, 1\n\t" + "mulv $vtmp1, T8B, $vtmp1, $vsrc\n\t" + "ins $vtmp2, S, $vtmp1, 0, 1\n\t" + "mulv $vtmp1, T8B, $vtmp2, $vtmp1\n\t" + "ins $vtmp2, H, $vtmp1, 0, 1\n\t" + "mulv $vtmp2, T8B, $vtmp2, $vtmp1\n\t" + "umov $itmp, $vtmp2, B, 0\n\t" + "mulw $dst, $itmp, $isrc\n\t" + "sxtb $dst, $dst\n\t" + "umov $itmp, $vtmp2, B, 1\n\t" + "mulw $dst, $itmp, $dst\n\t" + "sxtb $dst, $dst\t# mul reduction16B" + %} + ins_encode %{ + __ ins(as_FloatRegister($vtmp1$$reg), __ D, + as_FloatRegister($vsrc$$reg), 0, 1); + __ mulv(as_FloatRegister($vtmp1$$reg), __ T8B, + as_FloatRegister($vtmp1$$reg), as_FloatRegister($vsrc$$reg)); + __ ins(as_FloatRegister($vtmp2$$reg), __ S, + as_FloatRegister($vtmp1$$reg), 0, 1); + __ mulv(as_FloatRegister($vtmp1$$reg), __ T8B, + as_FloatRegister($vtmp2$$reg), as_FloatRegister($vtmp1$$reg)); + __ ins(as_FloatRegister($vtmp2$$reg), __ H, + as_FloatRegister($vtmp1$$reg), 0, 1); + __ mulv(as_FloatRegister($vtmp2$$reg), __ T8B, + as_FloatRegister($vtmp2$$reg), as_FloatRegister($vtmp1$$reg)); + __ umov($itmp$$Register, as_FloatRegister($vtmp2$$reg), __ B, 0); + __ mulw($dst$$Register, $itmp$$Register, $isrc$$Register); + __ sxtb($dst$$Register, $dst$$Register); + __ umov($itmp$$Register, as_FloatRegister($vtmp2$$reg), __ B, 1); + __ mulw($dst$$Register, $itmp$$Register, $dst$$Register); + __ sxtb($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_mul4S(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, vecD vtmp, iRegINoSp itmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (MulReductionVI isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP vtmp, TEMP itmp); + format %{ "ins $vtmp, S, $vsrc, 0, 1\n\t" + "mulv $vtmp, T4H, $vtmp, $vsrc\n\t" + "umov $itmp, $vtmp, H, 0\n\t" + "mulw $dst, $itmp, $isrc\n\t" + "sxth $dst, $dst\n\t" + "umov $itmp, $vtmp, H, 1\n\t" + "mulw $dst, $itmp, $dst\n\t" + "sxth $dst, $dst\t# mul reduction4S" + %} + ins_encode %{ + __ ins(as_FloatRegister($vtmp$$reg), __ S, + as_FloatRegister($vsrc$$reg), 0, 1); + __ mulv(as_FloatRegister($vtmp$$reg), __ T4H, + as_FloatRegister($vtmp$$reg), as_FloatRegister($vsrc$$reg)); + __ umov($itmp$$Register, as_FloatRegister($vtmp$$reg), __ H, 0); + __ mulw($dst$$Register, $itmp$$Register, $isrc$$Register); + __ sxth($dst$$Register, $dst$$Register); + __ umov($itmp$$Register, as_FloatRegister($vtmp$$reg), __ H, 1); + __ mulw($dst$$Register, $itmp$$Register, $dst$$Register); + __ sxth($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_mul8S(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, vecX vtmp1, vecX vtmp2, iRegINoSp itmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (MulReductionVI isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP vtmp1, TEMP vtmp2, TEMP itmp); + format %{ "ins $vtmp1, D, $vsrc, 0, 1\n\t" + "mulv $vtmp1, T4H, $vtmp1, $vsrc\n\t" + "ins $vtmp2, S, $vtmp1, 0, 1\n\t" + "mulv $vtmp2, T4H, $vtmp2, $vtmp1\n\t" + "umov $itmp, $vtmp2, H, 0\n\t" + "mulw $dst, $itmp, $isrc\n\t" + "sxth $dst, $dst\n\t" + "umov $itmp, $vtmp2, H, 1\n\t" + "mulw $dst, $itmp, $dst\n\t" + "sxth $dst, $dst\t# mul reduction8S" + %} + ins_encode %{ + __ ins(as_FloatRegister($vtmp1$$reg), __ D, + as_FloatRegister($vsrc$$reg), 0, 1); + __ mulv(as_FloatRegister($vtmp1$$reg), __ T4H, + as_FloatRegister($vtmp1$$reg), as_FloatRegister($vsrc$$reg)); + __ ins(as_FloatRegister($vtmp2$$reg), __ S, + as_FloatRegister($vtmp1$$reg), 0, 1); + __ mulv(as_FloatRegister($vtmp2$$reg), __ T4H, + as_FloatRegister($vtmp2$$reg), as_FloatRegister($vtmp1$$reg)); + __ umov($itmp$$Register, as_FloatRegister($vtmp2$$reg), __ H, 0); + __ mulw($dst$$Register, $itmp$$Register, $isrc$$Register); + __ sxth($dst$$Register, $dst$$Register); + __ umov($itmp$$Register, as_FloatRegister($vtmp2$$reg), __ H, 1); + __ mulw($dst$$Register, $itmp$$Register, $dst$$Register); + __ sxth($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_mul2L(iRegLNoSp dst, iRegL isrc, vecX vsrc, iRegLNoSp tmp) +%{ + match(Set dst (MulReductionVL isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "mul $dst, $isrc, $tmp\n\t" + "umov $tmp, $vsrc, D, 1\n\t" + "mul $dst, $dst, $tmp\t# mul reduction2L" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ mul($dst$$Register, $isrc$$Register, $tmp$$Register); + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ mul($dst$$Register, $dst$$Register, $tmp$$Register); + %} + ins_pipe(pipe_slow); +%} +dnl +define(`REDUCE_MAX_MIN_INT', ` +instruct reduce_$1$2$3`'(iRegINoSp dst, iRegIorL2I isrc, vec$4 vsrc, vec$4 tmp, rFlagsReg cr) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($3)); + match(Set dst ($5ReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "s$1v $tmp, T$2`'iTYPE2SIMD($3), $vsrc\n\t" + "$6mov $dst, $tmp, iTYPE2SIMD($3), 0\n\t" + "cmpw $dst, $isrc\n\t" + "cselw $dst, $dst, $isrc $7\t# $1 reduction$2$3" + %} + ins_encode %{ + __ s$1v(as_FloatRegister($tmp$$reg), __ T$2`'iTYPE2SIMD($3), as_FloatRegister($vsrc$$reg)); + __ $6mov(as_Register($dst$$reg), as_FloatRegister($tmp$$reg), __ iTYPE2SIMD($3), 0); + __ cmpw(as_Register($dst$$reg), as_Register($isrc$$reg)); + __ cselw(as_Register($dst$$reg), as_Register($dst$$reg), as_Register($isrc$$reg), Assembler::$7); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 $4 $5 $6 $7 +REDUCE_MAX_MIN_INT(max, 8, B, D, Max, s, GT) +REDUCE_MAX_MIN_INT(max, 16, B, X, Max, s, GT) +REDUCE_MAX_MIN_INT(max, 4, S, D, Max, s, GT) +REDUCE_MAX_MIN_INT(max, 8, S, X, Max, s, GT) +REDUCE_MAX_MIN_INT(max, 4, I, X, Max, u, GT) +REDUCE_MAX_MIN_INT(min, 8, B, D, Min, s, LT) +REDUCE_MAX_MIN_INT(min, 16, B, X, Min, s, LT) +REDUCE_MAX_MIN_INT(min, 4, S, D, Min, s, LT) +REDUCE_MAX_MIN_INT(min, 8, S, X, Min, s, LT) +REDUCE_MAX_MIN_INT(min, 4, I, X, Min, u, LT) +dnl +define(`REDUCE_MAX_MIN_2I', ` +instruct reduce_$1`'2I(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, vecX tmp, rFlagsReg cr) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst ($2ReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "dup $tmp, T2D, $vsrc\n\t" + "s$1v $tmp, T4S, $tmp\n\t" + "umov $dst, $tmp, S, 0\n\t" + "cmpw $dst, $isrc\n\t" + "cselw $dst, $dst, $isrc $3\t# $1 reduction2I" + %} + ins_encode %{ + __ dup(as_FloatRegister($tmp$$reg), __ T2D, as_FloatRegister($vsrc$$reg)); + __ s$1v(as_FloatRegister($tmp$$reg), __ T4S, as_FloatRegister($tmp$$reg)); + __ umov(as_Register($dst$$reg), as_FloatRegister($tmp$$reg), __ S, 0); + __ cmpw(as_Register($dst$$reg), as_Register($isrc$$reg)); + __ cselw(as_Register($dst$$reg), as_Register($dst$$reg), as_Register($isrc$$reg), Assembler::$3); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 +REDUCE_MAX_MIN_2I(max, Max, GT) +REDUCE_MAX_MIN_2I(min, Min, LT) +dnl +define(`REDUCE_MAX_MIN_2L', ` +instruct reduce_$1`'2L(iRegLNoSp dst, iRegL isrc, vecX vsrc, iRegLNoSp tmp, rFlagsReg cr) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst ($2ReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "cmp $isrc,$tmp\n\t" + "csel $dst, $isrc, $tmp $3\n\t" + "umov $tmp, $vsrc, D, 1\n\t" + "cmp $dst, $tmp\n\t" + "csel $dst, $dst, $tmp $3\t# $1 reduction2L" + %} + ins_encode %{ + __ umov(as_Register($tmp$$reg), as_FloatRegister($vsrc$$reg), __ D, 0); + __ cmp(as_Register($isrc$$reg), as_Register($tmp$$reg)); + __ csel(as_Register($dst$$reg), as_Register($isrc$$reg), as_Register($tmp$$reg), Assembler::$3); + __ umov(as_Register($tmp$$reg), as_FloatRegister($vsrc$$reg), __ D, 1); + __ cmp(as_Register($dst$$reg), as_Register($tmp$$reg)); + __ csel(as_Register($dst$$reg), as_Register($dst$$reg), as_Register($tmp$$reg), Assembler::$3); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 +REDUCE_MAX_MIN_2L(max, Max, GT) +REDUCE_MAX_MIN_2L(min, Min, LT) +dnl +define(`REDUCE_LOGIC_OP_8B', ` +instruct reduce_$1`'8B(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst ($2ReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, S, 0\n\t" + "umov $dst, $vsrc, S, 1\n\t" + "$1w $dst, $dst, $tmp\n\t" + "$1w $dst, $dst, $dst, LSR #16\n\t" + "$1w $dst, $dst, $dst, LSR #8\n\t" + "$1w $dst, $isrc, $dst\n\t" + "sxtb $dst, $dst\t# $1 reduction8B" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ S, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ S, 1); + __ $1w($dst$$Register, $dst$$Register, $tmp$$Register); + __ $1w($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 16); + __ $1w($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 8); + __ $1w($dst$$Register, $isrc$$Register, $dst$$Register); + __ sxtb($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 +REDUCE_LOGIC_OP_8B(and, And) +REDUCE_LOGIC_OP_8B(orr, Or) +REDUCE_LOGIC_OP_8B(eor, Xor) +define(`REDUCE_LOGIC_OP_16B', ` +instruct reduce_$1`'16B(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst ($2ReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "umov $dst, $vsrc, D, 1\n\t" + "$3 $dst, $dst, $tmp\n\t" + "$3 $dst, $dst, $dst, LSR #32\n\t" + "$1w $dst, $dst, $dst, LSR #16\n\t" + "$1w $dst, $dst, $dst, LSR #8\n\t" + "$1w $dst, $isrc, $dst\n\t" + "sxtb $dst, $dst\t# $1 reduction16B" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ $3($dst$$Register, $dst$$Register, $tmp$$Register); + __ $3($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 32); + __ $1w($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 16); + __ $1w($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 8); + __ $1w($dst$$Register, $isrc$$Register, $dst$$Register); + __ sxtb($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 +REDUCE_LOGIC_OP_16B(and, And, andr) +REDUCE_LOGIC_OP_16B(orr, Or, orr ) +REDUCE_LOGIC_OP_16B(eor, Xor, eor ) +dnl +define(`REDUCE_LOGIC_OP_4S', ` +instruct reduce_$1`'4S(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst ($2ReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, S, 0\n\t" + "umov $dst, $vsrc, S, 1\n\t" + "$1w $dst, $dst, $tmp\n\t" + "$1w $dst, $dst, $dst, LSR #16\n\t" + "$1w $dst, $isrc, $dst\n\t" + "sxth $dst, $dst\t# $1 reduction4S" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ S, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ S, 1); + __ $1w($dst$$Register, $dst$$Register, $tmp$$Register); + __ $1w($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 16); + __ $1w($dst$$Register, $isrc$$Register, $dst$$Register); + __ sxth($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 +REDUCE_LOGIC_OP_4S(and, And) +REDUCE_LOGIC_OP_4S(orr, Or) +REDUCE_LOGIC_OP_4S(eor, Xor) +dnl +define(`REDUCE_LOGIC_OP_8S', ` +instruct reduce_$1`'8S(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst ($2ReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "umov $dst, $vsrc, D, 1\n\t" + "$3 $dst, $dst, $tmp\n\t" + "$3 $dst, $dst, $dst, LSR #32\n\t" + "$1w $dst, $dst, $dst, LSR #16\n\t" + "$1w $dst, $isrc, $dst\n\t" + "sxth $dst, $dst\t# $1 reduction8S" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ $3($dst$$Register, $dst$$Register, $tmp$$Register); + __ $3($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 32); + __ $1w($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 16); + __ $1w($dst$$Register, $isrc$$Register, $dst$$Register); + __ sxth($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 +REDUCE_LOGIC_OP_8S(and, And, andr) +REDUCE_LOGIC_OP_8S(orr, Or, orr ) +REDUCE_LOGIC_OP_8S(eor, Xor, eor ) +dnl +define(`REDUCE_LOGIC_OP_2I', ` +instruct reduce_$1`'2I(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst ($2ReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, S, 0\n\t" + "$1w $dst, $tmp, $isrc\n\t" + "umov $tmp, $vsrc, S, 1\n\t" + "$1w $dst, $tmp, $dst\t# $1 reduction2I" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ S, 0); + __ $1w($dst$$Register, $tmp$$Register, $isrc$$Register); + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ S, 1); + __ $1w($dst$$Register, $tmp$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 +REDUCE_LOGIC_OP_2I(and, And) +REDUCE_LOGIC_OP_2I(orr, Or) +REDUCE_LOGIC_OP_2I(eor, Xor) +dnl +define(`REDUCE_LOGIC_OP_4I', ` +instruct reduce_$1`'4I(iRegINoSp dst, iRegIorL2I isrc, vecX vsrc, iRegINoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst ($2ReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "umov $dst, $vsrc, D, 1\n\t" + "$3 $dst, $dst, $tmp\n\t" + "$3 $dst, $dst, $dst, LSR #32\n\t" + "$1w $dst, $isrc, $dst\t# $1 reduction4I" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ umov($dst$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ $3($dst$$Register, $dst$$Register, $tmp$$Register); + __ $3($dst$$Register, $dst$$Register, $dst$$Register, Assembler::LSR, 32); + __ $1w($dst$$Register, $isrc$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 +REDUCE_LOGIC_OP_4I(and, And, andr) +REDUCE_LOGIC_OP_4I(orr, Or, orr ) +REDUCE_LOGIC_OP_4I(eor, Xor, eor ) +dnl +define(`REDUCE_LOGIC_OP_2L', ` +instruct reduce_$1`'2L(iRegLNoSp dst, iRegL isrc, vecX vsrc, iRegLNoSp tmp) +%{ + predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst ($2ReductionV isrc vsrc)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "umov $tmp, $vsrc, D, 0\n\t" + "$3 $dst, $isrc, $tmp\n\t" + "umov $tmp, $vsrc, D, 1\n\t" + "$3 $dst, $dst, $tmp\t# $1 reduction2L" + %} + ins_encode %{ + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 0); + __ $3($dst$$Register, $isrc$$Register, $tmp$$Register); + __ umov($tmp$$Register, as_FloatRegister($vsrc$$reg), __ D, 1); + __ $3($dst$$Register, $dst$$Register, $tmp$$Register); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 +REDUCE_LOGIC_OP_2L(and, And, andr) +REDUCE_LOGIC_OP_2L(orr, Or, orr ) +REDUCE_LOGIC_OP_2L(eor, Xor, eor ) +dnl + +// ------------------------------ Vector insert --------------------------------- +define(`VECTOR_INSERT_I', ` +instruct insert$1$2`'(vec$3 dst, vec$3 src, iReg$4`'ORL2I($4) val, immI idx) +%{ + predicate(n->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($2)); + match(Set dst (VectorInsert (Binary src val) idx)); + ins_cost(INSN_COST); + format %{ "orr $dst, T$5, $src, $src\n\t" + "mov $dst, T$1`'iTYPE2SIMD($2), $idx, $val\t# insert into vector($1$2)" %} + ins_encode %{ + if (as_FloatRegister($dst$$reg) != as_FloatRegister($src$$reg)) { + __ orr(as_FloatRegister($dst$$reg), __ T$5, + as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); + } + __ mov(as_FloatRegister($dst$$reg), __ T$1`'iTYPE2SIMD($2), $idx$$constant, $val$$Register); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 $4 $5 +VECTOR_INSERT_I(8, B, D, I, 8B) +VECTOR_INSERT_I(16, B, X, I, 16B) +VECTOR_INSERT_I(4, S, D, I, 8B) +VECTOR_INSERT_I(8, S, X, I, 16B) +VECTOR_INSERT_I(2, I, D, I, 8B) +VECTOR_INSERT_I(4, I, X, I, 16B) +VECTOR_INSERT_I(2, L, X, L, 16B) +dnl +define(`VECTOR_INSERT_F', ` +instruct insert$1`'(vec$2 dst, vec$2 src, vReg$3 val, immI idx) +%{ + predicate(n->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($3)); + match(Set dst (VectorInsert (Binary src val) idx)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst); + format %{ "orr $dst, T$4, $src, $src\n\t" + "ins $dst, $5, $val, $idx, 0\t# insert into vector($1)" %} + ins_encode %{ + __ orr(as_FloatRegister($dst$$reg), __ T$4, + as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); + __ ins(as_FloatRegister($dst$$reg), __ $5, + as_FloatRegister($val$$reg), $idx$$constant, 0); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 $4 $5 +VECTOR_INSERT_F(2F, D, F, 8B, S) +VECTOR_INSERT_F(4F, X, F, 16B, S) +VECTOR_INSERT_F(2D, X, D, 16B, D) +dnl + +// ------------------------------ Vector extract --------------------------------- +define(`VECTOR_EXTRACT_I', ` +instruct extract$1$2`'(iReg$3NoSp dst, vec$4 src, immI idx) +%{ + predicate(n->in(1)->bottom_type()->is_vect()->length() == $1); + match(Set dst (Extract$2 src idx)); + ins_cost(INSN_COST); + format %{ "$5mov $dst, $src, $6, $idx\t# extract from vector($1$2)" %} + ins_encode %{ + __ $5mov($dst$$Register, as_FloatRegister($src$$reg), __ $6, $idx$$constant); + %} + ins_pipe(pipe_class_default); +%}')dnl +dnl $1 $2 $3 $4 $5 $6 +VECTOR_EXTRACT_I(8, B, I, D, s, B) +VECTOR_EXTRACT_I(16, B, I, X, s, B) +VECTOR_EXTRACT_I(4, S, I, D, s, H) +VECTOR_EXTRACT_I(8, S, I, X, s, H) +VECTOR_EXTRACT_I(2, I, I, D, u, S) +VECTOR_EXTRACT_I(4, I, I, X, u, S) +VECTOR_EXTRACT_I(2, L, L, X, u, D) +dnl +define(`VECTOR_EXTRACT_F', ` +instruct extract$1$2`'(vReg$2 dst, vec$3 src, immI idx) +%{ + predicate(n->in(1)->bottom_type()->is_vect()->length() == $1); + match(Set dst (Extract$2 src idx)); + ins_cost(INSN_COST); + format %{ "ins $dst, $4, $src, 0, $idx\t# extract from vector($1$2)" %} + ins_encode %{ + __ ins(as_FloatRegister($dst$$reg), __ $4, + as_FloatRegister($src$$reg), 0, $idx$$constant); + %} + ins_pipe(pipe_class_default); +%}')dnl +dnl $1 $2 $3 $4 +VECTOR_EXTRACT_F(2, F, D, S) +VECTOR_EXTRACT_F(4, F, X, S) +VECTOR_EXTRACT_F(2, D, X, D) +dnl + +// ------------------------------ Vector comparison --------------------------------- +define(`VECTOR_CMP_EQ_GT_GE', ` +instruct vcm$1$2$3`'(vec$4 dst, vec$4 src1, vec$4 src2, immI cond) +%{ + predicate(n->as_Vector()->length() == $2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::$1 && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($3)); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "$6cm$1 $dst, $src1, $src2\t# vector cmp ($2$3)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ $6cm$1(as_FloatRegister($dst$$reg), __ T$2$5, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop$7); +%}')dnl +dnl $1 $2 $3 $4 $5 $6 $7 +VECTOR_CMP_EQ_GT_GE(eq, 8, B, D, B, , 64) +VECTOR_CMP_EQ_GT_GE(eq, 16,B, X, B, , 128) +VECTOR_CMP_EQ_GT_GE(eq, 4, S, D, H, , 64) +VECTOR_CMP_EQ_GT_GE(eq, 8, S, X, H, , 128) +VECTOR_CMP_EQ_GT_GE(eq, 2, I, D, S, , 64) +VECTOR_CMP_EQ_GT_GE(eq, 4, I, X, S, , 128) +VECTOR_CMP_EQ_GT_GE(eq, 2, L, X, D, , 128) +VECTOR_CMP_EQ_GT_GE(eq, 2, F, D, S, f, 64) +VECTOR_CMP_EQ_GT_GE(eq, 4, F, X, S, f, 128) +VECTOR_CMP_EQ_GT_GE(eq, 2, D, X, D, f, 128) +VECTOR_CMP_EQ_GT_GE(gt, 8, B, D, B, , 64) +VECTOR_CMP_EQ_GT_GE(gt, 16,B, X, B, , 128) +VECTOR_CMP_EQ_GT_GE(gt, 4, S, D, H, , 64) +VECTOR_CMP_EQ_GT_GE(gt, 8, S, X, H, , 128) +VECTOR_CMP_EQ_GT_GE(gt, 2, I, D, S, , 64) +VECTOR_CMP_EQ_GT_GE(gt, 4, I, X, S, , 128) +VECTOR_CMP_EQ_GT_GE(gt, 2, L, X, D, , 128) +VECTOR_CMP_EQ_GT_GE(gt, 2, F, D, S, f, 64) +VECTOR_CMP_EQ_GT_GE(gt, 4, F, X, S, f, 128) +VECTOR_CMP_EQ_GT_GE(gt, 2, D, X, D, f, 128) +VECTOR_CMP_EQ_GT_GE(ge, 8, B, D, B, , 64) +VECTOR_CMP_EQ_GT_GE(ge, 16,B, X, B, , 128) +VECTOR_CMP_EQ_GT_GE(ge, 4, S, D, H, , 64) +VECTOR_CMP_EQ_GT_GE(ge, 8, S, X, H, , 128) +VECTOR_CMP_EQ_GT_GE(ge, 2, I, D, S, , 64) +VECTOR_CMP_EQ_GT_GE(ge, 4, I, X, S, , 128) +VECTOR_CMP_EQ_GT_GE(ge, 2, L, X, D, , 128) +VECTOR_CMP_EQ_GT_GE(ge, 2, F, D, S, f, 64) +VECTOR_CMP_EQ_GT_GE(ge, 4, F, X, S, f, 128) +VECTOR_CMP_EQ_GT_GE(ge, 2, D, X, D, f, 128) +dnl +define(`VECTOR_CMP_NE', ` +instruct vcmne$1$2`'(vec$3 dst, vec$3 src1, vec$3 src2, immI cond) +%{ + predicate(n->as_Vector()->length() == $1 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::ne && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($2)); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "$5cmeq $dst, $src1, $src2\n\t# vector cmp ($1$2)" + "not $dst, $dst\t" %} + ins_cost(INSN_COST); + ins_encode %{ + __ $5cmeq(as_FloatRegister($dst$$reg), __ T$1$4, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + __ notr(as_FloatRegister($dst$$reg), __ T$6, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 $4 $5 $6 +VECTOR_CMP_NE(8, B, D, B, , 8B) +VECTOR_CMP_NE(16,B, X, B, , 16B) +VECTOR_CMP_NE(4, S, D, H, , 8B) +VECTOR_CMP_NE(8, S, X, H, , 16B) +VECTOR_CMP_NE(2, I, D, S, , 8B) +VECTOR_CMP_NE(4, I, X, S, , 16B) +VECTOR_CMP_NE(2, L, X, D, , 16B) +VECTOR_CMP_NE(2, F, D, S, f, 8B) +VECTOR_CMP_NE(4, F, X, S, f, 16B) +VECTOR_CMP_NE(2, D, X, D, f, 16B) +dnl +define(`VECTOR_CMP_LT_LE', ` +instruct vcm$1$2$3`'(vec$4 dst, vec$4 src1, vec$4 src2, immI cond) +%{ + predicate(n->as_Vector()->length() == $2 && + n->as_VectorMaskCmp()->get_predicate() == BoolTest::$1 && + n->in(1)->in(1)->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($3)); + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "$6cm$7 $dst, $src2, $src1\t# vector cmp ($2$3)" %} + ins_cost(INSN_COST); + ins_encode %{ + __ $6cm$7(as_FloatRegister($dst$$reg), __ T$2$5, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vdop$8); +%}')dnl +dnl $1 $2 $3 $4 $5 $6 $7 $8 +VECTOR_CMP_LT_LE(lt, 8, B, D, B, , gt, 64) +VECTOR_CMP_LT_LE(lt, 16,B, X, B, , gt, 128) +VECTOR_CMP_LT_LE(lt, 4, S, D, H, , gt, 64) +VECTOR_CMP_LT_LE(lt, 8, S, X, H, , gt, 128) +VECTOR_CMP_LT_LE(lt, 2, I, D, S, , gt, 64) +VECTOR_CMP_LT_LE(lt, 4, I, X, S, , gt, 128) +VECTOR_CMP_LT_LE(lt, 2, L, X, D, , gt, 128) +VECTOR_CMP_LT_LE(lt, 2, F, D, S, f, gt, 64) +VECTOR_CMP_LT_LE(lt, 4, F, X, S, f, gt, 128) +VECTOR_CMP_LT_LE(lt, 2, D, X, D, f, gt, 128) +VECTOR_CMP_LT_LE(le, 8, B, D, B, , ge, 64) +VECTOR_CMP_LT_LE(le, 16,B, X, B, , ge, 128) +VECTOR_CMP_LT_LE(le, 4, S, D, H, , ge, 64) +VECTOR_CMP_LT_LE(le, 8, S, X, H, , ge, 128) +VECTOR_CMP_LT_LE(le, 2, I, D, S, , ge, 64) +VECTOR_CMP_LT_LE(le, 4, I, X, S, , ge, 128) +VECTOR_CMP_LT_LE(le, 2, L, X, D, , ge, 128) +VECTOR_CMP_LT_LE(le, 2, F, D, S, f, ge, 64) +VECTOR_CMP_LT_LE(le, 4, F, X, S, f, ge, 128) +VECTOR_CMP_LT_LE(le, 2, D, X, D, f, ge, 128) +dnl + +// ------------------------------ Vector mul ----------------------------------- + +instruct vmul2L(vecX dst, vecX src1, vecX src2, iRegLNoSp tmp1, iRegLNoSp tmp2) +%{ + predicate(n->as_Vector()->length() == 2); + match(Set dst (MulVL src1 src2)); + ins_cost(INSN_COST); + effect(TEMP tmp1, TEMP tmp2); + format %{ "umov $tmp1, $src1, D, 0\n\t" + "umov $tmp2, $src2, D, 0\n\t" + "mul $tmp2, $tmp2, $tmp1\n\t" + "mov $dst, T2D, 0, $tmp2\t# insert into vector(2L)\n\t" + "umov $tmp1, $src1, D, 1\n\t" + "umov $tmp2, $src2, D, 1\n\t" + "mul $tmp2, $tmp2, $tmp1\n\t" + "mov $dst, T2D, 1, $tmp2\t# insert into vector(2L)\n\t" + %} + ins_encode %{ + __ umov($tmp1$$Register, as_FloatRegister($src1$$reg), __ D, 0); + __ umov($tmp2$$Register, as_FloatRegister($src2$$reg), __ D, 0); + __ mul(as_Register($tmp2$$reg), as_Register($tmp2$$reg), as_Register($tmp1$$reg)); + __ mov(as_FloatRegister($dst$$reg), __ T2D, 0, $tmp2$$Register); + __ umov($tmp1$$Register, as_FloatRegister($src1$$reg), __ D, 1); + __ umov($tmp2$$Register, as_FloatRegister($src2$$reg), __ D, 1); + __ mul(as_Register($tmp2$$reg), as_Register($tmp2$$reg), as_Register($tmp1$$reg)); + __ mov(as_FloatRegister($dst$$reg), __ T2D, 1, $tmp2$$Register); + %} + ins_pipe(pipe_slow); +%} + +// --------------------------------- Vector not -------------------------------- +dnl +define(`MATCH_RULE', `ifelse($1, I, +`match(Set dst (XorV src (ReplicateB m1))); + match(Set dst (XorV src (ReplicateS m1))); + match(Set dst (XorV src (ReplicateI m1)));', +`match(Set dst (XorV src (ReplicateL m1)));')')dnl +dnl +define(`VECTOR_NOT', ` +instruct vnot$1$2`'(vec$3 dst, vec$3 src, imm$2_M1 m1) +%{ + predicate(n->as_Vector()->length_in_bytes() == $4); + MATCH_RULE($2) + ins_cost(INSN_COST); + format %{ "not $dst, $src\t# vector ($5)" %} + ins_encode %{ + __ notr(as_FloatRegister($dst$$reg), __ T$5, + as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); +%}')dnl +dnl $1 $2 $3 $4 $5 +VECTOR_NOT(2, I, D, 8, 8B) +VECTOR_NOT(4, I, X, 16, 16B) +VECTOR_NOT(2, L, X, 16, 16B) +undefine(MATCH_RULE) +dnl +// ------------------------------ Vector max/min ------------------------------- +dnl +define(`PREDICATE', `ifelse($1, 8B, +`predicate((n->as_Vector()->length() == 4 || n->as_Vector()->length() == 8) && + n->bottom_type()->is_vect()->element_basic_type() == T_BYTE);', +`predicate(n->as_Vector()->length() == $2 && n->bottom_type()->is_vect()->element_basic_type() == T_$3);')')dnl +dnl +define(`VECTOR_MAX_MIN_INT', ` +instruct v$1$2$3`'(vec$4 dst, vec$4 src1, vec$4 src2) +%{ + PREDICATE(`$2$3', $2, TYPE2DATATYPE($3)) + match(Set dst ($5V src1 src2)); + ins_cost(INSN_COST); + format %{ "$1v $dst, $src1, $src2\t# vector ($2$3)" %} + ins_encode %{ + __ $1v(as_FloatRegister($dst$$reg), __ T$2`'iTYPE2SIMD($3), + as_FloatRegister($src1$$reg), + as_FloatRegister($src2$$reg)); + %} + ins_pipe(vdop$6); +%}')dnl +dnl $1 $2 $3 $4 $5 $6 +VECTOR_MAX_MIN_INT(max, 8, B, D, Max, 64) +VECTOR_MAX_MIN_INT(max, 16, B, X, Max, 128) +VECTOR_MAX_MIN_INT(max, 4, S, D, Max, 64) +VECTOR_MAX_MIN_INT(max, 8, S, X, Max, 128) +VECTOR_MAX_MIN_INT(max, 2, I, D, Max, 64) +VECTOR_MAX_MIN_INT(max, 4, I, X, Max, 128) +VECTOR_MAX_MIN_INT(min, 8, B, D, Min, 64) +VECTOR_MAX_MIN_INT(min, 16, B, X, Min, 128) +VECTOR_MAX_MIN_INT(min, 4, S, D, Min, 64) +VECTOR_MAX_MIN_INT(min, 8, S, X, Min, 128) +VECTOR_MAX_MIN_INT(min, 2, I, D, Min, 64) +VECTOR_MAX_MIN_INT(min, 4, I, X, Min, 128) +undefine(PREDICATE) +dnl +define(`VECTOR_MAX_MIN_LONG', ` +instruct v$1`'2L`'(vecX dst, vecX src1, vecX src2) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst ($2V src1 src2)); + ins_cost(INSN_COST); + effect(TEMP dst); + format %{ "cmgt $dst, $src1, $src2\t# vector (2L)\n\t" + "bsl $dst, $$3, $$4\t# vector (16B)" %} + ins_encode %{ + __ cmgt(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + __ bsl(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($$3$$reg), as_FloatRegister($$4$$reg)); + %} + ins_pipe(vdop128); +%}')dnl +dnl $1 $2 $3 $4 +VECTOR_MAX_MIN_LONG(max, Max, src1, src2) +VECTOR_MAX_MIN_LONG(min, Min, src2, src1) +dnl + +// --------------------------------- blend (bsl) ---------------------------- +dnl +define(`VECTOR_BSL', ` +instruct vbsl$1B`'(vec$2 dst, vec$2 src1, vec$2 src2) +%{ + predicate(n->as_Vector()->length_in_bytes() == $1); + match(Set dst (VectorBlend (Binary src1 src2) dst)); + ins_cost(INSN_COST); + format %{ "bsl $dst, $src2, $src1\t# vector ($1B)" %} + ins_encode %{ + __ bsl(as_FloatRegister($dst$$reg), __ T$1B, + as_FloatRegister($src2$$reg), as_FloatRegister($src1$$reg)); + %} + ins_pipe(vlogical$3); +%}')dnl +dnl $1 $2 $3 +VECTOR_BSL(8, D, 64) +VECTOR_BSL(16, X, 128) +dnl + +// --------------------------------- Load/store Mask ---------------------------- +dnl +define(`PREDICATE', `ifelse($1, load, +`predicate(n->as_Vector()->length() == $2 && n->bottom_type()->is_vect()->element_basic_type() == T_BYTE);', +`predicate(n->as_Vector()->length() == $2);')')dnl +dnl +define(`VECTOR_LOAD_STORE_MASK_B', ` +instruct $1mask$2B`'(vec$3 dst, vec$3 src $5 $6) +%{ + PREDICATE($1, $2) + match(Set dst (Vector$4Mask src $6)); + ins_cost(INSN_COST); + format %{ "negr $dst, $src\t# $1 mask ($2B to $2B)" %} + ins_encode %{ + __ negr(as_FloatRegister($dst$$reg), __ T$2B, as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); +%}')dnl +dnl $1 $2 $3 $4 $5 $6 +VECTOR_LOAD_STORE_MASK_B(load, 8, D, Load) +VECTOR_LOAD_STORE_MASK_B(load, 16, X, Load) +VECTOR_LOAD_STORE_MASK_B(store, 8, D, Store, `, immI_1', size) +VECTOR_LOAD_STORE_MASK_B(store, 16, X, Store, `, immI_1', size) +undefine(PREDICATE)dnl +dnl +define(`PREDICATE', `ifelse($1, load, +`predicate(n->as_Vector()->length() == $2 && n->bottom_type()->is_vect()->element_basic_type() == T_SHORT);', +`predicate(n->as_Vector()->length() == $2);')')dnl +dnl +define(`VECTOR_LOAD_STORE_MASK_S', ` +instruct $1mask$2S`'(vec$3 dst, vec$4 src $9 $10) +%{ + PREDICATE($1, $2) + match(Set dst (Vector$5Mask src $10)); + ins_cost(INSN_COST); + format %{ "$6 $dst, $src\n\t" + "negr $dst, $dst\t# $1 mask ($2$7 to $2$8)" %} + ins_encode %{ + __ $6(as_FloatRegister($dst$$reg), __ T8$8, as_FloatRegister($src$$reg), __ T8$7); + __ negr(as_FloatRegister($dst$$reg), __ T8$8, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 +VECTOR_LOAD_STORE_MASK_S(load, 4, D, D, Load, uxtl, B, H) +VECTOR_LOAD_STORE_MASK_S(load, 8, X, D, Load, uxtl, B, H) +VECTOR_LOAD_STORE_MASK_S(store, 4, D, D, Store, xtn, H, B, `, immI_2', size) +VECTOR_LOAD_STORE_MASK_S(store, 8, D, X, Store, xtn, H, B, `, immI_2', size) +undefine(PREDICATE)dnl +dnl +define(`PREDICATE', `ifelse($1, load, +`predicate(n->as_Vector()->length() == $2 && + (n->bottom_type()->is_vect()->element_basic_type() == T_INT || + n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT));', +`predicate(n->as_Vector()->length() == $2);')')dnl +dnl +define(`VECTOR_LOAD_STORE_MASK_I', ` +instruct $1mask$2I`'(vec$3 dst, vec$4 src $12 $13) +%{ + PREDICATE($1, $2) + match(Set dst (Vector$5Mask src $13)); + ins_cost(INSN_COST); + format %{ "$6 $dst, $src\t# $2$7 to $2$8\n\t" + "$6 $dst, $dst\t# $2$8 to $2$9\n\t" + "negr $dst, $dst\t# $1 mask ($2$7 to $2$9)" %} + ins_encode %{ + __ $6(as_FloatRegister($dst$$reg), __ T$10$8, as_FloatRegister($src$$reg), __ T$10$7); + __ $6(as_FloatRegister($dst$$reg), __ T$11$9, as_FloatRegister($dst$$reg), __ T$11$8); + __ negr(as_FloatRegister($dst$$reg), __ T$11$9, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 $4 $5 $6 $7 $8 $9 $10$11 $12 $13 +VECTOR_LOAD_STORE_MASK_I(load, 2, D, D, Load, uxtl, B, H, S, 8, 4) +VECTOR_LOAD_STORE_MASK_I(load, 4, X, D, Load, uxtl, B, H, S, 8, 4) +VECTOR_LOAD_STORE_MASK_I(store, 2, D, D, Store, xtn, S, H, B, 4, 8, `, immI_4', size) +VECTOR_LOAD_STORE_MASK_I(store, 4, D, X, Store, xtn, S, H, B, 4, 8, `, immI_4', size) +undefine(PREDICATE) +dnl +instruct loadmask2L(vecX dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 2 && + (n->bottom_type()->is_vect()->element_basic_type() == T_LONG || + n->bottom_type()->is_vect()->element_basic_type() == T_DOUBLE)); + match(Set dst (VectorLoadMask src)); + ins_cost(INSN_COST); + format %{ "uxtl $dst, $src\t# 2B to 2S\n\t" + "uxtl $dst, $dst\t# 2S to 2I\n\t" + "uxtl $dst, $dst\t# 2I to 2L\n\t" + "neg $dst, $dst\t# load mask (2B to 2L)" %} + ins_encode %{ + __ uxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + __ uxtl(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($dst$$reg), __ T4H); + __ uxtl(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($dst$$reg), __ T2S); + __ negr(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct storemask2L(vecD dst, vecX src, immI_8 size) +%{ + predicate(n->as_Vector()->length() == 2); + match(Set dst (VectorStoreMask src size)); + ins_cost(INSN_COST); + format %{ "xtn $dst, $src\t# 2L to 2I\n\t" + "xtn $dst, $dst\t# 2I to 2S\n\t" + "xtn $dst, $dst\t# 2S to 2B\n\t" + "neg $dst, $dst\t# store mask (2L to 2B)" %} + ins_encode %{ + __ xtn(as_FloatRegister($dst$$reg), __ T2S, as_FloatRegister($src$$reg), __ T2D); + __ xtn(as_FloatRegister($dst$$reg), __ T4H, as_FloatRegister($dst$$reg), __ T4S); + __ xtn(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg), __ T8H); + __ negr(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +//-------------------------------- LOAD_IOTA_INDICES---------------------------------- +dnl +define(`PREDICATE', `ifelse($1, 8, +`predicate((n->as_Vector()->length() == 2 || n->as_Vector()->length() == 4 || + n->as_Vector()->length() == 8) && + n->bottom_type()->is_vect()->element_basic_type() == T_BYTE);', +`predicate(n->as_Vector()->length() == 16 && n->bottom_type()->is_vect()->element_basic_type() == T_BYTE);')')dnl +dnl +define(`VECTOR_LOAD_CON', ` +instruct loadcon$1B`'(vec$2 dst, immI0 src) +%{ + PREDICATE($1) + match(Set dst (VectorLoadConst src)); + ins_cost(INSN_COST); + format %{ "ldr $dst, CONSTANT_MEMORY\t# load iota indices" %} + ins_encode %{ + __ lea(rscratch1, ExternalAddress(StubRoutines::aarch64::vector_iota_indices())); + __ ldr$3(as_FloatRegister($dst$$reg), rscratch1); + %} + ins_pipe(pipe_class_memory); +%}')dnl +dnl $1 $2 $3 +VECTOR_LOAD_CON(8, D, d) +VECTOR_LOAD_CON(16, X, q) +undefine(PREDICATE) +dnl +//-------------------------------- LOAD_SHUFFLE ---------------------------------- +dnl +define(`VECTOR_LOAD_SHUFFLE_B', ` +instruct loadshuffle$1B`'(vec$2 dst, vec$2 src) +%{ + predicate(n->as_Vector()->length() == $1 && + n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorLoadShuffle src)); + ins_cost(INSN_COST); + format %{ "mov $dst, $src\t# get $1B shuffle" %} + ins_encode %{ + __ orr(as_FloatRegister($dst$$reg), __ T$1B, + as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); +%}')dnl +dnl $1 $2 +VECTOR_LOAD_SHUFFLE_B(8, D) +VECTOR_LOAD_SHUFFLE_B(16, X) +dnl +define(`VECTOR_LOAD_SHUFFLE_S', ` +instruct loadshuffle$1S`'(vec$2 dst, vec$3 src) +%{ + predicate(n->as_Vector()->length() == $1 && + n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorLoadShuffle src)); + ins_cost(INSN_COST); + format %{ "uxtl $dst, $src\t# $1B to $1H" %} + ins_encode %{ + __ uxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + %} + ins_pipe(pipe_class_default); +%}')dnl +dnl $1 $2 $3 +VECTOR_LOAD_SHUFFLE_S(4, D, D) +VECTOR_LOAD_SHUFFLE_S(8, X, D) +dnl + +instruct loadshuffle4I(vecX dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 4 && + (n->bottom_type()->is_vect()->element_basic_type() == T_INT || + n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT)); + match(Set dst (VectorLoadShuffle src)); + ins_cost(INSN_COST); + format %{ "uxtl $dst, $src\t# 4B to 4H \n\t" + "uxtl $dst, $dst\t# 4H to 4S" %} + ins_encode %{ + __ uxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + __ uxtl(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($dst$$reg), __ T4H); + %} + ins_pipe(pipe_slow); +%} + +//-------------------------------- Rearrange ------------------------------------- +// Here is an example that rearranges a NEON vector with 4 ints: +// Rearrange V1 int[a0, a1, a2, a3] to V2 int[a2, a3, a0, a1] +// 1. Get the indices of V1 and store them as Vi byte[0, 1, 2, 3]. +// 2. Convert Vi byte[0, 1, 2, 3] to the indices of V2 and also store them as Vi byte[2, 3, 0, 1]. +// 3. Unsigned extend Long Vi from byte[2, 3, 0, 1] to int[2, 3, 0, 1]. +// 4. Multiply Vi int[2, 3, 0, 1] with constant int[0x04040404, 0x04040404, 0x04040404, 0x04040404] +// and get tbl base Vm int[0x08080808, 0x0c0c0c0c, 0x00000000, 0x04040404]. +// 5. Add Vm with constant int[0x03020100, 0x03020100, 0x03020100, 0x03020100] +// and get tbl index Vm int[0x0b0a0908, 0x0f0e0d0c, 0x03020100, 0x07060504] +// 6. Use Vm as index register, and use V1 as table register. +// Then get V2 as the result by tbl NEON instructions. +// Notes: +// Step 1 matches VectorLoadConst. +// Step 3 matches VectorLoadShuffle. +// Step 4, 5, 6 match VectorRearrange. +// For VectorRearrange short/int, the reason why such complex calculation is +// required is because NEON tbl supports bytes table only, so for short/int, we +// need to lookup 2/4 bytes as a group. For VectorRearrange long, we use bsl +// to implement rearrange. +define(`VECTOR_REARRANGE_B', ` +instruct rearrange$1B`'(vec$2 dst, vec$2 src, vec$2 shuffle) +%{ + predicate(n->as_Vector()->length() == $1 && + n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorRearrange src shuffle)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst); + format %{ "tbl $dst, {$dst}, $shuffle\t# rearrange $1B" %} + ins_encode %{ + __ tbl(as_FloatRegister($dst$$reg), __ T$1B, + as_FloatRegister($src$$reg), 1, as_FloatRegister($shuffle$$reg)); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 +VECTOR_REARRANGE_B(8, D) +VECTOR_REARRANGE_B(16, X) +dnl +define(`VECTOR_REARRANGE_S', ` +instruct rearrange$1S`'(vec$2 dst, vec$2 src, vec$2 shuffle, vec$2 tmp0, vec$2 tmp1) +%{ + predicate(n->as_Vector()->length() == $1 && + n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorRearrange src shuffle)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp0, TEMP tmp1); + format %{ "mov $tmp0, CONSTANT\t# constant 0x0202020202020202\n\t" + "mov $tmp1, CONSTANT\t# constant 0x0100010001000100\n\t" + "mulv $dst, T$1H, $shuffle, $tmp0\n\t" + "addv $dst, T$3B, $dst, $tmp1\n\t" + "tbl $dst, {$src}, $dst\t# rearrange $1S" %} + ins_encode %{ + __ mov(as_FloatRegister($tmp0$$reg), __ T$3B, 0x02); + __ mov(as_FloatRegister($tmp1$$reg), __ T$1H, 0x0100); + __ mulv(as_FloatRegister($dst$$reg), __ T$1H, + as_FloatRegister($shuffle$$reg), as_FloatRegister($tmp0$$reg)); + __ addv(as_FloatRegister($dst$$reg), __ T$3B, + as_FloatRegister($dst$$reg), as_FloatRegister($tmp1$$reg)); + __ tbl(as_FloatRegister($dst$$reg), __ T$3B, + as_FloatRegister($src$$reg), 1, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 +VECTOR_REARRANGE_S(4, D, 8) +VECTOR_REARRANGE_S(8, X, 16) + +instruct rearrange4I(vecX dst, vecX src, vecX shuffle, vecX tmp0, vecX tmp1) +%{ + predicate(n->as_Vector()->length() == 4 && + (n->bottom_type()->is_vect()->element_basic_type() == T_INT || + n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT)); + match(Set dst (VectorRearrange src shuffle)); + ins_cost(INSN_COST); + effect(TEMP_DEF dst, TEMP tmp0, TEMP tmp1); + format %{ "mov $tmp0, CONSTANT\t# constant 0x0404040404040404\n\t" + "mov $tmp1, CONSTANT\t# constant 0x0302010003020100\n\t" + "mulv $dst, T8H, $shuffle, $tmp0\n\t" + "addv $dst, T16B, $dst, $tmp1\n\t" + "tbl $dst, {$src}, $dst\t# rearrange 4I" %} + ins_encode %{ + __ mov(as_FloatRegister($tmp0$$reg), __ T16B, 0x04); + __ mov(as_FloatRegister($tmp1$$reg), __ T4S, 0x03020100); + __ mulv(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($shuffle$$reg), as_FloatRegister($tmp0$$reg)); + __ addv(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($dst$$reg), as_FloatRegister($tmp1$$reg)); + __ tbl(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg), 1, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +//-------------------------------- Anytrue/alltrue ----------------------------- +dnl +define(`ANYTRUE_IN_MASK', ` +instruct anytrue_in_mask$1B`'(iRegINoSp dst, vec$2 src1, vec$2 src2, vec$2 tmp, rFlagsReg cr) +%{ + predicate(static_cast(n)->get_predicate() == BoolTest::ne); + match(Set dst (VectorTest src1 src2 )); + ins_cost(INSN_COST); + effect(TEMP tmp, KILL cr); + format %{ "addv $tmp, T$1B, $src1\t# src1 and src2 are the same\n\t" + "umov $dst, $tmp, B, 0\n\t" + "cmp $dst, 0\n\t" + "cset $dst" %} + ins_encode %{ + __ addv(as_FloatRegister($tmp$$reg), __ T$1B, as_FloatRegister($src1$$reg)); + __ umov($dst$$Register, as_FloatRegister($tmp$$reg), __ B, 0); + __ cmpw($dst$$Register, zr); + __ csetw($dst$$Register, Assembler::NE); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 +ANYTRUE_IN_MASK(8, D) +ANYTRUE_IN_MASK(16, X) +dnl +define(`ALLTRUE_IN_MASK', ` +instruct alltrue_in_mask$1B`'(iRegINoSp dst, vec$2 src1, vec$2 src2, vec$2 tmp, rFlagsReg cr) +%{ + predicate(static_cast(n)->get_predicate() == BoolTest::overflow); + match(Set dst (VectorTest src1 src2 )); + ins_cost(INSN_COST); + effect(TEMP tmp, KILL cr); + format %{ "andr $tmp, T$1B, $src1, $src2\t# src2 is maskAllTrue\n\t" + "notr $tmp, T$1B, $tmp\n\t" + "addv $tmp, T$1B, $tmp\n\t" + "umov $dst, $tmp, B, 0\n\t" + "cmp $dst, 0\n\t" + "cset $dst" %} + ins_encode %{ + __ andr(as_FloatRegister($tmp$$reg), __ T$1B, + as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); + __ notr(as_FloatRegister($tmp$$reg), __ T$1B, as_FloatRegister($tmp$$reg)); + __ addv(as_FloatRegister($tmp$$reg), __ T$1B, as_FloatRegister($tmp$$reg)); + __ umov($dst$$Register, as_FloatRegister($tmp$$reg), __ B, 0); + __ cmpw($dst$$Register, zr); + __ csetw($dst$$Register, Assembler::EQ); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 +ALLTRUE_IN_MASK(8, D) +ALLTRUE_IN_MASK(16, X) +dnl diff --git a/src/hotspot/cpu/aarch64/aarch64_sve.ad b/src/hotspot/cpu/aarch64/aarch64_sve.ad index 90442c7b8b6..f34d4890c70 100644 --- a/src/hotspot/cpu/aarch64/aarch64_sve.ad +++ b/src/hotspot/cpu/aarch64/aarch64_sve.ad @@ -159,6 +159,31 @@ source %{ case Op_ExtractL: case Op_ExtractS: case Op_ExtractUB: + // Vector API specific + case Op_AndReductionV: + case Op_OrReductionV: + case Op_XorReductionV: + case Op_MaxReductionV: + case Op_MinReductionV: + case Op_LoadVectorGather: + case Op_StoreVectorScatter: + case Op_VectorBlend: + case Op_VectorCast: + case Op_VectorCastB2X: + case Op_VectorCastD2X: + case Op_VectorCastF2X: + case Op_VectorCastI2X: + case Op_VectorCastL2X: + case Op_VectorCastS2X: + case Op_VectorInsert: + case Op_VectorLoadConst: + case Op_VectorLoadMask: + case Op_VectorLoadShuffle: + case Op_VectorMaskCmp: + case Op_VectorRearrange: + case Op_VectorReinterpret: + case Op_VectorStoreMask: + case Op_VectorTest: return false; default: return true; @@ -846,9 +871,49 @@ instruct vpopcountI(vReg dst, vReg src) %{ // vector add reduction +instruct reduce_addB(iRegINoSp dst, iRegIorL2I src1, vReg src2, vRegD tmp) %{ + predicate(UseSVE > 0 && n->in(2)->bottom_type()->is_vect()->length_in_bytes() >= 16 && + n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (AddReductionVI src1 src2)); + effect(TEMP_DEF dst, TEMP tmp); + ins_cost(SVE_COST); + format %{ "sve_uaddv $tmp, $src2\t# vector (sve) (B)\n\t" + "smov $dst, $tmp, B, 0\n\t" + "addw $dst, $dst, $src1\n\t" + "sxtb $dst, $dst\t # add reduction B" %} + ins_encode %{ + __ sve_uaddv(as_FloatRegister($tmp$$reg), __ B, + ptrue, as_FloatRegister($src2$$reg)); + __ smov($dst$$Register, as_FloatRegister($tmp$$reg), __ B, 0); + __ addw($dst$$Register, $dst$$Register, $src1$$Register); + __ sxtb($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_addS(iRegINoSp dst, iRegIorL2I src1, vReg src2, vRegD tmp) %{ + predicate(UseSVE > 0 && n->in(2)->bottom_type()->is_vect()->length_in_bytes() >= 16 && + n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (AddReductionVI src1 src2)); + effect(TEMP_DEF dst, TEMP tmp); + ins_cost(SVE_COST); + format %{ "sve_uaddv $tmp, $src2\t# vector (sve) (H)\n\t" + "smov $dst, $tmp, H, 0\n\t" + "addw $dst, $dst, $src1\n\t" + "sxth $dst, $dst\t # add reduction H" %} + ins_encode %{ + __ sve_uaddv(as_FloatRegister($tmp$$reg), __ H, + ptrue, as_FloatRegister($src2$$reg)); + __ smov($dst$$Register, as_FloatRegister($tmp$$reg), __ H, 0); + __ addw($dst$$Register, $dst$$Register, $src1$$Register); + __ sxth($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + instruct reduce_addI(iRegINoSp dst, iRegIorL2I src1, vReg src2, vRegD tmp) %{ predicate(UseSVE > 0 && n->in(2)->bottom_type()->is_vect()->length_in_bytes() >= 16 && - (n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT)); + n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT); match(Set dst (AddReductionVI src1 src2)); effect(TEMP_DEF dst, TEMP tmp); ins_cost(SVE_COST); @@ -866,7 +931,7 @@ instruct reduce_addI(iRegINoSp dst, iRegIorL2I src1, vReg src2, vRegD tmp) %{ instruct reduce_addL(iRegLNoSp dst, iRegL src1, vReg src2, vRegD tmp) %{ predicate(UseSVE > 0 && n->in(2)->bottom_type()->is_vect()->length_in_bytes() >= 16 && - (n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_LONG)); + n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_LONG); match(Set dst (AddReductionVL src1 src2)); effect(TEMP_DEF dst, TEMP tmp); ins_cost(SVE_COST); @@ -1264,7 +1329,7 @@ instruct vlsrL(vReg dst, vReg shift) %{ instruct vasrB_imm(vReg dst, vReg src, immI shift) %{ predicate(UseSVE > 0 && n->as_Vector()->length() >= 16); - match(Set dst (RShiftVB src shift)); + match(Set dst (RShiftVB src (RShiftCntV shift))); ins_cost(SVE_COST); format %{ "sve_asr $dst, $src, $shift\t# vector (sve) (B)" %} ins_encode %{ @@ -1283,7 +1348,7 @@ instruct vasrB_imm(vReg dst, vReg src, immI shift) %{ instruct vasrS_imm(vReg dst, vReg src, immI shift) %{ predicate(UseSVE > 0 && n->as_Vector()->length() >= 8); - match(Set dst (RShiftVS src shift)); + match(Set dst (RShiftVS src (RShiftCntV shift))); ins_cost(SVE_COST); format %{ "sve_asr $dst, $src, $shift\t# vector (sve) (H)" %} ins_encode %{ @@ -1302,7 +1367,7 @@ instruct vasrS_imm(vReg dst, vReg src, immI shift) %{ instruct vasrI_imm(vReg dst, vReg src, immI shift) %{ predicate(UseSVE > 0 && n->as_Vector()->length() >= 4); - match(Set dst (RShiftVI src shift)); + match(Set dst (RShiftVI src (RShiftCntV shift))); ins_cost(SVE_COST); format %{ "sve_asr $dst, $src, $shift\t# vector (sve) (S)" %} ins_encode %{ @@ -1320,7 +1385,7 @@ instruct vasrI_imm(vReg dst, vReg src, immI shift) %{ instruct vasrL_imm(vReg dst, vReg src, immI shift) %{ predicate(UseSVE > 0 && n->as_Vector()->length() >= 2); - match(Set dst (RShiftVL src shift)); + match(Set dst (RShiftVL src (RShiftCntV shift))); ins_cost(SVE_COST); format %{ "sve_asr $dst, $src, $shift\t# vector (sve) (D)" %} ins_encode %{ @@ -1338,7 +1403,7 @@ instruct vasrL_imm(vReg dst, vReg src, immI shift) %{ instruct vlsrB_imm(vReg dst, vReg src, immI shift) %{ predicate(UseSVE > 0 && n->as_Vector()->length() >= 16); - match(Set dst (URShiftVB src shift)); + match(Set dst (URShiftVB src (RShiftCntV shift))); ins_cost(SVE_COST); format %{ "sve_lsr $dst, $src, $shift\t# vector (sve) (B)" %} ins_encode %{ @@ -1361,7 +1426,7 @@ instruct vlsrB_imm(vReg dst, vReg src, immI shift) %{ instruct vlsrS_imm(vReg dst, vReg src, immI shift) %{ predicate(UseSVE > 0 && n->as_Vector()->length() >= 8); - match(Set dst (URShiftVS src shift)); + match(Set dst (URShiftVS src (RShiftCntV shift))); ins_cost(SVE_COST); format %{ "sve_lsr $dst, $src, $shift\t# vector (sve) (H)" %} ins_encode %{ @@ -1371,7 +1436,7 @@ instruct vlsrS_imm(vReg dst, vReg src, immI shift) %{ as_FloatRegister($src$$reg)); return; } - if (con >= 8) { + if (con >= 16) { __ sve_eor(as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); return; @@ -1384,7 +1449,7 @@ instruct vlsrS_imm(vReg dst, vReg src, immI shift) %{ instruct vlsrI_imm(vReg dst, vReg src, immI shift) %{ predicate(UseSVE > 0 && n->as_Vector()->length() >= 4); - match(Set dst (URShiftVI src shift)); + match(Set dst (URShiftVI src (RShiftCntV shift))); ins_cost(SVE_COST); format %{ "sve_lsr $dst, $src, $shift\t# vector (sve) (S)" %} ins_encode %{ @@ -1402,7 +1467,7 @@ instruct vlsrI_imm(vReg dst, vReg src, immI shift) %{ instruct vlsrL_imm(vReg dst, vReg src, immI shift) %{ predicate(UseSVE > 0 && n->as_Vector()->length() >= 2); - match(Set dst (URShiftVL src shift)); + match(Set dst (URShiftVL src (RShiftCntV shift))); ins_cost(SVE_COST); format %{ "sve_lsr $dst, $src, $shift\t# vector (sve) (D)" %} ins_encode %{ @@ -1420,7 +1485,7 @@ instruct vlsrL_imm(vReg dst, vReg src, immI shift) %{ instruct vlslB_imm(vReg dst, vReg src, immI shift) %{ predicate(UseSVE > 0 && n->as_Vector()->length() >= 16); - match(Set dst (LShiftVB src shift)); + match(Set dst (LShiftVB src (LShiftCntV shift))); ins_cost(SVE_COST); format %{ "sve_lsl $dst, $src, $shift\t# vector (sve) (B)" %} ins_encode %{ @@ -1438,12 +1503,12 @@ instruct vlslB_imm(vReg dst, vReg src, immI shift) %{ instruct vlslS_imm(vReg dst, vReg src, immI shift) %{ predicate(UseSVE > 0 && n->as_Vector()->length() >= 8); - match(Set dst (LShiftVS src shift)); + match(Set dst (LShiftVS src (LShiftCntV shift))); ins_cost(SVE_COST); format %{ "sve_lsl $dst, $src, $shift\t# vector (sve) (H)" %} ins_encode %{ int con = (int)$shift$$constant; - if (con >= 8) { + if (con >= 16) { __ sve_eor(as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); return; @@ -1456,7 +1521,7 @@ instruct vlslS_imm(vReg dst, vReg src, immI shift) %{ instruct vlslI_imm(vReg dst, vReg src, immI shift) %{ predicate(UseSVE > 0 && n->as_Vector()->length() >= 4); - match(Set dst (LShiftVI src shift)); + match(Set dst (LShiftVI src (LShiftCntV shift))); ins_cost(SVE_COST); format %{ "sve_lsl $dst, $src, $shift\t# vector (sve) (S)" %} ins_encode %{ @@ -1469,7 +1534,7 @@ instruct vlslI_imm(vReg dst, vReg src, immI shift) %{ instruct vlslL_imm(vReg dst, vReg src, immI shift) %{ predicate(UseSVE > 0 && n->as_Vector()->length() >= 2); - match(Set dst (LShiftVL src shift)); + match(Set dst (LShiftVL src (LShiftCntV shift))); ins_cost(SVE_COST); format %{ "sve_lsl $dst, $src, $shift\t# vector (sve) (D)" %} ins_encode %{ diff --git a/src/hotspot/cpu/aarch64/aarch64_sve_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_sve_ad.m4 index 7bb76cc5941..7fe0861a717 100644 --- a/src/hotspot/cpu/aarch64/aarch64_sve_ad.m4 +++ b/src/hotspot/cpu/aarch64/aarch64_sve_ad.m4 @@ -146,6 +146,31 @@ source %{ case Op_ExtractL: case Op_ExtractS: case Op_ExtractUB: + // Vector API specific + case Op_AndReductionV: + case Op_OrReductionV: + case Op_XorReductionV: + case Op_MaxReductionV: + case Op_MinReductionV: + case Op_LoadVectorGather: + case Op_StoreVectorScatter: + case Op_VectorBlend: + case Op_VectorCast: + case Op_VectorCastB2X: + case Op_VectorCastD2X: + case Op_VectorCastF2X: + case Op_VectorCastI2X: + case Op_VectorCastL2X: + case Op_VectorCastS2X: + case Op_VectorInsert: + case Op_VectorLoadConst: + case Op_VectorLoadMask: + case Op_VectorLoadShuffle: + case Op_VectorMaskCmp: + case Op_VectorRearrange: + case Op_VectorReinterpret: + case Op_VectorStoreMask: + case Op_VectorTest: return false; default: return true; @@ -507,15 +532,38 @@ instruct vpopcountI(vReg dst, vReg src) %{ __ sve_cnt(as_FloatRegister($dst$$reg), __ S, ptrue, as_FloatRegister($src$$reg)); %} ins_pipe(pipe_slow); -%} +%}dnl +dnl +dnl REDUCE_ADD_EXT($1, $2, $3, $4, $5, $6, $7 ) +dnl REDUCE_ADD_EXT(insn_name, op_name, reg_dst, reg_src, size, elem_type, insn1) +define(`REDUCE_ADD_EXT', ` +instruct $1($3 dst, $4 src1, vReg src2, vRegD tmp) %{ + predicate(UseSVE > 0 && n->in(2)->bottom_type()->is_vect()->length_in_bytes() >= 16 && + n->in(2)->bottom_type()->is_vect()->element_basic_type() == $6); + match(Set dst ($2 src1 src2)); + effect(TEMP_DEF dst, TEMP tmp); + ins_cost(SVE_COST); + format %{ "sve_uaddv $tmp, $src2\t# vector (sve) ($5)\n\t" + "smov $dst, $tmp, $5, 0\n\t" + "addw $dst, $dst, $src1\n\t" + "$7 $dst, $dst\t # add reduction $5" %} + ins_encode %{ + __ sve_uaddv(as_FloatRegister($tmp$$reg), __ $5, + ptrue, as_FloatRegister($src2$$reg)); + __ smov($dst$$Register, as_FloatRegister($tmp$$reg), __ $5, 0); + __ addw($dst$$Register, $dst$$Register, $src1$$Register); + __ $7($dst$$Register, $dst$$Register); + %} + ins_pipe(pipe_slow); +%}')dnl dnl dnl REDUCE_ADD($1, $2, $3, $4, $5, $6, $7 ) dnl REDUCE_ADD(insn_name, op_name, reg_dst, reg_src, size, elem_type, insn1) define(`REDUCE_ADD', ` instruct $1($3 dst, $4 src1, vReg src2, vRegD tmp) %{ predicate(UseSVE > 0 && n->in(2)->bottom_type()->is_vect()->length_in_bytes() >= 16 && - ELEMENT_SHORT_CHAR($6, n->in(2))); + n->in(2)->bottom_type()->is_vect()->element_basic_type() == $6); match(Set dst ($2 src1 src2)); effect(TEMP_DEF dst, TEMP tmp); ins_cost(SVE_COST); @@ -545,8 +593,10 @@ instruct $1($3 src1_dst, vReg src2) %{ %} ins_pipe(pipe_slow); %}')dnl -dnl + // vector add reduction +REDUCE_ADD_EXT(reduce_addB, AddReductionVI, iRegINoSp, iRegIorL2I, B, T_BYTE, sxtb) +REDUCE_ADD_EXT(reduce_addS, AddReductionVI, iRegINoSp, iRegIorL2I, H, T_SHORT, sxth) REDUCE_ADD(reduce_addI, AddReductionVI, iRegINoSp, iRegIorL2I, S, T_INT, addw) REDUCE_ADD(reduce_addL, AddReductionVL, iRegLNoSp, iRegL, D, T_LONG, add) REDUCE_ADDF(reduce_addF, AddReductionVF, vRegF, S) @@ -677,14 +727,14 @@ instruct $1(vReg dst, vReg shift) %{ ins_pipe(pipe_slow); %}')dnl dnl -dnl VSHIFT_IMM_UNPREDICATE($1, $2, $3, $4, $5 ) -dnl VSHIFT_IMM_UNPREDICATE(insn_name, op_name, size, min_vec_len, insn) +dnl VSHIFT_IMM_UNPREDICATE($1, $2, $3, $4, $5, $6 ) +dnl VSHIFT_IMM_UNPREDICATE(insn_name, op_name, op_name2, size, min_vec_len, insn) define(`VSHIFT_IMM_UNPREDICATE', ` instruct $1(vReg dst, vReg src, immI shift) %{ - predicate(UseSVE > 0 && n->as_Vector()->length() >= $4); - match(Set dst ($2 src shift)); + predicate(UseSVE > 0 && n->as_Vector()->length() >= $5); + match(Set dst ($2 src ($3 shift))); ins_cost(SVE_COST); - format %{ "$5 $dst, $src, $shift\t# vector (sve) ($3)" %} + format %{ "$6 $dst, $src, $shift\t# vector (sve) ($4)" %} ins_encode %{ int con = (int)$shift$$constant;dnl ifelse(eval(index(`$1', `vasr') == 0 || index(`$1', `vlsr') == 0), 1, ` @@ -693,16 +743,21 @@ ifelse(eval(index(`$1', `vasr') == 0 || index(`$1', `vlsr') == 0), 1, ` as_FloatRegister($src$$reg)); return; }')dnl -ifelse(eval(index(`$1', `vasr') == 0), 1, `ifelse(eval(index(`$3', `B') == 0), 1, ` - if (con >= 8) con = 7;')ifelse(eval(index(`$3', `H') == 0), 1, ` +ifelse(eval(index(`$1', `vasr') == 0), 1, `ifelse(eval(index(`$4', `B') == 0), 1, ` + if (con >= 8) con = 7;')ifelse(eval(index(`$4', `H') == 0), 1, ` if (con >= 16) con = 15;')')dnl -ifelse(eval((index(`$1', `vlsl') == 0 || index(`$1', `vlsr') == 0) && (index(`$3', `B') == 0 || index(`$3', `H') == 0)), 1, ` +ifelse(eval(index(`$1', `vlsl') == 0 || index(`$1', `vlsr') == 0), 1, `ifelse(eval(index(`$4', `B') == 0), 1, ` if (con >= 8) { __ sve_eor(as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); return; - }') - __ $5(as_FloatRegister($dst$$reg), __ $3, + }')ifelse(eval(index(`$4', `H') == 0), 1, ` + if (con >= 16) { + __ sve_eor(as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), + as_FloatRegister($src$$reg)); + return; + }')') + __ $6(as_FloatRegister($dst$$reg), __ $4, as_FloatRegister($src$$reg), con); %} ins_pipe(pipe_slow); @@ -736,18 +791,18 @@ VSHIFT_TRUE_PREDICATE(vlsrB, URShiftVB, B, 16, sve_lsr) VSHIFT_TRUE_PREDICATE(vlsrS, URShiftVS, H, 8, sve_lsr) VSHIFT_TRUE_PREDICATE(vlsrI, URShiftVI, S, 4, sve_lsr) VSHIFT_TRUE_PREDICATE(vlsrL, URShiftVL, D, 2, sve_lsr) -VSHIFT_IMM_UNPREDICATE(vasrB_imm, RShiftVB, B, 16, sve_asr) -VSHIFT_IMM_UNPREDICATE(vasrS_imm, RShiftVS, H, 8, sve_asr) -VSHIFT_IMM_UNPREDICATE(vasrI_imm, RShiftVI, S, 4, sve_asr) -VSHIFT_IMM_UNPREDICATE(vasrL_imm, RShiftVL, D, 2, sve_asr) -VSHIFT_IMM_UNPREDICATE(vlsrB_imm, URShiftVB, B, 16, sve_lsr) -VSHIFT_IMM_UNPREDICATE(vlsrS_imm, URShiftVS, H, 8, sve_lsr) -VSHIFT_IMM_UNPREDICATE(vlsrI_imm, URShiftVI, S, 4, sve_lsr) -VSHIFT_IMM_UNPREDICATE(vlsrL_imm, URShiftVL, D, 2, sve_lsr) -VSHIFT_IMM_UNPREDICATE(vlslB_imm, LShiftVB, B, 16, sve_lsl) -VSHIFT_IMM_UNPREDICATE(vlslS_imm, LShiftVS, H, 8, sve_lsl) -VSHIFT_IMM_UNPREDICATE(vlslI_imm, LShiftVI, S, 4, sve_lsl) -VSHIFT_IMM_UNPREDICATE(vlslL_imm, LShiftVL, D, 2, sve_lsl) +VSHIFT_IMM_UNPREDICATE(vasrB_imm, RShiftVB, RShiftCntV, B, 16, sve_asr) +VSHIFT_IMM_UNPREDICATE(vasrS_imm, RShiftVS, RShiftCntV, H, 8, sve_asr) +VSHIFT_IMM_UNPREDICATE(vasrI_imm, RShiftVI, RShiftCntV, S, 4, sve_asr) +VSHIFT_IMM_UNPREDICATE(vasrL_imm, RShiftVL, RShiftCntV, D, 2, sve_asr) +VSHIFT_IMM_UNPREDICATE(vlsrB_imm, URShiftVB, RShiftCntV, B, 16, sve_lsr) +VSHIFT_IMM_UNPREDICATE(vlsrS_imm, URShiftVS, RShiftCntV, H, 8, sve_lsr) +VSHIFT_IMM_UNPREDICATE(vlsrI_imm, URShiftVI, RShiftCntV, S, 4, sve_lsr) +VSHIFT_IMM_UNPREDICATE(vlsrL_imm, URShiftVL, RShiftCntV, D, 2, sve_lsr) +VSHIFT_IMM_UNPREDICATE(vlslB_imm, LShiftVB, LShiftCntV, B, 16, sve_lsl) +VSHIFT_IMM_UNPREDICATE(vlslS_imm, LShiftVS, LShiftCntV, H, 8, sve_lsl) +VSHIFT_IMM_UNPREDICATE(vlslI_imm, LShiftVI, LShiftCntV, S, 4, sve_lsl) +VSHIFT_IMM_UNPREDICATE(vlslL_imm, LShiftVL, LShiftCntV, D, 2, sve_lsl) VSHIFT_COUNT(vshiftcntB, B, 16, T_BYTE) VSHIFT_COUNT(vshiftcntS, H, 8, T_SHORT) VSHIFT_COUNT(vshiftcntI, S, 4, T_INT) diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.cpp b/src/hotspot/cpu/aarch64/assembler_aarch64.cpp index 29f63ba69a4..c7fac2836b7 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.cpp @@ -590,7 +590,7 @@ void entry(CodeBuffer *cb) { __ stnp(r23, r29, Address(r12, 32)); // stnp x23, x29, [x12, #32] __ ldnp(r0, r6, Address(r21, -80)); // ldnp x0, x6, [x21, #-80] -// LdStSIMDOp +// LdStNEONOp __ ld1(v15, __ T8B, Address(r26)); // ld1 {v15.8B}, [x26] __ ld1(v23, v24, __ T16B, Address(__ post(r11, 32))); // ld1 {v23.16B, v24.16B}, [x11], 32 __ ld1(v8, v9, v10, __ T1D, Address(__ post(r23, r7))); // ld1 {v8.1D, v9.1D, v10.1D}, [x23], x7 @@ -614,11 +614,146 @@ void entry(CodeBuffer *cb) { __ ld4r(v0, v1, v2, v3, __ T4H, Address(__ post(r26, 8))); // ld4r {v0.4H, v1.4H, v2.4H, v3.4H}, [x26], 8 __ ld4r(v12, v13, v14, v15, __ T2S, Address(__ post(r25, r2))); // ld4r {v12.2S, v13.2S, v14.2S, v15.2S}, [x25], x2 -// SHA512SIMDOp - __ sha512h(v22, __ T2D, v27, v4); // sha512h q22, q27, v4.2D - __ sha512h2(v7, __ T2D, v6, v1); // sha512h2 q7, q6, v1.2D - __ sha512su0(v26, __ T2D, v15); // sha512su0 v26.2D, v15.2D - __ sha512su1(v2, __ T2D, v13, v13); // sha512su1 v2.2D, v13.2D, v13.2D +// NEONReduceInstruction + __ addv(v22, __ T8B, v23); // addv b22, v23.8B + __ addv(v27, __ T16B, v28); // addv b27, v28.16B + __ addv(v4, __ T4H, v5); // addv h4, v5.4H + __ addv(v7, __ T8H, v8); // addv h7, v8.8H + __ addv(v6, __ T4S, v7); // addv s6, v7.4S + __ smaxv(v1, __ T8B, v2); // smaxv b1, v2.8B + __ smaxv(v26, __ T16B, v27); // smaxv b26, v27.16B + __ smaxv(v15, __ T4H, v16); // smaxv h15, v16.4H + __ smaxv(v2, __ T8H, v3); // smaxv h2, v3.8H + __ smaxv(v13, __ T4S, v14); // smaxv s13, v14.4S + __ fmaxv(v13, __ T4S, v14); // fmaxv s13, v14.4S + __ sminv(v24, __ T8B, v25); // sminv b24, v25.8B + __ sminv(v23, __ T16B, v24); // sminv b23, v24.16B + __ sminv(v4, __ T4H, v5); // sminv h4, v5.4H + __ sminv(v19, __ T8H, v20); // sminv h19, v20.8H + __ sminv(v15, __ T4S, v16); // sminv s15, v16.4S + __ fminv(v0, __ T4S, v1); // fminv s0, v1.4S + +// TwoRegNEONOp + __ absr(v4, __ T8B, v5); // abs v4.8B, v5.8B + __ absr(v20, __ T16B, v21); // abs v20.16B, v21.16B + __ absr(v11, __ T4H, v12); // abs v11.4H, v12.4H + __ absr(v29, __ T8H, v30); // abs v29.8H, v30.8H + __ absr(v15, __ T2S, v16); // abs v15.2S, v16.2S + __ absr(v21, __ T4S, v22); // abs v21.4S, v22.4S + __ absr(v4, __ T2D, v5); // abs v4.2D, v5.2D + __ fabs(v14, __ T2S, v15); // fabs v14.2S, v15.2S + __ fabs(v22, __ T4S, v23); // fabs v22.4S, v23.4S + __ fabs(v25, __ T2D, v26); // fabs v25.2D, v26.2D + __ fneg(v6, __ T2S, v7); // fneg v6.2S, v7.2S + __ fneg(v12, __ T4S, v13); // fneg v12.4S, v13.4S + __ fneg(v14, __ T2D, v15); // fneg v14.2D, v15.2D + __ fsqrt(v13, __ T2S, v14); // fsqrt v13.2S, v14.2S + __ fsqrt(v14, __ T4S, v15); // fsqrt v14.4S, v15.4S + __ fsqrt(v9, __ T2D, v10); // fsqrt v9.2D, v10.2D + __ notr(v25, __ T8B, v26); // not v25.8B, v26.8B + __ notr(v28, __ T16B, v29); // not v28.16B, v29.16B + +// ThreeRegNEONOp + __ andr(v10, __ T8B, v11, v12); // and v10.8B, v11.8B, v12.8B + __ andr(v19, __ T16B, v20, v21); // and v19.16B, v20.16B, v21.16B + __ orr(v11, __ T8B, v12, v13); // orr v11.8B, v12.8B, v13.8B + __ orr(v17, __ T16B, v18, v19); // orr v17.16B, v18.16B, v19.16B + __ eor(v21, __ T8B, v22, v23); // eor v21.8B, v22.8B, v23.8B + __ eor(v15, __ T16B, v16, v17); // eor v15.16B, v16.16B, v17.16B + __ addv(v20, __ T8B, v21, v22); // add v20.8B, v21.8B, v22.8B + __ addv(v23, __ T16B, v24, v25); // add v23.16B, v24.16B, v25.16B + __ addv(v26, __ T4H, v27, v28); // add v26.4H, v27.4H, v28.4H + __ addv(v5, __ T8H, v6, v7); // add v5.8H, v6.8H, v7.8H + __ addv(v6, __ T2S, v7, v8); // add v6.2S, v7.2S, v8.2S + __ addv(v15, __ T4S, v16, v17); // add v15.4S, v16.4S, v17.4S + __ addv(v15, __ T2D, v16, v17); // add v15.2D, v16.2D, v17.2D + __ fadd(v25, __ T2S, v26, v27); // fadd v25.2S, v26.2S, v27.2S + __ fadd(v16, __ T4S, v17, v18); // fadd v16.4S, v17.4S, v18.4S + __ fadd(v27, __ T2D, v28, v29); // fadd v27.2D, v28.2D, v29.2D + __ subv(v24, __ T8B, v25, v26); // sub v24.8B, v25.8B, v26.8B + __ subv(v15, __ T16B, v16, v17); // sub v15.16B, v16.16B, v17.16B + __ subv(v25, __ T4H, v26, v27); // sub v25.4H, v26.4H, v27.4H + __ subv(v14, __ T8H, v15, v16); // sub v14.8H, v15.8H, v16.8H + __ subv(v10, __ T2S, v11, v12); // sub v10.2S, v11.2S, v12.2S + __ subv(v13, __ T4S, v14, v15); // sub v13.4S, v14.4S, v15.4S + __ subv(v14, __ T2D, v15, v16); // sub v14.2D, v15.2D, v16.2D + __ fsub(v20, __ T2S, v21, v22); // fsub v20.2S, v21.2S, v22.2S + __ fsub(v1, __ T4S, v2, v3); // fsub v1.4S, v2.4S, v3.4S + __ fsub(v22, __ T2D, v23, v24); // fsub v22.2D, v23.2D, v24.2D + __ mulv(v30, __ T8B, v31, v0); // mul v30.8B, v31.8B, v0.8B + __ mulv(v14, __ T16B, v15, v16); // mul v14.16B, v15.16B, v16.16B + __ mulv(v2, __ T4H, v3, v4); // mul v2.4H, v3.4H, v4.4H + __ mulv(v6, __ T8H, v7, v8); // mul v6.8H, v7.8H, v8.8H + __ mulv(v3, __ T2S, v4, v5); // mul v3.2S, v4.2S, v5.2S + __ mulv(v7, __ T4S, v8, v9); // mul v7.4S, v8.4S, v9.4S + __ fmul(v24, __ T2S, v25, v26); // fmul v24.2S, v25.2S, v26.2S + __ fmul(v0, __ T4S, v1, v2); // fmul v0.4S, v1.4S, v2.4S + __ fmul(v27, __ T2D, v28, v29); // fmul v27.2D, v28.2D, v29.2D + __ mlav(v29, __ T4H, v30, v31); // mla v29.4H, v30.4H, v31.4H + __ mlav(v5, __ T8H, v6, v7); // mla v5.8H, v6.8H, v7.8H + __ mlav(v5, __ T2S, v6, v7); // mla v5.2S, v6.2S, v7.2S + __ mlav(v29, __ T4S, v30, v31); // mla v29.4S, v30.4S, v31.4S + __ fmla(v11, __ T2S, v12, v13); // fmla v11.2S, v12.2S, v13.2S + __ fmla(v25, __ T4S, v26, v27); // fmla v25.4S, v26.4S, v27.4S + __ fmla(v0, __ T2D, v1, v2); // fmla v0.2D, v1.2D, v2.2D + __ mlsv(v30, __ T4H, v31, v0); // mls v30.4H, v31.4H, v0.4H + __ mlsv(v0, __ T8H, v1, v2); // mls v0.8H, v1.8H, v2.8H + __ mlsv(v17, __ T2S, v18, v19); // mls v17.2S, v18.2S, v19.2S + __ mlsv(v28, __ T4S, v29, v30); // mls v28.4S, v29.4S, v30.4S + __ fmls(v25, __ T2S, v26, v27); // fmls v25.2S, v26.2S, v27.2S + __ fmls(v9, __ T4S, v10, v11); // fmls v9.4S, v10.4S, v11.4S + __ fmls(v25, __ T2D, v26, v27); // fmls v25.2D, v26.2D, v27.2D + __ fdiv(v12, __ T2S, v13, v14); // fdiv v12.2S, v13.2S, v14.2S + __ fdiv(v15, __ T4S, v16, v17); // fdiv v15.4S, v16.4S, v17.4S + __ fdiv(v11, __ T2D, v12, v13); // fdiv v11.2D, v12.2D, v13.2D + __ maxv(v10, __ T8B, v11, v12); // smax v10.8B, v11.8B, v12.8B + __ maxv(v17, __ T16B, v18, v19); // smax v17.16B, v18.16B, v19.16B + __ maxv(v24, __ T4H, v25, v26); // smax v24.4H, v25.4H, v26.4H + __ maxv(v21, __ T8H, v22, v23); // smax v21.8H, v22.8H, v23.8H + __ maxv(v23, __ T2S, v24, v25); // smax v23.2S, v24.2S, v25.2S + __ maxv(v0, __ T4S, v1, v2); // smax v0.4S, v1.4S, v2.4S + __ fmax(v16, __ T2S, v17, v18); // fmax v16.2S, v17.2S, v18.2S + __ fmax(v10, __ T4S, v11, v12); // fmax v10.4S, v11.4S, v12.4S + __ fmax(v6, __ T2D, v7, v8); // fmax v6.2D, v7.2D, v8.2D + __ minv(v28, __ T8B, v29, v30); // smin v28.8B, v29.8B, v30.8B + __ minv(v6, __ T16B, v7, v8); // smin v6.16B, v7.16B, v8.16B + __ minv(v5, __ T4H, v6, v7); // smin v5.4H, v6.4H, v7.4H + __ minv(v5, __ T8H, v6, v7); // smin v5.8H, v6.8H, v7.8H + __ minv(v20, __ T2S, v21, v22); // smin v20.2S, v21.2S, v22.2S + __ minv(v17, __ T4S, v18, v19); // smin v17.4S, v18.4S, v19.4S + __ fmin(v15, __ T2S, v16, v17); // fmin v15.2S, v16.2S, v17.2S + __ fmin(v17, __ T4S, v18, v19); // fmin v17.4S, v18.4S, v19.4S + __ fmin(v29, __ T2D, v30, v31); // fmin v29.2D, v30.2D, v31.2D + __ cmeq(v26, __ T8B, v27, v28); // cmeq v26.8B, v27.8B, v28.8B + __ cmeq(v28, __ T16B, v29, v30); // cmeq v28.16B, v29.16B, v30.16B + __ cmeq(v1, __ T4H, v2, v3); // cmeq v1.4H, v2.4H, v3.4H + __ cmeq(v27, __ T8H, v28, v29); // cmeq v27.8H, v28.8H, v29.8H + __ cmeq(v0, __ T2S, v1, v2); // cmeq v0.2S, v1.2S, v2.2S + __ cmeq(v20, __ T4S, v21, v22); // cmeq v20.4S, v21.4S, v22.4S + __ cmeq(v28, __ T2D, v29, v30); // cmeq v28.2D, v29.2D, v30.2D + __ fcmeq(v15, __ T2S, v16, v17); // fcmeq v15.2S, v16.2S, v17.2S + __ fcmeq(v12, __ T4S, v13, v14); // fcmeq v12.4S, v13.4S, v14.4S + __ fcmeq(v10, __ T2D, v11, v12); // fcmeq v10.2D, v11.2D, v12.2D + __ cmgt(v28, __ T8B, v29, v30); // cmgt v28.8B, v29.8B, v30.8B + __ cmgt(v28, __ T16B, v29, v30); // cmgt v28.16B, v29.16B, v30.16B + __ cmgt(v19, __ T4H, v20, v21); // cmgt v19.4H, v20.4H, v21.4H + __ cmgt(v22, __ T8H, v23, v24); // cmgt v22.8H, v23.8H, v24.8H + __ cmgt(v10, __ T2S, v11, v12); // cmgt v10.2S, v11.2S, v12.2S + __ cmgt(v4, __ T4S, v5, v6); // cmgt v4.4S, v5.4S, v6.4S + __ cmgt(v30, __ T2D, v31, v0); // cmgt v30.2D, v31.2D, v0.2D + __ fcmgt(v20, __ T2S, v21, v22); // fcmgt v20.2S, v21.2S, v22.2S + __ fcmgt(v8, __ T4S, v9, v10); // fcmgt v8.4S, v9.4S, v10.4S + __ fcmgt(v30, __ T2D, v31, v0); // fcmgt v30.2D, v31.2D, v0.2D + __ cmge(v17, __ T8B, v18, v19); // cmge v17.8B, v18.8B, v19.8B + __ cmge(v10, __ T16B, v11, v12); // cmge v10.16B, v11.16B, v12.16B + __ cmge(v27, __ T4H, v28, v29); // cmge v27.4H, v28.4H, v29.4H + __ cmge(v2, __ T8H, v3, v4); // cmge v2.8H, v3.8H, v4.8H + __ cmge(v24, __ T2S, v25, v26); // cmge v24.2S, v25.2S, v26.2S + __ cmge(v4, __ T4S, v5, v6); // cmge v4.4S, v5.4S, v6.4S + __ cmge(v3, __ T2D, v4, v5); // cmge v3.2D, v4.2D, v5.2D + __ fcmge(v8, __ T2S, v9, v10); // fcmge v8.2S, v9.2S, v10.2S + __ fcmge(v22, __ T4S, v23, v24); // fcmge v22.4S, v23.4S, v24.4S + __ fcmge(v17, __ T2D, v18, v19); // fcmge v17.2D, v18.2D, v19.2D // SpecialCases __ ccmn(zr, zr, 3u, Assembler::LE); // ccmn xzr, xzr, #3, LE @@ -705,774 +840,160 @@ void entry(CodeBuffer *cb) { __ fmovd(v0, -1.0625); // fmov d0, #-1.0625 // LSEOp - __ swp(Assembler::xword, r24, r24, r4); // swp x24, x24, [x4] - __ ldadd(Assembler::xword, r20, r16, r0); // ldadd x20, x16, [x0] - __ ldbic(Assembler::xword, r4, r21, r11); // ldclr x4, x21, [x11] - __ ldeor(Assembler::xword, r30, r16, r22); // ldeor x30, x16, [x22] - __ ldorr(Assembler::xword, r4, r15, r23); // ldset x4, x15, [x23] - __ ldsmin(Assembler::xword, r26, r6, r12); // ldsmin x26, x6, [x12] - __ ldsmax(Assembler::xword, r15, r14, r15); // ldsmax x15, x14, [x15] - __ ldumin(Assembler::xword, r9, r25, r29); // ldumin x9, x25, [x29] - __ ldumax(Assembler::xword, r11, r20, r12); // ldumax x11, x20, [x12] + __ swp(Assembler::xword, r13, r5, r29); // swp x13, x5, [x29] + __ ldadd(Assembler::xword, r24, r21, r26); // ldadd x24, x21, [x26] + __ ldbic(Assembler::xword, r24, r3, r24); // ldclr x24, x3, [x24] + __ ldeor(Assembler::xword, r26, r23, r15); // ldeor x26, x23, [x15] + __ ldorr(Assembler::xword, r21, r3, r24); // ldset x21, x3, [x24] + __ ldsmin(Assembler::xword, r8, r25, r20); // ldsmin x8, x25, [x20] + __ ldsmax(Assembler::xword, r16, r17, r2); // ldsmax x16, x17, [x2] + __ ldumin(Assembler::xword, r1, r0, r24); // ldumin x1, x0, [x24] + __ ldumax(Assembler::xword, r4, r3, r12); // ldumax x4, x3, [x12] // LSEOp - __ swpa(Assembler::xword, r16, r22, r16); // swpa x16, x22, [x16] - __ ldadda(Assembler::xword, r21, r24, r26); // ldadda x21, x24, [x26] - __ ldbica(Assembler::xword, r6, r6, r16); // ldclra x6, x6, [x16] - __ ldeora(Assembler::xword, r16, r25, r16); // ldeora x16, x25, [x16] - __ ldorra(Assembler::xword, r28, r24, r16); // ldseta x28, x24, [x16] - __ ldsmina(Assembler::xword, r26, r15, r10); // ldsmina x26, x15, [x10] - __ ldsmaxa(Assembler::xword, r13, r14, r20); // ldsmaxa x13, x14, [x20] - __ ldumina(Assembler::xword, r1, r23, r30); // ldumina x1, x23, [x30] - __ ldumaxa(Assembler::xword, r14, r2, r6); // ldumaxa x14, x2, [x6] + __ swpa(Assembler::xword, zr, r28, r10); // swpa xzr, x28, [x10] + __ ldadda(Assembler::xword, r26, r2, r12); // ldadda x26, x2, [x12] + __ ldbica(Assembler::xword, r16, zr, r1); // ldclra x16, xzr, [x1] + __ ldeora(Assembler::xword, r13, r29, r0); // ldeora x13, x29, [x0] + __ ldorra(Assembler::xword, r19, r12, r17); // ldseta x19, x12, [x17] + __ ldsmina(Assembler::xword, r22, r13, r28); // ldsmina x22, x13, [x28] + __ ldsmaxa(Assembler::xword, r30, zr, r1); // ldsmaxa x30, xzr, [x1] + __ ldumina(Assembler::xword, r26, r28, r4); // ldumina x26, x28, [x4] + __ ldumaxa(Assembler::xword, r30, r4, r6); // ldumaxa x30, x4, [x6] // LSEOp - __ swpal(Assembler::xword, r3, r8, r25); // swpal x3, x8, [x25] - __ ldaddal(Assembler::xword, r0, r27, r30); // ldaddal x0, x27, [x30] - __ ldbical(Assembler::xword, r5, r5, r30); // ldclral x5, x5, [x30] - __ ldeoral(Assembler::xword, r11, r25, r0); // ldeoral x11, x25, [x0] - __ ldorral(Assembler::xword, zr, r0, r19); // ldsetal xzr, x0, [x19] - __ ldsminal(Assembler::xword, r29, r26, r9); // ldsminal x29, x26, [x9] - __ ldsmaxal(Assembler::xword, r26, r12, r15); // ldsmaxal x26, x12, [x15] - __ lduminal(Assembler::xword, r11, r11, r15); // lduminal x11, x11, [x15] - __ ldumaxal(Assembler::xword, r25, r22, r24); // ldumaxal x25, x22, [x24] + __ swpal(Assembler::xword, r30, r26, r15); // swpal x30, x26, [x15] + __ ldaddal(Assembler::xword, r9, r8, r12); // ldaddal x9, x8, [x12] + __ ldbical(Assembler::xword, r0, r20, r1); // ldclral x0, x20, [x1] + __ ldeoral(Assembler::xword, r24, r2, r0); // ldeoral x24, x2, [x0] + __ ldorral(Assembler::xword, r9, r24, r26); // ldsetal x9, x24, [x26] + __ ldsminal(Assembler::xword, r16, r30, r3); // ldsminal x16, x30, [x3] + __ ldsmaxal(Assembler::xword, r10, r23, r10); // ldsmaxal x10, x23, [x10] + __ lduminal(Assembler::xword, r4, r16, r2); // lduminal x4, x16, [x2] + __ ldumaxal(Assembler::xword, r11, r8, r10); // ldumaxal x11, x8, [x10] // LSEOp - __ swpl(Assembler::xword, r0, r17, r11); // swpl x0, x17, [x11] - __ ldaddl(Assembler::xword, r6, r29, r6); // ldaddl x6, x29, [x6] - __ ldbicl(Assembler::xword, r5, r5, r21); // ldclrl x5, x5, [x21] - __ ldeorl(Assembler::xword, r19, r16, r15); // ldeorl x19, x16, [x15] - __ ldorrl(Assembler::xword, r30, r27, r28); // ldsetl x30, x27, [x28] - __ ldsminl(Assembler::xword, r1, r28, r1); // ldsminl x1, x28, [x1] - __ ldsmaxl(Assembler::xword, r20, r29, r16); // ldsmaxl x20, x29, [x16] - __ lduminl(Assembler::xword, r13, r10, r29); // lduminl x13, x10, [x29] - __ ldumaxl(Assembler::xword, r29, r19, r22); // ldumaxl x29, x19, [x22] + __ swpl(Assembler::xword, r15, r17, r2); // swpl x15, x17, [x2] + __ ldaddl(Assembler::xword, r10, r12, r12); // ldaddl x10, x12, [x12] + __ ldbicl(Assembler::xword, r15, r13, r2); // ldclrl x15, x13, [x2] + __ ldeorl(Assembler::xword, r7, r20, r26); // ldeorl x7, x20, [x26] + __ ldorrl(Assembler::xword, r16, r4, r2); // ldsetl x16, x4, [x2] + __ ldsminl(Assembler::xword, r4, r12, r15); // ldsminl x4, x12, [x15] + __ ldsmaxl(Assembler::xword, r21, r16, r15); // ldsmaxl x21, x16, [x15] + __ lduminl(Assembler::xword, r11, r21, r23); // lduminl x11, x21, [x23] + __ ldumaxl(Assembler::xword, r12, r26, r23); // ldumaxl x12, x26, [x23] // LSEOp - __ swp(Assembler::word, r10, r4, sp); // swp w10, w4, [sp] - __ ldadd(Assembler::word, r21, r8, sp); // ldadd w21, w8, [sp] - __ ldbic(Assembler::word, r19, r10, r28); // ldclr w19, w10, [x28] - __ ldeor(Assembler::word, r2, r25, r5); // ldeor w2, w25, [x5] - __ ldorr(Assembler::word, r3, r8, r22); // ldset w3, w8, [x22] - __ ldsmin(Assembler::word, r19, r13, r5); // ldsmin w19, w13, [x5] - __ ldsmax(Assembler::word, r29, r24, r21); // ldsmax w29, w24, [x21] - __ ldumin(Assembler::word, r26, r24, r3); // ldumin w26, w24, [x3] - __ ldumax(Assembler::word, r24, r26, r23); // ldumax w24, w26, [x23] + __ swp(Assembler::word, r28, r14, r11); // swp w28, w14, [x11] + __ ldadd(Assembler::word, r24, r1, r12); // ldadd w24, w1, [x12] + __ ldbic(Assembler::word, zr, r10, r16); // ldclr wzr, w10, [x16] + __ ldeor(Assembler::word, r7, r2, r3); // ldeor w7, w2, [x3] + __ ldorr(Assembler::word, r13, r19, r17); // ldset w13, w19, [x17] + __ ldsmin(Assembler::word, r16, r3, r1); // ldsmin w16, w3, [x1] + __ ldsmax(Assembler::word, r11, r30, r5); // ldsmax w11, w30, [x5] + __ ldumin(Assembler::word, r8, r15, r29); // ldumin w8, w15, [x29] + __ ldumax(Assembler::word, r30, r0, r20); // ldumax w30, w0, [x20] // LSEOp - __ swpa(Assembler::word, r15, r21, r3); // swpa w15, w21, [x3] - __ ldadda(Assembler::word, r24, r8, r25); // ldadda w24, w8, [x25] - __ ldbica(Assembler::word, r20, r16, r17); // ldclra w20, w16, [x17] - __ ldeora(Assembler::word, r2, r1, r0); // ldeora w2, w1, [x0] - __ ldorra(Assembler::word, r24, r4, r3); // ldseta w24, w4, [x3] - __ ldsmina(Assembler::word, r12, zr, r28); // ldsmina w12, wzr, [x28] - __ ldsmaxa(Assembler::word, r10, r26, r2); // ldsmaxa w10, w26, [x2] - __ ldumina(Assembler::word, r12, r16, sp); // ldumina w12, w16, [sp] - __ ldumaxa(Assembler::word, r1, r13, r29); // ldumaxa w1, w13, [x29] + __ swpa(Assembler::word, r7, r20, r23); // swpa w7, w20, [x23] + __ ldadda(Assembler::word, r28, r21, r27); // ldadda w28, w21, [x27] + __ ldbica(Assembler::word, r25, r5, r1); // ldclra w25, w5, [x1] + __ ldeora(Assembler::word, r23, r16, sp); // ldeora w23, w16, [sp] + __ ldorra(Assembler::word, r5, r12, r9); // ldseta w5, w12, [x9] + __ ldsmina(Assembler::word, r28, r15, r29); // ldsmina w28, w15, [x29] + __ ldsmaxa(Assembler::word, r22, zr, r19); // ldsmaxa w22, wzr, [x19] + __ ldumina(Assembler::word, zr, r5, r14); // ldumina wzr, w5, [x14] + __ ldumaxa(Assembler::word, r16, zr, r15); // ldumaxa w16, wzr, [x15] // LSEOp - __ swpal(Assembler::word, r0, r19, r12); // swpal w0, w19, [x12] - __ ldaddal(Assembler::word, r17, r22, r13); // ldaddal w17, w22, [x13] - __ ldbical(Assembler::word, r28, r30, sp); // ldclral w28, w30, [sp] - __ ldeoral(Assembler::word, r1, r26, r28); // ldeoral w1, w26, [x28] - __ ldorral(Assembler::word, r4, r30, r4); // ldsetal w4, w30, [x4] - __ ldsminal(Assembler::word, r6, r30, r26); // ldsminal w6, w30, [x26] - __ ldsmaxal(Assembler::word, r16, r9, r8); // ldsmaxal w16, w9, [x8] - __ lduminal(Assembler::word, r12, r0, r20); // lduminal w12, w0, [x20] - __ ldumaxal(Assembler::word, r1, r24, r2); // ldumaxal w1, w24, [x2] + __ swpal(Assembler::word, r27, r20, r16); // swpal w27, w20, [x16] + __ ldaddal(Assembler::word, r12, r11, r9); // ldaddal w12, w11, [x9] + __ ldbical(Assembler::word, r6, r30, r17); // ldclral w6, w30, [x17] + __ ldeoral(Assembler::word, r27, r28, r30); // ldeoral w27, w28, [x30] + __ ldorral(Assembler::word, r7, r10, r20); // ldsetal w7, w10, [x20] + __ ldsminal(Assembler::word, r10, r4, r24); // ldsminal w10, w4, [x24] + __ ldsmaxal(Assembler::word, r17, r17, r22); // ldsmaxal w17, w17, [x22] + __ lduminal(Assembler::word, r3, r29, r15); // lduminal w3, w29, [x15] + __ ldumaxal(Assembler::word, r22, r19, r19); // ldumaxal w22, w19, [x19] // LSEOp - __ swpl(Assembler::word, r0, r9, r24); // swpl w0, w9, [x24] - __ ldaddl(Assembler::word, r26, r16, r30); // ldaddl w26, w16, [x30] - __ ldbicl(Assembler::word, r3, r10, r23); // ldclrl w3, w10, [x23] - __ ldeorl(Assembler::word, r10, r4, r15); // ldeorl w10, w4, [x15] - __ ldorrl(Assembler::word, r2, r11, r8); // ldsetl w2, w11, [x8] - __ ldsminl(Assembler::word, r10, r15, r17); // ldsminl w10, w15, [x17] - __ ldsmaxl(Assembler::word, r2, r10, r12); // ldsmaxl w2, w10, [x12] - __ lduminl(Assembler::word, r12, r15, r13); // lduminl w12, w15, [x13] - __ ldumaxl(Assembler::word, r2, r7, r20); // ldumaxl w2, w7, [x20] + __ swpl(Assembler::word, r22, r2, r15); // swpl w22, w2, [x15] + __ ldaddl(Assembler::word, r6, r12, r16); // ldaddl w6, w12, [x16] + __ ldbicl(Assembler::word, r11, r13, r23); // ldclrl w11, w13, [x23] + __ ldeorl(Assembler::word, r1, r30, r19); // ldeorl w1, w30, [x19] + __ ldorrl(Assembler::word, r5, r17, r2); // ldsetl w5, w17, [x2] + __ ldsminl(Assembler::word, r16, r22, r13); // ldsminl w16, w22, [x13] + __ ldsmaxl(Assembler::word, r10, r21, r29); // ldsmaxl w10, w21, [x29] + __ lduminl(Assembler::word, r27, r12, r27); // lduminl w27, w12, [x27] + __ ldumaxl(Assembler::word, r3, r1, sp); // ldumaxl w3, w1, [sp] + +// SHA3SIMDOp + __ bcax(v23, __ T16B, v19, v17, v9); // bcax v23.16B, v19.16B, v17.16B, v9.16B + __ eor3(v27, __ T16B, v26, v14, v6); // eor3 v27.16B, v26.16B, v14.16B, v6.16B + __ rax1(v20, __ T2D, v22, v30); // rax1 v20.2D, v22.2D, v30.2D + __ xar(v24, __ T2D, v2, v30, 54); // xar v24.2D, v2.2D, v30.2D, #54 + +// SHA512SIMDOp + __ sha512h(v17, __ T2D, v10, v22); // sha512h q17, q10, v22.2D + __ sha512h2(v17, __ T2D, v2, v17); // sha512h2 q17, q2, v17.2D + __ sha512su0(v0, __ T2D, v24); // sha512su0 v0.2D, v24.2D + __ sha512su1(v25, __ T2D, v22, v2); // sha512su1 v25.2D, v22.2D, v2.2D // SVEVectorOp - __ sve_add(z25, __ B, z15, z4); // add z25.b, z15.b, z4.b - __ sve_sub(z4, __ S, z11, z17); // sub z4.s, z11.s, z17.s - __ sve_fadd(z16, __ D, z17, z10); // fadd z16.d, z17.d, z10.d - __ sve_fmul(z22, __ D, z12, z25); // fmul z22.d, z12.d, z25.d - __ sve_fsub(z28, __ D, z14, z10); // fsub z28.d, z14.d, z10.d - __ sve_abs(z1, __ H, p3, z30); // abs z1.h, p3/m, z30.h - __ sve_add(z15, __ B, p1, z2); // add z15.b, p1/m, z15.b, z2.b - __ sve_asr(z13, __ S, p4, z16); // asr z13.s, p4/m, z13.s, z16.s - __ sve_cnt(z3, __ D, p0, z11); // cnt z3.d, p0/m, z11.d - __ sve_lsl(z5, __ D, p2, z14); // lsl z5.d, p2/m, z5.d, z14.d - __ sve_lsr(z29, __ B, p0, z20); // lsr z29.b, p0/m, z29.b, z20.b - __ sve_mul(z20, __ S, p5, z27); // mul z20.s, p5/m, z20.s, z27.s - __ sve_neg(z26, __ B, p6, z4); // neg z26.b, p6/m, z4.b - __ sve_not(z22, __ B, p4, z30); // not z22.b, p4/m, z30.b - __ sve_smax(z11, __ H, p2, z27); // smax z11.h, p2/m, z11.h, z27.h - __ sve_smin(z28, __ S, p5, z30); // smin z28.s, p5/m, z28.s, z30.s - __ sve_sub(z30, __ S, p1, z13); // sub z30.s, p1/m, z30.s, z13.s - __ sve_fabs(z30, __ D, p4, z26); // fabs z30.d, p4/m, z26.d - __ sve_fadd(z15, __ S, p3, z11); // fadd z15.s, p3/m, z15.s, z11.s - __ sve_fdiv(z6, __ D, p7, z16); // fdiv z6.d, p7/m, z6.d, z16.d - __ sve_fmax(z27, __ S, p7, z7); // fmax z27.s, p7/m, z27.s, z7.s - __ sve_fmin(z19, __ D, p2, z4); // fmin z19.d, p2/m, z19.d, z4.d - __ sve_fmul(z17, __ S, p4, z22); // fmul z17.s, p4/m, z17.s, z22.s - __ sve_fneg(z28, __ D, p3, z21); // fneg z28.d, p3/m, z21.d - __ sve_frintm(z17, __ S, p5, z2); // frintm z17.s, p5/m, z2.s - __ sve_frintn(z6, __ S, p3, z15); // frintn z6.s, p3/m, z15.s - __ sve_frintp(z12, __ D, p5, z1); // frintp z12.d, p5/m, z1.d - __ sve_fsqrt(z17, __ S, p1, z17); // fsqrt z17.s, p1/m, z17.s - __ sve_fsub(z15, __ S, p5, z13); // fsub z15.s, p5/m, z15.s, z13.s - __ sve_fmla(z20, __ D, p7, z27, z11); // fmla z20.d, p7/m, z27.d, z11.d - __ sve_fmls(z3, __ D, p0, z30, z23); // fmls z3.d, p0/m, z30.d, z23.d - __ sve_fnmla(z17, __ S, p2, z27, z26); // fnmla z17.s, p2/m, z27.s, z26.s - __ sve_fnmls(z6, __ D, p5, z22, z30); // fnmls z6.d, p5/m, z22.d, z30.d - __ sve_mla(z2, __ H, p7, z26, z17); // mla z2.h, p7/m, z26.h, z17.h - __ sve_mls(z22, __ B, p4, z2, z17); // mls z22.b, p4/m, z2.b, z17.b - __ sve_and(z24, z25, z22); // and z24.d, z25.d, z22.d - __ sve_eor(z17, z12, z3); // eor z17.d, z12.d, z3.d - __ sve_orr(z29, z28, z16); // orr z29.d, z28.d, z16.d + __ sve_add(z17, __ D, z12, z3); // add z17.d, z12.d, z3.d + __ sve_sub(z29, __ D, z28, z16); // sub z29.d, z28.d, z16.d + __ sve_fadd(z6, __ D, z9, z28); // fadd z6.d, z9.d, z28.d + __ sve_fmul(z7, __ S, z4, z7); // fmul z7.s, z4.s, z7.s + __ sve_fsub(z9, __ S, z22, z8); // fsub z9.s, z22.s, z8.s + __ sve_abs(z27, __ B, p5, z30); // abs z27.b, p5/m, z30.b + __ sve_add(z26, __ H, p0, z16); // add z26.h, p0/m, z26.h, z16.h + __ sve_asr(z3, __ D, p6, z8); // asr z3.d, p6/m, z3.d, z8.d + __ sve_cnt(z21, __ D, p6, z26); // cnt z21.d, p6/m, z26.d + __ sve_lsl(z22, __ B, p0, z4); // lsl z22.b, p0/m, z22.b, z4.b + __ sve_lsr(z17, __ H, p0, z3); // lsr z17.h, p0/m, z17.h, z3.h + __ sve_mul(z1, __ B, p2, z6); // mul z1.b, p2/m, z1.b, z6.b + __ sve_neg(z9, __ S, p7, z7); // neg z9.s, p7/m, z7.s + __ sve_not(z22, __ H, p5, z5); // not z22.h, p5/m, z5.h + __ sve_smax(z8, __ B, p4, z30); // smax z8.b, p4/m, z8.b, z30.b + __ sve_smin(z17, __ D, p0, z11); // smin z17.d, p0/m, z17.d, z11.d + __ sve_sub(z28, __ S, p0, z26); // sub z28.s, p0/m, z28.s, z26.s + __ sve_fabs(z28, __ D, p3, z13); // fabs z28.d, p3/m, z13.d + __ sve_fadd(z16, __ S, p6, z5); // fadd z16.s, p6/m, z16.s, z5.s + __ sve_fdiv(z13, __ S, p2, z15); // fdiv z13.s, p2/m, z13.s, z15.s + __ sve_fmax(z26, __ S, p5, z11); // fmax z26.s, p5/m, z26.s, z11.s + __ sve_fmin(z22, __ S, p4, z4); // fmin z22.s, p4/m, z22.s, z4.s + __ sve_fmul(z19, __ S, p4, z17); // fmul z19.s, p4/m, z19.s, z17.s + __ sve_fneg(z14, __ D, p3, z2); // fneg z14.d, p3/m, z2.d + __ sve_frintm(z3, __ S, p5, z23); // frintm z3.s, p5/m, z23.s + __ sve_frintn(z6, __ S, p1, z17); // frintn z6.s, p1/m, z17.s + __ sve_frintp(z27, __ S, p4, z16); // frintp z27.s, p4/m, z16.s + __ sve_fsqrt(z2, __ S, p7, z3); // fsqrt z2.s, p7/m, z3.s + __ sve_fsub(z6, __ S, p4, z19); // fsub z6.s, p4/m, z6.s, z19.s + __ sve_fmla(z12, __ D, p5, z8, z24); // fmla z12.d, p5/m, z8.d, z24.d + __ sve_fmls(z17, __ S, p0, z10, z23); // fmls z17.s, p0/m, z10.s, z23.s + __ sve_fnmla(z19, __ S, p7, z13, z16); // fnmla z19.s, p7/m, z13.s, z16.s + __ sve_fnmls(z0, __ D, p1, z14, z17); // fnmls z0.d, p1/m, z14.d, z17.d + __ sve_mla(z8, __ S, p2, z22, z20); // mla z8.s, p2/m, z22.s, z20.s + __ sve_mls(z27, __ S, p0, z3, z15); // mls z27.s, p0/m, z3.s, z15.s + __ sve_and(z20, z7, z4); // and z20.d, z7.d, z4.d + __ sve_eor(z7, z0, z8); // eor z7.d, z0.d, z8.d + __ sve_orr(z19, z22, z4); // orr z19.d, z22.d, z4.d // SVEReductionOp - __ sve_andv(v6, __ S, p2, z28); // andv s6, p2, z28.s - __ sve_orv(v7, __ H, p1, z7); // orv h7, p1, z7.h - __ sve_eorv(v9, __ B, p5, z8); // eorv b9, p5, z8.b - __ sve_smaxv(v27, __ B, p5, z30); // smaxv b27, p5, z30.b - __ sve_sminv(v26, __ H, p0, z16); // sminv h26, p0, z16.h - __ sve_fminv(v3, __ D, p6, z8); // fminv d3, p6, z8.d - __ sve_fmaxv(v21, __ D, p6, z26); // fmaxv d21, p6, z26.d - __ sve_fadda(v22, __ S, p0, z4); // fadda s22, p0, s22, z4.s - __ sve_uaddv(v17, __ H, p0, z3); // uaddv d17, p0, z3.h + __ sve_andv(v9, __ D, p5, z11); // andv d9, p5, z11.d + __ sve_orv(v5, __ H, p7, z16); // orv h5, p7, z16.h + __ sve_eorv(v22, __ H, p3, z1); // eorv h22, p3, z1.h + __ sve_smaxv(v8, __ D, p5, z16); // smaxv d8, p5, z16.d + __ sve_sminv(v15, __ S, p1, z4); // sminv s15, p1, z4.s + __ sve_fminv(v8, __ S, p1, z29); // fminv s8, p1, z29.s + __ sve_fmaxv(v28, __ D, p4, z29); // fmaxv d28, p4, z29.d + __ sve_fadda(v9, __ S, p3, z2); // fadda s9, p3, s9, z2.s + __ sve_uaddv(v28, __ B, p0, z7); // uaddv d28, p0, z7.b __ bind(forth); /* -aarch64ops.o: file format elf64-littleaarch64 - - -Disassembly of section .text: - -0000000000000000 : - 0: 8b0d82fa add x26, x23, x13, lsl #32 - 4: cb49970c sub x12, x24, x9, lsr #37 - 8: ab889dfc adds x28, x15, x8, asr #39 - c: eb9ee787 subs x7, x28, x30, asr #57 - 10: 0b9b3ec9 add w9, w22, w27, asr #15 - 14: 4b9179a3 sub w3, w13, w17, asr #30 - 18: 2b88474e adds w14, w26, w8, asr #17 - 1c: 6b8c56c0 subs w0, w22, w12, asr #21 - 20: 8a1a51e0 and x0, x15, x26, lsl #20 - 24: aa11f4ba orr x26, x5, x17, lsl #61 - 28: ca0281b8 eor x24, x13, x2, lsl #32 - 2c: ea918c7c ands x28, x3, x17, asr #35 - 30: 0a5d4a19 and w25, w16, w29, lsr #18 - 34: 2a4b262d orr w13, w17, w11, lsr #9 - 38: 4a513ca5 eor w5, w5, w17, lsr #15 - 3c: 6a9b6ae2 ands w2, w23, w27, asr #26 - 40: 8a70b79b bic x27, x28, x16, lsr #45 - 44: aaba9728 orn x8, x25, x26, asr #37 - 48: ca6dfe3d eon x29, x17, x13, lsr #63 - 4c: ea627f1c bics x28, x24, x2, lsr #31 - 50: 0aa70f53 bic w19, w26, w7, asr #3 - 54: 2aaa0f06 orn w6, w24, w10, asr #3 - 58: 4a6176a4 eon w4, w21, w1, lsr #29 - 5c: 6a604eb0 bics w16, w21, w0, lsr #19 - 60: 1105ed91 add w17, w12, #0x17b - 64: 3100583e adds w30, w1, #0x16 - 68: 5101f8bd sub w29, w5, #0x7e - 6c: 710f0306 subs w6, w24, #0x3c0 - 70: 9101a1a0 add x0, x13, #0x68 - 74: b10a5cc8 adds x8, x6, #0x297 - 78: d10810aa sub x10, x5, #0x204 - 7c: f10fd061 subs x1, x3, #0x3f4 - 80: 120cb166 and w6, w11, #0xfff1fff1 - 84: 321764bc orr w28, w5, #0xfffffe07 - 88: 52174681 eor w1, w20, #0x7fffe00 - 8c: 720c0227 ands w7, w17, #0x100000 - 90: 9241018e and x14, x12, #0x8000000000000000 - 94: b25a2969 orr x9, x11, #0x1ffc000000000 - 98: d278b411 eor x17, x0, #0x3fffffffffff00 - 9c: f26aad01 ands x1, x8, #0xffffffffffc00003 - a0: 14000000 b a0 - a4: 17ffffd7 b 0 - a8: 14000242 b 9b0 - ac: 94000000 bl ac - b0: 97ffffd4 bl 0 - b4: 9400023f bl 9b0 - b8: 3400000a cbz w10, b8 - bc: 34fffa2a cbz w10, 0 - c0: 3400478a cbz w10, 9b0 - c4: 35000008 cbnz w8, c4 - c8: 35fff9c8 cbnz w8, 0 - cc: 35004728 cbnz w8, 9b0 - d0: b400000b cbz x11, d0 - d4: b4fff96b cbz x11, 0 - d8: b40046cb cbz x11, 9b0 - dc: b500001d cbnz x29, dc - e0: b5fff91d cbnz x29, 0 - e4: b500467d cbnz x29, 9b0 - e8: 10000013 adr x19, e8 - ec: 10fff8b3 adr x19, 0 - f0: 10004613 adr x19, 9b0 - f4: 90000013 adrp x19, 0 - f8: 36300016 tbz w22, #6, f8 - fc: 3637f836 tbz w22, #6, 0 - 100: 36304596 tbz w22, #6, 9b0 - 104: 3758000c tbnz w12, #11, 104 - 108: 375ff7cc tbnz w12, #11, 0 - 10c: 3758452c tbnz w12, #11, 9b0 - 110: 128313a0 mov w0, #0xffffe762 // #-6302 - 114: 528a32c7 mov w7, #0x5196 // #20886 - 118: 7289173b movk w27, #0x48b9 - 11c: 92ab3acc mov x12, #0xffffffffa629ffff // #-1507196929 - 120: d2a0bf94 mov x20, #0x5fc0000 // #100401152 - 124: f2c285e8 movk x8, #0x142f, lsl #32 - 128: 9358722f sbfx x15, x17, #24, #5 - 12c: 330e652f bfxil w15, w9, #14, #12 - 130: 53067f3b lsr w27, w25, #6 - 134: 93577c53 sbfx x19, x2, #23, #9 - 138: b34a1aac bfi x12, x21, #54, #7 - 13c: d35a4016 ubfiz x22, x0, #38, #17 - 140: 13946c63 extr w3, w3, w20, #27 - 144: 93c3dbc8 extr x8, x30, x3, #54 - 148: 54000000 b.eq 148 // b.none - 14c: 54fff5a0 b.eq 0 // b.none - 150: 54004300 b.eq 9b0 // b.none - 154: 54000001 b.ne 154 // b.any - 158: 54fff541 b.ne 0 // b.any - 15c: 540042a1 b.ne 9b0 // b.any - 160: 54000002 b.cs 160 // b.hs, b.nlast - 164: 54fff4e2 b.cs 0 // b.hs, b.nlast - 168: 54004242 b.cs 9b0 // b.hs, b.nlast - 16c: 54000002 b.cs 16c // b.hs, b.nlast - 170: 54fff482 b.cs 0 // b.hs, b.nlast - 174: 540041e2 b.cs 9b0 // b.hs, b.nlast - 178: 54000003 b.cc 178 // b.lo, b.ul, b.last - 17c: 54fff423 b.cc 0 // b.lo, b.ul, b.last - 180: 54004183 b.cc 9b0 // b.lo, b.ul, b.last - 184: 54000003 b.cc 184 // b.lo, b.ul, b.last - 188: 54fff3c3 b.cc 0 // b.lo, b.ul, b.last - 18c: 54004123 b.cc 9b0 // b.lo, b.ul, b.last - 190: 54000004 b.mi 190 // b.first - 194: 54fff364 b.mi 0 // b.first - 198: 540040c4 b.mi 9b0 // b.first - 19c: 54000005 b.pl 19c // b.nfrst - 1a0: 54fff305 b.pl 0 // b.nfrst - 1a4: 54004065 b.pl 9b0 // b.nfrst - 1a8: 54000006 b.vs 1a8 - 1ac: 54fff2a6 b.vs 0 - 1b0: 54004006 b.vs 9b0 - 1b4: 54000007 b.vc 1b4 - 1b8: 54fff247 b.vc 0 - 1bc: 54003fa7 b.vc 9b0 - 1c0: 54000008 b.hi 1c0 // b.pmore - 1c4: 54fff1e8 b.hi 0 // b.pmore - 1c8: 54003f48 b.hi 9b0 // b.pmore - 1cc: 54000009 b.ls 1cc // b.plast - 1d0: 54fff189 b.ls 0 // b.plast - 1d4: 54003ee9 b.ls 9b0 // b.plast - 1d8: 5400000a b.ge 1d8 // b.tcont - 1dc: 54fff12a b.ge 0 // b.tcont - 1e0: 54003e8a b.ge 9b0 // b.tcont - 1e4: 5400000b b.lt 1e4 // b.tstop - 1e8: 54fff0cb b.lt 0 // b.tstop - 1ec: 54003e2b b.lt 9b0 // b.tstop - 1f0: 5400000c b.gt 1f0 - 1f4: 54fff06c b.gt 0 - 1f8: 54003dcc b.gt 9b0 - 1fc: 5400000d b.le 1fc - 200: 54fff00d b.le 0 - 204: 54003d6d b.le 9b0 - 208: 5400000e b.al 208 - 20c: 54ffefae b.al 0 - 210: 54003d0e b.al 9b0 - 214: 5400000f b.nv 214 - 218: 54ffef4f b.nv 0 - 21c: 54003caf b.nv 9b0 - 220: d40658e1 svc #0x32c7 - 224: d4014d22 hvc #0xa69 - 228: d4046543 smc #0x232a - 22c: d4273f60 brk #0x39fb - 230: d44cad80 hlt #0x656c - 234: d503201f nop - 238: d69f03e0 eret - 23c: d6bf03e0 drps - 240: d5033fdf isb - 244: d5033e9f dsb st - 248: d50332bf dmb oshst - 24c: d61f0200 br x16 - 250: d63f0280 blr x20 - 254: c80a7d1b stxr w10, x27, [x8] - 258: c800fea1 stlxr w0, x1, [x21] - 25c: c85f7fb1 ldxr x17, [x29] - 260: c85fff9d ldaxr x29, [x28] - 264: c89ffee1 stlr x1, [x23] - 268: c8dffe95 ldar x21, [x20] - 26c: 88167e7b stxr w22, w27, [x19] - 270: 880bfcd0 stlxr w11, w16, [x6] - 274: 885f7c11 ldxr w17, [x0] - 278: 885ffd44 ldaxr w4, [x10] - 27c: 889ffed8 stlr w24, [x22] - 280: 88dffe6a ldar w10, [x19] - 284: 48017fc5 stxrh w1, w5, [x30] - 288: 4808fe2c stlxrh w8, w12, [x17] - 28c: 485f7dc9 ldxrh w9, [x14] - 290: 485ffc27 ldaxrh w7, [x1] - 294: 489ffe05 stlrh w5, [x16] - 298: 48dffd82 ldarh w2, [x12] - 29c: 080a7c6c stxrb w10, w12, [x3] - 2a0: 081cff4e stlxrb w28, w14, [x26] - 2a4: 085f7d5e ldxrb w30, [x10] - 2a8: 085ffeae ldaxrb w14, [x21] - 2ac: 089ffd2d stlrb w13, [x9] - 2b0: 08dfff76 ldarb w22, [x27] - 2b4: c87f4d7c ldxp x28, x19, [x11] - 2b8: c87fcc5e ldaxp x30, x19, [x2] - 2bc: c8220417 stxp w2, x23, x1, [x0] - 2c0: c82cb5f0 stlxp w12, x16, x13, [x15] - 2c4: 887f55b1 ldxp w17, w21, [x13] - 2c8: 887ff90b ldaxp w11, w30, [x8] - 2cc: 88382c2d stxp w24, w13, w11, [x1] - 2d0: 883aedb5 stlxp w26, w21, w27, [x13] - 2d4: f819928b stur x11, [x20, #-103] - 2d8: b803e21c stur w28, [x16, #62] - 2dc: 381f713b sturb w27, [x9, #-9] - 2e0: 781ce322 sturh w2, [x25, #-50] - 2e4: f850f044 ldur x4, [x2, #-241] - 2e8: b85e129e ldur w30, [x20, #-31] - 2ec: 385e92f1 ldurb w17, [x23, #-23] - 2f0: 785ff35d ldurh w29, [x26, #-1] - 2f4: 39801921 ldrsb x1, [x9, #6] - 2f8: 7881318b ldursh x11, [x12, #19] - 2fc: 78dce02b ldursh w11, [x1, #-50] - 300: b8829313 ldursw x19, [x24, #41] - 304: fc45f318 ldur d24, [x24, #95] - 308: bc5d50af ldur s15, [x5, #-43] - 30c: fc001375 stur d21, [x27, #1] - 310: bc1951b7 stur s23, [x13, #-107] - 314: f8008c0b str x11, [x0, #8]! - 318: b801dc03 str w3, [x0, #29]! - 31c: 38009dcb strb w11, [x14, #9]! - 320: 781fdf1d strh w29, [x24, #-3]! - 324: f8570e2d ldr x13, [x17, #-144]! - 328: b85faecc ldr w12, [x22, #-6]! - 32c: 385f6d8d ldrb w13, [x12, #-10]! - 330: 785ebea0 ldrh w0, [x21, #-21]! - 334: 38804cf7 ldrsb x23, [x7, #4]! - 338: 789cbce3 ldrsh x3, [x7, #-53]! - 33c: 78df9cbc ldrsh w28, [x5, #-7]! - 340: b89eed38 ldrsw x24, [x9, #-18]! - 344: fc40cd6e ldr d14, [x11, #12]! - 348: bc5bdd93 ldr s19, [x12, #-67]! - 34c: fc103c14 str d20, [x0, #-253]! - 350: bc040c08 str s8, [x0, #64]! - 354: f81a2784 str x4, [x28], #-94 - 358: b81ca4ec str w12, [x7], #-54 - 35c: 381e855b strb w27, [x10], #-24 - 360: 7801b506 strh w6, [x8], #27 - 364: f853654e ldr x14, [x10], #-202 - 368: b85d74b0 ldr w16, [x5], #-41 - 36c: 384095c2 ldrb w2, [x14], #9 - 370: 785ec5bc ldrh w28, [x13], #-20 - 374: 389e15a9 ldrsb x9, [x13], #-31 - 378: 789dc703 ldrsh x3, [x24], #-36 - 37c: 78c06474 ldrsh w20, [x3], #6 - 380: b89ff667 ldrsw x7, [x19], #-1 - 384: fc57e51e ldr d30, [x8], #-130 - 388: bc4155f9 ldr s25, [x15], #21 - 38c: fc05a6ee str d14, [x23], #90 - 390: bc1df408 str s8, [x0], #-33 - 394: f835da2a str x10, [x17, w21, sxtw #3] - 398: b836d9a4 str w4, [x13, w22, sxtw #2] - 39c: 3833580d strb w13, [x0, w19, uxtw #0] - 3a0: 7826cb6c strh w12, [x27, w6, sxtw] - 3a4: f8706900 ldr x0, [x8, x16] - 3a8: b87ae880 ldr w0, [x4, x26, sxtx] - 3ac: 3865db2e ldrb w14, [x25, w5, sxtw #0] - 3b0: 78714889 ldrh w9, [x4, w17, uxtw] - 3b4: 38a7789b ldrsb x27, [x4, x7, lsl #0] - 3b8: 78beca2f ldrsh x15, [x17, w30, sxtw] - 3bc: 78f6c810 ldrsh w16, [x0, w22, sxtw] - 3c0: b8bef956 ldrsw x22, [x10, x30, sxtx #2] - 3c4: fc6afabd ldr d29, [x21, x10, sxtx #3] - 3c8: bc734963 ldr s3, [x11, w19, uxtw] - 3cc: fc3d5b8d str d13, [x28, w29, uxtw #3] - 3d0: bc25fbb7 str s23, [x29, x5, sxtx #2] - 3d4: f9189d05 str x5, [x8, #12600] - 3d8: b91ecb1d str w29, [x24, #7880] - 3dc: 39187a33 strb w19, [x17, #1566] - 3e0: 791f226d strh w13, [x19, #3984] - 3e4: f95aa2f3 ldr x19, [x23, #13632] - 3e8: b9587bb7 ldr w23, [x29, #6264] - 3ec: 395f7176 ldrb w22, [x11, #2012] - 3f0: 795d9143 ldrh w3, [x10, #3784] - 3f4: 399e7e08 ldrsb x8, [x16, #1951] - 3f8: 799a2697 ldrsh x23, [x20, #3346] - 3fc: 79df3422 ldrsh w2, [x1, #3994] - 400: b99c2624 ldrsw x4, [x17, #7204] - 404: fd5c2374 ldr d20, [x27, #14400] - 408: bd5fa1d9 ldr s25, [x14, #8096] - 40c: fd1d595a str d26, [x10, #15024] - 410: bd1b1869 str s9, [x3, #6936] - 414: 58002cfb ldr x27, 9b0 - 418: 1800000b ldr w11, 418 - 41c: f8945060 prfum pldl1keep, [x3, #-187] - 420: d8000000 prfm pldl1keep, 420 - 424: f8ae6ba0 prfm pldl1keep, [x29, x14] - 428: f99a0080 prfm pldl1keep, [x4, #13312] - 42c: 1a070035 adc w21, w1, w7 - 430: 3a0700a8 adcs w8, w5, w7 - 434: 5a0e0367 sbc w7, w27, w14 - 438: 7a11009b sbcs w27, w4, w17 - 43c: 9a000380 adc x0, x28, x0 - 440: ba1e030c adcs x12, x24, x30 - 444: da0f0320 sbc x0, x25, x15 - 448: fa030301 sbcs x1, x24, x3 - 44c: 0b340b11 add w17, w24, w20, uxtb #2 - 450: 2b2a278d adds w13, w28, w10, uxth #1 - 454: cb22aa0f sub x15, x16, w2, sxth #2 - 458: 6b2d29bd subs w29, w13, w13, uxth #2 - 45c: 8b2cce8c add x12, x20, w12, sxtw #3 - 460: ab2b877e adds x30, x27, w11, sxtb #1 - 464: cb21c8ee sub x14, x7, w1, sxtw #2 - 468: eb3ba47d subs x29, x3, w27, sxth #1 - 46c: 3a4d400e ccmn w0, w13, #0xe, mi // mi = first - 470: 7a5132c6 ccmp w22, w17, #0x6, cc // cc = lo, ul, last - 474: ba5e622e ccmn x17, x30, #0xe, vs - 478: fa53814c ccmp x10, x19, #0xc, hi // hi = pmore - 47c: 3a52d8c2 ccmn w6, #0x12, #0x2, le - 480: 7a4d8924 ccmp w9, #0xd, #0x4, hi // hi = pmore - 484: ba4b3aab ccmn x21, #0xb, #0xb, cc // cc = lo, ul, last - 488: fa4d7882 ccmp x4, #0xd, #0x2, vc - 48c: 1a96804c csel w12, w2, w22, hi // hi = pmore - 490: 1a912618 csinc w24, w16, w17, cs // cs = hs, nlast - 494: 5a90b0e6 csinv w6, w7, w16, lt // lt = tstop - 498: 5a96976b csneg w11, w27, w22, ls // ls = plast - 49c: 9a9db06a csel x10, x3, x29, lt // lt = tstop - 4a0: 9a9b374c csinc x12, x26, x27, cc // cc = lo, ul, last - 4a4: da95c14f csinv x15, x10, x21, gt - 4a8: da89c6fe csneg x30, x23, x9, gt - 4ac: 5ac0015e rbit w30, w10 - 4b0: 5ac005fd rev16 w29, w15 - 4b4: 5ac00bdd rev w29, w30 - 4b8: 5ac012b9 clz w25, w21 - 4bc: 5ac01404 cls w4, w0 - 4c0: dac002b1 rbit x17, x21 - 4c4: dac0061d rev16 x29, x16 - 4c8: dac00a95 rev32 x21, x20 - 4cc: dac00e66 rev x6, x19 - 4d0: dac0107e clz x30, x3 - 4d4: dac01675 cls x21, x19 - 4d8: 1ac00b0b udiv w11, w24, w0 - 4dc: 1ace0f3b sdiv w27, w25, w14 - 4e0: 1ad121c3 lsl w3, w14, w17 - 4e4: 1ad825e7 lsr w7, w15, w24 - 4e8: 1ad92a3c asr w28, w17, w25 - 4ec: 1adc2f42 ror w2, w26, w28 - 4f0: 9ada0b25 udiv x5, x25, x26 - 4f4: 9ad10e1b sdiv x27, x16, x17 - 4f8: 9acc22a6 lsl x6, x21, x12 - 4fc: 9acc2480 lsr x0, x4, x12 - 500: 9adc2a3b asr x27, x17, x28 - 504: 9ad12c5c ror x28, x2, x17 - 508: 9bce7dea umulh x10, x15, x14 - 50c: 9b597c6e smulh x14, x3, x25 - 510: 1b0e166f madd w15, w19, w14, w5 - 514: 1b1ae490 msub w16, w4, w26, w25 - 518: 9b023044 madd x4, x2, x2, x12 - 51c: 9b089e3d msub x29, x17, x8, x7 - 520: 9b391083 smaddl x3, w4, w25, x4 - 524: 9b24c73a smsubl x26, w25, w4, x17 - 528: 9bb15f40 umaddl x0, w26, w17, x23 - 52c: 9bbcc6af umsubl x15, w21, w28, x17 - 530: 1e23095b fmul s27, s10, s3 - 534: 1e3918e0 fdiv s0, s7, s25 - 538: 1e2f28c9 fadd s9, s6, s15 - 53c: 1e2a39fd fsub s29, s15, s10 - 540: 1e270a22 fmul s2, s17, s7 - 544: 1e77096b fmul d11, d11, d23 - 548: 1e771ba7 fdiv d7, d29, d23 - 54c: 1e6b2b6e fadd d14, d27, d11 - 550: 1e78388b fsub d11, d4, d24 - 554: 1e6e09ec fmul d12, d15, d14 - 558: 1f1c3574 fmadd s20, s11, s28, s13 - 55c: 1f17f98b fmsub s11, s12, s23, s30 - 560: 1f2935da fnmadd s26, s14, s9, s13 - 564: 1f2574ea fnmadd s10, s7, s5, s29 - 568: 1f4b306f fmadd d15, d3, d11, d12 - 56c: 1f5ec7cf fmsub d15, d30, d30, d17 - 570: 1f6f3e93 fnmadd d19, d20, d15, d15 - 574: 1f6226a9 fnmadd d9, d21, d2, d9 - 578: 1e2040fb fmov s27, s7 - 57c: 1e20c3dd fabs s29, s30 - 580: 1e214031 fneg s17, s1 - 584: 1e21c0c2 fsqrt s2, s6 - 588: 1e22c06a fcvt d10, s3 - 58c: 1e604178 fmov d24, d11 - 590: 1e60c027 fabs d7, d1 - 594: 1e61400b fneg d11, d0 - 598: 1e61c223 fsqrt d3, d17 - 59c: 1e6240dc fcvt s28, d6 - 5a0: 1e3800d6 fcvtzs w22, s6 - 5a4: 9e380360 fcvtzs x0, s27 - 5a8: 1e78005a fcvtzs w26, d2 - 5ac: 9e7800e5 fcvtzs x5, d7 - 5b0: 1e22017c scvtf s28, w11 - 5b4: 9e2201b9 scvtf s25, x13 - 5b8: 1e6202eb scvtf d11, w23 - 5bc: 9e620113 scvtf d19, x8 - 5c0: 1e2602b1 fmov w17, s21 - 5c4: 9e660299 fmov x25, d20 - 5c8: 1e270233 fmov s19, w17 - 5cc: 9e6703a2 fmov d2, x29 - 5d0: 1e2822c0 fcmp s22, s8 - 5d4: 1e7322a0 fcmp d21, d19 - 5d8: 1e202288 fcmp s20, #0.0 - 5dc: 1e602168 fcmp d11, #0.0 - 5e0: 293c19f4 stp w20, w6, [x15, #-32] - 5e4: 2966387b ldp w27, w14, [x3, #-208] - 5e8: 69762971 ldpsw x17, x10, [x11, #-80] - 5ec: a9041dc7 stp x7, x7, [x14, #64] - 5f0: a9475c0c ldp x12, x23, [x0, #112] - 5f4: 29b61ccd stp w13, w7, [x6, #-80]! - 5f8: 29ee405e ldp w30, w16, [x2, #-144]! - 5fc: 69ee0744 ldpsw x4, x1, [x26, #-144]! - 600: a9843977 stp x23, x14, [x11, #64]! - 604: a9f46ebd ldp x29, x27, [x21, #-192]! - 608: 28ba16b6 stp w22, w5, [x21], #-48 - 60c: 28fc44db ldp w27, w17, [x6], #-32 - 610: 68f61831 ldpsw x17, x6, [x1], #-80 - 614: a8b352ad stp x13, x20, [x21], #-208 - 618: a8c56d5e ldp x30, x27, [x10], #80 - 61c: 28024565 stnp w5, w17, [x11, #16] - 620: 2874134e ldnp w14, w4, [x26, #-96] - 624: a8027597 stnp x23, x29, [x12, #32] - 628: a87b1aa0 ldnp x0, x6, [x21, #-80] - 62c: 0c40734f ld1 {v15.8b}, [x26] - 630: 4cdfa177 ld1 {v23.16b, v24.16b}, [x11], #32 - 634: 0cc76ee8 ld1 {v8.1d-v10.1d}, [x23], x7 - 638: 4cdf2733 ld1 {v19.8h-v22.8h}, [x25], #64 - 63c: 0d40c23d ld1r {v29.8b}, [x17] - 640: 4ddfcaf8 ld1r {v24.4s}, [x23], #4 - 644: 0dd9ccaa ld1r {v10.1d}, [x5], x25 - 648: 4c408d51 ld2 {v17.2d, v18.2d}, [x10] - 64c: 0cdf85ec ld2 {v12.4h, v13.4h}, [x15], #16 - 650: 4d60c239 ld2r {v25.16b, v26.16b}, [x17] - 654: 0dffcbc1 ld2r {v1.2s, v2.2s}, [x30], #8 - 658: 4de9ce30 ld2r {v16.2d, v17.2d}, [x17], x9 - 65c: 4cc24999 ld3 {v25.4s-v27.4s}, [x12], x2 - 660: 0c404a7a ld3 {v26.2s-v28.2s}, [x19] - 664: 4d40e6af ld3r {v15.8h-v17.8h}, [x21] - 668: 4ddfe9b9 ld3r {v25.4s-v27.4s}, [x13], #12 - 66c: 0dddef8e ld3r {v14.1d-v16.1d}, [x28], x29 - 670: 4cdf07b1 ld4 {v17.8h-v20.8h}, [x29], #64 - 674: 0cc000fb ld4 {v27.8b-v30.8b}, [x7], x0 - 678: 0d60e238 ld4r {v24.8b-v27.8b}, [x17] - 67c: 0dffe740 ld4r {v0.4h-v3.4h}, [x26], #8 - 680: 0de2eb2c ld4r {v12.2s-v15.2s}, [x25], x2 - 684: ce648376 sha512h q22, q27, v4.2d - 688: ce6184c7 sha512h2 q7, q6, v1.2d - 68c: cec081fa sha512su0 v26.2d, v15.2d - 690: ce6d89a2 sha512su1 v2.2d, v13.2d, v13.2d - 694: ba5fd3e3 ccmn xzr, xzr, #0x3, le - 698: 3a5f03e5 ccmn wzr, wzr, #0x5, eq // eq = none - 69c: fa411be4 ccmp xzr, #0x1, #0x4, ne // ne = any - 6a0: 7a42cbe2 ccmp wzr, #0x2, #0x2, gt - 6a4: 93df03ff ror xzr, xzr, #0 - 6a8: c820ffff stlxp w0, xzr, xzr, [sp] - 6ac: 8822fc7f stlxp w2, wzr, wzr, [x3] - 6b0: c8247cbf stxp w4, xzr, xzr, [x5] - 6b4: 88267fff stxp w6, wzr, wzr, [sp] - 6b8: 4e010fe0 dup v0.16b, wzr - 6bc: 4e081fe1 mov v1.d[0], xzr - 6c0: 4e0c1fe1 mov v1.s[1], wzr - 6c4: 4e0a1fe1 mov v1.h[2], wzr - 6c8: 4e071fe1 mov v1.b[3], wzr - 6cc: 4cc0ac3f ld1 {v31.2d, v0.2d}, [x1], x0 - 6d0: 05a08020 mov z0.s, p0/m, s1 - 6d4: 04b0e3e0 incw x0 - 6d8: 0470e7e1 dech x1 - 6dc: 042f9c20 lsl z0.b, z1.b, #7 - 6e0: 043f9c35 lsl z21.h, z1.h, #15 - 6e4: 047f9c20 lsl z0.s, z1.s, #31 - 6e8: 04ff9c20 lsl z0.d, z1.d, #63 - 6ec: 04299420 lsr z0.b, z1.b, #7 - 6f0: 04319160 asr z0.h, z11.h, #15 - 6f4: 0461943e lsr z30.s, z1.s, #31 - 6f8: 04a19020 asr z0.d, z1.d, #63 - 6fc: 042053ff addvl sp, x0, #31 - 700: 047f5401 addpl x1, sp, #-32 - 704: 25208028 cntp x8, p0, p1.b - 708: 2538cfe0 mov z0.b, #127 - 70c: 2578d001 mov z1.h, #-128 - 710: 25b8efe2 mov z2.s, #32512 - 714: 25f8f007 mov z7.d, #-32768 - 718: a400a3e0 ld1b {z0.b}, p0/z, [sp] - 71c: a4a8a7ea ld1h {z10.h}, p1/z, [sp, #-8, mul vl] - 720: a547a814 ld1w {z20.s}, p2/z, [x0, #7, mul vl] - 724: a4084ffe ld1b {z30.b}, p3/z, [sp, x8] - 728: a55c53e0 ld1w {z0.s}, p4/z, [sp, x28, lsl #2] - 72c: a5e1540b ld1d {z11.d}, p5/z, [x0, x1, lsl #3] - 730: e400fbf6 st1b {z22.b}, p6, [sp] - 734: e408ffff st1b {z31.b}, p7, [sp, #-8, mul vl] - 738: e547e400 st1w {z0.s}, p1, [x0, #7, mul vl] - 73c: e4014be0 st1b {z0.b}, p2, [sp, x1] - 740: e4a84fe0 st1h {z0.h}, p3, [sp, x8, lsl #1] - 744: e5f15000 st1d {z0.d}, p4, [x0, x17, lsl #3] - 748: 858043e0 ldr z0, [sp] - 74c: 85a043ff ldr z31, [sp, #-256, mul vl] - 750: e59f5d08 str z8, [x8, #255, mul vl] - 754: 1e601000 fmov d0, #2.000000000000000000e+00 - 758: 1e603000 fmov d0, #2.125000000000000000e+00 - 75c: 1e621000 fmov d0, #4.000000000000000000e+00 - 760: 1e623000 fmov d0, #4.250000000000000000e+00 - 764: 1e641000 fmov d0, #8.000000000000000000e+00 - 768: 1e643000 fmov d0, #8.500000000000000000e+00 - 76c: 1e661000 fmov d0, #1.600000000000000000e+01 - 770: 1e663000 fmov d0, #1.700000000000000000e+01 - 774: 1e681000 fmov d0, #1.250000000000000000e-01 - 778: 1e683000 fmov d0, #1.328125000000000000e-01 - 77c: 1e6a1000 fmov d0, #2.500000000000000000e-01 - 780: 1e6a3000 fmov d0, #2.656250000000000000e-01 - 784: 1e6c1000 fmov d0, #5.000000000000000000e-01 - 788: 1e6c3000 fmov d0, #5.312500000000000000e-01 - 78c: 1e6e1000 fmov d0, #1.000000000000000000e+00 - 790: 1e6e3000 fmov d0, #1.062500000000000000e+00 - 794: 1e701000 fmov d0, #-2.000000000000000000e+00 - 798: 1e703000 fmov d0, #-2.125000000000000000e+00 - 79c: 1e721000 fmov d0, #-4.000000000000000000e+00 - 7a0: 1e723000 fmov d0, #-4.250000000000000000e+00 - 7a4: 1e741000 fmov d0, #-8.000000000000000000e+00 - 7a8: 1e743000 fmov d0, #-8.500000000000000000e+00 - 7ac: 1e761000 fmov d0, #-1.600000000000000000e+01 - 7b0: 1e763000 fmov d0, #-1.700000000000000000e+01 - 7b4: 1e781000 fmov d0, #-1.250000000000000000e-01 - 7b8: 1e783000 fmov d0, #-1.328125000000000000e-01 - 7bc: 1e7a1000 fmov d0, #-2.500000000000000000e-01 - 7c0: 1e7a3000 fmov d0, #-2.656250000000000000e-01 - 7c4: 1e7c1000 fmov d0, #-5.000000000000000000e-01 - 7c8: 1e7c3000 fmov d0, #-5.312500000000000000e-01 - 7cc: 1e7e1000 fmov d0, #-1.000000000000000000e+00 - 7d0: 1e7e3000 fmov d0, #-1.062500000000000000e+00 - 7d4: f8388098 swp x24, x24, [x4] - 7d8: f8340010 ldadd x20, x16, [x0] - 7dc: f8241175 ldclr x4, x21, [x11] - 7e0: f83e22d0 ldeor x30, x16, [x22] - 7e4: f82432ef ldset x4, x15, [x23] - 7e8: f83a5186 ldsmin x26, x6, [x12] - 7ec: f82f41ee ldsmax x15, x14, [x15] - 7f0: f82973b9 ldumin x9, x25, [x29] - 7f4: f82b6194 ldumax x11, x20, [x12] - 7f8: f8b08216 swpa x16, x22, [x16] - 7fc: f8b50358 ldadda x21, x24, [x26] - 800: f8a61206 ldclra x6, x6, [x16] - 804: f8b02219 ldeora x16, x25, [x16] - 808: f8bc3218 ldseta x28, x24, [x16] - 80c: f8ba514f ldsmina x26, x15, [x10] - 810: f8ad428e ldsmaxa x13, x14, [x20] - 814: f8a173d7 ldumina x1, x23, [x30] - 818: f8ae60c2 ldumaxa x14, x2, [x6] - 81c: f8e38328 swpal x3, x8, [x25] - 820: f8e003db ldaddal x0, x27, [x30] - 824: f8e513c5 ldclral x5, x5, [x30] - 828: f8eb2019 ldeoral x11, x25, [x0] - 82c: f8ff3260 ldsetal xzr, x0, [x19] - 830: f8fd513a ldsminal x29, x26, [x9] - 834: f8fa41ec ldsmaxal x26, x12, [x15] - 838: f8eb71eb lduminal x11, x11, [x15] - 83c: f8f96316 ldumaxal x25, x22, [x24] - 840: f8608171 swpl x0, x17, [x11] - 844: f86600dd ldaddl x6, x29, [x6] - 848: f86512a5 ldclrl x5, x5, [x21] - 84c: f87321f0 ldeorl x19, x16, [x15] - 850: f87e339b ldsetl x30, x27, [x28] - 854: f861503c ldsminl x1, x28, [x1] - 858: f874421d ldsmaxl x20, x29, [x16] - 85c: f86d73aa lduminl x13, x10, [x29] - 860: f87d62d3 ldumaxl x29, x19, [x22] - 864: b82a83e4 swp w10, w4, [sp] - 868: b83503e8 ldadd w21, w8, [sp] - 86c: b833138a ldclr w19, w10, [x28] - 870: b82220b9 ldeor w2, w25, [x5] - 874: b82332c8 ldset w3, w8, [x22] - 878: b83350ad ldsmin w19, w13, [x5] - 87c: b83d42b8 ldsmax w29, w24, [x21] - 880: b83a7078 ldumin w26, w24, [x3] - 884: b83862fa ldumax w24, w26, [x23] - 888: b8af8075 swpa w15, w21, [x3] - 88c: b8b80328 ldadda w24, w8, [x25] - 890: b8b41230 ldclra w20, w16, [x17] - 894: b8a22001 ldeora w2, w1, [x0] - 898: b8b83064 ldseta w24, w4, [x3] - 89c: b8ac539f ldsmina w12, wzr, [x28] - 8a0: b8aa405a ldsmaxa w10, w26, [x2] - 8a4: b8ac73f0 ldumina w12, w16, [sp] - 8a8: b8a163ad ldumaxa w1, w13, [x29] - 8ac: b8e08193 swpal w0, w19, [x12] - 8b0: b8f101b6 ldaddal w17, w22, [x13] - 8b4: b8fc13fe ldclral w28, w30, [sp] - 8b8: b8e1239a ldeoral w1, w26, [x28] - 8bc: b8e4309e ldsetal w4, w30, [x4] - 8c0: b8e6535e ldsminal w6, w30, [x26] - 8c4: b8f04109 ldsmaxal w16, w9, [x8] - 8c8: b8ec7280 lduminal w12, w0, [x20] - 8cc: b8e16058 ldumaxal w1, w24, [x2] - 8d0: b8608309 swpl w0, w9, [x24] - 8d4: b87a03d0 ldaddl w26, w16, [x30] - 8d8: b86312ea ldclrl w3, w10, [x23] - 8dc: b86a21e4 ldeorl w10, w4, [x15] - 8e0: b862310b ldsetl w2, w11, [x8] - 8e4: b86a522f ldsminl w10, w15, [x17] - 8e8: b862418a ldsmaxl w2, w10, [x12] - 8ec: b86c71af lduminl w12, w15, [x13] - 8f0: b8626287 ldumaxl w2, w7, [x20] - 8f4: 042401f9 add z25.b, z15.b, z4.b - 8f8: 04b10564 sub z4.s, z11.s, z17.s - 8fc: 65ca0230 fadd z16.d, z17.d, z10.d - 900: 65d90996 fmul z22.d, z12.d, z25.d - 904: 65ca05dc fsub z28.d, z14.d, z10.d - 908: 0456afc1 abs z1.h, p3/m, z30.h - 90c: 0400044f add z15.b, p1/m, z15.b, z2.b - 910: 0490920d asr z13.s, p4/m, z13.s, z16.s - 914: 04daa163 cnt z3.d, p0/m, z11.d - 918: 04d389c5 lsl z5.d, p2/m, z5.d, z14.d - 91c: 0411829d lsr z29.b, p0/m, z29.b, z20.b - 920: 04901774 mul z20.s, p5/m, z20.s, z27.s - 924: 0417b89a neg z26.b, p6/m, z4.b - 928: 041eb3d6 not z22.b, p4/m, z30.b - 92c: 04480b6b smax z11.h, p2/m, z11.h, z27.h - 930: 048a17dc smin z28.s, p5/m, z28.s, z30.s - 934: 048105be sub z30.s, p1/m, z30.s, z13.s - 938: 04dcb35e fabs z30.d, p4/m, z26.d - 93c: 65808d6f fadd z15.s, p3/m, z15.s, z11.s - 940: 65cd9e06 fdiv z6.d, p7/m, z6.d, z16.d - 944: 65869cfb fmax z27.s, p7/m, z27.s, z7.s - 948: 65c78893 fmin z19.d, p2/m, z19.d, z4.d - 94c: 658292d1 fmul z17.s, p4/m, z17.s, z22.s - 950: 04ddaebc fneg z28.d, p3/m, z21.d - 954: 6582b451 frintm z17.s, p5/m, z2.s - 958: 6580ade6 frintn z6.s, p3/m, z15.s - 95c: 65c1b42c frintp z12.d, p5/m, z1.d - 960: 658da631 fsqrt z17.s, p1/m, z17.s - 964: 658195af fsub z15.s, p5/m, z15.s, z13.s - 968: 65eb1f74 fmla z20.d, p7/m, z27.d, z11.d - 96c: 65f723c3 fmls z3.d, p0/m, z30.d, z23.d - 970: 65ba4b71 fnmla z17.s, p2/m, z27.s, z26.s - 974: 65fe76c6 fnmls z6.d, p5/m, z22.d, z30.d - 978: 04515f42 mla z2.h, p7/m, z26.h, z17.h - 97c: 04117056 mls z22.b, p4/m, z2.b, z17.b - 980: 04363338 and z24.d, z25.d, z22.d - 984: 04a33191 eor z17.d, z12.d, z3.d - 988: 0470339d orr z29.d, z28.d, z16.d - 98c: 049a2b86 andv s6, p2, z28.s - 990: 045824e7 orv h7, p1, z7.h - 994: 04193509 eorv b9, p5, z8.b - 998: 040837db smaxv b27, p5, z30.b - 99c: 044a221a sminv h26, p0, z16.h - 9a0: 65c73903 fminv d3, p6, z8.d - 9a4: 65c63b55 fmaxv d21, p6, z26.d - 9a8: 65982096 fadda s22, p0, s22, z4.s - 9ac: 04412071 uaddv d17, p0, z3.h - */ +*/ static const unsigned int insns[] = { @@ -1486,30 +1007,30 @@ Disassembly of section .text: 0x9101a1a0, 0xb10a5cc8, 0xd10810aa, 0xf10fd061, 0x120cb166, 0x321764bc, 0x52174681, 0x720c0227, 0x9241018e, 0xb25a2969, 0xd278b411, 0xf26aad01, - 0x14000000, 0x17ffffd7, 0x14000242, 0x94000000, - 0x97ffffd4, 0x9400023f, 0x3400000a, 0x34fffa2a, - 0x3400478a, 0x35000008, 0x35fff9c8, 0x35004728, - 0xb400000b, 0xb4fff96b, 0xb40046cb, 0xb500001d, - 0xb5fff91d, 0xb500467d, 0x10000013, 0x10fff8b3, - 0x10004613, 0x90000013, 0x36300016, 0x3637f836, - 0x36304596, 0x3758000c, 0x375ff7cc, 0x3758452c, + 0x14000000, 0x17ffffd7, 0x140002cd, 0x94000000, + 0x97ffffd4, 0x940002ca, 0x3400000a, 0x34fffa2a, + 0x340058ea, 0x35000008, 0x35fff9c8, 0x35005888, + 0xb400000b, 0xb4fff96b, 0xb400582b, 0xb500001d, + 0xb5fff91d, 0xb50057dd, 0x10000013, 0x10fff8b3, + 0x10005773, 0x90000013, 0x36300016, 0x3637f836, + 0x363056f6, 0x3758000c, 0x375ff7cc, 0x3758568c, 0x128313a0, 0x528a32c7, 0x7289173b, 0x92ab3acc, 0xd2a0bf94, 0xf2c285e8, 0x9358722f, 0x330e652f, 0x53067f3b, 0x93577c53, 0xb34a1aac, 0xd35a4016, 0x13946c63, 0x93c3dbc8, 0x54000000, 0x54fff5a0, - 0x54004300, 0x54000001, 0x54fff541, 0x540042a1, - 0x54000002, 0x54fff4e2, 0x54004242, 0x54000002, - 0x54fff482, 0x540041e2, 0x54000003, 0x54fff423, - 0x54004183, 0x54000003, 0x54fff3c3, 0x54004123, - 0x54000004, 0x54fff364, 0x540040c4, 0x54000005, - 0x54fff305, 0x54004065, 0x54000006, 0x54fff2a6, - 0x54004006, 0x54000007, 0x54fff247, 0x54003fa7, - 0x54000008, 0x54fff1e8, 0x54003f48, 0x54000009, - 0x54fff189, 0x54003ee9, 0x5400000a, 0x54fff12a, - 0x54003e8a, 0x5400000b, 0x54fff0cb, 0x54003e2b, - 0x5400000c, 0x54fff06c, 0x54003dcc, 0x5400000d, - 0x54fff00d, 0x54003d6d, 0x5400000e, 0x54ffefae, - 0x54003d0e, 0x5400000f, 0x54ffef4f, 0x54003caf, + 0x54005460, 0x54000001, 0x54fff541, 0x54005401, + 0x54000002, 0x54fff4e2, 0x540053a2, 0x54000002, + 0x54fff482, 0x54005342, 0x54000003, 0x54fff423, + 0x540052e3, 0x54000003, 0x54fff3c3, 0x54005283, + 0x54000004, 0x54fff364, 0x54005224, 0x54000005, + 0x54fff305, 0x540051c5, 0x54000006, 0x54fff2a6, + 0x54005166, 0x54000007, 0x54fff247, 0x54005107, + 0x54000008, 0x54fff1e8, 0x540050a8, 0x54000009, + 0x54fff189, 0x54005049, 0x5400000a, 0x54fff12a, + 0x54004fea, 0x5400000b, 0x54fff0cb, 0x54004f8b, + 0x5400000c, 0x54fff06c, 0x54004f2c, 0x5400000d, + 0x54fff00d, 0x54004ecd, 0x5400000e, 0x54ffefae, + 0x54004e6e, 0x5400000f, 0x54ffef4f, 0x54004e0f, 0xd40658e1, 0xd4014d22, 0xd4046543, 0xd4273f60, 0xd44cad80, 0xd503201f, 0xd69f03e0, 0xd6bf03e0, 0xd5033fdf, 0xd5033e9f, 0xd50332bf, 0xd61f0200, @@ -1541,7 +1062,7 @@ Disassembly of section .text: 0x791f226d, 0xf95aa2f3, 0xb9587bb7, 0x395f7176, 0x795d9143, 0x399e7e08, 0x799a2697, 0x79df3422, 0xb99c2624, 0xfd5c2374, 0xbd5fa1d9, 0xfd1d595a, - 0xbd1b1869, 0x58002cfb, 0x1800000b, 0xf8945060, + 0xbd1b1869, 0x58003e5b, 0x1800000b, 0xf8945060, 0xd8000000, 0xf8ae6ba0, 0xf99a0080, 0x1a070035, 0x3a0700a8, 0x5a0e0367, 0x7a11009b, 0x9a000380, 0xba1e030c, 0xda0f0320, 0xfa030301, 0x0b340b11, @@ -1580,58 +1101,92 @@ Disassembly of section .text: 0x4d60c239, 0x0dffcbc1, 0x4de9ce30, 0x4cc24999, 0x0c404a7a, 0x4d40e6af, 0x4ddfe9b9, 0x0dddef8e, 0x4cdf07b1, 0x0cc000fb, 0x0d60e238, 0x0dffe740, - 0x0de2eb2c, 0xce648376, 0xce6184c7, 0xcec081fa, - 0xce6d89a2, 0xba5fd3e3, 0x3a5f03e5, 0xfa411be4, - 0x7a42cbe2, 0x93df03ff, 0xc820ffff, 0x8822fc7f, - 0xc8247cbf, 0x88267fff, 0x4e010fe0, 0x4e081fe1, - 0x4e0c1fe1, 0x4e0a1fe1, 0x4e071fe1, 0x4cc0ac3f, - 0x05a08020, 0x04b0e3e0, 0x0470e7e1, 0x042f9c20, - 0x043f9c35, 0x047f9c20, 0x04ff9c20, 0x04299420, - 0x04319160, 0x0461943e, 0x04a19020, 0x042053ff, - 0x047f5401, 0x25208028, 0x2538cfe0, 0x2578d001, - 0x25b8efe2, 0x25f8f007, 0xa400a3e0, 0xa4a8a7ea, - 0xa547a814, 0xa4084ffe, 0xa55c53e0, 0xa5e1540b, - 0xe400fbf6, 0xe408ffff, 0xe547e400, 0xe4014be0, - 0xe4a84fe0, 0xe5f15000, 0x858043e0, 0x85a043ff, - 0xe59f5d08, 0x1e601000, 0x1e603000, 0x1e621000, - 0x1e623000, 0x1e641000, 0x1e643000, 0x1e661000, - 0x1e663000, 0x1e681000, 0x1e683000, 0x1e6a1000, - 0x1e6a3000, 0x1e6c1000, 0x1e6c3000, 0x1e6e1000, - 0x1e6e3000, 0x1e701000, 0x1e703000, 0x1e721000, - 0x1e723000, 0x1e741000, 0x1e743000, 0x1e761000, - 0x1e763000, 0x1e781000, 0x1e783000, 0x1e7a1000, - 0x1e7a3000, 0x1e7c1000, 0x1e7c3000, 0x1e7e1000, - 0x1e7e3000, 0xf8388098, 0xf8340010, 0xf8241175, - 0xf83e22d0, 0xf82432ef, 0xf83a5186, 0xf82f41ee, - 0xf82973b9, 0xf82b6194, 0xf8b08216, 0xf8b50358, - 0xf8a61206, 0xf8b02219, 0xf8bc3218, 0xf8ba514f, - 0xf8ad428e, 0xf8a173d7, 0xf8ae60c2, 0xf8e38328, - 0xf8e003db, 0xf8e513c5, 0xf8eb2019, 0xf8ff3260, - 0xf8fd513a, 0xf8fa41ec, 0xf8eb71eb, 0xf8f96316, - 0xf8608171, 0xf86600dd, 0xf86512a5, 0xf87321f0, - 0xf87e339b, 0xf861503c, 0xf874421d, 0xf86d73aa, - 0xf87d62d3, 0xb82a83e4, 0xb83503e8, 0xb833138a, - 0xb82220b9, 0xb82332c8, 0xb83350ad, 0xb83d42b8, - 0xb83a7078, 0xb83862fa, 0xb8af8075, 0xb8b80328, - 0xb8b41230, 0xb8a22001, 0xb8b83064, 0xb8ac539f, - 0xb8aa405a, 0xb8ac73f0, 0xb8a163ad, 0xb8e08193, - 0xb8f101b6, 0xb8fc13fe, 0xb8e1239a, 0xb8e4309e, - 0xb8e6535e, 0xb8f04109, 0xb8ec7280, 0xb8e16058, - 0xb8608309, 0xb87a03d0, 0xb86312ea, 0xb86a21e4, - 0xb862310b, 0xb86a522f, 0xb862418a, 0xb86c71af, - 0xb8626287, 0x042401f9, 0x04b10564, 0x65ca0230, - 0x65d90996, 0x65ca05dc, 0x0456afc1, 0x0400044f, - 0x0490920d, 0x04daa163, 0x04d389c5, 0x0411829d, - 0x04901774, 0x0417b89a, 0x041eb3d6, 0x04480b6b, - 0x048a17dc, 0x048105be, 0x04dcb35e, 0x65808d6f, - 0x65cd9e06, 0x65869cfb, 0x65c78893, 0x658292d1, - 0x04ddaebc, 0x6582b451, 0x6580ade6, 0x65c1b42c, - 0x658da631, 0x658195af, 0x65eb1f74, 0x65f723c3, - 0x65ba4b71, 0x65fe76c6, 0x04515f42, 0x04117056, - 0x04363338, 0x04a33191, 0x0470339d, 0x049a2b86, - 0x045824e7, 0x04193509, 0x040837db, 0x044a221a, - 0x65c73903, 0x65c63b55, 0x65982096, 0x04412071, - + 0x0de2eb2c, 0x0e31baf6, 0x4e31bb9b, 0x0e71b8a4, + 0x4e71b907, 0x4eb1b8e6, 0x0e30a841, 0x4e30ab7a, + 0x0e70aa0f, 0x4e70a862, 0x4eb0a9cd, 0x6e30f9cd, + 0x0e31ab38, 0x4e31ab17, 0x0e71a8a4, 0x4e71aa93, + 0x4eb1aa0f, 0x6eb0f820, 0x0e20b8a4, 0x4e20bab4, + 0x0e60b98b, 0x4e60bbdd, 0x0ea0ba0f, 0x4ea0bad5, + 0x4ee0b8a4, 0x0ea0f9ee, 0x4ea0faf6, 0x4ee0fb59, + 0x2ea0f8e6, 0x6ea0f9ac, 0x6ee0f9ee, 0x2ea1f9cd, + 0x6ea1f9ee, 0x6ee1f949, 0x2e205b59, 0x6e205bbc, + 0x0e2c1d6a, 0x4e351e93, 0x0ead1d8b, 0x4eb31e51, + 0x2e371ed5, 0x6e311e0f, 0x0e3686b4, 0x4e398717, + 0x0e7c877a, 0x4e6784c5, 0x0ea884e6, 0x4eb1860f, + 0x4ef1860f, 0x0e3bd759, 0x4e32d630, 0x4e7dd79b, + 0x2e3a8738, 0x6e31860f, 0x2e7b8759, 0x6e7085ee, + 0x2eac856a, 0x6eaf85cd, 0x6ef085ee, 0x0eb6d6b4, + 0x4ea3d441, 0x4ef8d6f6, 0x0e209ffe, 0x4e309dee, + 0x0e649c62, 0x4e689ce6, 0x0ea59c83, 0x4ea99d07, + 0x2e3adf38, 0x6e22dc20, 0x6e7ddf9b, 0x0e7f97dd, + 0x4e6794c5, 0x0ea794c5, 0x4ebf97dd, 0x0e2dcd8b, + 0x4e3bcf59, 0x4e62cc20, 0x2e6097fe, 0x6e629420, + 0x2eb39651, 0x6ebe97bc, 0x0ebbcf59, 0x4eabcd49, + 0x4efbcf59, 0x2e2efdac, 0x6e31fe0f, 0x6e6dfd8b, + 0x0e2c656a, 0x4e336651, 0x0e7a6738, 0x4e7766d5, + 0x0eb96717, 0x4ea26420, 0x0e32f630, 0x4e2cf56a, + 0x4e68f4e6, 0x0e3e6fbc, 0x4e286ce6, 0x0e676cc5, + 0x4e676cc5, 0x0eb66eb4, 0x4eb36e51, 0x0eb1f60f, + 0x4eb3f651, 0x4efff7dd, 0x2e3c8f7a, 0x6e3e8fbc, + 0x2e638c41, 0x6e7d8f9b, 0x2ea28c20, 0x6eb68eb4, + 0x6efe8fbc, 0x0e31e60f, 0x4e2ee5ac, 0x4e6ce56a, + 0x0e3e37bc, 0x4e3e37bc, 0x0e753693, 0x4e7836f6, + 0x0eac356a, 0x4ea634a4, 0x4ee037fe, 0x2eb6e6b4, + 0x6eaae528, 0x6ee0e7fe, 0x0e333e51, 0x4e2c3d6a, + 0x0e7d3f9b, 0x4e643c62, 0x0eba3f38, 0x4ea63ca4, + 0x4ee53c83, 0x2e2ae528, 0x6e38e6f6, 0x6e73e651, + 0xba5fd3e3, 0x3a5f03e5, 0xfa411be4, 0x7a42cbe2, + 0x93df03ff, 0xc820ffff, 0x8822fc7f, 0xc8247cbf, + 0x88267fff, 0x4e010fe0, 0x4e081fe1, 0x4e0c1fe1, + 0x4e0a1fe1, 0x4e071fe1, 0x4cc0ac3f, 0x05a08020, + 0x04b0e3e0, 0x0470e7e1, 0x042f9c20, 0x043f9c35, + 0x047f9c20, 0x04ff9c20, 0x04299420, 0x04319160, + 0x0461943e, 0x04a19020, 0x042053ff, 0x047f5401, + 0x25208028, 0x2538cfe0, 0x2578d001, 0x25b8efe2, + 0x25f8f007, 0xa400a3e0, 0xa4a8a7ea, 0xa547a814, + 0xa4084ffe, 0xa55c53e0, 0xa5e1540b, 0xe400fbf6, + 0xe408ffff, 0xe547e400, 0xe4014be0, 0xe4a84fe0, + 0xe5f15000, 0x858043e0, 0x85a043ff, 0xe59f5d08, + 0x1e601000, 0x1e603000, 0x1e621000, 0x1e623000, + 0x1e641000, 0x1e643000, 0x1e661000, 0x1e663000, + 0x1e681000, 0x1e683000, 0x1e6a1000, 0x1e6a3000, + 0x1e6c1000, 0x1e6c3000, 0x1e6e1000, 0x1e6e3000, + 0x1e701000, 0x1e703000, 0x1e721000, 0x1e723000, + 0x1e741000, 0x1e743000, 0x1e761000, 0x1e763000, + 0x1e781000, 0x1e783000, 0x1e7a1000, 0x1e7a3000, + 0x1e7c1000, 0x1e7c3000, 0x1e7e1000, 0x1e7e3000, + 0xf82d83a5, 0xf8380355, 0xf8381303, 0xf83a21f7, + 0xf8353303, 0xf8285299, 0xf8304051, 0xf8217300, + 0xf8246183, 0xf8bf815c, 0xf8ba0182, 0xf8b0103f, + 0xf8ad201d, 0xf8b3322c, 0xf8b6538d, 0xf8be403f, + 0xf8ba709c, 0xf8be60c4, 0xf8fe81fa, 0xf8e90188, + 0xf8e01034, 0xf8f82002, 0xf8e93358, 0xf8f0507e, + 0xf8ea4157, 0xf8e47050, 0xf8eb6148, 0xf86f8051, + 0xf86a018c, 0xf86f104d, 0xf8672354, 0xf8703044, + 0xf86451ec, 0xf87541f0, 0xf86b72f5, 0xf86c62fa, + 0xb83c816e, 0xb8380181, 0xb83f120a, 0xb8272062, + 0xb82d3233, 0xb8305023, 0xb82b40be, 0xb82873af, + 0xb83e6280, 0xb8a782f4, 0xb8bc0375, 0xb8b91025, + 0xb8b723f0, 0xb8a5312c, 0xb8bc53af, 0xb8b6427f, + 0xb8bf71c5, 0xb8b061ff, 0xb8fb8214, 0xb8ec012b, + 0xb8e6123e, 0xb8fb23dc, 0xb8e7328a, 0xb8ea5304, + 0xb8f142d1, 0xb8e371fd, 0xb8f66273, 0xb87681e2, + 0xb866020c, 0xb86b12ed, 0xb861227e, 0xb8653051, + 0xb87051b6, 0xb86a43b5, 0xb87b736c, 0xb86363e1, + 0xce312677, 0xce0e1b5b, 0xce7e8ed4, 0xce9ed858, + 0xce768151, 0xce718451, 0xcec08300, 0xce628ad9, + 0x04e30191, 0x04f0079d, 0x65dc0126, 0x65870887, + 0x658806c9, 0x0416b7db, 0x0440021a, 0x04d09903, + 0x04dabb55, 0x04138096, 0x04518071, 0x041008c1, + 0x0497bce9, 0x045eb4b6, 0x040813c8, 0x04ca0171, + 0x0481035c, 0x04dcadbc, 0x658098b0, 0x658d89ed, + 0x6586957a, 0x65879096, 0x65829233, 0x04ddac4e, + 0x6582b6e3, 0x6580a626, 0x6581b21b, 0x658dbc62, + 0x65819266, 0x65f8150c, 0x65b72151, 0x65b05db3, + 0x65f165c0, 0x04944ac8, 0x048f607b, 0x042430f4, + 0x04a83007, 0x046432d3, 0x04da3569, 0x04583e05, + 0x04592c36, 0x04c83608, 0x048a248f, 0x658727a8, + 0x65c633bc, 0x65982c49, 0x040120fc, }; // END Generated code -- do not edit diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index 711d9db07e5..7ff9c018bef 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -217,7 +217,7 @@ class Instruction_aarch64 { static void patch(address a, int msb, int lsb, uint64_t val) { int nbits = msb - lsb + 1; - guarantee(val < (1U << nbits), "Field too big for insn"); + guarantee(val < (1ULL << nbits), "Field too big for insn"); assert_cond(msb >= lsb); unsigned mask = (1U << nbits) - 1; val <<= lsb; @@ -445,8 +445,8 @@ class Address { } Register base() const { - guarantee((_mode == base_plus_offset | _mode == base_plus_offset_reg - | _mode == post | _mode == post_reg), + guarantee((_mode == base_plus_offset || _mode == base_plus_offset_reg + || _mode == post || _mode == post_reg), "wrong mode"); return _base; } @@ -1371,6 +1371,21 @@ class Assembler : public AbstractAssembler { #undef INSN +#define INSN(NAME, size, opc) \ + void NAME(FloatRegister Rt, Register Rn) { \ + starti; \ + f(size, 31, 30), f(0b111100, 29, 24), f(opc, 23, 22), f(0, 21); \ + f(0, 20, 12), f(0b01, 11, 10); \ + rf(Rn, 5), rf((Register)Rt, 0); \ + } + + INSN(ldrs, 0b10, 0b01); + INSN(ldrd, 0b11, 0b01); + INSN(ldrq, 0b00, 0b11); + +#undef INSN + + #define INSN(NAME, opc, V) \ void NAME(address dest, prfop op = PLDL1KEEP) { \ int64_t offset = (dest - pc()) >> 2; \ @@ -1508,6 +1523,21 @@ class Assembler : public AbstractAssembler { #undef INSN +/* SIMD extensions + * + * We just use FloatRegister in the following. They are exactly the same + * as SIMD registers. + */ +public: + + enum SIMD_Arrangement { + T8B, T16B, T4H, T8H, T2S, T4S, T1D, T2D, T1Q + }; + + enum SIMD_RegVariant { + B, H, S, D, Q + }; + enum shift_kind { LSL, LSR, ASR, ROR }; void op_shifted_reg(unsigned decode, @@ -1887,6 +1917,30 @@ void mvnw(Register Rd, Register Rm, i_fmovs(Vd, Vn); } +private: + void _fcvt_narrow_extend(FloatRegister Vd, SIMD_Arrangement Ta, + FloatRegister Vn, SIMD_Arrangement Tb, bool do_extend) { + assert((do_extend && (Tb >> 1) + 1 == (Ta >> 1)) + || (!do_extend && (Ta >> 1) + 1 == (Tb >> 1)), "Incompatible arrangement"); + starti; + int op30 = (do_extend ? Tb : Ta) & 1; + int op22 = ((do_extend ? Ta : Tb) >> 1) & 1; + f(0, 31), f(op30, 30), f(0b0011100, 29, 23), f(op22, 22); + f(0b100001011, 21, 13), f(do_extend ? 1 : 0, 12), f(0b10, 11, 10); + rf(Vn, 5), rf(Vd, 0); + } + +public: + void fcvtl(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, SIMD_Arrangement Tb) { + assert(Tb == T4H || Tb == T8H|| Tb == T2S || Tb == T4S, "invalid arrangement"); + _fcvt_narrow_extend(Vd, Ta, Vn, Tb, true); + } + + void fcvtn(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, SIMD_Arrangement Tb) { + assert(Ta == T4H || Ta == T8H|| Ta == T2S || Ta == T4S, "invalid arrangement"); + _fcvt_narrow_extend(Vd, Ta, Vn, Tb, false); + } + #undef INSN // Floating-point data-processing (2 source) @@ -2023,6 +2077,43 @@ void mvnw(Register Rd, Register Rm, #undef INSN + enum sign_kind { SIGNED, UNSIGNED }; + +private: + void _xcvtf_scalar_integer(sign_kind sign, unsigned sz, + FloatRegister Rd, FloatRegister Rn) { + starti; + f(0b01, 31, 30), f(sign == SIGNED ? 0 : 1, 29); + f(0b111100, 27, 23), f((sz >> 1) & 1, 22), f(0b100001110110, 21, 10); + rf(Rn, 5), rf(Rd, 0); + } + +public: +#define INSN(NAME, sign, sz) \ + void NAME(FloatRegister Rd, FloatRegister Rn) { \ + _xcvtf_scalar_integer(sign, sz, Rd, Rn); \ + } + + INSN(scvtfs, SIGNED, 0); + INSN(scvtfd, SIGNED, 1); + +#undef INSN + +private: + void _xcvtf_vector_integer(sign_kind sign, SIMD_Arrangement T, + FloatRegister Rd, FloatRegister Rn) { + assert(T == T2S || T == T4S || T == T2D, "invalid arrangement"); + starti; + f(0, 31), f(T & 1, 30), f(sign == SIGNED ? 0 : 1, 29); + f(0b011100, 28, 23), f((T >> 1) & 1, 22), f(0b100001110110, 21, 10); + rf(Rn, 5), rf(Rd, 0); + } + +public: + void scvtfv(SIMD_Arrangement T, FloatRegister Rd, FloatRegister Rn) { + _xcvtf_vector_integer(SIGNED, T, Rd, Rn); + } + // Floating-point compare void float_compare(unsigned op31, unsigned type, unsigned op, unsigned op2, @@ -2152,21 +2243,6 @@ void mvnw(Register Rd, Register Rm, INSN(frintzd, 0b01, 0b011); #undef INSN -/* SIMD extensions - * - * We just use FloatRegister in the following. They are exactly the same - * as SIMD registers. - */ - public: - - enum SIMD_Arrangement { - T8B, T16B, T4H, T8H, T2S, T4S, T1D, T2D, T1Q - }; - - enum SIMD_RegVariant { - B, H, S, D, Q - }; - private: static short SIMD_Size_in_bytes[]; @@ -2324,6 +2400,11 @@ void mvnw(Register Rd, Register Rm, INSN(smullv, 0, 0b110000, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S INSN(umullv, 1, 0b110000, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S INSN(umlalv, 1, 0b100000, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S + INSN(maxv, 0, 0b011001, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S + INSN(minv, 0, 0b011011, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S + INSN(cmeq, 1, 0b100011, true); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S, T2D + INSN(cmgt, 0, 0b001101, true); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S, T2D + INSN(cmge, 0, 0b001111, true); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S, T2D #undef INSN @@ -2343,6 +2424,8 @@ void mvnw(Register Rd, Register Rm, INSN(negr, 1, 0b100000101110, 3); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S, T2D INSN(notr, 1, 0b100000010110, 0); // accepted arrangements: T8B, T16B INSN(addv, 0, 0b110001101110, 1); // accepted arrangements: T8B, T16B, T4H, T8H, T4S + INSN(smaxv, 0, 0b110000101010, 1); // accepted arrangements: T8B, T16B, T4H, T8H, T4S + INSN(sminv, 0, 0b110001101010, 1); // accepted arrangements: T8B, T16B, T4H, T8H, T4S INSN(cls, 0, 0b100000010010, 2); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S INSN(clz, 1, 0b100000010010, 2); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S INSN(cnt, 0, 0b100000010110, 0); // accepted arrangements: T8B, T16B @@ -2407,6 +2490,9 @@ void mvnw(Register Rd, Register Rm, INSN(fmls, 0, 1, 0b110011); INSN(fmax, 0, 0, 0b111101); INSN(fmin, 0, 1, 0b111101); + INSN(fcmeq, 0, 0, 0b111001); + INSN(fcmgt, 1, 1, 0b111001); + INSN(fcmge, 1, 0, 0b111001); #undef INSN @@ -2464,6 +2550,40 @@ void mvnw(Register Rd, Register Rm, #undef INSN +#define INSN(NAME, opc) \ + void NAME(FloatRegister Vd, SIMD_Arrangement T, FloatRegister Vn, FloatRegister Vm, FloatRegister Va) { \ + starti; \ + assert(T == T16B, "arrangement must be T16B"); \ + f(0b11001110, 31, 24), f(opc, 23, 21), rf(Vm, 16), f(0b0, 15, 15), rf(Va, 10), rf(Vn, 5), rf(Vd, 0); \ + } + + INSN(eor3, 0b000); + INSN(bcax, 0b001); + +#undef INSN + +#define INSN(NAME, opc) \ + void NAME(FloatRegister Vd, SIMD_Arrangement T, FloatRegister Vn, FloatRegister Vm, unsigned imm) { \ + starti; \ + assert(T == T2D, "arrangement must be T2D"); \ + f(0b11001110, 31, 24), f(opc, 23, 21), rf(Vm, 16), f(imm, 15, 10), rf(Vn, 5), rf(Vd, 0); \ + } + + INSN(xar, 0b100); + +#undef INSN + +#define INSN(NAME, opc) \ + void NAME(FloatRegister Vd, SIMD_Arrangement T, FloatRegister Vn, FloatRegister Vm) { \ + starti; \ + assert(T == T2D, "arrangement must be T2D"); \ + f(0b11001110, 31, 24), f(opc, 23, 21), rf(Vm, 16), f(0b100011, 15, 10), rf(Vn, 5), rf(Vd, 0); \ + } + + INSN(rax1, 0b011); + +#undef INSN + #define INSN(NAME, opc) \ void NAME(FloatRegister Vd, FloatRegister Vn) { \ starti; \ @@ -2506,10 +2626,20 @@ void mvnw(Register Rd, Register Rm, rf(Vn, 5), rf(Vd, 0); } - // (double) {a, b} -> (a + b) - void faddpd(FloatRegister Vd, FloatRegister Vn) { + // (long) {a, b} -> (a + b) + void addpd(FloatRegister Vd, FloatRegister Vn) { starti; - f(0b0111111001110000110110, 31, 10); + f(0b0101111011110001101110, 31, 10); + rf(Vn, 5), rf(Vd, 0); + } + + // (Floating-point) {a, b} -> (a + b) + void faddp(FloatRegister Vd, FloatRegister Vn, SIMD_RegVariant type) { + assert(type == D || type == S, "Wrong type for faddp"); + starti; + f(0b011111100, 31, 23); + f(type == D ? 1 : 0, 22); + f(0b110000110110, 21, 10); rf(Vn, 5), rf(Vd, 0); } @@ -2558,6 +2688,8 @@ void mvnw(Register Rd, Register Rm, INSN(shl, 0, 0b010101, /* isSHR = */ false); INSN(sshr, 0, 0b000001, /* isSHR = */ true); INSN(ushr, 1, 0b000001, /* isSHR = */ true); + INSN(usra, 1, 0b000101, /* isSHR = */ true); + INSN(ssra, 0, 0b000101, /* isSHAR =*/ true); #undef INSN @@ -2576,29 +2708,48 @@ void mvnw(Register Rd, Register Rm, #undef INSN private: - void _ushll(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, SIMD_Arrangement Tb, int shift) { + void _xshll(sign_kind sign, FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, SIMD_Arrangement Tb, int shift) { starti; /* The encodings for the immh:immb fields (bits 22:16) are - * 0001 xxx 8H, 8B/16b shift = xxx + * 0001 xxx 8H, 8B/16B shift = xxx * 001x xxx 4S, 4H/8H shift = xxxx * 01xx xxx 2D, 2S/4S shift = xxxxx * 1xxx xxx RESERVED */ assert((Tb >> 1) + 1 == (Ta >> 1), "Incompatible arrangement"); assert((1 << ((Tb>>1)+3)) > shift, "Invalid shift value"); - f(0, 31), f(Tb & 1, 30), f(0b1011110, 29, 23), f((1 << ((Tb>>1)+3))|shift, 22, 16); + f(0, 31), f(Tb & 1, 30), f(sign == SIGNED ? 0 : 1, 29), f(0b011110, 28, 23); + f((1 << ((Tb>>1)+3))|shift, 22, 16); f(0b101001, 15, 10), rf(Vn, 5), rf(Vd, 0); } public: void ushll(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, SIMD_Arrangement Tb, int shift) { assert(Tb == T8B || Tb == T4H || Tb == T2S, "invalid arrangement"); - _ushll(Vd, Ta, Vn, Tb, shift); + _xshll(UNSIGNED, Vd, Ta, Vn, Tb, shift); } void ushll2(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, SIMD_Arrangement Tb, int shift) { assert(Tb == T16B || Tb == T8H || Tb == T4S, "invalid arrangement"); - _ushll(Vd, Ta, Vn, Tb, shift); + _xshll(UNSIGNED, Vd, Ta, Vn, Tb, shift); + } + + void uxtl(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, SIMD_Arrangement Tb) { + ushll(Vd, Ta, Vn, Tb, 0); + } + + void sshll(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, SIMD_Arrangement Tb, int shift) { + assert(Tb == T8B || Tb == T4H || Tb == T2S, "invalid arrangement"); + _xshll(SIGNED, Vd, Ta, Vn, Tb, shift); + } + + void sshll2(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, SIMD_Arrangement Tb, int shift) { + assert(Tb == T16B || Tb == T8H || Tb == T4S, "invalid arrangement"); + _xshll(SIGNED, Vd, Ta, Vn, Tb, shift); + } + + void sxtl(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, SIMD_Arrangement Tb) { + sshll(Vd, Ta, Vn, Tb, 0); } // Move from general purpose register @@ -2649,6 +2800,15 @@ void mvnw(Register Rd, Register Rm, f(0b100001010010, 21, 10), rf(Vn, 5), rf(Vd, 0); } + void xtn(FloatRegister Vd, SIMD_Arrangement Tb, FloatRegister Vn, SIMD_Arrangement Ta) { + starti; + int size_b = (int)Tb >> 1; + int size_a = (int)Ta >> 1; + assert(size_b < 3 && size_b == size_a - 1, "Invalid size specifier"); + f(0, 31), f(Tb & 1, 30), f(0b001110, 29, 24), f(size_b, 23, 22); + f(0b100001001010, 21, 10), rf(Vn, 5), rf(Vd, 0); + } + void dup(FloatRegister Vd, SIMD_Arrangement T, Register Xs) { starti; @@ -3062,13 +3222,6 @@ void mvnw(Register Rd, Register Rm, Assembler(CodeBuffer* code) : AbstractAssembler(code) { } - virtual RegisterOrConstant delayed_value_impl(intptr_t* delayed_value_addr, - Register tmp, - int offset) { - ShouldNotCallThis(); - return RegisterOrConstant(); - } - // Stack overflow checking virtual void bang_stack_with_offset(int offset); diff --git a/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp index 99469bb04c0..119bc979e0a 100644 --- a/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp @@ -38,6 +38,19 @@ #define __ ce->masm()-> +void C1SafepointPollStub::emit_code(LIR_Assembler* ce) { + __ bind(_entry); + InternalAddress safepoint_pc(ce->masm()->pc() - ce->masm()->offset() + safepoint_offset()); + __ adr(rscratch1, safepoint_pc); + __ str(rscratch1, Address(rthread, JavaThread::saved_exception_pc_offset())); + + assert(SharedRuntime::polling_page_return_handler_blob() != NULL, + "polling page return stub not created yet"); + address stub = SharedRuntime::polling_page_return_handler_blob()->entry_point(); + + __ far_jump(RuntimeAddress(stub)); +} + void CounterOverflowStub::emit_code(LIR_Assembler* ce) { __ bind(_entry); Metadata *m = _method->as_constant_ptr()->as_metadata(); diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index b5ab058d44c..8dac1d9ebe8 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -504,7 +504,7 @@ void LIR_Assembler::add_debug_info_for_branch(address adr, CodeEmitInfo* info) { } } -void LIR_Assembler::return_op(LIR_Opr result) { +void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) { assert(result->is_illegal() || !result->is_single_cpu() || result->as_register() == r0, "word returns are in r0,"); // Pop the stack before the safepoint code @@ -514,7 +514,9 @@ void LIR_Assembler::return_op(LIR_Opr result) { __ reserved_stack_check(); } - __ fetch_and_read_polling_page(rscratch1, relocInfo::poll_return_type); + code_stub->set_safepoint_offset(__ offset()); + __ relocate(relocInfo::poll_return_type); + __ safepoint_poll(*code_stub->entry(), true /* at_return */, false /* acquire */, true /* in_nmethod */); __ ret(lr); } diff --git a/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp b/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp index 4e4262d5d6d..d2520014ed1 100644 --- a/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp @@ -34,8 +34,6 @@ #ifndef TIERED define_pd_global(bool, BackgroundCompilation, true ); -define_pd_global(bool, UseTLAB, true ); -define_pd_global(bool, ResizeTLAB, true ); define_pd_global(bool, InlineIntrinsics, true ); define_pd_global(bool, PreferInterpreterNativeStubs, false); define_pd_global(bool, ProfileTraps, false); diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index 24b32187b7c..032e9e80756 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -538,6 +538,70 @@ void C2_MacroAssembler::string_indexof_char(Register str1, Register cnt1, BIND(DONE); } +void C2_MacroAssembler::stringL_indexof_char(Register str1, Register cnt1, + Register ch, Register result, + Register tmp1, Register tmp2, Register tmp3) +{ + Label CH1_LOOP, HAS_ZERO, DO1_SHORT, DO1_LOOP, MATCH, NOMATCH, DONE; + Register cnt1_neg = cnt1; + Register ch1 = rscratch1; + Register result_tmp = rscratch2; + + cbz(cnt1, NOMATCH); + + cmp(cnt1, (u1)8); + br(LT, DO1_SHORT); + + orr(ch, ch, ch, LSL, 8); + orr(ch, ch, ch, LSL, 16); + orr(ch, ch, ch, LSL, 32); + + sub(cnt1, cnt1, 8); + mov(result_tmp, cnt1); + lea(str1, Address(str1, cnt1)); + sub(cnt1_neg, zr, cnt1); + + mov(tmp3, 0x0101010101010101); + + BIND(CH1_LOOP); + ldr(ch1, Address(str1, cnt1_neg)); + eor(ch1, ch, ch1); + sub(tmp1, ch1, tmp3); + orr(tmp2, ch1, 0x7f7f7f7f7f7f7f7f); + bics(tmp1, tmp1, tmp2); + br(NE, HAS_ZERO); + adds(cnt1_neg, cnt1_neg, 8); + br(LT, CH1_LOOP); + + cmp(cnt1_neg, (u1)8); + mov(cnt1_neg, 0); + br(LT, CH1_LOOP); + b(NOMATCH); + + BIND(HAS_ZERO); + rev(tmp1, tmp1); + clz(tmp1, tmp1); + add(cnt1_neg, cnt1_neg, tmp1, LSR, 3); + b(MATCH); + + BIND(DO1_SHORT); + mov(result_tmp, cnt1); + lea(str1, Address(str1, cnt1)); + sub(cnt1_neg, zr, cnt1); + BIND(DO1_LOOP); + ldrb(ch1, Address(str1, cnt1_neg)); + cmp(ch, ch1); + br(EQ, MATCH); + adds(cnt1_neg, cnt1_neg, 1); + br(LT, DO1_LOOP); + BIND(NOMATCH); + mov(result, -1); + b(DONE); + BIND(MATCH); + add(result, result_tmp, cnt1_neg); + BIND(DONE); +} + // Compare strings. void C2_MacroAssembler::string_compare(Register str1, Register str2, Register cnt1, Register cnt2, Register result, Register tmp1, Register tmp2, diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp index f359e35974a..b2f6226bf9e 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp @@ -45,4 +45,8 @@ Register ch, Register result, Register tmp1, Register tmp2, Register tmp3); + void stringL_indexof_char(Register str1, Register cnt1, + Register ch, Register result, + Register tmp1, Register tmp2, Register tmp3); + #endif // CPU_AARCH64_C2_MACROASSEMBLER_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp index 973cbe740bd..5a019eba6ae 100644 --- a/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp @@ -33,8 +33,6 @@ // (see c2_globals.hpp). Alpha-sorted. define_pd_global(bool, BackgroundCompilation, true); -define_pd_global(bool, UseTLAB, true); -define_pd_global(bool, ResizeTLAB, true); define_pd_global(bool, CICompileOSR, true); define_pd_global(bool, InlineIntrinsics, true); define_pd_global(bool, PreferInterpreterNativeStubs, false); diff --git a/src/hotspot/cpu/aarch64/c2_safepointPollStubTable_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_safepointPollStubTable_aarch64.cpp new file mode 100644 index 00000000000..fb36406fbde --- /dev/null +++ b/src/hotspot/cpu/aarch64/c2_safepointPollStubTable_aarch64.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "asm/macroAssembler.hpp" +#include "opto/compile.hpp" +#include "opto/node.hpp" +#include "opto/output.hpp" +#include "runtime/sharedRuntime.hpp" + +#define __ masm. +void C2SafepointPollStubTable::emit_stub_impl(MacroAssembler& masm, C2SafepointPollStub* entry) const { + assert(SharedRuntime::polling_page_return_handler_blob() != NULL, + "polling page return stub not created yet"); + address stub = SharedRuntime::polling_page_return_handler_blob()->entry_point(); + + RuntimeAddress callback_addr(stub); + + __ bind(entry->_stub_label); + InternalAddress safepoint_pc(masm.pc() - masm.offset() + entry->_safepoint_offset); + __ adr(rscratch1, safepoint_pc); + __ str(rscratch1, Address(rthread, JavaThread::saved_exception_pc_offset())); + __ far_jump(callback_addr); +} +#undef __ diff --git a/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp b/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp index 75cc249cf08..2e89960778e 100644 --- a/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp @@ -36,6 +36,9 @@ #define __ _masm. address CompiledStaticCall::emit_to_interp_stub(CodeBuffer &cbuf, address mark) { + precond(cbuf.stubs()->start() != badAddress); + precond(cbuf.stubs()->end() != badAddress); + // Stub is fixed up when the corresponding call is converted from // calling compiled code to calling interpreted code. // mov rmethod, 0 diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.cpp b/src/hotspot/cpu/aarch64/frame_aarch64.cpp index 46261c70dbe..15c5e16f380 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.cpp @@ -37,6 +37,7 @@ #include "runtime/monitorChunk.hpp" #include "runtime/os.inline.hpp" #include "runtime/signature.hpp" +#include "runtime/stackWatermarkSet.hpp" #include "runtime/stubCodeGenerator.hpp" #include "runtime/stubRoutines.hpp" #include "vmreg_aarch64.inline.hpp" @@ -476,8 +477,8 @@ frame frame::sender_for_compiled_frame(RegisterMap* map) const { } //------------------------------------------------------------------------------ -// frame::sender -frame frame::sender(RegisterMap* map) const { +// frame::sender_raw +frame frame::sender_raw(RegisterMap* map) const { // Default is we done have to follow them. The sender_for_xxx will // update it accordingly map->set_include_argument_oops(false); @@ -499,6 +500,16 @@ frame frame::sender(RegisterMap* map) const { return frame(sender_sp(), link(), sender_pc()); } +frame frame::sender(RegisterMap* map) const { + frame result = sender_raw(map); + + if (map->process_frames()) { + StackWatermarkSet::on_iteration(map->thread(), result); + } + + return result; +} + bool frame::is_interpreted_frame_valid(JavaThread* thread) const { assert(is_interpreted_frame(), "Not an interpreted frame"); // These are reasonable sanity checks @@ -651,11 +662,12 @@ intptr_t* frame::real_fp() const { #undef DESCRIBE_FP_OFFSET -#define DESCRIBE_FP_OFFSET(name) \ - { \ - uintptr_t *p = (uintptr_t *)fp; \ - printf("0x%016lx 0x%016lx %s\n", (uintptr_t)(p + frame::name##_offset), \ - p[frame::name##_offset], #name); \ +#define DESCRIBE_FP_OFFSET(name) \ + { \ + uintptr_t *p = (uintptr_t *)fp; \ + printf(INTPTR_FORMAT " " INTPTR_FORMAT " %s\n", \ + (uintptr_t)(p + frame::name##_offset), \ + p[frame::name##_offset], #name); \ } static THREAD_LOCAL uintptr_t nextfp; diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.hpp b/src/hotspot/cpu/aarch64/frame_aarch64.hpp index 6c639a05961..e2490d28611 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.hpp @@ -161,4 +161,7 @@ static jint interpreter_frame_expression_stack_direction() { return -1; } + // returns the sending frame, without applying any barriers + frame sender_raw(RegisterMap* map) const; + #endif // CPU_AARCH64_FRAME_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp index b3530509b03..db9c7577e60 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp @@ -109,7 +109,7 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt __ xchg(access.resolved_addr(), value_opr, result, tmp); if (access.is_oop()) { - result = load_reference_barrier(access.gen(), result, LIR_OprFact::addressConst(0), false); + result = load_reference_barrier(access.gen(), result, LIR_OprFact::addressConst(0), ShenandoahBarrierSet::AccessKind::NORMAL); LIR_Opr tmp = gen->new_register(type); __ move(result, tmp); result = tmp; diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index c2d53df4f67..840464b251f 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -43,8 +43,6 @@ #define __ masm-> -address ShenandoahBarrierSetAssembler::_shenandoah_lrb = NULL; - void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop, Register src, Register dst, Register count, RegSet saved_regs) { if (is_oop) { @@ -227,18 +225,18 @@ void ShenandoahBarrierSetAssembler::resolve_forward_pointer_not_null(MacroAssemb } } -void ShenandoahBarrierSetAssembler::load_reference_barrier_not_null(MacroAssembler* masm, Register dst, Address load_addr) { +void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, Register dst, Address load_addr, ShenandoahBarrierSet::AccessKind kind) { assert(ShenandoahLoadRefBarrier, "Should be enabled"); assert(dst != rscratch2, "need rscratch2"); assert_different_registers(load_addr.base(), load_addr.index(), rscratch1, rscratch2); - Label done; + Label heap_stable, not_cset; __ enter(); Address gc_state(rthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); __ ldrb(rscratch2, gc_state); // Check for heap stability - __ tbz(rscratch2, ShenandoahHeap::HAS_FORWARDED_BITPOS, done); + __ tbz(rscratch2, ShenandoahHeap::HAS_FORWARDED_BITPOS, heap_stable); // use r1 for load address Register result_dst = dst; @@ -253,51 +251,48 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier_not_null(MacroAssembl __ lea(r1, load_addr); __ mov(r0, dst); - __ far_call(RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahBarrierSetAssembler::shenandoah_lrb()))); - - __ mov(result_dst, r0); - __ pop(to_save, sp); - - __ bind(done); - __ leave(); -} - -void ShenandoahBarrierSetAssembler::load_reference_barrier_native(MacroAssembler* masm, Register dst, Address load_addr) { - if (!ShenandoahLoadRefBarrier) { - return; + // Test for in-cset + if (kind == ShenandoahBarrierSet::AccessKind::NORMAL) { + __ mov(rscratch2, ShenandoahHeap::in_cset_fast_test_addr()); + __ lsr(rscratch1, r0, ShenandoahHeapRegion::region_size_bytes_shift_jint()); + __ ldrb(rscratch2, Address(rscratch2, rscratch1)); + __ tbz(rscratch2, 0, not_cset); } - assert(dst != rscratch2, "need rscratch2"); - - Label is_null; - Label done; - - __ block_comment("load_reference_barrier_native { "); - - __ cbz(dst, is_null); - - __ enter(); - - Address gc_state(rthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); - __ ldrb(rscratch2, gc_state); - - // Check for heap in evacuation phase - __ tbz(rscratch2, ShenandoahHeap::EVACUATION_BITPOS, done); - - __ mov(rscratch2, dst); __ push_call_clobbered_registers(); - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_native)); - __ lea(r1, load_addr); - __ mov(r0, rscratch2); + switch (kind) { + case ShenandoahBarrierSet::AccessKind::NORMAL: + if (UseCompressedOops) { + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_narrow)); + } else { + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier)); + } + break; + case ShenandoahBarrierSet::AccessKind::WEAK: + if (UseCompressedOops) { + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow)); + } else { + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak)); + } + break; + case ShenandoahBarrierSet::AccessKind::NATIVE: + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak)); + break; + default: + ShouldNotReachHere(); + } __ blr(lr); - __ mov(rscratch2, r0); + __ mov(rscratch1, r0); __ pop_call_clobbered_registers(); - __ mov(dst, rscratch2); + __ mov(r0, rscratch1); - __ bind(done); + __ bind(not_cset); + + __ mov(result_dst, r0); + __ pop(to_save, sp); + + __ bind(heap_stable); __ leave(); - __ bind(is_null); - __ block_comment("} load_reference_barrier_native"); } void ShenandoahBarrierSetAssembler::storeval_barrier(MacroAssembler* masm, Register dst, Register tmp) { @@ -308,15 +303,6 @@ void ShenandoahBarrierSetAssembler::storeval_barrier(MacroAssembler* masm, Regis } } -void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, Register dst, Address load_addr) { - if (ShenandoahLoadRefBarrier) { - Label is_null; - __ cbz(dst, is_null); - load_reference_barrier_not_null(masm, dst, load_addr); - __ bind(is_null); - } -} - // // Arguments: // @@ -352,11 +338,8 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet d BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1, tmp_thread); - if (ShenandoahBarrierSet::use_load_reference_barrier_native(decorators, type)) { - load_reference_barrier_native(masm, dst, src); - } else { - load_reference_barrier(masm, dst, src); - } + ShenandoahBarrierSet::AccessKind kind = ShenandoahBarrierSet::access_kind(decorators, type); + load_reference_barrier(masm, dst, src, kind); if (dst != result_dst) { __ mov(result_dst, dst); @@ -477,7 +460,8 @@ void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler* masm, bool is_narrow = UseCompressedOops; Assembler::operand_size size = is_narrow ? Assembler::word : Assembler::xword; - assert_different_registers(addr, expected, new_val, tmp1, tmp2); + assert_different_registers(addr, expected, tmp1, tmp2); + assert_different_registers(addr, new_val, tmp1, tmp2); Label step4, done; @@ -669,10 +653,18 @@ void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assemble __ bind(slow_path); ce->store_parameter(res, 0); ce->store_parameter(addr, 1); - if (stub->is_native()) { - __ far_call(RuntimeAddress(bs->load_reference_barrier_native_rt_code_blob()->code_begin())); - } else { - __ far_call(RuntimeAddress(bs->load_reference_barrier_rt_code_blob()->code_begin())); + switch (stub->kind()) { + case ShenandoahBarrierSet::AccessKind::NORMAL: + __ far_call(RuntimeAddress(bs->load_reference_barrier_normal_rt_code_blob()->code_begin())); + break; + case ShenandoahBarrierSet::AccessKind::WEAK: + __ far_call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin())); + break; + case ShenandoahBarrierSet::AccessKind::NATIVE: + __ far_call(RuntimeAddress(bs->load_reference_barrier_native_rt_code_blob()->code_begin())); + break; + default: + ShouldNotReachHere(); } __ b(*stub->continuation()); @@ -728,19 +720,33 @@ void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAss __ epilogue(); } -void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, bool is_native) { +void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, ShenandoahBarrierSet::AccessKind kind) { __ prologue("shenandoah_load_reference_barrier", false); // arg0 : object to be resolved __ push_call_clobbered_registers(); __ load_parameter(0, r0); __ load_parameter(1, r1); - if (is_native) { - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_native)); - } else if (UseCompressedOops) { - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_narrow)); - } else { - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier)); + switch (kind) { + case ShenandoahBarrierSet::AccessKind::NORMAL: + if (UseCompressedOops) { + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_narrow)); + } else { + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier)); + } + break; + case ShenandoahBarrierSet::AccessKind::WEAK: + if (UseCompressedOops) { + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow)); + } else { + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak)); + } + break; + case ShenandoahBarrierSet::AccessKind::NATIVE: + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak)); + break; + default: + ShouldNotReachHere(); } __ blr(lr); __ mov(rscratch1, r0); @@ -753,67 +759,3 @@ void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_s #undef __ #endif // COMPILER1 - -address ShenandoahBarrierSetAssembler::shenandoah_lrb() { - assert(_shenandoah_lrb != NULL, "need load reference barrier stub"); - return _shenandoah_lrb; -} - -#define __ cgen->assembler()-> - -// Shenandoah load reference barrier. -// -// Input: -// r0: OOP to evacuate. Not null. -// r1: load address -// -// Output: -// r0: Pointer to evacuated OOP. -// -// Trash rscratch1, rscratch2. Preserve everything else. -address ShenandoahBarrierSetAssembler::generate_shenandoah_lrb(StubCodeGenerator* cgen) { - - __ align(6); - StubCodeMark mark(cgen, "StubRoutines", "shenandoah_lrb"); - address start = __ pc(); - - Label slow_path; - __ mov(rscratch2, ShenandoahHeap::in_cset_fast_test_addr()); - __ lsr(rscratch1, r0, ShenandoahHeapRegion::region_size_bytes_shift_jint()); - __ ldrb(rscratch2, Address(rscratch2, rscratch1)); - __ tbnz(rscratch2, 0, slow_path); - __ ret(lr); - - __ bind(slow_path); - __ enter(); // required for proper stackwalking of RuntimeStub frame - - __ push_call_clobbered_registers(); - - if (UseCompressedOops) { - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_narrow)); - } else { - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier)); - } - __ blr(lr); - __ mov(rscratch1, r0); - __ pop_call_clobbered_registers(); - __ mov(r0, rscratch1); - - __ leave(); // required for proper stackwalking of RuntimeStub frame - __ ret(lr); - - return start; -} - -#undef __ - -void ShenandoahBarrierSetAssembler::barrier_stubs_init() { - if (ShenandoahLoadRefBarrier) { - int stub_code_size = 2048; - ResourceMark rm; - BufferBlob* bb = BufferBlob::create("shenandoah_barrier_stubs", stub_code_size); - CodeBuffer buf(bb); - StubCodeGenerator cgen(&buf); - _shenandoah_lrb = generate_shenandoah_lrb(&cgen); - } -} diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp index 88aa9a2b95f..60303725fd8 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp @@ -27,6 +27,7 @@ #include "asm/macroAssembler.hpp" #include "gc/shared/barrierSetAssembler.hpp" +#include "gc/shenandoah/shenandoahBarrierSet.hpp" #ifdef COMPILER1 class LIR_Assembler; class ShenandoahPreBarrierStub; @@ -38,8 +39,6 @@ class StubCodeGenerator; class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { private: - static address _shenandoah_lrb; - void satb_write_barrier_pre(MacroAssembler* masm, Register obj, Register pre_val, @@ -57,14 +56,9 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { void resolve_forward_pointer(MacroAssembler* masm, Register dst, Register tmp = noreg); void resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp = noreg); - void load_reference_barrier(MacroAssembler* masm, Register dst, Address load_addr); - void load_reference_barrier_not_null(MacroAssembler* masm, Register dst, Address load_addr); - void load_reference_barrier_native(MacroAssembler* masm, Register dst, Address load_addr); - - address generate_shenandoah_lrb(StubCodeGenerator* cgen); + void load_reference_barrier(MacroAssembler* masm, Register dst, Address load_addr, ShenandoahBarrierSet::AccessKind kind); public: - static address shenandoah_lrb(); void storeval_barrier(MacroAssembler* masm, Register dst, Register tmp); @@ -72,7 +66,7 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); - void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, bool is_native); + void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, ShenandoahBarrierSet::AccessKind kind); #endif virtual void arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop, @@ -85,8 +79,6 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register obj, Register tmp, Label& slowpath); void cmpxchg_oop(MacroAssembler* masm, Register addr, Register expected, Register new_val, bool acquire, bool release, bool is_cae, Register result); - - virtual void barrier_stubs_init(); }; #endif // CPU_AARCH64_GC_SHENANDOAH_SHENANDOAHBARRIERSETASSEMBLER_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/gc/z/zGlobals_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/z/zGlobals_aarch64.hpp index 35e261fa7ae..3187808b65a 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zGlobals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/z/zGlobals_aarch64.hpp @@ -24,10 +24,9 @@ #ifndef CPU_AARCH64_GC_Z_ZGLOBALS_AARCH64_HPP #define CPU_AARCH64_GC_Z_ZGLOBALS_AARCH64_HPP -const size_t ZPlatformGranuleSizeShift = 21; // 2MB -const size_t ZPlatformHeapViews = 3; -const size_t ZPlatformNMethodDisarmedOffset = 4; -const size_t ZPlatformCacheLineSize = 64; +const size_t ZPlatformGranuleSizeShift = 21; // 2MB +const size_t ZPlatformHeapViews = 3; +const size_t ZPlatformCacheLineSize = 64; size_t ZPlatformAddressOffsetBits(); size_t ZPlatformAddressMetadataShift(); diff --git a/src/hotspot/cpu/aarch64/globals_aarch64.hpp b/src/hotspot/cpu/aarch64/globals_aarch64.hpp index 294b6b13495..9ad1360fa91 100644 --- a/src/hotspot/cpu/aarch64/globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/globals_aarch64.hpp @@ -93,6 +93,8 @@ define_pd_global(intx, InlineSmallCode, 1000); "Use SIMD instructions in generated array equals code") \ product(bool, UseSimpleArrayEquals, false, \ "Use simpliest and shortest implementation for array equals") \ + product(bool, UseSIMDForBigIntegerShiftIntrinsics, true, \ + "Use SIMD instructions for left/right shift of BigInteger") \ product(bool, AvoidUnalignedAccesses, false, \ "Avoid generating unaligned memory accesses") \ product(bool, UseLSE, false, \ diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index 1d635429336..09632154630 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -473,7 +473,7 @@ void InterpreterMacroAssembler::dispatch_base(TosState state, if (needs_thread_local_poll) { NOT_PRODUCT(block_comment("Thread-local Safepoint poll")); - ldr(rscratch2, Address(rthread, Thread::polling_page_offset())); + ldr(rscratch2, Address(rthread, Thread::polling_word_offset())); tbnz(rscratch2, exact_log2(SafepointMechanism::poll_bit()), safepoint); } @@ -521,6 +521,7 @@ void InterpreterMacroAssembler::dispatch_via(TosState state, address* table) { // remove activation // +// Apply stack watermark barrier. // Unlock the receiver if this is a synchronized method. // Unlock any Java monitors from syncronized blocks. // Remove the activation from the stack. @@ -541,6 +542,21 @@ void InterpreterMacroAssembler::remove_activation( // result check if synchronized method Label unlocked, unlock, no_unlock; + // The below poll is for the stack watermark barrier. It allows fixing up frames lazily, + // that would normally not be safe to use. Such bad returns into unsafe territory of + // the stack, will call InterpreterRuntime::at_unwind. + Label slow_path; + Label fast_path; + safepoint_poll(slow_path, true /* at_return */, false /* acquire */, false /* in_nmethod */); + br(Assembler::AL, fast_path); + bind(slow_path); + push(state); + set_last_Java_frame(esp, rfp, (address)pc(), rscratch1); + super_call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::at_unwind), rthread); + reset_last_Java_frame(true); + pop(state); + bind(fast_path); + // get the value of _do_not_unlock_if_synchronized into r3 const Address do_not_unlock_if_synchronized(rthread, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); diff --git a/src/hotspot/cpu/aarch64/jvmciCodeInstaller_aarch64.cpp b/src/hotspot/cpu/aarch64/jvmciCodeInstaller_aarch64.cpp index 3156b4b8e83..f41d79e1021 100644 --- a/src/hotspot/cpu/aarch64/jvmciCodeInstaller_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/jvmciCodeInstaller_aarch64.cpp @@ -21,8 +21,9 @@ * questions. */ - #include "jvmci/jvmci.hpp" - #include "jvmci/jvmciCodeInstaller.hpp" +#include "precompiled.hpp" +#include "jvmci/jvmci.hpp" +#include "jvmci/jvmciCodeInstaller.hpp" #include "jvmci/jvmciRuntime.hpp" #include "jvmci/jvmciCompilerToVM.hpp" #include "jvmci/jvmciJavaClasses.hpp" diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 81fd87614e5..005ad3f5930 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -288,27 +288,21 @@ address MacroAssembler::target_addr_for_insn(address insn_addr, unsigned insn) { return address(((uint64_t)insn_addr + (offset << 2))); } -void MacroAssembler::safepoint_poll(Label& slow_path) { - ldr(rscratch1, Address(rthread, Thread::polling_page_offset())); - tbnz(rscratch1, exact_log2(SafepointMechanism::poll_bit()), slow_path); -} - -// Just like safepoint_poll, but use an acquiring load for thread- -// local polling. -// -// We need an acquire here to ensure that any subsequent load of the -// global SafepointSynchronize::_state flag is ordered after this load -// of the local Thread::_polling page. We don't want this poll to -// return false (i.e. not safepointing) and a later poll of the global -// SafepointSynchronize::_state spuriously to return true. -// -// This is to avoid a race when we're in a native->Java transition -// racing the code which wakes up from a safepoint. -// -void MacroAssembler::safepoint_poll_acquire(Label& slow_path) { - lea(rscratch1, Address(rthread, Thread::polling_page_offset())); - ldar(rscratch1, rscratch1); - tbnz(rscratch1, exact_log2(SafepointMechanism::poll_bit()), slow_path); +void MacroAssembler::safepoint_poll(Label& slow_path, bool at_return, bool acquire, bool in_nmethod) { + if (acquire) { + lea(rscratch1, Address(rthread, Thread::polling_word_offset())); + ldar(rscratch1, rscratch1); + } else { + ldr(rscratch1, Address(rthread, Thread::polling_word_offset())); + } + if (at_return) { + // Note that when in_nmethod is set, the stack pointer is incremented before the poll. Therefore, + // we may safely use the sp instead to perform the stack watermark check. + cmp(in_nmethod ? sp : rfp, rscratch1); + br(Assembler::HI, slow_path); + } else { + tbnz(rscratch1, exact_log2(SafepointMechanism::poll_bit()), slow_path); + } } void MacroAssembler::reset_last_Java_frame(bool clear_fp) { @@ -711,7 +705,7 @@ void MacroAssembler::call_VM_helper(Register oop_result, address entry_point, in // Maybe emit a call via a trampoline. If the code cache is small // trampolines won't be emitted. -address MacroAssembler::trampoline_call(Address entry, CodeBuffer *cbuf) { +address MacroAssembler::trampoline_call(Address entry, CodeBuffer* cbuf) { assert(JavaThread::current()->is_Compiler_thread(), "just checking"); assert(entry.rspec().type() == relocInfo::runtime_call_type || entry.rspec().type() == relocInfo::opt_virtual_call_type @@ -732,6 +726,7 @@ address MacroAssembler::trampoline_call(Address entry, CodeBuffer *cbuf) { if (!in_scratch_emit_size) { address stub = emit_trampoline_stub(offset(), entry.target()); if (stub == NULL) { + postcond(pc() == badAddress); return NULL; // CodeCache is full } } @@ -745,6 +740,7 @@ address MacroAssembler::trampoline_call(Address entry, CodeBuffer *cbuf) { bl(pc()); } // just need to return a non-null address + postcond(pc() != badAddress); return pc(); } @@ -938,23 +934,6 @@ void MacroAssembler::check_and_handle_earlyret(Register java_thread) { } void MacroAssembler::check_and_handle_popframe(Register java_thread) { } - -RegisterOrConstant MacroAssembler::delayed_value_impl(intptr_t* delayed_value_addr, - Register tmp, - int offset) { - intptr_t value = *delayed_value_addr; - if (value != 0) - return RegisterOrConstant(value + offset); - - // load indirectly to solve generation ordering problem - ldr(tmp, ExternalAddress((address) delayed_value_addr)); - - if (offset != 0) - add(tmp, tmp, offset); - - return RegisterOrConstant(tmp); -} - // Look up the method for a megamorphic invokeinterface call. // The target method is determined by . // The receiver klass is in recv_klass. @@ -1834,7 +1813,7 @@ bool MacroAssembler::try_merge_ldst(Register rt, const Address &adr, size_t size return true; } else { assert(size_in_bytes == 8 || size_in_bytes == 4, "only 8 bytes or 4 bytes load/store is supported."); - const unsigned mask = size_in_bytes - 1; + const uint64_t mask = size_in_bytes - 1; if (adr.getMode() == Address::base_plus_offset && (adr.offset() & mask) == 0) { // only supports base_plus_offset. code()->set_last_insn(pc()); @@ -2898,7 +2877,7 @@ void MacroAssembler::merge_ldst(Register rt, // Overwrite previous generated binary. code_section()->set_end(prev); - const int sz = prev_ldst->size_in_bytes(); + const size_t sz = prev_ldst->size_in_bytes(); assert(sz == 8 || sz == 4, "only supports 64/32bit merging."); if (!is_store) { BLOCK_COMMENT("merged ldr pair"); @@ -4405,13 +4384,6 @@ void MacroAssembler::get_polling_page(Register dest, relocInfo::relocType rtype) ldr(dest, Address(rthread, Thread::polling_page_offset())); } -// Move the address of the polling page into r, then read the polling -// page. -address MacroAssembler::fetch_and_read_polling_page(Register r, relocInfo::relocType rtype) { - get_polling_page(r, rtype); - return read_polling_page(r, rtype); -} - // Read the polling page. The address of the polling page must // already be in r. address MacroAssembler::read_polling_page(Register r, relocInfo::relocType rtype) { @@ -4503,7 +4475,7 @@ void MacroAssembler::remove_frame(int framesize) { // This method checks if provided byte array contains byte with highest bit set. -void MacroAssembler::has_negatives(Register ary1, Register len, Register result) { +address MacroAssembler::has_negatives(Register ary1, Register len, Register result) { // Simple and most common case of aligned small array which is not at the // end of memory page is placed here. All other cases are in stub. Label LOOP, END, STUB, STUB_LONG, SET_RESULT, DONE; @@ -4540,27 +4512,38 @@ void MacroAssembler::has_negatives(Register ary1, Register len, Register result) b(SET_RESULT); BIND(STUB); - RuntimeAddress has_neg = RuntimeAddress(StubRoutines::aarch64::has_negatives()); + RuntimeAddress has_neg = RuntimeAddress(StubRoutines::aarch64::has_negatives()); assert(has_neg.target() != NULL, "has_negatives stub has not been generated"); - trampoline_call(has_neg); + address tpc1 = trampoline_call(has_neg); + if (tpc1 == NULL) { + DEBUG_ONLY(reset_labels(STUB_LONG, SET_RESULT, DONE)); + postcond(pc() == badAddress); + return NULL; + } b(DONE); BIND(STUB_LONG); - RuntimeAddress has_neg_long = RuntimeAddress( - StubRoutines::aarch64::has_negatives_long()); + RuntimeAddress has_neg_long = RuntimeAddress(StubRoutines::aarch64::has_negatives_long()); assert(has_neg_long.target() != NULL, "has_negatives stub has not been generated"); - trampoline_call(has_neg_long); + address tpc2 = trampoline_call(has_neg_long); + if (tpc2 == NULL) { + DEBUG_ONLY(reset_labels(SET_RESULT, DONE)); + postcond(pc() == badAddress); + return NULL; + } b(DONE); BIND(SET_RESULT); cset(result, NE); // set true or false BIND(DONE); + postcond(pc() != badAddress); + return pc(); } -void MacroAssembler::arrays_equals(Register a1, Register a2, Register tmp3, - Register tmp4, Register tmp5, Register result, - Register cnt1, int elem_size) { +address MacroAssembler::arrays_equals(Register a1, Register a2, Register tmp3, + Register tmp4, Register tmp5, Register result, + Register cnt1, int elem_size) { Label DONE, SAME; Register tmp1 = rscratch1; Register tmp2 = rscratch2; @@ -4664,7 +4647,7 @@ void MacroAssembler::arrays_equals(Register a1, Register a2, Register tmp3, } } } else { - Label NEXT_DWORD, SHORT, TAIL, TAIL2, STUB, EARLY_OUT, + Label NEXT_DWORD, SHORT, TAIL, TAIL2, STUB, CSET_EQ, LAST_CHECK; mov(result, false); cbz(a1, DONE); @@ -4723,10 +4706,14 @@ void MacroAssembler::arrays_equals(Register a1, Register a2, Register tmp3, cbnz(tmp5, DONE); RuntimeAddress stub = RuntimeAddress(StubRoutines::aarch64::large_array_equals()); assert(stub.target() != NULL, "array_equals_long stub has not been generated"); - trampoline_call(stub); + address tpc = trampoline_call(stub); + if (tpc == NULL) { + DEBUG_ONLY(reset_labels(SHORT, LAST_CHECK, CSET_EQ, SAME, DONE)); + postcond(pc() == badAddress); + return NULL; + } b(DONE); - bind(EARLY_OUT); // (a1 != null && a2 == null) || (a1 != null && a2 != null && a1 == a2) // so, if a2 == null => return false(0), else return true, so we can return a2 mov(result, a2); @@ -4753,6 +4740,8 @@ void MacroAssembler::arrays_equals(Register a1, Register a2, Register tmp3, bind(DONE); BLOCK_COMMENT("} array_equals"); + postcond(pc() != badAddress); + return pc(); } // Compare Strings @@ -4860,7 +4849,7 @@ const int MacroAssembler::zero_words_block_size = 8; // cnt: Count in HeapWords. // // ptr, cnt, rscratch1, and rscratch2 are clobbered. -void MacroAssembler::zero_words(Register ptr, Register cnt) +address MacroAssembler::zero_words(Register ptr, Register cnt) { assert(is_power_of_2(zero_words_block_size), "adjust this"); assert(ptr == r10 && cnt == r11, "mismatch in register usage"); @@ -4870,10 +4859,15 @@ void MacroAssembler::zero_words(Register ptr, Register cnt) Label around; br(LO, around); { - RuntimeAddress zero_blocks = RuntimeAddress(StubRoutines::aarch64::zero_blocks()); + RuntimeAddress zero_blocks = RuntimeAddress(StubRoutines::aarch64::zero_blocks()); assert(zero_blocks.target() != NULL, "zero_blocks stub has not been generated"); if (StubRoutines::aarch64::complete()) { - trampoline_call(zero_blocks); + address tpc = trampoline_call(zero_blocks); + if (tpc == NULL) { + DEBUG_ONLY(reset_labels(around)); + postcond(pc() == badAddress); + return NULL; + } } else { bl(zero_blocks); } @@ -4894,6 +4888,8 @@ void MacroAssembler::zero_words(Register ptr, Register cnt) bind(l); } BLOCK_COMMENT("} zero_words"); + postcond(pc() != badAddress); + return pc(); } // base: Address of a buffer to be zeroed, 8 bytes aligned. @@ -4906,14 +4902,15 @@ void MacroAssembler::zero_words(Register base, uint64_t cnt) if (i) str(zr, Address(base)); if (cnt <= SmallArraySize / BytesPerLong) { - for (; i < (int)cnt; i += 2) + for (; i < (int)cnt; i += 2) { stp(zr, zr, Address(base, i * wordSize)); + } } else { const int unroll = 4; // Number of stp(zr, zr) instructions we'll unroll int remainder = cnt % (2 * unroll); - for (; i < remainder; i += 2) + for (; i < remainder; i += 2) { stp(zr, zr, Address(base, i * wordSize)); - + } Label loop; Register cnt_reg = rscratch1; Register loop_base = rscratch2; @@ -4923,8 +4920,9 @@ void MacroAssembler::zero_words(Register base, uint64_t cnt) add(loop_base, base, (remainder - 2) * wordSize); bind(loop); sub(cnt_reg, cnt_reg, 2 * unroll); - for (i = 1; i < unroll; i++) + for (i = 1; i < unroll; i++) { stp(zr, zr, Address(loop_base, 2 * i * wordSize)); + } stp(zr, zr, Address(pre(loop_base, 2 * unroll * wordSize))); cbnz(cnt_reg, loop); } @@ -5140,9 +5138,9 @@ void MacroAssembler::encode_iso_array(Register src, Register dst, // Inflate byte[] array to char[]. -void MacroAssembler::byte_array_inflate(Register src, Register dst, Register len, - FloatRegister vtmp1, FloatRegister vtmp2, FloatRegister vtmp3, - Register tmp4) { +address MacroAssembler::byte_array_inflate(Register src, Register dst, Register len, + FloatRegister vtmp1, FloatRegister vtmp2, + FloatRegister vtmp3, Register tmp4) { Label big, done, after_init, to_stub; assert_different_registers(src, dst, len, tmp4, rscratch1); @@ -5179,9 +5177,14 @@ void MacroAssembler::byte_array_inflate(Register src, Register dst, Register len if (SoftwarePrefetchHintDistance >= 0) { bind(to_stub); - RuntimeAddress stub = RuntimeAddress(StubRoutines::aarch64::large_byte_array_inflate()); + RuntimeAddress stub = RuntimeAddress(StubRoutines::aarch64::large_byte_array_inflate()); assert(stub.target() != NULL, "large_byte_array_inflate stub has not been generated"); - trampoline_call(stub); + address tpc = trampoline_call(stub); + if (tpc == NULL) { + DEBUG_ONLY(reset_labels(big, done)); + postcond(pc() == badAddress); + return NULL; + } b(after_init); } @@ -5235,6 +5238,8 @@ void MacroAssembler::byte_array_inflate(Register src, Register dst, Register len strq(vtmp3, Address(dst, -16)); bind(done); + postcond(pc() != badAddress); + return pc(); } // Compress char[] array to byte[]. diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 998f1afc1c7..1d597fb429c 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -102,8 +102,7 @@ class MacroAssembler: public Assembler { virtual void check_and_handle_popframe(Register java_thread); virtual void check_and_handle_earlyret(Register java_thread); - void safepoint_poll(Label& slow_path); - void safepoint_poll_acquire(Label& slow_path); + void safepoint_poll(Label& slow_path, bool at_return, bool acquire, bool in_nmethod); // Biased locking support // lock_reg and obj_reg must be loaded up with the appropriate values. @@ -1014,10 +1013,6 @@ class MacroAssembler: public Assembler { // Check for reserved stack access in method being exited (for JIT) void reserved_stack_check(); - virtual RegisterOrConstant delayed_value_impl(intptr_t* delayed_value_addr, - Register tmp, - int offset); - // Arithmetics void addptr(const Address &dst, int32_t src); @@ -1063,10 +1058,24 @@ class MacroAssembler: public Assembler { private: void compare_eq(Register rn, Register rm, enum operand_size size); +#ifdef ASSERT + // Template short-hand support to clean-up after a failed call to trampoline + // call generation (see trampoline_call() below), when a set of Labels must + // be reset (before returning). + template + void reset_labels(Label &lbl, More&... more) { + lbl.reset(); reset_labels(more...); + } + template + void reset_labels(Label &lbl) { + lbl.reset(); + } +#endif + public: // Calls - address trampoline_call(Address entry, CodeBuffer *cbuf = NULL); + address trampoline_call(Address entry, CodeBuffer* cbuf = NULL); static bool far_branches() { return ReservedCodeCacheSize > branch_range || UseAOT; @@ -1231,7 +1240,6 @@ class MacroAssembler: public Assembler { address read_polling_page(Register r, relocInfo::relocType rtype); void get_polling_page(Register dest, relocInfo::relocType rtype); - address fetch_and_read_polling_page(Register r, relocInfo::relocType rtype); // CRC32 code for java.util.zip.CRC32::updateBytes() instrinsic. void update_byte_crc32(Register crc, Register val, Register table); @@ -1239,24 +1247,24 @@ class MacroAssembler: public Assembler { Register table0, Register table1, Register table2, Register table3, bool upper = false); - void has_negatives(Register ary1, Register len, Register result); + address has_negatives(Register ary1, Register len, Register result); - void arrays_equals(Register a1, Register a2, Register result, Register cnt1, - Register tmp1, Register tmp2, Register tmp3, int elem_size); + address arrays_equals(Register a1, Register a2, Register result, Register cnt1, + Register tmp1, Register tmp2, Register tmp3, int elem_size); void string_equals(Register a1, Register a2, Register result, Register cnt1, int elem_size); void fill_words(Register base, Register cnt, Register value); void zero_words(Register base, uint64_t cnt); - void zero_words(Register ptr, Register cnt); + address zero_words(Register ptr, Register cnt); void zero_dcache_blocks(Register base, Register cnt); static const int zero_words_block_size; - void byte_array_inflate(Register src, Register dst, Register len, - FloatRegister vtmp1, FloatRegister vtmp2, - FloatRegister vtmp3, Register tmp4); + address byte_array_inflate(Register src, Register dst, Register len, + FloatRegister vtmp1, FloatRegister vtmp2, + FloatRegister vtmp3, Register tmp4); void char_array_compress(Register src, Register dst, Register len, FloatRegister tmp1Reg, FloatRegister tmp2Reg, diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp index d40c533a82c..dcf87913a88 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp @@ -655,7 +655,7 @@ class NativeLdSt : public NativeInstruction { return 0; } } - size_t size_in_bytes() { return 1 << size(); } + size_t size_in_bytes() { return 1ULL << size(); } bool is_not_pre_post_index() { return (is_ldst_ur() || is_ldst_unsigned_offset()); } bool is_load() { assert(Instruction_aarch64::extract(uint_at(0), 23, 22) == 0b01 || diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index d556d957e6b..92a07a84d2a 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -38,6 +38,7 @@ #include "nativeInst_aarch64.hpp" #include "oops/compiledICHolder.hpp" #include "oops/klass.inline.hpp" +#include "prims/methodHandles.hpp" #include "runtime/safepointMechanism.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/vframeArray.hpp" @@ -1080,20 +1081,6 @@ static void restore_args(MacroAssembler *masm, int arg_count, int first_arg, VMR } } - -// Check GCLocker::needs_gc and enter the runtime if it's true. This -// keeps a new JNI critical region from starting until a GC has been -// forced. Save down any oops in registers and describe them in an -// OopMap. -static void check_needs_gc_for_critical_native(MacroAssembler* masm, - int stack_slots, - int total_c_args, - int total_in_args, - int arg_save_area, - OopMapSet* oop_maps, - VMRegPair* in_regs, - BasicType* in_sig_bt) { Unimplemented(); } - // Unpack an array argument into a pointer to the body and the length // if the array is non-null, otherwise pass 0 for both. static void unpack_array_argument(MacroAssembler* masm, VMRegPair reg, BasicType in_elem_type, VMRegPair body_arg, VMRegPair length_arg) { Unimplemented(); } @@ -1259,25 +1246,12 @@ static void gen_special_dispatch(MacroAssembler* masm, // Critical native functions are a shorthand for the use of // GetPrimtiveArrayCritical and disallow the use of any other JNI // functions. The wrapper is expected to unpack the arguments before -// passing them to the callee and perform checks before and after the -// native call to ensure that they GCLocker -// lock_critical/unlock_critical semantics are followed. Some other -// parts of JNI setup are skipped like the tear down of the JNI handle +// passing them to the callee. Critical native functions leave the state _in_Java, +// since they block out GC. +// Some other parts of JNI setup are skipped like the tear down of the JNI handle // block and the check for pending exceptions it's impossible for them // to be thrown. // -// They are roughly structured like this: -// if (GCLocker::needs_gc()) -// SharedRuntime::block_for_jni_critical(); -// tranistion to thread_in_native -// unpack arrray arguments and call native entry point -// check for safepoint in progress -// check if any thread suspend flags are set -// call into JVM and possible unlock the JNI critical -// if a GC was suppressed while in the critical native. -// transition back to thread_in_Java -// return to caller -// nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, const methodHandle& method, int compile_id, @@ -1524,7 +1498,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Generate stack overflow check if (UseStackBanging) { - __ bang_stack_with_offset(StackOverflow::stack_shadow_zone_size()); + __ bang_stack_with_offset(checked_cast(StackOverflow::stack_shadow_zone_size())); } else { Unimplemented(); } @@ -1545,11 +1519,6 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, const Register oop_handle_reg = r20; - if (is_critical_native) { - check_needs_gc_for_critical_native(masm, stack_slots, total_c_args, total_in_args, - oop_handle_offset, oop_maps, in_regs, in_sig_bt); - } - // // We immediately shuffle the arguments so that any vm call we have to // make from here on out (sync slow path, jvmti, etc.) we will have @@ -1822,12 +1791,12 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // get JNIEnv* which is first argument to native if (!is_critical_native) { __ lea(c_rarg0, Address(rthread, in_bytes(JavaThread::jni_environment_offset()))); - } - // Now set thread in native - __ mov(rscratch1, _thread_in_native); - __ lea(rscratch2, Address(rthread, JavaThread::thread_state_offset())); - __ stlrw(rscratch1, rscratch2); + // Now set thread in native + __ mov(rscratch1, _thread_in_native); + __ lea(rscratch2, Address(rthread, JavaThread::thread_state_offset())); + __ stlrw(rscratch1, rscratch2); + } rt_call(masm, native_func); @@ -1855,6 +1824,21 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, default : ShouldNotReachHere(); } + Label safepoint_in_progress, safepoint_in_progress_done; + Label after_transition; + + // If this is a critical native, check for a safepoint or suspend request after the call. + // If a safepoint is needed, transition to native, then to native_trans to handle + // safepoints like the native methods that are not critical natives. + if (is_critical_native) { + Label needs_safepoint; + __ safepoint_poll(needs_safepoint, false /* at_return */, true /* acquire */, false /* in_nmethod */); + __ ldrw(rscratch1, Address(rthread, JavaThread::suspend_flags_offset())); + __ cbnzw(rscratch1, needs_safepoint); + __ b(after_transition); + __ bind(needs_safepoint); + } + // Switch thread to "native transition" state before reading the synchronization state. // This additional state is necessary because reading and testing the synchronization // state is not atomic w.r.t. GC, as this scenario demonstrates: @@ -1875,16 +1859,23 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, } // check for safepoint operation in progress and/or pending suspend requests - Label safepoint_in_progress, safepoint_in_progress_done; { - __ safepoint_poll_acquire(safepoint_in_progress); + // We need an acquire here to ensure that any subsequent load of the + // global SafepointSynchronize::_state flag is ordered after this load + // of the thread-local polling word. We don't want this poll to + // return false (i.e. not safepointing) and a later poll of the global + // SafepointSynchronize::_state spuriously to return true. + // + // This is to avoid a race when we're in a native->Java transition + // racing the code which wakes up from a safepoint. + + __ safepoint_poll(safepoint_in_progress, true /* at_return */, true /* acquire */, false /* in_nmethod */); __ ldrw(rscratch1, Address(rthread, JavaThread::suspend_flags_offset())); __ cbnzw(rscratch1, safepoint_in_progress); __ bind(safepoint_in_progress_done); } // change thread state - Label after_transition; __ mov(rscratch1, _thread_in_Java); __ lea(rscratch2, Address(rthread, JavaThread::thread_state_offset())); __ stlrw(rscratch1, rscratch2); @@ -2089,22 +2080,12 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, #ifndef PRODUCT assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); #endif - if (!is_critical_native) { - __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans))); - } else { - __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans_and_transition))); - } + __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans))); __ blr(rscratch1); __ maybe_isb(); // Restore any method result value restore_native_result(masm, ret_type, stack_slots); - if (is_critical_native) { - // The call above performed the transition to thread_in_Java so - // skip the transition logic above. - __ b(after_transition); - } - __ b(safepoint_in_progress_done); __ block_comment("} safepoint"); } @@ -2153,12 +2134,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, in_ByteSize(lock_slot_offset*VMRegImpl::stack_slot_size), oop_maps); - if (is_critical_native) { - nm->set_lazy_critical_native(true); - } - return nm; - } // this function returns the adjust size (in number of words) to a c2i adapter @@ -2469,7 +2445,7 @@ void SharedRuntime::generate_deopt_blob() { __ sub(sp, sp, r19); // Push interpreter frames in a loop - __ mov(rscratch1, (address)0xDEADDEAD); // Make a recognizable pattern + __ mov(rscratch1, (uint64_t)0xDEADDEAD); // Make a recognizable pattern __ mov(rscratch2, rscratch1); Label loop; __ bind(loop); diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 412578eea5c..09ea5387165 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -611,6 +611,16 @@ class StubGenerator: public StubCodeGenerator { void array_overlap_test(Label& L_no_overlap, Address::sxtw sf) { __ b(L_no_overlap); } + // Generate indices for iota vector. + address generate_iota_indices(const char *stub_name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", stub_name); + address start = __ pc(); + __ emit_data64(0x0706050403020100, relocInfo::none); + __ emit_data64(0x0F0E0D0C0B0A0908, relocInfo::none); + return start; + } + // The inner part of zero_words(). This is the bulk operation, // zeroing words in blocks, possibly using DC ZVA to do it. The // caller is responsible for zeroing the last few words. @@ -1295,14 +1305,14 @@ class StubGenerator: public StubCodeGenerator { // Scan over array at a for count oops, verifying each one. // Preserves a and count, clobbers rscratch1 and rscratch2. - void verify_oop_array (size_t size, Register a, Register count, Register temp) { + void verify_oop_array (int size, Register a, Register count, Register temp) { Label loop, end; __ mov(rscratch1, a); __ mov(rscratch2, zr); __ bind(loop); __ cmp(rscratch2, count); __ br(Assembler::HS, end); - if (size == (size_t)wordSize) { + if (size == wordSize) { __ ldr(temp, Address(a, rscratch2, Address::lsl(exact_log2(size)))); __ verify_oop(temp); } else { @@ -1333,7 +1343,7 @@ class StubGenerator: public StubCodeGenerator { // disjoint_int_copy_entry is set to the no-overlap entry point // used by generate_conjoint_int_oop_copy(). // - address generate_disjoint_copy(size_t size, bool aligned, bool is_oop, address *entry, + address generate_disjoint_copy(int size, bool aligned, bool is_oop, address *entry, const char *name, bool dest_uninitialized = false) { Register s = c_rarg0, d = c_rarg1, count = c_rarg2; RegSet saved_reg = RegSet::of(s, d, count); @@ -1399,7 +1409,7 @@ class StubGenerator: public StubCodeGenerator { // the hardware handle it. The two dwords within qwords that span // cache line boundaries will still be loaded and stored atomicly. // - address generate_conjoint_copy(size_t size, bool aligned, bool is_oop, address nooverlap_target, + address generate_conjoint_copy(int size, bool aligned, bool is_oop, address nooverlap_target, address *entry, const char *name, bool dest_uninitialized = false) { Register s = c_rarg0, d = c_rarg1, count = c_rarg2; @@ -1650,7 +1660,7 @@ class StubGenerator: public StubCodeGenerator { address generate_disjoint_oop_copy(bool aligned, address *entry, const char *name, bool dest_uninitialized) { const bool is_oop = true; - const size_t size = UseCompressedOops ? sizeof (jint) : sizeof (jlong); + const int size = UseCompressedOops ? sizeof (jint) : sizeof (jlong); return generate_disjoint_copy(size, aligned, is_oop, entry, name, dest_uninitialized); } @@ -1668,7 +1678,7 @@ class StubGenerator: public StubCodeGenerator { address nooverlap_target, address *entry, const char *name, bool dest_uninitialized) { const bool is_oop = true; - const size_t size = UseCompressedOops ? sizeof (jint) : sizeof (jlong); + const int size = UseCompressedOops ? sizeof (jint) : sizeof (jlong); return generate_conjoint_copy(size, aligned, is_oop, nooverlap_target, entry, name, dest_uninitialized); } @@ -3299,6 +3309,225 @@ class StubGenerator: public StubCodeGenerator { return start; } + // Arguments: + // + // Inputs: + // c_rarg0 - byte[] source+offset + // c_rarg1 - byte[] SHA.state + // c_rarg2 - int digest_length + // c_rarg3 - int offset + // c_rarg4 - int limit + // + address generate_sha3_implCompress(bool multi_block, const char *name) { + static const uint64_t round_consts[24] = { + 0x0000000000000001L, 0x0000000000008082L, 0x800000000000808AL, + 0x8000000080008000L, 0x000000000000808BL, 0x0000000080000001L, + 0x8000000080008081L, 0x8000000000008009L, 0x000000000000008AL, + 0x0000000000000088L, 0x0000000080008009L, 0x000000008000000AL, + 0x000000008000808BL, 0x800000000000008BL, 0x8000000000008089L, + 0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L, + 0x000000000000800AL, 0x800000008000000AL, 0x8000000080008081L, + 0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L + }; + + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + Register buf = c_rarg0; + Register state = c_rarg1; + Register digest_length = c_rarg2; + Register ofs = c_rarg3; + Register limit = c_rarg4; + + Label sha3_loop, rounds24_loop; + Label sha3_512, sha3_384_or_224, sha3_256; + + __ stpd(v8, v9, __ pre(sp, -64)); + __ stpd(v10, v11, Address(sp, 16)); + __ stpd(v12, v13, Address(sp, 32)); + __ stpd(v14, v15, Address(sp, 48)); + + // load state + __ add(rscratch1, state, 32); + __ ld1(v0, v1, v2, v3, __ T1D, state); + __ ld1(v4, v5, v6, v7, __ T1D, __ post(rscratch1, 32)); + __ ld1(v8, v9, v10, v11, __ T1D, __ post(rscratch1, 32)); + __ ld1(v12, v13, v14, v15, __ T1D, __ post(rscratch1, 32)); + __ ld1(v16, v17, v18, v19, __ T1D, __ post(rscratch1, 32)); + __ ld1(v20, v21, v22, v23, __ T1D, __ post(rscratch1, 32)); + __ ld1(v24, __ T1D, rscratch1); + + __ BIND(sha3_loop); + + // 24 keccak rounds + __ movw(rscratch2, 24); + + // load round_constants base + __ lea(rscratch1, ExternalAddress((address) round_consts)); + + // load input + __ ld1(v25, v26, v27, v28, __ T8B, __ post(buf, 32)); + __ ld1(v29, v30, v31, __ T8B, __ post(buf, 24)); + __ eor(v0, __ T8B, v0, v25); + __ eor(v1, __ T8B, v1, v26); + __ eor(v2, __ T8B, v2, v27); + __ eor(v3, __ T8B, v3, v28); + __ eor(v4, __ T8B, v4, v29); + __ eor(v5, __ T8B, v5, v30); + __ eor(v6, __ T8B, v6, v31); + + // digest_length == 64, SHA3-512 + __ tbnz(digest_length, 6, sha3_512); + + __ ld1(v25, v26, v27, v28, __ T8B, __ post(buf, 32)); + __ ld1(v29, v30, __ T8B, __ post(buf, 16)); + __ eor(v7, __ T8B, v7, v25); + __ eor(v8, __ T8B, v8, v26); + __ eor(v9, __ T8B, v9, v27); + __ eor(v10, __ T8B, v10, v28); + __ eor(v11, __ T8B, v11, v29); + __ eor(v12, __ T8B, v12, v30); + + // digest_length == 28, SHA3-224; digest_length == 48, SHA3-384 + __ tbnz(digest_length, 4, sha3_384_or_224); + + // SHA3-256 + __ ld1(v25, v26, v27, v28, __ T8B, __ post(buf, 32)); + __ eor(v13, __ T8B, v13, v25); + __ eor(v14, __ T8B, v14, v26); + __ eor(v15, __ T8B, v15, v27); + __ eor(v16, __ T8B, v16, v28); + __ b(rounds24_loop); + + __ BIND(sha3_384_or_224); + __ tbz(digest_length, 2, rounds24_loop); // bit 2 cleared? SHA-384 + + // SHA3-224 + __ ld1(v25, v26, v27, v28, __ T8B, __ post(buf, 32)); + __ ld1(v29, __ T8B, __ post(buf, 8)); + __ eor(v13, __ T8B, v13, v25); + __ eor(v14, __ T8B, v14, v26); + __ eor(v15, __ T8B, v15, v27); + __ eor(v16, __ T8B, v16, v28); + __ eor(v17, __ T8B, v17, v29); + __ b(rounds24_loop); + + __ BIND(sha3_512); + __ ld1(v25, v26, __ T8B, __ post(buf, 16)); + __ eor(v7, __ T8B, v7, v25); + __ eor(v8, __ T8B, v8, v26); + + __ BIND(rounds24_loop); + __ subw(rscratch2, rscratch2, 1); + + __ eor3(v29, __ T16B, v4, v9, v14); + __ eor3(v26, __ T16B, v1, v6, v11); + __ eor3(v28, __ T16B, v3, v8, v13); + __ eor3(v25, __ T16B, v0, v5, v10); + __ eor3(v27, __ T16B, v2, v7, v12); + __ eor3(v29, __ T16B, v29, v19, v24); + __ eor3(v26, __ T16B, v26, v16, v21); + __ eor3(v28, __ T16B, v28, v18, v23); + __ eor3(v25, __ T16B, v25, v15, v20); + __ eor3(v27, __ T16B, v27, v17, v22); + + __ rax1(v30, __ T2D, v29, v26); + __ rax1(v26, __ T2D, v26, v28); + __ rax1(v28, __ T2D, v28, v25); + __ rax1(v25, __ T2D, v25, v27); + __ rax1(v27, __ T2D, v27, v29); + + __ eor(v0, __ T16B, v0, v30); + __ xar(v29, __ T2D, v1, v25, (64 - 1)); + __ xar(v1, __ T2D, v6, v25, (64 - 44)); + __ xar(v6, __ T2D, v9, v28, (64 - 20)); + __ xar(v9, __ T2D, v22, v26, (64 - 61)); + __ xar(v22, __ T2D, v14, v28, (64 - 39)); + __ xar(v14, __ T2D, v20, v30, (64 - 18)); + __ xar(v31, __ T2D, v2, v26, (64 - 62)); + __ xar(v2, __ T2D, v12, v26, (64 - 43)); + __ xar(v12, __ T2D, v13, v27, (64 - 25)); + __ xar(v13, __ T2D, v19, v28, (64 - 8)); + __ xar(v19, __ T2D, v23, v27, (64 - 56)); + __ xar(v23, __ T2D, v15, v30, (64 - 41)); + __ xar(v15, __ T2D, v4, v28, (64 - 27)); + __ xar(v28, __ T2D, v24, v28, (64 - 14)); + __ xar(v24, __ T2D, v21, v25, (64 - 2)); + __ xar(v8, __ T2D, v8, v27, (64 - 55)); + __ xar(v4, __ T2D, v16, v25, (64 - 45)); + __ xar(v16, __ T2D, v5, v30, (64 - 36)); + __ xar(v5, __ T2D, v3, v27, (64 - 28)); + __ xar(v27, __ T2D, v18, v27, (64 - 21)); + __ xar(v3, __ T2D, v17, v26, (64 - 15)); + __ xar(v25, __ T2D, v11, v25, (64 - 10)); + __ xar(v26, __ T2D, v7, v26, (64 - 6)); + __ xar(v30, __ T2D, v10, v30, (64 - 3)); + + __ bcax(v20, __ T16B, v31, v22, v8); + __ bcax(v21, __ T16B, v8, v23, v22); + __ bcax(v22, __ T16B, v22, v24, v23); + __ bcax(v23, __ T16B, v23, v31, v24); + __ bcax(v24, __ T16B, v24, v8, v31); + + __ ld1r(v31, __ T2D, __ post(rscratch1, 8)); + + __ bcax(v17, __ T16B, v25, v19, v3); + __ bcax(v18, __ T16B, v3, v15, v19); + __ bcax(v19, __ T16B, v19, v16, v15); + __ bcax(v15, __ T16B, v15, v25, v16); + __ bcax(v16, __ T16B, v16, v3, v25); + + __ bcax(v10, __ T16B, v29, v12, v26); + __ bcax(v11, __ T16B, v26, v13, v12); + __ bcax(v12, __ T16B, v12, v14, v13); + __ bcax(v13, __ T16B, v13, v29, v14); + __ bcax(v14, __ T16B, v14, v26, v29); + + __ bcax(v7, __ T16B, v30, v9, v4); + __ bcax(v8, __ T16B, v4, v5, v9); + __ bcax(v9, __ T16B, v9, v6, v5); + __ bcax(v5, __ T16B, v5, v30, v6); + __ bcax(v6, __ T16B, v6, v4, v30); + + __ bcax(v3, __ T16B, v27, v0, v28); + __ bcax(v4, __ T16B, v28, v1, v0); + __ bcax(v0, __ T16B, v0, v2, v1); + __ bcax(v1, __ T16B, v1, v27, v2); + __ bcax(v2, __ T16B, v2, v28, v27); + + __ eor(v0, __ T16B, v0, v31); + + __ cbnzw(rscratch2, rounds24_loop); + + if (multi_block) { + // block_size = 200 - 2 * digest_length, ofs += block_size + __ add(ofs, ofs, 200); + __ sub(ofs, ofs, digest_length, Assembler::LSL, 1); + + __ cmp(ofs, limit); + __ br(Assembler::LE, sha3_loop); + __ mov(c_rarg0, ofs); // return ofs + } + + __ st1(v0, v1, v2, v3, __ T1D, __ post(state, 32)); + __ st1(v4, v5, v6, v7, __ T1D, __ post(state, 32)); + __ st1(v8, v9, v10, v11, __ T1D, __ post(state, 32)); + __ st1(v12, v13, v14, v15, __ T1D, __ post(state, 32)); + __ st1(v16, v17, v18, v19, __ T1D, __ post(state, 32)); + __ st1(v20, v21, v22, v23, __ T1D, __ post(state, 32)); + __ st1(v24, __ T1D, state); + + __ ldpd(v14, v15, Address(sp, 48)); + __ ldpd(v12, v13, Address(sp, 32)); + __ ldpd(v10, v11, Address(sp, 16)); + __ ldpd(v8, v9, __ post(sp, 64)); + + __ ret(lr); + + return start; + } + // Safefetch stubs. void generate_safefetch(const char* name, int size, address* entry, address* fault_pc, address* continuation_pc) { @@ -3739,6 +3968,238 @@ class StubGenerator: public StubCodeGenerator { return start; } + // Arguments: + // + // Input: + // c_rarg0 - newArr address + // c_rarg1 - oldArr address + // c_rarg2 - newIdx + // c_rarg3 - shiftCount + // c_rarg4 - numIter + // + address generate_bigIntegerRightShift() { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "bigIntegerRightShiftWorker"); + address start = __ pc(); + + Label ShiftSIMDLoop, ShiftTwoLoop, ShiftThree, ShiftTwo, ShiftOne, Exit; + + Register newArr = c_rarg0; + Register oldArr = c_rarg1; + Register newIdx = c_rarg2; + Register shiftCount = c_rarg3; + Register numIter = c_rarg4; + Register idx = numIter; + + Register newArrCur = rscratch1; + Register shiftRevCount = rscratch2; + Register oldArrCur = r13; + Register oldArrNext = r14; + + FloatRegister oldElem0 = v0; + FloatRegister oldElem1 = v1; + FloatRegister newElem = v2; + FloatRegister shiftVCount = v3; + FloatRegister shiftVRevCount = v4; + + __ cbz(idx, Exit); + + __ add(newArr, newArr, newIdx, Assembler::LSL, 2); + + // left shift count + __ movw(shiftRevCount, 32); + __ subw(shiftRevCount, shiftRevCount, shiftCount); + + // numIter too small to allow a 4-words SIMD loop, rolling back + __ cmp(numIter, (u1)4); + __ br(Assembler::LT, ShiftThree); + + __ dup(shiftVCount, __ T4S, shiftCount); + __ dup(shiftVRevCount, __ T4S, shiftRevCount); + __ negr(shiftVCount, __ T4S, shiftVCount); + + __ BIND(ShiftSIMDLoop); + + // Calculate the load addresses + __ sub(idx, idx, 4); + __ add(oldArrNext, oldArr, idx, Assembler::LSL, 2); + __ add(newArrCur, newArr, idx, Assembler::LSL, 2); + __ add(oldArrCur, oldArrNext, 4); + + // Load 4 words and process + __ ld1(oldElem0, __ T4S, Address(oldArrCur)); + __ ld1(oldElem1, __ T4S, Address(oldArrNext)); + __ ushl(oldElem0, __ T4S, oldElem0, shiftVCount); + __ ushl(oldElem1, __ T4S, oldElem1, shiftVRevCount); + __ orr(newElem, __ T16B, oldElem0, oldElem1); + __ st1(newElem, __ T4S, Address(newArrCur)); + + __ cmp(idx, (u1)4); + __ br(Assembler::LT, ShiftTwoLoop); + __ b(ShiftSIMDLoop); + + __ BIND(ShiftTwoLoop); + __ cbz(idx, Exit); + __ cmp(idx, (u1)1); + __ br(Assembler::EQ, ShiftOne); + + // Calculate the load addresses + __ sub(idx, idx, 2); + __ add(oldArrNext, oldArr, idx, Assembler::LSL, 2); + __ add(newArrCur, newArr, idx, Assembler::LSL, 2); + __ add(oldArrCur, oldArrNext, 4); + + // Load 2 words and process + __ ld1(oldElem0, __ T2S, Address(oldArrCur)); + __ ld1(oldElem1, __ T2S, Address(oldArrNext)); + __ ushl(oldElem0, __ T2S, oldElem0, shiftVCount); + __ ushl(oldElem1, __ T2S, oldElem1, shiftVRevCount); + __ orr(newElem, __ T8B, oldElem0, oldElem1); + __ st1(newElem, __ T2S, Address(newArrCur)); + __ b(ShiftTwoLoop); + + __ BIND(ShiftThree); + __ tbz(idx, 1, ShiftOne); + __ tbz(idx, 0, ShiftTwo); + __ ldrw(r10, Address(oldArr, 12)); + __ ldrw(r11, Address(oldArr, 8)); + __ lsrvw(r10, r10, shiftCount); + __ lslvw(r11, r11, shiftRevCount); + __ orrw(r12, r10, r11); + __ strw(r12, Address(newArr, 8)); + + __ BIND(ShiftTwo); + __ ldrw(r10, Address(oldArr, 8)); + __ ldrw(r11, Address(oldArr, 4)); + __ lsrvw(r10, r10, shiftCount); + __ lslvw(r11, r11, shiftRevCount); + __ orrw(r12, r10, r11); + __ strw(r12, Address(newArr, 4)); + + __ BIND(ShiftOne); + __ ldrw(r10, Address(oldArr, 4)); + __ ldrw(r11, Address(oldArr)); + __ lsrvw(r10, r10, shiftCount); + __ lslvw(r11, r11, shiftRevCount); + __ orrw(r12, r10, r11); + __ strw(r12, Address(newArr)); + + __ BIND(Exit); + __ ret(lr); + + return start; + } + + // Arguments: + // + // Input: + // c_rarg0 - newArr address + // c_rarg1 - oldArr address + // c_rarg2 - newIdx + // c_rarg3 - shiftCount + // c_rarg4 - numIter + // + address generate_bigIntegerLeftShift() { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "bigIntegerLeftShiftWorker"); + address start = __ pc(); + + Label ShiftSIMDLoop, ShiftTwoLoop, ShiftThree, ShiftTwo, ShiftOne, Exit; + + Register newArr = c_rarg0; + Register oldArr = c_rarg1; + Register newIdx = c_rarg2; + Register shiftCount = c_rarg3; + Register numIter = c_rarg4; + + Register shiftRevCount = rscratch1; + Register oldArrNext = rscratch2; + + FloatRegister oldElem0 = v0; + FloatRegister oldElem1 = v1; + FloatRegister newElem = v2; + FloatRegister shiftVCount = v3; + FloatRegister shiftVRevCount = v4; + + __ cbz(numIter, Exit); + + __ add(oldArrNext, oldArr, 4); + __ add(newArr, newArr, newIdx, Assembler::LSL, 2); + + // right shift count + __ movw(shiftRevCount, 32); + __ subw(shiftRevCount, shiftRevCount, shiftCount); + + // numIter too small to allow a 4-words SIMD loop, rolling back + __ cmp(numIter, (u1)4); + __ br(Assembler::LT, ShiftThree); + + __ dup(shiftVCount, __ T4S, shiftCount); + __ dup(shiftVRevCount, __ T4S, shiftRevCount); + __ negr(shiftVRevCount, __ T4S, shiftVRevCount); + + __ BIND(ShiftSIMDLoop); + + // load 4 words and process + __ ld1(oldElem0, __ T4S, __ post(oldArr, 16)); + __ ld1(oldElem1, __ T4S, __ post(oldArrNext, 16)); + __ ushl(oldElem0, __ T4S, oldElem0, shiftVCount); + __ ushl(oldElem1, __ T4S, oldElem1, shiftVRevCount); + __ orr(newElem, __ T16B, oldElem0, oldElem1); + __ st1(newElem, __ T4S, __ post(newArr, 16)); + __ sub(numIter, numIter, 4); + + __ cmp(numIter, (u1)4); + __ br(Assembler::LT, ShiftTwoLoop); + __ b(ShiftSIMDLoop); + + __ BIND(ShiftTwoLoop); + __ cbz(numIter, Exit); + __ cmp(numIter, (u1)1); + __ br(Assembler::EQ, ShiftOne); + + // load 2 words and process + __ ld1(oldElem0, __ T2S, __ post(oldArr, 8)); + __ ld1(oldElem1, __ T2S, __ post(oldArrNext, 8)); + __ ushl(oldElem0, __ T2S, oldElem0, shiftVCount); + __ ushl(oldElem1, __ T2S, oldElem1, shiftVRevCount); + __ orr(newElem, __ T8B, oldElem0, oldElem1); + __ st1(newElem, __ T2S, __ post(newArr, 8)); + __ sub(numIter, numIter, 2); + __ b(ShiftTwoLoop); + + __ BIND(ShiftThree); + __ ldrw(r10, __ post(oldArr, 4)); + __ ldrw(r11, __ post(oldArrNext, 4)); + __ lslvw(r10, r10, shiftCount); + __ lsrvw(r11, r11, shiftRevCount); + __ orrw(r12, r10, r11); + __ strw(r12, __ post(newArr, 4)); + __ tbz(numIter, 1, Exit); + __ tbz(numIter, 0, ShiftOne); + + __ BIND(ShiftTwo); + __ ldrw(r10, __ post(oldArr, 4)); + __ ldrw(r11, __ post(oldArrNext, 4)); + __ lslvw(r10, r10, shiftCount); + __ lsrvw(r11, r11, shiftRevCount); + __ orrw(r12, r10, r11); + __ strw(r12, __ post(newArr, 4)); + + __ BIND(ShiftOne); + __ ldrw(r10, Address(oldArr)); + __ ldrw(r11, Address(oldArrNext)); + __ lslvw(r10, r10, shiftCount); + __ lsrvw(r11, r11, shiftRevCount); + __ orrw(r12, r10, r11); + __ strw(r12, Address(newArr)); + + __ BIND(Exit); + __ ret(lr); + + return start; + } + void ghash_multiply(FloatRegister result_lo, FloatRegister result_hi, FloatRegister a, FloatRegister b, FloatRegister a1_xor_a0, FloatRegister tmp1, FloatRegister tmp2, FloatRegister tmp3, FloatRegister tmp4) { @@ -4942,6 +5403,150 @@ class StubGenerator: public StubCodeGenerator { return start; } + void generate_base64_encode_simdround(Register src, Register dst, + FloatRegister codec, u8 size) { + + FloatRegister in0 = v4, in1 = v5, in2 = v6; + FloatRegister out0 = v16, out1 = v17, out2 = v18, out3 = v19; + FloatRegister ind0 = v20, ind1 = v21, ind2 = v22, ind3 = v23; + + Assembler::SIMD_Arrangement arrangement = size == 16 ? __ T16B : __ T8B; + + __ ld3(in0, in1, in2, arrangement, __ post(src, 3 * size)); + + __ ushr(ind0, arrangement, in0, 2); + + __ ushr(ind1, arrangement, in1, 2); + __ shl(in0, arrangement, in0, 6); + __ orr(ind1, arrangement, ind1, in0); + __ ushr(ind1, arrangement, ind1, 2); + + __ ushr(ind2, arrangement, in2, 4); + __ shl(in1, arrangement, in1, 4); + __ orr(ind2, arrangement, in1, ind2); + __ ushr(ind2, arrangement, ind2, 2); + + __ shl(ind3, arrangement, in2, 2); + __ ushr(ind3, arrangement, ind3, 2); + + __ tbl(out0, arrangement, codec, 4, ind0); + __ tbl(out1, arrangement, codec, 4, ind1); + __ tbl(out2, arrangement, codec, 4, ind2); + __ tbl(out3, arrangement, codec, 4, ind3); + + __ st4(out0, out1, out2, out3, arrangement, __ post(dst, 4 * size)); + } + + /** + * Arguments: + * + * Input: + * c_rarg0 - src_start + * c_rarg1 - src_offset + * c_rarg2 - src_length + * c_rarg3 - dest_start + * c_rarg4 - dest_offset + * c_rarg5 - isURL + * + */ + address generate_base64_encodeBlock() { + + static const char toBase64[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' + }; + + static const char toBase64URL[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' + }; + + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "encodeBlock"); + address start = __ pc(); + + Register src = c_rarg0; // source array + Register soff = c_rarg1; // source start offset + Register send = c_rarg2; // source end offset + Register dst = c_rarg3; // dest array + Register doff = c_rarg4; // position for writing to dest array + Register isURL = c_rarg5; // Base64 or URL chracter set + + // c_rarg6 and c_rarg7 are free to use as temps + Register codec = c_rarg6; + Register length = c_rarg7; + + Label ProcessData, Process48B, Process24B, Process3B, SIMDExit, Exit; + + __ add(src, src, soff); + __ add(dst, dst, doff); + __ sub(length, send, soff); + + // load the codec base address + __ lea(codec, ExternalAddress((address) toBase64)); + __ cbz(isURL, ProcessData); + __ lea(codec, ExternalAddress((address) toBase64URL)); + + __ BIND(ProcessData); + + // too short to formup a SIMD loop, roll back + __ cmp(length, (u1)24); + __ br(Assembler::LT, Process3B); + + __ ld1(v0, v1, v2, v3, __ T16B, Address(codec)); + + __ BIND(Process48B); + __ cmp(length, (u1)48); + __ br(Assembler::LT, Process24B); + generate_base64_encode_simdround(src, dst, v0, 16); + __ sub(length, length, 48); + __ b(Process48B); + + __ BIND(Process24B); + __ cmp(length, (u1)24); + __ br(Assembler::LT, SIMDExit); + generate_base64_encode_simdround(src, dst, v0, 8); + __ sub(length, length, 24); + + __ BIND(SIMDExit); + __ cbz(length, Exit); + + __ BIND(Process3B); + // 3 src bytes, 24 bits + __ ldrb(r10, __ post(src, 1)); + __ ldrb(r11, __ post(src, 1)); + __ ldrb(r12, __ post(src, 1)); + __ orrw(r11, r11, r10, Assembler::LSL, 8); + __ orrw(r12, r12, r11, Assembler::LSL, 8); + // codec index + __ ubfmw(r15, r12, 18, 23); + __ ubfmw(r14, r12, 12, 17); + __ ubfmw(r13, r12, 6, 11); + __ andw(r12, r12, 63); + // get the code based on the codec + __ ldrb(r15, Address(codec, r15, Address::uxtw(0))); + __ ldrb(r14, Address(codec, r14, Address::uxtw(0))); + __ ldrb(r13, Address(codec, r13, Address::uxtw(0))); + __ ldrb(r12, Address(codec, r12, Address::uxtw(0))); + __ strb(r15, __ post(dst, 1)); + __ strb(r14, __ post(dst, 1)); + __ strb(r13, __ post(dst, 1)); + __ strb(r12, __ post(dst, 1)); + __ sub(length, length, 3); + __ cbnz(length, Process3B); + + __ BIND(Exit); + __ ret(lr); + + return start; + } + // Continuation point for throwing of implicit exceptions that are // not handled in the current activation. Fabricates an exception // oop and initiates normal exception dispatching in this @@ -5958,6 +6563,8 @@ class StubGenerator: public StubCodeGenerator { SharedRuntime:: throw_NullPointerException_at_call)); + StubRoutines::aarch64::_vector_iota_indices = generate_iota_indices("iota_indices"); + // arraycopy stubs used by compilers generate_arraycopy_stubs(); @@ -5993,6 +6600,11 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_mulAdd = generate_mulAdd(); } + if (UseSIMDForBigIntegerShiftIntrinsics) { + StubRoutines::_bigIntegerRightShiftWorker = generate_bigIntegerRightShift(); + StubRoutines::_bigIntegerLeftShiftWorker = generate_bigIntegerLeftShift(); + } + if (UseMontgomeryMultiplyIntrinsic) { StubCodeMark mark(this, "StubRoutines", "montgomeryMultiply"); MontgomeryMultiplyGenerator g(_masm, /*squaring*/false); @@ -6013,6 +6625,10 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks(); } + if (UseBASE64Intrinsics) { + StubRoutines::_base64_encodeBlock = generate_base64_encodeBlock(); + } + // data cache line writeback StubRoutines::_data_cache_writeback = generate_data_cache_writeback(); StubRoutines::_data_cache_writeback_sync = generate_data_cache_writeback_sync(); @@ -6036,6 +6652,10 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_sha512_implCompress = generate_sha512_implCompress(false, "sha512_implCompress"); StubRoutines::_sha512_implCompressMB = generate_sha512_implCompress(true, "sha512_implCompressMB"); } + if (UseSHA3Intrinsics) { + StubRoutines::_sha3_implCompress = generate_sha3_implCompress(false, "sha3_implCompress"); + StubRoutines::_sha3_implCompressMB = generate_sha3_implCompress(true, "sha3_implCompressMB"); + } // generate Adler32 intrinsics code if (UseAdler32Intrinsics) { diff --git a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp index b2d0d5dbff8..f471209a4c0 100644 --- a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp @@ -40,6 +40,7 @@ address StubRoutines::aarch64::_f2i_fixup = NULL; address StubRoutines::aarch64::_f2l_fixup = NULL; address StubRoutines::aarch64::_d2i_fixup = NULL; address StubRoutines::aarch64::_d2l_fixup = NULL; +address StubRoutines::aarch64::_vector_iota_indices = NULL; address StubRoutines::aarch64::_float_sign_mask = NULL; address StubRoutines::aarch64::_float_sign_flip = NULL; address StubRoutines::aarch64::_double_sign_mask = NULL; diff --git a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp index 4ace7b5c808..6960a19b3f5 100644 --- a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp @@ -51,6 +51,7 @@ class aarch64 { static address _d2i_fixup; static address _d2l_fixup; + static address _vector_iota_indices; static address _float_sign_mask; static address _float_sign_flip; static address _double_sign_mask; @@ -106,6 +107,10 @@ class aarch64 { return _d2l_fixup; } + static address vector_iota_indices() { + return _vector_iota_indices; + } + static address float_sign_mask() { return _float_sign_mask; diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp index 21566592a9f..874d8ce2766 100644 --- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp @@ -980,7 +980,7 @@ address TemplateInterpreterGenerator::generate_CRC32_update_entry() { Label slow_path; // If we need a safepoint check, generate full interpreter entry. - __ safepoint_poll(slow_path); + __ safepoint_poll(slow_path, false /* at_return */, false /* acquire */, false /* in_nmethod */); // We don't generate local frame and don't align stack because // we call stub code and there is no safepoint on this path. @@ -1029,7 +1029,7 @@ address TemplateInterpreterGenerator::generate_CRC32_updateBytes_entry(AbstractI Label slow_path; // If we need a safepoint check, generate full interpreter entry. - __ safepoint_poll(slow_path); + __ safepoint_poll(slow_path, false /* at_return */, false /* acquire */, false /* in_nmethod */); // We don't generate local frame and don't align stack because // we call stub code and there is no safepoint on this path. @@ -1120,7 +1120,7 @@ void TemplateInterpreterGenerator::bang_stack_shadow_pages(bool native_call) { // an interpreter frame with greater than a page of locals, so each page // needs to be checked. Only true for non-native. if (UseStackBanging) { - const int n_shadow_pages = StackOverflow::stack_shadow_zone_size() / os::vm_page_size(); + const int n_shadow_pages = (int)(StackOverflow::stack_shadow_zone_size() / os::vm_page_size()); const int start_page = native_call ? n_shadow_pages : 1; const int page_size = os::vm_page_size(); for (int pages = start_page; pages <= n_shadow_pages ; pages++) { @@ -1388,7 +1388,16 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // check for safepoint operation in progress and/or pending suspend requests { Label L, Continue; - __ safepoint_poll_acquire(L); + + // We need an acquire here to ensure that any subsequent load of the + // global SafepointSynchronize::_state flag is ordered after this load + // of the thread-local polling word. We don't want this poll to + // return false (i.e. not safepointing) and a later poll of the global + // SafepointSynchronize::_state spuriously to return true. + // + // This is to avoid a race when we're in a native->Java transition + // racing the code which wakes up from a safepoint. + __ safepoint_poll(L, true /* at_return */, true /* acquire */, false /* in_nmethod */); __ ldrw(rscratch2, Address(rthread, JavaThread::suspend_flags_offset())); __ cbz(rscratch2, Continue); __ bind(L); diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index 842f07ae9a0..811783fcb7d 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp @@ -1906,7 +1906,7 @@ void TemplateTable::branch(bool is_jsr, bool is_wide) __ dispatch_only(vtos, /*generate_poll*/true); if (UseLoopCounter) { - if (ProfileInterpreter) { + if (ProfileInterpreter && !TieredCompilation) { // Out-of-line code to allocate method data oop. __ bind(profile_method); __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::profile_method)); diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 343a2bbd50f..2a6553d9c21 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -181,10 +181,6 @@ void VM_Version::initialize() { } if (_cpu == CPU_ARM && (_model == 0xd07 || _model2 == 0xd07)) _features |= CPU_STXR_PREFETCH; - // If an olde style /proc/cpuinfo (cores == 1) then if _model is an A57 (0xd07) - // we assume the worst and assume we could be on a big little system and have - // undisclosed A53 cores which we could be swapped to at any stage - if (_cpu == CPU_ARM && os::processor_count() == 1 && _model == 0xd07) _features |= CPU_A53MAC; char buf[512]; sprintf(buf, "0x%02x:0x%x:0x%03x:%d", _cpu, _variant, _model, _revision); @@ -194,6 +190,7 @@ void VM_Version::initialize() { if (_features & CPU_AES) strcat(buf, ", aes"); if (_features & CPU_SHA1) strcat(buf, ", sha1"); if (_features & CPU_SHA2) strcat(buf, ", sha256"); + if (_features & CPU_SHA3) strcat(buf, ", sha3"); if (_features & CPU_SHA512) strcat(buf, ", sha512"); if (_features & CPU_LSE) strcat(buf, ", lse"); if (_features & CPU_SVE) strcat(buf, ", sve"); @@ -275,7 +272,7 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseMD5Intrinsics, false); } - if (_features & (CPU_SHA1 | CPU_SHA2)) { + if (_features & (CPU_SHA1 | CPU_SHA2 | CPU_SHA3 | CPU_SHA512)) { if (FLAG_IS_DEFAULT(UseSHA)) { FLAG_SET_DEFAULT(UseSHA, true); } @@ -302,6 +299,16 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA256Intrinsics, false); } + if (UseSHA && (_features & CPU_SHA3)) { + // Do not auto-enable UseSHA3Intrinsics until it has been fully tested on hardware + // if (FLAG_IS_DEFAULT(UseSHA3Intrinsics)) { + // FLAG_SET_DEFAULT(UseSHA3Intrinsics, true); + // } + } else if (UseSHA3Intrinsics) { + warning("Intrinsics for SHA3-224, SHA3-256, SHA3-384 and SHA3-512 crypto hash functions not available on this CPU."); + FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); + } + if (UseSHA && (_features & CPU_SHA512)) { // Do not auto-enable UseSHA512Intrinsics until it has been fully tested on hardware // if (FLAG_IS_DEFAULT(UseSHA512Intrinsics)) { @@ -312,7 +319,7 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA512Intrinsics, false); } - if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics)) { + if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA3Intrinsics || UseSHA512Intrinsics)) { FLAG_SET_DEFAULT(UseSHA, false); } @@ -325,6 +332,10 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseGHASHIntrinsics, false); } + if (FLAG_IS_DEFAULT(UseBASE64Intrinsics)) { + UseBASE64Intrinsics = true; + } + if (is_zva_enabled()) { if (FLAG_IS_DEFAULT(UseBlockZeroing)) { FLAG_SET_DEFAULT(UseBlockZeroing, true); @@ -390,7 +401,7 @@ void VM_Version::initialize() { warning("SVE does not support vector length less than 16 bytes. Disabling SVE."); UseSVE = 0; } else if ((MaxVectorSize % 16) == 0 && is_power_of_2(MaxVectorSize)) { - int new_vl = set_and_get_current_sve_vector_lenght(MaxVectorSize); + int new_vl = set_and_get_current_sve_vector_length(MaxVectorSize); _initial_sve_vector_length = new_vl; // Update MaxVectorSize to the largest supported value. if (new_vl < 0) { diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp index 292550529b4..45838f87072 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp @@ -51,7 +51,7 @@ class VM_Version : public Abstract_VM_Version { // Sets the SVE length and returns a new actual value or negative on error. // If the len is larger than the system largest supported SVE vector length, // the function sets the largest supported value. - static int set_and_get_current_sve_vector_lenght(int len); + static int set_and_get_current_sve_vector_length(int len); static int get_current_sve_vector_length(); public: @@ -103,6 +103,7 @@ class VM_Version : public Abstract_VM_Version { CPU_CRC32 = (1<<7), CPU_LSE = (1<<8), CPU_DCPOP = (1<<16), + CPU_SHA3 = (1<<17), CPU_SHA512 = (1<<21), CPU_SVE = (1<<22), // flags above must follow Linux HWCAP @@ -128,6 +129,7 @@ class VM_Version : public Abstract_VM_Version { static int get_initial_sve_vector_length() { return _initial_sve_vector_length; }; static bool supports_fast_class_init_checks() { return true; } + constexpr static bool supports_stack_watermark_barrier() { return true; } }; #endif // CPU_AARCH64_VM_VERSION_AARCH64_HPP diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 4c237673181..b7c6ec48896 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -993,6 +993,10 @@ const bool Matcher::has_predicated_vectors(void) { return false; } +bool Matcher::supports_vector_variable_shifts(void) { + return VM_Version::has_simd(); +} + const int Matcher::float_pressure(int default_pressure_threshold) { return default_pressure_threshold; } @@ -1155,10 +1159,6 @@ const bool Matcher::rematerialize_float_constants = false; // Java calling convention forces doubles to be aligned. const bool Matcher::misaligned_doubles_ok = false; -// No-op on ARM. -void Matcher::pd_implicit_null_fixup(MachNode *node, uint idx) { -} - // Advertise here if the CPU requires explicit rounding operations to implement strictfp mode. const bool Matcher::strict_fp_requires_explicit_rounding = false; @@ -1663,7 +1663,6 @@ frame %{ // These two registers define part of the calling convention // between compiled code and the interpreter. inline_cache_reg(R_Ricklass); // Inline Cache Register or Method* for I2C - interpreter_method_reg(R_Rmethod); // Method Register when calling interpreter // Optional: name the operand used by cisc-spilling to access [stack_pointer + offset] cisc_spilling_operand_name(indOffset); @@ -2523,14 +2522,6 @@ operand inline_cache_regP(iRegP reg) %{ interface(REG_INTER); %} -operand interpreter_method_regP(iRegP reg) %{ - constraint(ALLOC_IN_RC(Rmethod_regP)); - match(reg); - format %{ %} - interface(REG_INTER); -%} - - //----------Complex Operands--------------------------------------------------- // Indirect Memory Reference operand indirect(sp_ptr_RegP reg) %{ diff --git a/src/hotspot/cpu/arm/arm_32.ad b/src/hotspot/cpu/arm/arm_32.ad index 177c1a7cae0..09fce8c4c4f 100644 --- a/src/hotspot/cpu/arm/arm_32.ad +++ b/src/hotspot/cpu/arm/arm_32.ad @@ -182,11 +182,11 @@ alloc_class chunk0( alloc_class chunk1( R_S16, R_S17, R_S18, R_S19, R_S20, R_S21, R_S22, R_S23, R_S24, R_S25, R_S26, R_S27, R_S28, R_S29, R_S30, R_S31, - R_S0, R_S1, R_S2, R_S3, R_S4, R_S5, R_S6, R_S7, + R_S0, R_S1, R_S2, R_S3, R_S4, R_S5, R_S6, R_S7, R_S8, R_S9, R_S10, R_S11, R_S12, R_S13, R_S14, R_S15, - R_D16, R_D16x,R_D17, R_D17x,R_D18, R_D18x,R_D19, R_D19x, - R_D20, R_D20x,R_D21, R_D21x,R_D22, R_D22x,R_D23, R_D23x, - R_D24, R_D24x,R_D25, R_D25x,R_D26, R_D26x,R_D27, R_D27x, + R_D16, R_D16x,R_D17, R_D17x,R_D18, R_D18x,R_D19, R_D19x, + R_D20, R_D20x,R_D21, R_D21x,R_D22, R_D22x,R_D23, R_D23x, + R_D24, R_D24x,R_D25, R_D25x,R_D26, R_D26x,R_D27, R_D27x, R_D28, R_D28x,R_D29, R_D29x,R_D30, R_D30x,R_D31, R_D31x ); @@ -196,8 +196,7 @@ alloc_class chunk2(APSR, FPSCR); // Several register classes are automatically defined based upon information in // this architecture description. // 1) reg_class inline_cache_reg ( as defined in frame section ) -// 2) reg_class interpreter_method_reg ( as defined in frame section ) -// 3) reg_class stack_slots( /* one chunk of stack-based "registers" */ ) +// 2) reg_class stack_slots( /* one chunk of stack-based "registers" */ ) // // ---------------------------- @@ -223,7 +222,6 @@ reg_class ptr_reg(R_R0, R_R1, R_R2, R_R3, R_R4, R_R5, R_R6, R_R7, R_R8, R_R9, R_ reg_class sp_ptr_reg(R_R0, R_R1, R_R2, R_R3, R_R4, R_R5, R_R6, R_R7, R_R8, R_R9, R_R11, R_R12, R_R14, R_R10 /* TLS*/, R_R13 /* SP*/); #define R_Ricklass R_R8 -#define R_Rmethod R_R9 #define R_Rthread R_R10 #define R_Rexception_obj R_R4 @@ -237,7 +235,6 @@ reg_class R9_regP(R_R9); reg_class R12_regP(R_R12); reg_class Rexception_regP(R_Rexception_obj); reg_class Ricklass_regP(R_Ricklass); -reg_class Rmethod_regP(R_Rmethod); reg_class Rthread_regP(R_Rthread); reg_class IP_regP(R_R12); reg_class SP_regP(R_R13); @@ -442,7 +439,7 @@ int MachCallStaticJavaNode::ret_addr_offset() { int MachCallDynamicJavaNode::ret_addr_offset() { bool far = !cache_reachable(); // mov_oop is always 2 words - return (2 + (far ? 3 : 1)) * NativeInstruction::instruction_size; + return (2 + (far ? 3 : 1)) * NativeInstruction::instruction_size; } int MachCallRuntimeNode::ret_addr_offset() { diff --git a/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp b/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp index 915eb73730c..6b390c1cda3 100644 --- a/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp +++ b/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp @@ -38,6 +38,10 @@ #define __ ce->masm()-> +void C1SafepointPollStub::emit_code(LIR_Assembler* ce) { + ShouldNotReachHere(); +} + void CounterOverflowStub::emit_code(LIR_Assembler* ce) { __ bind(_entry); ce->store_parameter(_bci, 0); diff --git a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp index 7b0794afc9f..f9b5fc69a89 100644 --- a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp @@ -283,7 +283,7 @@ int LIR_Assembler::emit_deopt_handler() { } -void LIR_Assembler::return_op(LIR_Opr result) { +void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) { // Pop the frame before safepoint polling __ remove_frame(initial_frame_size_in_bytes()); __ read_polling_page(Rtemp, relocInfo::poll_return_type); diff --git a/src/hotspot/cpu/arm/c1_globals_arm.hpp b/src/hotspot/cpu/arm/c1_globals_arm.hpp index 8141870536b..7077a87092c 100644 --- a/src/hotspot/cpu/arm/c1_globals_arm.hpp +++ b/src/hotspot/cpu/arm/c1_globals_arm.hpp @@ -35,8 +35,6 @@ #ifndef COMPILER2 // avoid duplicated definitions, favoring C2 version define_pd_global(bool, BackgroundCompilation, true ); -define_pd_global(bool, UseTLAB, true ); -define_pd_global(bool, ResizeTLAB, true ); define_pd_global(bool, InlineIntrinsics, false); // TODO: ARM define_pd_global(bool, PreferInterpreterNativeStubs, false); define_pd_global(bool, ProfileTraps, false); diff --git a/src/hotspot/cpu/arm/c2_globals_arm.hpp b/src/hotspot/cpu/arm/c2_globals_arm.hpp index 3708e38da2e..525af8b1edc 100644 --- a/src/hotspot/cpu/arm/c2_globals_arm.hpp +++ b/src/hotspot/cpu/arm/c2_globals_arm.hpp @@ -54,8 +54,6 @@ define_pd_global(size_t, NewSizeThreadIncrease, ScaleForWordSize(4*K)); // (For _228_jack 16/16 is 2% better than 4/4, 16/4, 32/32, 32/16, or 16/32.) //define_pd_global(intx, OptoLoopAlignment, 16); // = 4*wordSize define_pd_global(intx, RegisterCostAreaRatio, 16000); -define_pd_global(bool, UseTLAB, true); -define_pd_global(bool, ResizeTLAB, true); define_pd_global(intx, LoopUnrollLimit, 60); // Design center runs on 1.3.1 define_pd_global(intx, LoopPercentProfileLimit, 10); define_pd_global(intx, MinJumpTableSize, 16); diff --git a/src/hotspot/cpu/arm/interp_masm_arm.cpp b/src/hotspot/cpu/arm/interp_masm_arm.cpp index 116d2d40b2e..01ff3a5d39c 100644 --- a/src/hotspot/cpu/arm/interp_masm_arm.cpp +++ b/src/hotspot/cpu/arm/interp_masm_arm.cpp @@ -580,7 +580,7 @@ void InterpreterMacroAssembler::dispatch_base(TosState state, if (needs_thread_local_poll) { NOT_PRODUCT(block_comment("Thread-local Safepoint poll")); - ldr(Rtemp, Address(Rthread, Thread::polling_page_offset())); + ldr(Rtemp, Address(Rthread, Thread::polling_word_offset())); tbnz(Rtemp, exact_log2(SafepointMechanism::poll_bit()), safepoint); } @@ -983,7 +983,7 @@ void InterpreterMacroAssembler::lock_object(Register Rlock) { // Unlocks an object. Used in monitorexit bytecode and remove_activation. // -// Argument: R1: Points to BasicObjectLock structure for lock +// Argument: R0: Points to BasicObjectLock structure for lock // Throw an IllegalMonitorException if object is not locked by current thread // Blows volatile registers R0-R3, Rtemp, LR. Calls VM. void InterpreterMacroAssembler::unlock_object(Register Rlock) { @@ -996,8 +996,7 @@ void InterpreterMacroAssembler::unlock_object(Register Rlock) { const Register Robj = R2; const Register Rmark = R3; - const Register Rresult = R0; - assert_different_registers(Robj, Rmark, Rlock, R0, Rtemp); + assert_different_registers(Robj, Rmark, Rlock, Rtemp); const int obj_offset = BasicObjectLock::obj_offset_in_bytes(); const int lock_offset = BasicObjectLock::lock_offset_in_bytes (); diff --git a/src/hotspot/cpu/arm/macroAssembler_arm.cpp b/src/hotspot/cpu/arm/macroAssembler_arm.cpp index 14ac1163da0..067ec704376 100644 --- a/src/hotspot/cpu/arm/macroAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/macroAssembler_arm.cpp @@ -85,20 +85,6 @@ void AddressLiteral::set_rspec(relocInfo::relocType rtype) { } } -// Initially added to the Assembler interface as a pure virtual: -// RegisterConstant delayed_value(..) -// for: -// 6812678 macro assembler needs delayed binding of a few constants (for 6655638) -// this was subsequently modified to its present name and return type -RegisterOrConstant MacroAssembler::delayed_value_impl(intptr_t* delayed_value_addr, - Register tmp, - int offset) { - ShouldNotReachHere(); - return RegisterOrConstant(-1); -} - - - // virtual method calling void MacroAssembler::lookup_virtual_method(Register recv_klass, @@ -1914,7 +1900,7 @@ void MacroAssembler::resolve(DecoratorSet decorators, Register obj) { } void MacroAssembler::safepoint_poll(Register tmp1, Label& slow_path) { - ldr_u32(tmp1, Address(Rthread, Thread::polling_page_offset())); + ldr_u32(tmp1, Address(Rthread, Thread::polling_word_offset())); tst(tmp1, exact_log2(SafepointMechanism::poll_bit())); b(slow_path, eq); } diff --git a/src/hotspot/cpu/arm/macroAssembler_arm.hpp b/src/hotspot/cpu/arm/macroAssembler_arm.hpp index de40c5741a7..a07ca65d99e 100644 --- a/src/hotspot/cpu/arm/macroAssembler_arm.hpp +++ b/src/hotspot/cpu/arm/macroAssembler_arm.hpp @@ -222,14 +222,6 @@ class MacroAssembler: public Assembler { // returning false to preserve all relocation information. inline bool ignore_non_patchable_relocations() { return true; } - // Initially added to the Assembler interface as a pure virtual: - // RegisterConstant delayed_value(..) - // for: - // 6812678 macro assembler needs delayed binding of a few constants (for 6655638) - // this was subsequently modified to its present name and return type - virtual RegisterOrConstant delayed_value_impl(intptr_t* delayed_value_addr, Register tmp, int offset); - - void align(int modulus); // Support for VM calls diff --git a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp index 7dd1f21a244..a4216785e4e 100644 --- a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp +++ b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp @@ -33,6 +33,7 @@ #include "memory/resourceArea.hpp" #include "oops/compiledICHolder.hpp" #include "oops/klass.inline.hpp" +#include "prims/methodHandles.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/safepointMechanism.hpp" #include "runtime/vframeArray.hpp" diff --git a/src/hotspot/cpu/arm/templateTable_arm.cpp b/src/hotspot/cpu/arm/templateTable_arm.cpp index a27bd25557c..d0bcfccbb8d 100644 --- a/src/hotspot/cpu/arm/templateTable_arm.cpp +++ b/src/hotspot/cpu/arm/templateTable_arm.cpp @@ -2101,7 +2101,7 @@ void TemplateTable::branch(bool is_jsr, bool is_wide) { const Address mask(Rcounters, in_bytes(MethodCounters::backedge_mask_offset())); __ increment_mask_and_jump(Address(Rcounters, be_offset), increment, mask, Rcnt, R4_tmp, eq, &backedge_counter_overflow); - } else { + } else { // not TieredCompilation // Increment backedge counter in MethodCounters* __ get_method_counters(Rmethod, Rcounters, dispatch, true /*saveRegs*/, Rdisp, R3_bytecode, @@ -2166,7 +2166,7 @@ void TemplateTable::branch(bool is_jsr, bool is_wide) { __ dispatch_only(vtos, true); if (UseLoopCounter) { - if (ProfileInterpreter) { + if (ProfileInterpreter && !TieredCompilation) { // Out-of-line code to allocate method data oop. __ bind(profile_method); diff --git a/src/hotspot/cpu/arm/vm_version_arm_32.cpp b/src/hotspot/cpu/arm/vm_version_arm_32.cpp index 5331a20f2fe..e6fd8b98668 100644 --- a/src/hotspot/cpu/arm/vm_version_arm_32.cpp +++ b/src/hotspot/cpu/arm/vm_version_arm_32.cpp @@ -236,6 +236,11 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA512Intrinsics, false); } + if (UseSHA3Intrinsics) { + warning("Intrinsics for SHA3-224, SHA3-256, SHA3-384 and SHA3-512 crypto hash functions not available on this CPU."); + FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); + } + if (UseCRC32Intrinsics) { if (!FLAG_IS_DEFAULT(UseCRC32Intrinsics)) warning("CRC32 intrinsics are not available on this CPU"); diff --git a/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp b/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp index b13e18efc12..6902c47d71b 100644 --- a/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp @@ -38,6 +38,9 @@ #define __ ce->masm()-> +void C1SafepointPollStub::emit_code(LIR_Assembler* ce) { + ShouldNotReachHere(); +} RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, LIR_Opr array) : _index(index), _array(array), _throw_index_out_of_bounds_exception(false) { diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index 54e79f9d4bd..72adb74f4cc 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -1324,7 +1324,7 @@ void LIR_Assembler::reg2mem(LIR_Opr from_reg, LIR_Opr dest, BasicType type, } -void LIR_Assembler::return_op(LIR_Opr result) { +void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) { const Register return_pc = R31; // Must survive C-call to enable_stack_reserved_zone(). const Register polling_page = R12; diff --git a/src/hotspot/cpu/ppc/c1_globals_ppc.hpp b/src/hotspot/cpu/ppc/c1_globals_ppc.hpp index 60b0005e034..f90c1e8b1d2 100644 --- a/src/hotspot/cpu/ppc/c1_globals_ppc.hpp +++ b/src/hotspot/cpu/ppc/c1_globals_ppc.hpp @@ -43,9 +43,7 @@ define_pd_global(bool, TieredCompilation, false); define_pd_global(intx, CompileThreshold, 1000); define_pd_global(intx, OnStackReplacePercentage, 1400); -define_pd_global(bool, UseTLAB, true); define_pd_global(bool, ProfileInterpreter, false); -define_pd_global(bool, ResizeTLAB, true); define_pd_global(uintx, ReservedCodeCacheSize, 32*M); define_pd_global(uintx, NonProfiledCodeHeapSize, 13*M ); define_pd_global(uintx, ProfiledCodeHeapSize, 14*M ); diff --git a/src/hotspot/cpu/ppc/c2_globals_ppc.hpp b/src/hotspot/cpu/ppc/c2_globals_ppc.hpp index 7a0c311e719..c576ddc95c4 100644 --- a/src/hotspot/cpu/ppc/c2_globals_ppc.hpp +++ b/src/hotspot/cpu/ppc/c2_globals_ppc.hpp @@ -51,8 +51,6 @@ define_pd_global(intx, INTPRESSURE, 26); define_pd_global(intx, InteriorEntryAlignment, 16); define_pd_global(size_t, NewSizeThreadIncrease, ScaleForWordSize(4*K)); define_pd_global(intx, RegisterCostAreaRatio, 16000); -define_pd_global(bool, UseTLAB, true); -define_pd_global(bool, ResizeTLAB, true); define_pd_global(intx, LoopUnrollLimit, 60); define_pd_global(intx, LoopPercentProfileLimit, 10); diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp index d58740d5a74..67b18dc0e31 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp @@ -211,7 +211,7 @@ class InterpreterMacroAssembler: public MacroAssembler { // Object locking void lock_object (Register lock_reg, Register obj_reg); - void unlock_object(Register lock_reg, bool check_for_exceptions = true); + void unlock_object(Register lock_reg); // Interpreter profiling operations void set_method_data_pointer_for_bcp(); diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp index 555cfd41418..292accb7852 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp @@ -223,7 +223,7 @@ void InterpreterMacroAssembler::dispatch_Lbyte_code(TosState state, Register byt address *sfpt_tbl = Interpreter::safept_table(state); if (table != sfpt_tbl) { Label dispatch; - ld(R0, in_bytes(Thread::polling_page_offset()), R16_thread); + ld(R0, in_bytes(Thread::polling_word_offset()), R16_thread); // Armed page has poll_bit set, if poll bit is cleared just continue. andi_(R0, R0, SafepointMechanism::poll_bit()); beq(CCR0, dispatch); @@ -878,8 +878,7 @@ void InterpreterMacroAssembler::remove_activation(TosState state, // void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { if (UseHeavyMonitors) { - call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - monitor, /*check_for_exceptions=*/true); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor); } else { // template code: // @@ -980,8 +979,7 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { // None of the above fast optimizations worked so we have to get into the // slow case of monitor enter. bind(slow_case); - call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - monitor, /*check_for_exceptions=*/true); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor); // } align(32, 12); bind(done); @@ -995,7 +993,7 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { // which must be initialized with the object to lock. // // Throw IllegalMonitorException if object is not locked by current thread. -void InterpreterMacroAssembler::unlock_object(Register monitor, bool check_for_exceptions) { +void InterpreterMacroAssembler::unlock_object(Register monitor) { if (UseHeavyMonitors) { call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), monitor); } else { @@ -2401,8 +2399,7 @@ void InterpreterMacroAssembler::notify_method_entry() { lwz(R0, in_bytes(JavaThread::interp_only_mode_offset()), R16_thread); cmpwi(CCR0, R0, 0); beq(CCR0, jvmti_post_done); - call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_entry), - /*check_exceptions=*/true); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_entry)); bind(jvmti_post_done); } @@ -2437,8 +2434,7 @@ void InterpreterMacroAssembler::notify_method_exit(bool is_native_method, TosSta cmpwi(CCR0, R0, 0); beq(CCR0, jvmti_post_done); if (!is_native_method) { push(state); } // Expose tos to GC. - call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit), - /*check_exceptions=*/check_exceptions); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit), check_exceptions); if (!is_native_method) { pop(state); } align(32, 12); diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp index 3d3c39cf5d5..ca1c0c24987 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -382,25 +382,6 @@ AddressLiteral MacroAssembler::constant_oop_address(jobject obj) { return AddressLiteral(address(obj), oop_Relocation::spec(oop_index)); } -RegisterOrConstant MacroAssembler::delayed_value_impl(intptr_t* delayed_value_addr, - Register tmp, int offset) { - intptr_t value = *delayed_value_addr; - if (value != 0) { - return RegisterOrConstant(value + offset); - } - - // Load indirectly to solve generation ordering problem. - // static address, no relocation - int simm16_offset = load_const_optimized(tmp, delayed_value_addr, noreg, true); - ld(tmp, simm16_offset, tmp); // must be aligned ((xa & 3) == 0) - - if (offset != 0) { - addi(tmp, tmp, offset); - } - - return RegisterOrConstant(tmp); -} - #ifndef PRODUCT void MacroAssembler::pd_print_patched_instruction(address branch) { Unimplemented(); // TODO: PPC port @@ -3044,7 +3025,7 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe } void MacroAssembler::safepoint_poll(Label& slow_path, Register temp_reg) { - ld(temp_reg, in_bytes(Thread::polling_page_offset()), R16_thread); + ld(temp_reg, in_bytes(Thread::polling_word_offset()), R16_thread); // Armed page has poll_bit set. andi_(temp_reg, temp_reg, SafepointMechanism::poll_bit()); bne(CCR0, slow_path); diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp index a8e43cabdc4..1859483c470 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp @@ -152,12 +152,6 @@ class MacroAssembler: public Assembler { // Same as load_address. inline void set_oop (AddressLiteral obj_addr, Register d); - // Read runtime constant: Issue load if constant not yet established, - // else use real constant. - virtual RegisterOrConstant delayed_value_impl(intptr_t* delayed_value_addr, - Register tmp, - int offset); - // // branch, jump // diff --git a/src/hotspot/cpu/ppc/nativeInst_ppc.cpp b/src/hotspot/cpu/ppc/nativeInst_ppc.cpp index fbe956322a6..1134ed0366b 100644 --- a/src/hotspot/cpu/ppc/nativeInst_ppc.cpp +++ b/src/hotspot/cpu/ppc/nativeInst_ppc.cpp @@ -197,7 +197,11 @@ intptr_t NativeMovConstReg::data() const { CodeBlob* cb = CodeCache::find_blob_unsafe(addr); if (MacroAssembler::is_set_narrow_oop(addr, cb->content_begin())) { narrowOop no = MacroAssembler::get_narrow_oop(addr, cb->content_begin()); - return cast_from_oop(CompressedOops::decode(no)); + // We can reach here during GC with 'no' pointing to new object location + // while 'heap()->is_in' still reports false (e.g. with SerialGC). + // Therefore we use raw decoding. + if (CompressedOops::is_null(no)) return 0; + return cast_from_oop(CompressedOops::decode_raw(no)); } else { assert(MacroAssembler::is_load_const_from_method_toc_at(addr), "must be load_const_from_pool"); diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index d9c7c350e8e..b8f4f26995f 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -535,9 +535,7 @@ alloc_class chunk4 ( // information in this architecture description. // 1) reg_class inline_cache_reg ( as defined in frame section ) -// 2) reg_class compiler_method_reg ( as defined in frame section ) -// 2) reg_class interpreter_method_reg ( as defined in frame section ) -// 3) reg_class stack_slots( /* one chunk of stack-based "registers" */ ) +// 2) reg_class stack_slots( /* one chunk of stack-based "registers" */ ) // // ---------------------------- @@ -2064,103 +2062,88 @@ static int frame_slots_bias(int reg_enc, PhaseRegAlloc* ra_) { } const bool Matcher::match_rule_supported(int opcode) { - if (!has_match_rule(opcode)) - return false; + if (!has_match_rule(opcode)) { + return false; // no match rule present + } - bool ret_value = true; switch (opcode) { - case Op_SqrtD: - return VM_Version::has_fsqrt(); - case Op_CountLeadingZerosI: - case Op_CountLeadingZerosL: - if (!UseCountLeadingZerosInstructionsPPC64) - return false; - break; - case Op_CountTrailingZerosI: - case Op_CountTrailingZerosL: - if (!UseCountLeadingZerosInstructionsPPC64 && - !UseCountTrailingZerosInstructionsPPC64) - return false; - break; - - case Op_PopCountI: - case Op_PopCountL: - return (UsePopCountInstruction && VM_Version::has_popcntw()); - - case Op_StrComp: - return SpecialStringCompareTo; - case Op_StrEquals: - return SpecialStringEquals; - case Op_StrIndexOf: - case Op_StrIndexOfChar: - return SpecialStringIndexOf; - case Op_AddVB: - case Op_AddVS: - case Op_AddVI: - case Op_AddVF: - case Op_AddVD: - case Op_SubVB: - case Op_SubVS: - case Op_SubVI: - case Op_SubVF: - case Op_SubVD: - case Op_MulVS: - case Op_MulVF: - case Op_MulVD: - case Op_DivVF: - case Op_DivVD: - case Op_AbsVF: - case Op_AbsVD: - case Op_NegVF: - case Op_NegVD: - case Op_SqrtVF: - case Op_SqrtVD: - case Op_AddVL: - case Op_SubVL: - case Op_MulVI: - case Op_RoundDoubleModeV: - return SuperwordUseVSX; - case Op_PopCountVI: - return (SuperwordUseVSX && UsePopCountInstruction); - case Op_FmaVF: - case Op_FmaVD: - return (SuperwordUseVSX && UseFMA); - case Op_Digit: - return vmIntrinsics::is_intrinsic_available(vmIntrinsics::_isDigit); - case Op_LowerCase: - return vmIntrinsics::is_intrinsic_available(vmIntrinsics::_isLowerCase); - case Op_UpperCase: - return vmIntrinsics::is_intrinsic_available(vmIntrinsics::_isUpperCase); - case Op_Whitespace: - return vmIntrinsics::is_intrinsic_available(vmIntrinsics::_isWhitespace); - - case Op_CacheWB: - case Op_CacheWBPreSync: - case Op_CacheWBPostSync: - if (!VM_Version::supports_data_cache_line_flush()) { - ret_value = false; - } - break; + case Op_SqrtD: + return VM_Version::has_fsqrt(); + case Op_CountLeadingZerosI: + case Op_CountLeadingZerosL: + return UseCountLeadingZerosInstructionsPPC64; + case Op_CountTrailingZerosI: + case Op_CountTrailingZerosL: + return (UseCountLeadingZerosInstructionsPPC64 || UseCountTrailingZerosInstructionsPPC64); + case Op_PopCountI: + case Op_PopCountL: + return (UsePopCountInstruction && VM_Version::has_popcntw()); + + case Op_AddVB: + case Op_AddVS: + case Op_AddVI: + case Op_AddVF: + case Op_AddVD: + case Op_SubVB: + case Op_SubVS: + case Op_SubVI: + case Op_SubVF: + case Op_SubVD: + case Op_MulVS: + case Op_MulVF: + case Op_MulVD: + case Op_DivVF: + case Op_DivVD: + case Op_AbsVF: + case Op_AbsVD: + case Op_NegVF: + case Op_NegVD: + case Op_SqrtVF: + case Op_SqrtVD: + case Op_AddVL: + case Op_SubVL: + case Op_MulVI: + case Op_RoundDoubleModeV: + return SuperwordUseVSX; + case Op_PopCountVI: + return (SuperwordUseVSX && UsePopCountInstruction); + case Op_FmaVF: + case Op_FmaVD: + return (SuperwordUseVSX && UseFMA); + + case Op_Digit: + return vmIntrinsics::is_intrinsic_available(vmIntrinsics::_isDigit); + case Op_LowerCase: + return vmIntrinsics::is_intrinsic_available(vmIntrinsics::_isLowerCase); + case Op_UpperCase: + return vmIntrinsics::is_intrinsic_available(vmIntrinsics::_isUpperCase); + case Op_Whitespace: + return vmIntrinsics::is_intrinsic_available(vmIntrinsics::_isWhitespace); + + case Op_CacheWB: + case Op_CacheWBPreSync: + case Op_CacheWBPostSync: + return VM_Version::supports_data_cache_line_flush(); } - return ret_value; // Per default match rules are supported. + return true; // Per default match rules are supported. } const bool Matcher::match_rule_supported_vector(int opcode, int vlen, BasicType bt) { - - // TODO - // identify extra cases that we might want to provide match rules for - // e.g. Op_ vector nodes and other intrinsics while guarding with vlen - bool ret_value = match_rule_supported(opcode); - // Add rules here. - - return ret_value; // Per default match rules are supported. + if (!match_rule_supported(opcode) || !vector_size_supported(bt, vlen)) { + return false; + } + return true; // Per default match rules are supported. } const bool Matcher::has_predicated_vectors(void) { return false; } +bool Matcher::supports_vector_variable_shifts(void) { + return false; // not supported +} + const int Matcher::float_pressure(int default_pressure_threshold) { return default_pressure_threshold; } @@ -2340,10 +2323,6 @@ const bool Matcher::rematerialize_float_constants = false; // Java calling convention forces doubles to be aligned. const bool Matcher::misaligned_doubles_ok = true; -void Matcher::pd_implicit_null_fixup(MachNode *node, uint idx) { - Unimplemented(); -} - // Advertise here if the CPU requires explicit rounding operations to implement strictfp mode. const bool Matcher::strict_fp_requires_explicit_rounding = false; @@ -3855,9 +3834,6 @@ frame %{ // Inline Cache Register or method for I2C. inline_cache_reg(R19); // R19_method - // Method Register when calling interpreter. - interpreter_method_reg(R19); // R19_method - // Optional: name the operand used by cisc-spilling to access // [stack_pointer + offset]. cisc_spilling_operand_name(indOffset); @@ -3912,7 +3888,7 @@ frame %{ // The `sig' array is to be updated. sig[j] represents the location // of the j-th argument, either a register or a stack slot. - // Comment taken from i486.ad: + // Comment taken from x86_32.ad: // Body of function which returns an integer array locating // arguments either in registers or in stack slots. Passed an array // of ideal registers called "sig" and a "length" count. Stack-slot @@ -3924,7 +3900,7 @@ frame %{ SharedRuntime::java_calling_convention(sig_bt, regs, length, false); %} - // Comment taken from i486.ad: + // Comment taken from x86_32.ad: // Body of function which returns an integer array locating // arguments either in registers or in stack slots. Passed an array // of ideal registers called "sig" and a "length" count. Stack-slot @@ -4765,20 +4741,6 @@ operand inline_cache_regP(iRegPdst reg) %{ interface(REG_INTER); %} -operand compiler_method_regP(iRegPdst reg) %{ - constraint(ALLOC_IN_RC(rscratch1_bits64_reg)); // compiler_method_reg - match(reg); - format %{ %} - interface(REG_INTER); -%} - -operand interpreter_method_regP(iRegPdst reg) %{ - constraint(ALLOC_IN_RC(r19_bits64_reg)); // interpreter_method_reg - match(reg); - format %{ %} - interface(REG_INTER); -%} - // Operands to remove register moves in unscaled mode. // Match read/write registers with an EncodeP node if neither shift nor add are required. operand iRegP2N(iRegPsrc reg) %{ @@ -6588,6 +6550,23 @@ instruct storeV16(indirect mem, vecX src) %{ ins_pipe(pipe_class_default); %} +// Reinterpret: only one vector size used: either L or X +instruct reinterpretL(iRegLdst dst) %{ + match(Set dst (VectorReinterpret dst)); + ins_cost(0); + format %{ "reinterpret $dst" %} + ins_encode( /*empty*/ ); + ins_pipe(pipe_class_empty); +%} + +instruct reinterpretX(vecX dst) %{ + match(Set dst (VectorReinterpret dst)); + ins_cost(0); + format %{ "reinterpret $dst" %} + ins_encode( /*empty*/ ); + ins_pipe(pipe_class_empty); +%} + // Store Compressed Oop instruct storeN(memory dst, iRegN_P2N src) %{ match(Set dst (StoreN dst src)); @@ -12618,9 +12597,10 @@ instruct indexOfChar_U(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, flagsRegCR0 cr0, flagsRegCR1 cr1, regCTR ctr) %{ match(Set result (StrIndexOfChar (Binary haystack haycnt) ch)); effect(TEMP tmp1, TEMP tmp2, KILL cr0, KILL cr1, KILL ctr); + predicate(((StrIndexOfCharNode*)n)->encoding() == StrIntrinsicNode::U); ins_cost(180); - format %{ "String IndexOfChar $haystack[0..$haycnt], $ch" + format %{ "StringUTF16 IndexOfChar $haystack[0..$haycnt], $ch" " -> $result \t// KILL $haycnt, $tmp1, $tmp2, $cr0, $cr1" %} ins_encode %{ __ string_indexof_char($result$$Register, @@ -12631,6 +12611,25 @@ instruct indexOfChar_U(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, ins_pipe(pipe_class_compare); %} +instruct indexOfChar_L(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, + iRegIsrc ch, iRegIdst tmp1, iRegIdst tmp2, + flagsRegCR0 cr0, flagsRegCR1 cr1, regCTR ctr) %{ + match(Set result (StrIndexOfChar (Binary haystack haycnt) ch)); + effect(TEMP tmp1, TEMP tmp2, KILL cr0, KILL cr1, KILL ctr); + predicate(((StrIndexOfCharNode*)n)->encoding() == StrIntrinsicNode::L); + ins_cost(180); + + format %{ "StringLatin1 IndexOfChar $haystack[0..$haycnt], $ch" + " -> $result \t// KILL $haycnt, $tmp1, $tmp2, $cr0, $cr1" %} + ins_encode %{ + __ string_indexof_char($result$$Register, + $haystack$$Register, $haycnt$$Register, + $ch$$Register, 0 /* this is not used if the character is already in a register */, + $tmp1$$Register, $tmp2$$Register, true /*is_byte*/); + %} + ins_pipe(pipe_class_compare); +%} + instruct indexOf_imm_U(iRegIdst result, iRegPsrc haystack, rscratch1RegI haycnt, iRegPsrc needle, uimmI15 needlecntImm, iRegIdst tmp1, iRegIdst tmp2, iRegIdst tmp3, iRegIdst tmp4, iRegIdst tmp5, diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index add61ad738c..e8498ba0ed3 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -35,6 +35,7 @@ #include "memory/resourceArea.hpp" #include "oops/compiledICHolder.hpp" #include "oops/klass.inline.hpp" +#include "prims/methodHandles.hpp" #include "runtime/safepointMechanism.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/vframeArray.hpp" @@ -1529,156 +1530,6 @@ void SharedRuntime::restore_native_result(MacroAssembler *masm, BasicType ret_ty } } -static void save_or_restore_arguments(MacroAssembler* masm, - const int stack_slots, - const int total_in_args, - const int arg_save_area, - OopMap* map, - VMRegPair* in_regs, - BasicType* in_sig_bt) { - // If map is non-NULL then the code should store the values, - // otherwise it should load them. - int slot = arg_save_area; - // Save down double word first. - for (int i = 0; i < total_in_args; i++) { - if (in_regs[i].first()->is_FloatRegister() && in_sig_bt[i] == T_DOUBLE) { - int offset = slot * VMRegImpl::stack_slot_size; - slot += VMRegImpl::slots_per_word; - assert(slot <= stack_slots, "overflow (after DOUBLE stack slot)"); - if (map != NULL) { - __ stfd(in_regs[i].first()->as_FloatRegister(), offset, R1_SP); - } else { - __ lfd(in_regs[i].first()->as_FloatRegister(), offset, R1_SP); - } - } else if (in_regs[i].first()->is_Register() && - (in_sig_bt[i] == T_LONG || in_sig_bt[i] == T_ARRAY)) { - int offset = slot * VMRegImpl::stack_slot_size; - if (map != NULL) { - __ std(in_regs[i].first()->as_Register(), offset, R1_SP); - if (in_sig_bt[i] == T_ARRAY) { - map->set_oop(VMRegImpl::stack2reg(slot)); - } - } else { - __ ld(in_regs[i].first()->as_Register(), offset, R1_SP); - } - slot += VMRegImpl::slots_per_word; - assert(slot <= stack_slots, "overflow (after LONG/ARRAY stack slot)"); - } - } - // Save or restore single word registers. - for (int i = 0; i < total_in_args; i++) { - if (in_regs[i].first()->is_Register()) { - int offset = slot * VMRegImpl::stack_slot_size; - // Value lives in an input register. Save it on stack. - switch (in_sig_bt[i]) { - case T_BOOLEAN: - case T_CHAR: - case T_BYTE: - case T_SHORT: - case T_INT: - if (map != NULL) { - __ stw(in_regs[i].first()->as_Register(), offset, R1_SP); - } else { - __ lwa(in_regs[i].first()->as_Register(), offset, R1_SP); - } - slot++; - assert(slot <= stack_slots, "overflow (after INT or smaller stack slot)"); - break; - case T_ARRAY: - case T_LONG: - // handled above - break; - case T_OBJECT: - default: ShouldNotReachHere(); - } - } else if (in_regs[i].first()->is_FloatRegister()) { - if (in_sig_bt[i] == T_FLOAT) { - int offset = slot * VMRegImpl::stack_slot_size; - slot++; - assert(slot <= stack_slots, "overflow (after FLOAT stack slot)"); - if (map != NULL) { - __ stfs(in_regs[i].first()->as_FloatRegister(), offset, R1_SP); - } else { - __ lfs(in_regs[i].first()->as_FloatRegister(), offset, R1_SP); - } - } - } else if (in_regs[i].first()->is_stack()) { - if (in_sig_bt[i] == T_ARRAY && map != NULL) { - int offset_in_older_frame = in_regs[i].first()->reg2stack() + SharedRuntime::out_preserve_stack_slots(); - map->set_oop(VMRegImpl::stack2reg(offset_in_older_frame + stack_slots)); - } - } - } -} - -// Check GCLocker::needs_gc and enter the runtime if it's true. This -// keeps a new JNI critical region from starting until a GC has been -// forced. Save down any oops in registers and describe them in an -// OopMap. -static void check_needs_gc_for_critical_native(MacroAssembler* masm, - const int stack_slots, - const int total_in_args, - const int arg_save_area, - OopMapSet* oop_maps, - VMRegPair* in_regs, - BasicType* in_sig_bt, - Register tmp_reg ) { - __ block_comment("check GCLocker::needs_gc"); - Label cont; - __ lbz(tmp_reg, (RegisterOrConstant)(intptr_t)GCLocker::needs_gc_address()); - __ cmplwi(CCR0, tmp_reg, 0); - __ beq(CCR0, cont); - - // Save down any values that are live in registers and call into the - // runtime to halt for a GC. - OopMap* map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); - save_or_restore_arguments(masm, stack_slots, total_in_args, - arg_save_area, map, in_regs, in_sig_bt); - - __ mr(R3_ARG1, R16_thread); - __ set_last_Java_frame(R1_SP, noreg); - - __ block_comment("block_for_jni_critical"); - address entry_point = CAST_FROM_FN_PTR(address, SharedRuntime::block_for_jni_critical); -#if defined(ABI_ELFv2) - __ call_c(entry_point, relocInfo::runtime_call_type); -#else - __ call_c(CAST_FROM_FN_PTR(FunctionDescriptor*, entry_point), relocInfo::runtime_call_type); -#endif - address start = __ pc() - __ offset(), - calls_return_pc = __ last_calls_return_pc(); - oop_maps->add_gc_map(calls_return_pc - start, map); - - __ reset_last_Java_frame(); - - // Reload all the register arguments. - save_or_restore_arguments(masm, stack_slots, total_in_args, - arg_save_area, NULL, in_regs, in_sig_bt); - - __ BIND(cont); - -#ifdef ASSERT - if (StressCriticalJNINatives) { - // Stress register saving. - OopMap* map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); - save_or_restore_arguments(masm, stack_slots, total_in_args, - arg_save_area, map, in_regs, in_sig_bt); - // Destroy argument registers. - for (int i = 0; i < total_in_args; i++) { - if (in_regs[i].first()->is_Register()) { - const Register reg = in_regs[i].first()->as_Register(); - __ neg(reg, reg); - } else if (in_regs[i].first()->is_FloatRegister()) { - __ fneg(in_regs[i].first()->as_FloatRegister(), in_regs[i].first()->as_FloatRegister()); - } - } - - save_or_restore_arguments(masm, stack_slots, total_in_args, - arg_save_area, NULL, in_regs, in_sig_bt); - } -#endif -} - static void move_ptr(MacroAssembler* masm, VMRegPair src, VMRegPair dst, Register r_caller_sp, Register r_temp) { if (src.first()->is_stack()) { if (dst.first()->is_stack()) { @@ -1820,25 +1671,12 @@ static void gen_special_dispatch(MacroAssembler* masm, // Critical native functions are a shorthand for the use of // GetPrimtiveArrayCritical and disallow the use of any other JNI // functions. The wrapper is expected to unpack the arguments before -// passing them to the callee and perform checks before and after the -// native call to ensure that they GCLocker -// lock_critical/unlock_critical semantics are followed. Some other -// parts of JNI setup are skipped like the tear down of the JNI handle +// passing them to the callee. Critical native functions leave the state _in_Java, +// since they cannot stop for GC. +// Some other parts of JNI setup are skipped like the tear down of the JNI handle // block and the check for pending exceptions it's impossible for them // to be thrown. // -// They are roughly structured like this: -// if (GCLocker::needs_gc()) -// SharedRuntime::block_for_jni_critical(); -// tranistion to thread_in_native -// unpack arrray arguments and call native entry point -// check for safepoint in progress -// check if any thread suspend flags are set -// call into JVM and possible unlock the JNI critical -// if a GC was suppressed while in the critical native. -// transition back to thread_in_Java -// return to caller -// nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, const methodHandle& method, int compile_id, @@ -2145,11 +1983,6 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, OopMapSet *oop_maps = new OopMapSet(); OopMap *oop_map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); - if (is_critical_native) { - check_needs_gc_for_critical_native(masm, stack_slots, total_in_args, oop_handle_slot_offset, - oop_maps, in_regs, in_sig_bt, r_temp_1); - } - // Move arguments from register/stack to register/stack. // -------------------------------------------------------------------------- // @@ -2350,18 +2183,19 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, __ bind(locked); } - - // Publish thread state - // -------------------------------------------------------------------------- - // Use that pc we placed in r_return_pc a while back as the current frame anchor. __ set_last_Java_frame(R1_SP, r_return_pc); - // Transition from _thread_in_Java to _thread_in_native. - __ li(R0, _thread_in_native); - __ release(); - // TODO: PPC port assert(4 == JavaThread::sz_thread_state(), "unexpected field size"); - __ stw(R0, thread_(thread_state)); + if (!is_critical_native) { + // Publish thread state + // -------------------------------------------------------------------------- + + // Transition from _thread_in_Java to _thread_in_native. + __ li(R0, _thread_in_native); + __ release(); + // TODO: PPC port assert(4 == JavaThread::sz_thread_state(), "unexpected field size"); + __ stw(R0, thread_(thread_state)); + } // The JNI call @@ -2421,6 +2255,22 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, break; } + Label after_transition; + + // If this is a critical native, check for a safepoint or suspend request after the call. + // If a safepoint is needed, transition to native, then to native_trans to handle + // safepoints like the native methods that are not critical natives. + if (is_critical_native) { + Label needs_safepoint; + Register sync_state = r_temp_5; + __ safepoint_poll(needs_safepoint, sync_state); + + Register suspend_flags = r_temp_6; + __ lwz(suspend_flags, thread_(suspend_flags)); + __ cmpwi(CCR1, suspend_flags, 0); + __ beq(CCR1, after_transition); + __ bind(needs_safepoint); + } // Publish thread state // -------------------------------------------------------------------------- @@ -2448,7 +2298,6 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // Block, if necessary, before resuming in _thread_in_Java state. // In order for GC to work, don't clear the last_Java_sp until after blocking. - Label after_transition; { Label no_block, sync; @@ -2476,31 +2325,27 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, __ bind(sync); __ isync(); - address entry_point = is_critical_native - ? CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans_and_transition) - : CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans); + address entry_point = + CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans); save_native_result(masm, ret_type, workspace_slot_offset); __ call_VM_leaf(entry_point, R16_thread); restore_native_result(masm, ret_type, workspace_slot_offset); - if (is_critical_native) { - __ b(after_transition); // No thread state transition here. - } __ bind(no_block); - } - // Publish thread state. - // -------------------------------------------------------------------------- + // Publish thread state. + // -------------------------------------------------------------------------- - // Thread state is thread_in_native_trans. Any safepoint blocking has - // already happened so we can now change state to _thread_in_Java. + // Thread state is thread_in_native_trans. Any safepoint blocking has + // already happened so we can now change state to _thread_in_Java. - // Transition from _thread_in_native_trans to _thread_in_Java. - __ li(R0, _thread_in_Java); - __ lwsync(); // Acquire safepoint and suspend state, release thread state. - // TODO: PPC port assert(4 == JavaThread::sz_thread_state(), "unexpected field size"); - __ stw(R0, thread_(thread_state)); - __ bind(after_transition); + // Transition from _thread_in_native_trans to _thread_in_Java. + __ li(R0, _thread_in_Java); + __ lwsync(); // Acquire safepoint and suspend state, release thread state. + // TODO: PPC port assert(4 == JavaThread::sz_thread_state(), "unexpected field size"); + __ stw(R0, thread_(thread_state)); + __ bind(after_transition); + } // Reguard any pages if necessary. // -------------------------------------------------------------------------- @@ -2657,10 +2502,6 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, in_ByteSize(lock_offset), oop_maps); - if (is_critical_native) { - nm->set_lazy_critical_native(true); - } - return nm; } diff --git a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp index 994f0a93827..525e4f05255 100644 --- a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp @@ -1549,9 +1549,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // Handle exceptions if (synchronized) { - // Don't check for exceptions since we're still in the i2n frame. Do that - // manually afterwards. - __ unlock_object(R26_monitor, false); // Can also unlock methods. + __ unlock_object(R26_monitor); // Can also unlock methods. } // Reset active handles after returning from native. @@ -1592,9 +1590,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { BIND(exception_return_sync_check); if (synchronized) { - // Don't check for exceptions since we're still in the i2n frame. Do that - // manually afterwards. - __ unlock_object(R26_monitor, false); // Can also unlock methods. + __ unlock_object(R26_monitor); // Can also unlock methods. } BIND(exception_return_sync_check_already_unlocked); @@ -2105,7 +2101,7 @@ void TemplateInterpreterGenerator::generate_throw_exception() { // Detect such a case in the InterpreterRuntime function and return the member name argument, or NULL. __ ld(R4_ARG2, 0, R18_locals); __ call_VM(R4_ARG2, CAST_FROM_FN_PTR(address, InterpreterRuntime::member_name_arg_or_null), R4_ARG2, R19_method, R14_bcp); - __ restore_interpreter_state(R11_scratch1, /*bcp_and_mdx_only*/ true); + __ cmpdi(CCR0, R4_ARG2, 0); __ beq(CCR0, L_done); __ std(R4_ARG2, wordSize, R15_esp); diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp index e9ccfc7c481..cc341d83072 100644 --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp @@ -2173,7 +2173,7 @@ void TemplateTable::_return(TosState state) { if (_desc->bytecode() != Bytecodes::_return_register_finalizer) { Label no_safepoint; - __ ld(R11_scratch1, in_bytes(Thread::polling_page_offset()), R16_thread); + __ ld(R11_scratch1, in_bytes(Thread::polling_word_offset()), R16_thread); __ andi_(R11_scratch1, R11_scratch1, SafepointMechanism::poll_bit()); __ beq(CCR0, no_safepoint); __ push(state); diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.cpp b/src/hotspot/cpu/ppc/vm_version_ppc.cpp index fd62cb5813a..f64999d108a 100644 --- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp +++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp @@ -331,6 +331,11 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA512Intrinsics, false); } + if (UseSHA3Intrinsics) { + warning("Intrinsics for SHA3-224, SHA3-256, SHA3-384 and SHA3-512 crypto hash functions not available on this CPU."); + FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); + } + if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics)) { FLAG_SET_DEFAULT(UseSHA, false); } diff --git a/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp b/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp index 367d2a43af5..329c163f313 100644 --- a/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp +++ b/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp @@ -41,6 +41,10 @@ #undef CHECK_BAILOUT #define CHECK_BAILOUT() { if (ce->compilation()->bailed_out()) return; } +void C1SafepointPollStub::emit_code(LIR_Assembler* ce) { + ShouldNotReachHere(); +} + RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, LIR_Opr array) : _index(index), _array(array), _throw_index_out_of_bounds_exception(false) { assert(info != NULL, "must have info"); diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index 24c8178f1dc..4c7dc79e5e7 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -1207,7 +1207,7 @@ void LIR_Assembler::reg2mem(LIR_Opr from, LIR_Opr dest_opr, BasicType type, } -void LIR_Assembler::return_op(LIR_Opr result) { +void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) { assert(result->is_illegal() || (result->is_single_cpu() && result->as_register() == Z_R2) || (result->is_double_cpu() && result->as_register_lo() == Z_R2) || diff --git a/src/hotspot/cpu/s390/c1_globals_s390.hpp b/src/hotspot/cpu/s390/c1_globals_s390.hpp index 99e26e5e3f8..7fcb1ee0617 100644 --- a/src/hotspot/cpu/s390/c1_globals_s390.hpp +++ b/src/hotspot/cpu/s390/c1_globals_s390.hpp @@ -43,9 +43,7 @@ define_pd_global(bool, TieredCompilation, false); define_pd_global(intx, CompileThreshold, 1000); define_pd_global(intx, OnStackReplacePercentage, 1400); -define_pd_global(bool, UseTLAB, true); define_pd_global(bool, ProfileInterpreter, false); -define_pd_global(bool, ResizeTLAB, true); define_pd_global(uintx, ReservedCodeCacheSize, 32*M); define_pd_global(uintx, NonProfiledCodeHeapSize, 13*M); define_pd_global(uintx, ProfiledCodeHeapSize, 14*M); diff --git a/src/hotspot/cpu/s390/c2_globals_s390.hpp b/src/hotspot/cpu/s390/c2_globals_s390.hpp index 2f44fa73a2e..64d5585d616 100644 --- a/src/hotspot/cpu/s390/c2_globals_s390.hpp +++ b/src/hotspot/cpu/s390/c2_globals_s390.hpp @@ -51,8 +51,6 @@ define_pd_global(intx, INTPRESSURE, 10); // Medium size registe define_pd_global(intx, InteriorEntryAlignment, 2); define_pd_global(size_t, NewSizeThreadIncrease, ScaleForWordSize(4*K)); define_pd_global(intx, RegisterCostAreaRatio, 12000); -define_pd_global(bool, UseTLAB, true); -define_pd_global(bool, ResizeTLAB, true); define_pd_global(intx, LoopUnrollLimit, 60); define_pd_global(intx, LoopPercentProfileLimit, 10); define_pd_global(intx, MinJumpTableSize, 18); diff --git a/src/hotspot/cpu/s390/interp_masm_s390.cpp b/src/hotspot/cpu/s390/interp_masm_s390.cpp index d612d528c51..4f44359b04d 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.cpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.cpp @@ -121,7 +121,7 @@ void InterpreterMacroAssembler::dispatch_base(TosState state, address* table, bo address *sfpt_tbl = Interpreter::safept_table(state); if (table != sfpt_tbl) { Label dispatch; - const Address poll_byte_addr(Z_thread, in_bytes(Thread::polling_page_offset()) + 7 /* Big Endian */); + const Address poll_byte_addr(Z_thread, in_bytes(Thread::polling_word_offset()) + 7 /* Big Endian */); // Armed page has poll_bit set, if poll bit is cleared just continue. z_tm(poll_byte_addr, SafepointMechanism::poll_bit()); z_braz(dispatch); @@ -969,8 +969,7 @@ void InterpreterMacroAssembler::remove_activation(TosState state, void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { if (UseHeavyMonitors) { - call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - monitor, /*check_for_exceptions=*/false); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor); return; } @@ -1061,9 +1060,7 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { // None of the above fast optimizations worked so we have to get into the // slow case of monitor enter. bind(slow_case); - - call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - monitor, /*check_for_exceptions=*/false); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor); // } diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index c71a15daa7c..d7c95ee96ee 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -1810,34 +1810,6 @@ void MacroAssembler::c2bool(Register r, Register t) { z_srl(r, 31); // Yields 0 if r was 0, 1 otherwise. } -RegisterOrConstant MacroAssembler::delayed_value_impl(intptr_t* delayed_value_addr, - Register tmp, - int offset) { - intptr_t value = *delayed_value_addr; - if (value != 0) { - return RegisterOrConstant(value + offset); - } - - BLOCK_COMMENT("delayed_value {"); - // Load indirectly to solve generation ordering problem. - load_absolute_address(tmp, (address) delayed_value_addr); // tmp = a; - z_lg(tmp, 0, tmp); // tmp = *tmp; - -#ifdef ASSERT - NearLabel L; - compare64_and_branch(tmp, (intptr_t)0L, Assembler::bcondNotEqual, L); - z_illtrap(); - bind(L); -#endif - - if (offset != 0) { - z_agfi(tmp, offset); // tmp = tmp + offset; - } - - BLOCK_COMMENT("} delayed_value"); - return RegisterOrConstant(tmp); -} - // Patch instruction `inst' at offset `inst_pos' to refer to `dest_pos' // and return the resulting instruction. // Dest_pos and inst_pos are 32 bit only. These parms can only designate @@ -2680,7 +2652,7 @@ uint MacroAssembler::get_poll_register(address instr_loc) { } void MacroAssembler::safepoint_poll(Label& slow_path, Register temp_reg) { - const Address poll_byte_addr(Z_thread, in_bytes(Thread::polling_page_offset()) + 7 /* Big Endian */); + const Address poll_byte_addr(Z_thread, in_bytes(Thread::polling_word_offset()) + 7 /* Big Endian */); // Armed page has poll_bit set. z_tm(poll_byte_addr, SafepointMechanism::poll_bit()); z_brnaz(slow_path); diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.hpp index 41294b0fe87..113a1a3db2a 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.hpp @@ -350,9 +350,6 @@ class MacroAssembler: public Assembler { // Uses constant_metadata_address. inline bool set_metadata_constant(Metadata* md, Register d); - virtual RegisterOrConstant delayed_value_impl(intptr_t* delayed_value_addr, - Register tmp, - int offset); // // branch, jump // diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index bb98182d781..de1565194ed 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -278,9 +278,7 @@ alloc_class chunk2( // information in this architecture description. // 1) reg_class inline_cache_reg (as defined in frame section) -// 2) reg_class compiler_method_reg (as defined in frame section) -// 2) reg_class interpreter_method_reg (as defined in frame section) -// 3) reg_class stack_slots(/* one chunk of stack-based "registers" */) +// 2) reg_class stack_slots(/* one chunk of stack-based "registers" */) // Integer Register Classes reg_class z_int_reg( @@ -1513,66 +1511,38 @@ static Register reg_to_register_object(int register_encoding) { } const bool Matcher::match_rule_supported(int opcode) { - if (!has_match_rule(opcode)) return false; + if (!has_match_rule(opcode)) { + return false; // no match rule present + } switch (opcode) { - case Op_CountLeadingZerosI: - case Op_CountLeadingZerosL: - case Op_CountTrailingZerosI: - case Op_CountTrailingZerosL: - // Implementation requires FLOGR instruction, which is available since z9. - return true; - case Op_ReverseBytesI: case Op_ReverseBytesL: return UseByteReverseInstruction; - - // PopCount supported by H/W from z/Architecture G5 (z196) on. case Op_PopCountI: case Op_PopCountL: - return UsePopCountInstruction && VM_Version::has_PopCount(); - - case Op_StrComp: - return SpecialStringCompareTo; - case Op_StrEquals: - return SpecialStringEquals; - case Op_StrIndexOf: - case Op_StrIndexOfChar: - return SpecialStringIndexOf; - - case Op_GetAndAddI: - case Op_GetAndAddL: - return true; - // return VM_Version::has_AtomicMemWithImmALUOps(); - case Op_GetAndSetI: - case Op_GetAndSetL: - case Op_GetAndSetP: - case Op_GetAndSetN: - return true; // General CAS implementation, always available. - - default: - return true; // Per default match rules are supported. - // BUT: make sure match rule is not disabled by a false predicate! + // PopCount supported by H/W from z/Architecture G5 (z196) on. + return (UsePopCountInstruction && VM_Version::has_PopCount()); } - return true; // Per default match rules are supported. - // BUT: make sure match rule is not disabled by a false predicate! + return true; // Per default match rules are supported. } const bool Matcher::match_rule_supported_vector(int opcode, int vlen, BasicType bt) { - // TODO - // Identify extra cases that we might want to provide match rules for - // e.g. Op_ vector nodes and other intrinsics while guarding with vlen. - bool ret_value = match_rule_supported(opcode); - // Add rules here. - - return ret_value; // Per default match rules are supported. + if (!match_rule_supported(opcode) || !vector_size_supported(bt, vlen)) { + return false; + } + return true; // Per default match rules are supported. } const bool Matcher::has_predicated_vectors(void) { return false; } +bool Matcher::supports_vector_variable_shifts(void) { + return false; // not supported +} + const int Matcher::float_pressure(int default_pressure_threshold) { return default_pressure_threshold; } @@ -2462,12 +2432,6 @@ frame %{ // Tos is loaded in run_compiled_code to Z_ARG5=Z_R6. // interpreter_arg_ptr_reg(Z_R6); - // Temporary in compiled entry-points - // compiler_method_reg(Z_R1);//Z_R1_scratch - - // Method Register when calling interpreter - interpreter_method_reg(Z_R9);//Z_method - // Optional: name the operand used by cisc-spilling to access // [stack_pointer + offset]. cisc_spilling_operand_name(indOffset12); @@ -3531,20 +3495,6 @@ operand inline_cache_regP(iRegP reg) %{ interface(REG_INTER); %} -operand compiler_method_regP(iRegP reg) %{ - constraint(ALLOC_IN_RC(z_r1_RegP)); // compiler_method_reg - match(reg); - format %{ %} - interface(REG_INTER); -%} - -operand interpreter_method_regP(iRegP reg) %{ - constraint(ALLOC_IN_RC(z_r9_regP)); // interpreter_method_reg - match(reg); - format %{ %} - interface(REG_INTER); -%} - // Operands to remove register moves in unscaled mode. // Match read/write registers with an EncodeP node if neither shift nor add are required. operand iRegP2N(iRegP reg) %{ @@ -10172,8 +10122,9 @@ instruct string_compareUL(iRegP str1, iRegP str2, rarg2RegI cnt1, rarg5RegI cnt2 instruct indexOfChar_U(iRegP haystack, iRegI haycnt, iRegI ch, iRegI result, roddRegL oddReg, revenRegL evenReg, flagsReg cr) %{ match(Set result (StrIndexOfChar (Binary haystack haycnt) ch)); effect(TEMP_DEF result, TEMP evenReg, TEMP oddReg, KILL cr); // R0, R1 are killed, too. + predicate(((StrIndexOfCharNode*)n)->encoding() == StrIntrinsicNode::U); ins_cost(200); - format %{ "String IndexOfChar [0..$haycnt]($haystack), $ch -> $result" %} + format %{ "StringUTF16 IndexOfChar [0..$haycnt]($haystack), $ch -> $result" %} ins_encode %{ __ string_indexof_char($result$$Register, $haystack$$Register, $haycnt$$Register, @@ -10183,6 +10134,21 @@ instruct indexOfChar_U(iRegP haystack, iRegI haycnt, iRegI ch, iRegI result, rod ins_pipe(pipe_class_dummy); %} +instruct indexOfChar_L(iRegP haystack, iRegI haycnt, iRegI ch, iRegI result, roddRegL oddReg, revenRegL evenReg, flagsReg cr) %{ + match(Set result (StrIndexOfChar (Binary haystack haycnt) ch)); + effect(TEMP_DEF result, TEMP evenReg, TEMP oddReg, KILL cr); // R0, R1 are killed, too. + predicate(((StrIndexOfCharNode*)n)->encoding() == StrIntrinsicNode::L); + ins_cost(200); + format %{ "StringLatin1 IndexOfChar [0..$haycnt]($haystack), $ch -> $result" %} + ins_encode %{ + __ string_indexof_char($result$$Register, + $haystack$$Register, $haycnt$$Register, + $ch$$Register, 0 /* unused, ch is in register */, + $oddReg$$Register, $evenReg$$Register, true /*is_byte*/); + %} + ins_pipe(pipe_class_dummy); +%} + instruct indexOf_imm1_U(iRegP haystack, iRegI haycnt, immP needle, immI_1 needlecnt, iRegI result, roddRegL oddReg, revenRegL evenReg, flagsReg cr) %{ match(Set result (StrIndexOf (Binary haystack haycnt) (Binary needle needlecnt))); effect(TEMP_DEF result, TEMP evenReg, TEMP oddReg, KILL cr); // R0, R1 are killed, too. @@ -10809,7 +10775,7 @@ instruct Repl2F_imm0(iRegL dst, immFp0 src) %{ ins_pipe(pipe_class_dummy); %} -// Store +// Load/Store vector // Store Aligned Packed Byte register to memory (8 Bytes). instruct storeA8B(memory mem, iRegL src) %{ @@ -10823,8 +10789,6 @@ instruct storeA8B(memory mem, iRegL src) %{ ins_pipe(pipe_class_dummy); %} -// Load - instruct loadV8(iRegL dst, memory mem) %{ match(Set dst (LoadVector mem)); predicate(n->as_LoadVector()->memory_size() == 8); @@ -10836,6 +10800,15 @@ instruct loadV8(iRegL dst, memory mem) %{ ins_pipe(pipe_class_dummy); %} +// Reinterpret: only one vector size used +instruct reinterpret(iRegL dst) %{ + match(Set dst (VectorReinterpret dst)); + ins_cost(0); + format %{ "reinterpret $dst" %} + ins_encode( /*empty*/ ); + ins_pipe(pipe_class_dummy); +%} + //----------POPULATION COUNT RULES-------------------------------------------- // Byte reverse diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index 48ac8ae443c..a0c46b182ff 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -35,6 +35,7 @@ #include "nativeInst_s390.hpp" #include "oops/compiledICHolder.hpp" #include "oops/klass.inline.hpp" +#include "prims/methodHandles.hpp" #include "registerSaver_s390.hpp" #include "runtime/safepointMechanism.hpp" #include "runtime/sharedRuntime.hpp" @@ -1284,163 +1285,6 @@ static void move32_64(MacroAssembler *masm, } } -static void save_or_restore_arguments(MacroAssembler *masm, - const int stack_slots, - const int total_in_args, - const int arg_save_area, - OopMap *map, - VMRegPair *in_regs, - BasicType *in_sig_bt) { - - // If map is non-NULL then the code should store the values, - // otherwise it should load them. - int slot = arg_save_area; - // Handle double words first. - for (int i = 0; i < total_in_args; i++) { - if (in_regs[i].first()->is_FloatRegister() && in_sig_bt[i] == T_DOUBLE) { - int offset = slot * VMRegImpl::stack_slot_size; - slot += VMRegImpl::slots_per_word; - assert(slot <= stack_slots, "overflow (after DOUBLE stack slot)"); - const FloatRegister freg = in_regs[i].first()->as_FloatRegister(); - Address stackaddr(Z_SP, offset); - if (map != NULL) { - __ freg2mem_opt(freg, stackaddr); - } else { - __ mem2freg_opt(freg, stackaddr); - } - } else if (in_regs[i].first()->is_Register() && - (in_sig_bt[i] == T_LONG || in_sig_bt[i] == T_ARRAY)) { - int offset = slot * VMRegImpl::stack_slot_size; - const Register reg = in_regs[i].first()->as_Register(); - if (map != NULL) { - __ z_stg(reg, offset, Z_SP); - if (in_sig_bt[i] == T_ARRAY) { - map->set_oop(VMRegImpl::stack2reg(slot)); - } - } else { - __ z_lg(reg, offset, Z_SP); - } - slot += VMRegImpl::slots_per_word; - assert(slot <= stack_slots, "overflow (after LONG/ARRAY stack slot)"); - } - } - - // Save or restore single word registers. - for (int i = 0; i < total_in_args; i++) { - if (in_regs[i].first()->is_Register()) { - int offset = slot * VMRegImpl::stack_slot_size; - // Value lives in an input register. Save it on stack. - switch (in_sig_bt[i]) { - case T_BOOLEAN: - case T_CHAR: - case T_BYTE: - case T_SHORT: - case T_INT: { - const Register reg = in_regs[i].first()->as_Register(); - Address stackaddr(Z_SP, offset); - if (map != NULL) { - __ z_st(reg, stackaddr); - } else { - __ z_lgf(reg, stackaddr); - } - slot++; - assert(slot <= stack_slots, "overflow (after INT or smaller stack slot)"); - break; - } - case T_ARRAY: - case T_LONG: - // handled above - break; - case T_OBJECT: - default: ShouldNotReachHere(); - } - } else if (in_regs[i].first()->is_FloatRegister()) { - if (in_sig_bt[i] == T_FLOAT) { - int offset = slot * VMRegImpl::stack_slot_size; - slot++; - assert(slot <= stack_slots, "overflow (after FLOAT stack slot)"); - const FloatRegister freg = in_regs[i].first()->as_FloatRegister(); - Address stackaddr(Z_SP, offset); - if (map != NULL) { - __ freg2mem_opt(freg, stackaddr, false); - } else { - __ mem2freg_opt(freg, stackaddr, false); - } - } - } else if (in_regs[i].first()->is_stack() && - in_sig_bt[i] == T_ARRAY && map != NULL) { - int offset_in_older_frame = in_regs[i].first()->reg2stack() + SharedRuntime::out_preserve_stack_slots(); - map->set_oop(VMRegImpl::stack2reg(offset_in_older_frame + stack_slots)); - } - } -} - -// Check GCLocker::needs_gc and enter the runtime if it's true. This -// keeps a new JNI critical region from starting until a GC has been -// forced. Save down any oops in registers and describe them in an OopMap. -static void check_needs_gc_for_critical_native(MacroAssembler *masm, - const int stack_slots, - const int total_in_args, - const int arg_save_area, - OopMapSet *oop_maps, - VMRegPair *in_regs, - BasicType *in_sig_bt) { - __ block_comment("check GCLocker::needs_gc"); - Label cont; - - // Check GCLocker::_needs_gc flag. - __ load_const_optimized(Z_R1_scratch, (long) GCLocker::needs_gc_address()); - __ z_cli(0, Z_R1_scratch, 0); - __ z_bre(cont); - - // Save down any values that are live in registers and call into the - // runtime to halt for a GC. - OopMap *map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); - - save_or_restore_arguments(masm, stack_slots, total_in_args, - arg_save_area, map, in_regs, in_sig_bt); - address the_pc = __ pc(); - __ set_last_Java_frame(Z_SP, noreg); - - __ block_comment("block_for_jni_critical"); - __ z_lgr(Z_ARG1, Z_thread); - - address entry_point = CAST_FROM_FN_PTR(address, SharedRuntime::block_for_jni_critical); - __ call_c(entry_point); - oop_maps->add_gc_map(__ offset(), map); - - __ reset_last_Java_frame(); - - // Reload all the register arguments. - save_or_restore_arguments(masm, stack_slots, total_in_args, - arg_save_area, NULL, in_regs, in_sig_bt); - - __ bind(cont); - - if (StressCriticalJNINatives) { - // Stress register saving - OopMap *map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); - save_or_restore_arguments(masm, stack_slots, total_in_args, - arg_save_area, map, in_regs, in_sig_bt); - - // Destroy argument registers. - for (int i = 0; i < total_in_args; i++) { - if (in_regs[i].first()->is_Register()) { - // Don't set CC. - __ clear_reg(in_regs[i].first()->as_Register(), true, false); - } else { - if (in_regs[i].first()->is_FloatRegister()) { - FloatRegister fr = in_regs[i].first()->as_FloatRegister(); - __ z_lcdbr(fr, fr); - } - } - } - - save_or_restore_arguments(masm, stack_slots, total_in_args, - arg_save_area, NULL, in_regs, in_sig_bt); - } -} - static void move_ptr(MacroAssembler *masm, VMRegPair src, VMRegPair dst, @@ -1857,12 +1701,6 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, OopMapSet *oop_maps = new OopMapSet(); OopMap *map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); - if (is_critical_native) { - check_needs_gc_for_critical_native(masm, stack_slots, total_in_args, - oop_handle_slot_offset, oop_maps, in_regs, in_sig_bt); - } - - ////////////////////////////////////////////////////////////////////// // // The Grand Shuffle @@ -2091,9 +1929,10 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // Use that pc we placed in Z_R10 a while back as the current frame anchor. __ set_last_Java_frame(Z_SP, Z_R10); - // Transition from _thread_in_Java to _thread_in_native. - __ set_thread_state(_thread_in_native); - + if (!is_critical_native) { + // Transition from _thread_in_Java to _thread_in_native. + __ set_thread_state(_thread_in_native); + } ////////////////////////////////////////////////////////////////////// // This is the JNI call. @@ -2139,6 +1978,19 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, break; } + Label after_transition; + + // If this is a critical native, check for a safepoint or suspend request after the call. + // If a safepoint is needed, transition to native, then to native_trans to handle + // safepoints like the native methods that are not critical natives. + if (is_critical_native) { + Label needs_safepoint; + // Does this need to save_native_result and fences? + __ safepoint_poll(needs_safepoint, Z_R1); + __ load_and_test_int(Z_R0, Address(Z_thread, JavaThread::suspend_flags_offset())); + __ z_bre(after_transition); + __ bind(needs_safepoint); + } // Switch thread to "native transition" state before reading the synchronization state. // This additional state is necessary because reading and testing the synchronization @@ -2158,7 +2010,6 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // Block, if necessary, before resuming in _thread_in_Java state. // In order for GC to work, don't clear the last_Java_sp until after blocking. //-------------------------------------------------------------------- - Label after_transition; { Label no_block, sync; @@ -2180,15 +2031,10 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, __ bind(sync); __ z_acquire(); - address entry_point = is_critical_native ? CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans_and_transition) - : CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans); + address entry_point = CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans); __ call_VM_leaf(entry_point, Z_thread); - if (is_critical_native) { - restore_native_result(masm, ret_type, workspace_slot_offset); - __ z_bru(after_transition); // No thread state transition here. - } __ bind(no_block); restore_native_result(masm, ret_type, workspace_slot_offset); } @@ -2201,7 +2047,6 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, __ set_thread_state(_thread_in_Java); __ bind(after_transition); - //-------------------------------------------------------------------- // Reguard any pages if necessary. // Protect native result from being destroyed. @@ -2384,10 +2229,6 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, in_ByteSize(lock_offset), oop_maps); - if (is_critical_native) { - nm->set_lazy_critical_native(true); - } - return nm; } diff --git a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp index 5d8b11332d8..e1862f11c49 100644 --- a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp @@ -856,7 +856,7 @@ void TemplateInterpreterGenerator::generate_stack_overflow_check(Register frame_ // Compute the beginning of the protected zone minus the requested frame size. __ z_sgr(tmp1, tmp2); - __ add2reg(tmp1, JavaThread::stack_guard_zone_size()); + __ add2reg(tmp1, StackOverflow::stack_guard_zone_size()); // Add in the size of the frame (which is the same as subtracting it from the // SP, which would take another register. diff --git a/src/hotspot/cpu/s390/templateTable_s390.cpp b/src/hotspot/cpu/s390/templateTable_s390.cpp index 9c372db9e78..7a4cf869c30 100644 --- a/src/hotspot/cpu/s390/templateTable_s390.cpp +++ b/src/hotspot/cpu/s390/templateTable_s390.cpp @@ -2007,7 +2007,7 @@ void TemplateTable::branch(bool is_jsr, bool is_wide) { // Out-of-line code runtime calls. if (UseLoopCounter) { - if (ProfileInterpreter) { + if (ProfileInterpreter && !TieredCompilation) { // Out-of-line code to allocate method data oop. __ bind(profile_method); @@ -2377,7 +2377,7 @@ void TemplateTable::_return(TosState state) { if (_desc->bytecode() != Bytecodes::_return_register_finalizer) { Label no_safepoint; - const Address poll_byte_addr(Z_thread, in_bytes(Thread::polling_page_offset()) + 7 /* Big Endian */); + const Address poll_byte_addr(Z_thread, in_bytes(Thread::polling_word_offset()) + 7 /* Big Endian */); __ z_tm(poll_byte_addr, SafepointMechanism::poll_bit()); __ z_braz(no_safepoint); __ push(state); diff --git a/src/hotspot/cpu/s390/vm_version_s390.cpp b/src/hotspot/cpu/s390/vm_version_s390.cpp index 3460a767fac..0a769c9401f 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.cpp +++ b/src/hotspot/cpu/s390/vm_version_s390.cpp @@ -221,6 +221,11 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA512Intrinsics, false); } + if (UseSHA3Intrinsics) { + warning("Intrinsics for SHA3-224, SHA3-256, SHA3-384 and SHA3-512 crypto hash functions not available on this CPU."); + FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); + } + if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics)) { FLAG_SET_DEFAULT(UseSHA, false); } @@ -831,7 +836,7 @@ void VM_Version::determine_features() { code_end-code, cbuf_size, cbuf_size-(code_end-code)); // Use existing decode function. This enables the [MachCode] format which is needed to DecodeErrorFile. - Disassembler::decode(&cbuf, code, code_end, tty); + Disassembler::decode(code, code_end, tty); } // Prepare for detection code execution and clear work buffer. diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index ef04d33c7f4..3933bac000f 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -984,6 +984,8 @@ address Assembler::locate_operand(address inst, WhichOperand which) { case 0x61: // pcmpestri r, r/a, #8 case 0x70: // pshufd r, r/a, #8 case 0x73: // psrldq r, #8 + case 0x1f: // evpcmpd/evpcmpq + case 0x3f: // evpcmpb/evpcmpw tail_size = 1; // the imm8 break; default: @@ -1209,6 +1211,11 @@ void Assembler::addb(Address dst, int imm8) { emit_int8(imm8); } +void Assembler::addw(Register dst, Register src) { + (void)prefix_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x03, 0xC0, dst, src); +} + void Assembler::addw(Address dst, int imm16) { InstructionMark im(this); emit_int8(0x66); @@ -1415,6 +1422,11 @@ void Assembler::vaesenclast(XMMRegister dst, XMMRegister nds, XMMRegister src, i emit_int16((unsigned char)0xDD, (0xC0 | encode)); } +void Assembler::andw(Register dst, Register src) { + (void)prefix_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x23, 0xC0, dst, src); +} + void Assembler::andl(Address dst, int32_t imm32) { InstructionMark im(this); prefix(dst); @@ -1783,6 +1795,13 @@ void Assembler::cvtdq2pd(XMMRegister dst, XMMRegister src) { emit_int16((unsigned char)0xE6, (0xC0 | encode)); } +void Assembler::vcvtdq2pd(XMMRegister dst, XMMRegister src, int vector_len) { + assert(vector_len <= AVX_256bit ? VM_Version::supports_avx() : VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xE6, (0xC0 | encode)); +} + void Assembler::cvtdq2ps(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); @@ -1790,6 +1809,13 @@ void Assembler::cvtdq2ps(XMMRegister dst, XMMRegister src) { emit_int16(0x5B, (0xC0 | encode)); } +void Assembler::vcvtdq2ps(XMMRegister dst, XMMRegister src, int vector_len) { + assert(vector_len <= AVX_256bit ? VM_Version::supports_avx() : VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int16(0x5B, (0xC0 | encode)); +} + void Assembler::cvtsd2ss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); @@ -1912,18 +1938,18 @@ void Assembler::pabsd(XMMRegister dst, XMMRegister src) { } void Assembler::vpabsb(XMMRegister dst, XMMRegister src, int vector_len) { - assert(vector_len == AVX_128bit? VM_Version::supports_avx() : - vector_len == AVX_256bit? VM_Version::supports_avx2() : - vector_len == AVX_512bit? VM_Version::supports_avx512bw() : 0, ""); + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : + vector_len == AVX_256bit ? VM_Version::supports_avx2() : + vector_len == AVX_512bit ? VM_Version::supports_avx512bw() : false, "not supported"); InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int16(0x1C, (0xC0 | encode)); } void Assembler::vpabsw(XMMRegister dst, XMMRegister src, int vector_len) { - assert(vector_len == AVX_128bit? VM_Version::supports_avx() : - vector_len == AVX_256bit? VM_Version::supports_avx2() : - vector_len == AVX_512bit? VM_Version::supports_avx512bw() : 0, ""); + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : + vector_len == AVX_256bit ? VM_Version::supports_avx2() : + vector_len == AVX_512bit ? VM_Version::supports_avx512bw() : false, ""); InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int16(0x1D, (0xC0 | encode)); @@ -1946,6 +1972,85 @@ void Assembler::evpabsq(XMMRegister dst, XMMRegister src, int vector_len) { emit_int16(0x1F, (0xC0 | encode)); } +void Assembler::vcvtps2pd(XMMRegister dst, XMMRegister src, int vector_len) { + assert(vector_len <= AVX_256bit ? VM_Version::supports_avx() : VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int16(0x5A, (0xC0 | encode)); +} + +void Assembler::vcvtpd2ps(XMMRegister dst, XMMRegister src, int vector_len) { + assert(vector_len <= AVX_256bit ? VM_Version::supports_avx() : VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + attributes.set_rex_vex_w_reverted(); + emit_int16(0x5A, (0xC0 | encode)); +} + +void Assembler::evcvtqq2ps(XMMRegister dst, XMMRegister src, int vector_len) { + assert(UseAVX > 2 && VM_Version::supports_avx512dq(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int16(0x5B, (0xC0 | encode)); +} + +void Assembler::evcvtqq2pd(XMMRegister dst, XMMRegister src, int vector_len) { + assert(UseAVX > 2 && VM_Version::supports_avx512dq(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xE6, (0xC0 | encode)); +} + +void Assembler::evpmovwb(XMMRegister dst, XMMRegister src, int vector_len) { + assert(UseAVX > 2 && VM_Version::supports_avx512bw(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(src->encoding(), 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x30, (0xC0 | encode)); +} + +void Assembler::evpmovdw(XMMRegister dst, XMMRegister src, int vector_len) { + assert(UseAVX > 2, ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(src->encoding(), 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x33, (0xC0 | encode)); +} + +void Assembler::evpmovdb(XMMRegister dst, XMMRegister src, int vector_len) { + assert(UseAVX > 2, ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(src->encoding(), 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x31, (0xC0 | encode)); +} + +void Assembler::evpmovqd(XMMRegister dst, XMMRegister src, int vector_len) { + assert(UseAVX > 2, ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(src->encoding(), 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x35, (0xC0 | encode)); +} + +void Assembler::evpmovqb(XMMRegister dst, XMMRegister src, int vector_len) { + assert(UseAVX > 2, ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(src->encoding(), 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x32, (0xC0 | encode)); +} + +void Assembler::evpmovqw(XMMRegister dst, XMMRegister src, int vector_len) { + assert(UseAVX > 2, ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(src->encoding(), 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x34, (0xC0 | encode)); +} + void Assembler::decl(Address dst) { // Don't use it directly. Use MacroAssembler::decrement() instead. InstructionMark im(this); @@ -2543,28 +2648,34 @@ void Assembler::vmovdqu(Address dst, XMMRegister src) { } // Move Unaligned EVEX enabled Vector (programmable : 8,16,32,64) -void Assembler::evmovdqub(XMMRegister dst, XMMRegister src, int vector_len) { +void Assembler::evmovdqub(XMMRegister dst, XMMRegister src, bool merge, int vector_len) { assert(VM_Version::supports_evex(), ""); InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); attributes.set_is_evex_instruction(); + if (merge) { + attributes.reset_is_clear_context(); + } int prefix = (_legacy_mode_bw) ? VEX_SIMD_F2 : VEX_SIMD_F3; int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), (Assembler::VexSimdPrefix)prefix, VEX_OPCODE_0F, &attributes); emit_int16(0x6F, (0xC0 | encode)); } -void Assembler::evmovdqub(XMMRegister dst, Address src, int vector_len) { +void Assembler::evmovdqub(XMMRegister dst, Address src, bool merge, int vector_len) { assert(VM_Version::supports_evex(), ""); InstructionMark im(this); InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); int prefix = (_legacy_mode_bw) ? VEX_SIMD_F2 : VEX_SIMD_F3; attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); attributes.set_is_evex_instruction(); + if (merge) { + attributes.reset_is_clear_context(); + } vex_prefix(src, 0, dst->encoding(), (Assembler::VexSimdPrefix)prefix, VEX_OPCODE_0F, &attributes); emit_int8(0x6F); emit_operand(dst, src); } -void Assembler::evmovdqub(Address dst, XMMRegister src, int vector_len) { +void Assembler::evmovdqub(Address dst, XMMRegister src, bool merge, int vector_len) { assert(VM_Version::supports_evex(), ""); assert(src != xnoreg, "sanity"); InstructionMark im(this); @@ -2572,132 +2683,234 @@ void Assembler::evmovdqub(Address dst, XMMRegister src, int vector_len) { int prefix = (_legacy_mode_bw) ? VEX_SIMD_F2 : VEX_SIMD_F3; attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); attributes.set_is_evex_instruction(); + if (merge) { + attributes.reset_is_clear_context(); + } vex_prefix(dst, 0, src->encoding(), (Assembler::VexSimdPrefix)prefix, VEX_OPCODE_0F, &attributes); emit_int8(0x7F); emit_operand(src, dst); } -void Assembler::evmovdqub(XMMRegister dst, KRegister mask, Address src, int vector_len) { +void Assembler::evmovdqub(XMMRegister dst, KRegister mask, Address src, bool merge, int vector_len) { assert(VM_Version::supports_avx512vlbw(), ""); InstructionMark im(this); InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); attributes.set_embedded_opmask_register_specifier(mask); attributes.set_is_evex_instruction(); + if (merge) { + attributes.reset_is_clear_context(); + } vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x6F); emit_operand(dst, src); } -void Assembler::evmovdquw(XMMRegister dst, Address src, int vector_len) { +void Assembler::evmovdqu(XMMRegister dst, KRegister mask, Address src, int vector_len, int type) { + assert(VM_Version::supports_avx512vlbw(), ""); + assert(type == T_BYTE || type == T_SHORT || type == T_CHAR || type == T_INT || type == T_LONG, ""); + InstructionMark im(this); + bool wide = type == T_SHORT || type == T_CHAR || type == T_LONG; + int prefix = (type == T_BYTE || type == T_SHORT || type == T_CHAR) ? VEX_SIMD_F2 : VEX_SIMD_F3; + InstructionAttr attributes(vector_len, /* vex_w */ wide, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.set_is_evex_instruction(); + vex_prefix(src, 0, dst->encoding(), (Assembler::VexSimdPrefix)prefix, VEX_OPCODE_0F, &attributes); + emit_int8(0x6F); + emit_operand(dst, src); +} + +void Assembler::evmovdqu(Address dst, KRegister mask, XMMRegister src, int vector_len, int type) { + assert(VM_Version::supports_avx512vlbw(), ""); + assert(src != xnoreg, "sanity"); + assert(type == T_BYTE || type == T_SHORT || type == T_CHAR || type == T_INT || type == T_LONG, ""); + InstructionMark im(this); + bool wide = type == T_SHORT || type == T_CHAR || type == T_LONG; + int prefix = (type == T_BYTE || type == T_SHORT || type == T_CHAR) ? VEX_SIMD_F2 : VEX_SIMD_F3; + InstructionAttr attributes(vector_len, /* vex_w */ wide, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + attributes.reset_is_clear_context(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.set_is_evex_instruction(); + vex_prefix(dst, 0, src->encoding(), (Assembler::VexSimdPrefix)prefix, VEX_OPCODE_0F, &attributes); + emit_int8(0x7F); + emit_operand(src, dst); +} + +void Assembler::evmovdquw(XMMRegister dst, Address src, bool merge, int vector_len) { assert(VM_Version::supports_evex(), ""); InstructionMark im(this); InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); attributes.set_is_evex_instruction(); + if (merge) { + attributes.reset_is_clear_context(); + } int prefix = (_legacy_mode_bw) ? VEX_SIMD_F2 : VEX_SIMD_F3; vex_prefix(src, 0, dst->encoding(), (Assembler::VexSimdPrefix)prefix, VEX_OPCODE_0F, &attributes); emit_int8(0x6F); emit_operand(dst, src); } -void Assembler::evmovdquw(XMMRegister dst, KRegister mask, Address src, int vector_len) { +void Assembler::evmovdquw(XMMRegister dst, KRegister mask, Address src, bool merge, int vector_len) { assert(VM_Version::supports_avx512vlbw(), ""); InstructionMark im(this); InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); attributes.set_embedded_opmask_register_specifier(mask); attributes.set_is_evex_instruction(); + if (merge) { + attributes.reset_is_clear_context(); + } vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x6F); emit_operand(dst, src); } -void Assembler::evmovdquw(Address dst, XMMRegister src, int vector_len) { +void Assembler::evmovdquw(Address dst, XMMRegister src, bool merge, int vector_len) { assert(VM_Version::supports_evex(), ""); assert(src != xnoreg, "sanity"); InstructionMark im(this); InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); attributes.set_is_evex_instruction(); + if (merge) { + attributes.reset_is_clear_context(); + } int prefix = (_legacy_mode_bw) ? VEX_SIMD_F2 : VEX_SIMD_F3; vex_prefix(dst, 0, src->encoding(), (Assembler::VexSimdPrefix)prefix, VEX_OPCODE_0F, &attributes); emit_int8(0x7F); emit_operand(src, dst); } -void Assembler::evmovdquw(Address dst, KRegister mask, XMMRegister src, int vector_len) { +void Assembler::evmovdquw(Address dst, KRegister mask, XMMRegister src, bool merge, int vector_len) { assert(VM_Version::supports_avx512vlbw(), ""); assert(src != xnoreg, "sanity"); InstructionMark im(this); InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); - attributes.reset_is_clear_context(); attributes.set_embedded_opmask_register_specifier(mask); attributes.set_is_evex_instruction(); + if (merge) { + attributes.reset_is_clear_context(); + } vex_prefix(dst, 0, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x7F); emit_operand(src, dst); } void Assembler::evmovdqul(XMMRegister dst, XMMRegister src, int vector_len) { + // Unmasked instruction + evmovdqul(dst, k0, src, /*merge*/ false, vector_len); +} + +void Assembler::evmovdqul(XMMRegister dst, KRegister mask, XMMRegister src, bool merge, int vector_len) { assert(VM_Version::supports_evex(), ""); - InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_embedded_opmask_register_specifier(mask); attributes.set_is_evex_instruction(); + if (merge) { + attributes.reset_is_clear_context(); + } int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int16(0x6F, (0xC0 | encode)); } void Assembler::evmovdqul(XMMRegister dst, Address src, int vector_len) { + // Unmasked instruction + evmovdqul(dst, k0, src, /*merge*/ false, vector_len); +} + +void Assembler::evmovdqul(XMMRegister dst, KRegister mask, Address src, bool merge, int vector_len) { assert(VM_Version::supports_evex(), ""); InstructionMark im(this); - InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true , /* uses_vl */ true); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false , /* uses_vl */ true); attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + attributes.set_embedded_opmask_register_specifier(mask); attributes.set_is_evex_instruction(); + if (merge) { + attributes.reset_is_clear_context(); + } vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x6F); emit_operand(dst, src); } void Assembler::evmovdqul(Address dst, XMMRegister src, int vector_len) { + // Unmasked isntruction + evmovdqul(dst, k0, src, /*merge*/ true, vector_len); +} + +void Assembler::evmovdqul(Address dst, KRegister mask, XMMRegister src, bool merge, int vector_len) { assert(VM_Version::supports_evex(), ""); assert(src != xnoreg, "sanity"); InstructionMark im(this); - InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); - attributes.reset_is_clear_context(); + attributes.set_embedded_opmask_register_specifier(mask); attributes.set_is_evex_instruction(); + if (merge) { + attributes.reset_is_clear_context(); + } vex_prefix(dst, 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x7F); emit_operand(src, dst); } void Assembler::evmovdquq(XMMRegister dst, XMMRegister src, int vector_len) { + // Unmasked instruction + if (dst->encoding() == src->encoding()) return; + evmovdquq(dst, k0, src, /*merge*/ false, vector_len); +} + +void Assembler::evmovdquq(XMMRegister dst, KRegister mask, XMMRegister src, bool merge, int vector_len) { assert(VM_Version::supports_evex(), ""); - InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_embedded_opmask_register_specifier(mask); attributes.set_is_evex_instruction(); + if (merge) { + attributes.reset_is_clear_context(); + } int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int16(0x6F, (0xC0 | encode)); } void Assembler::evmovdquq(XMMRegister dst, Address src, int vector_len) { + // Unmasked instruction + evmovdquq(dst, k0, src, /*merge*/ false, vector_len); +} + +void Assembler::evmovdquq(XMMRegister dst, KRegister mask, Address src, bool merge, int vector_len) { assert(VM_Version::supports_evex(), ""); InstructionMark im(this); - InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + attributes.set_embedded_opmask_register_specifier(mask); attributes.set_is_evex_instruction(); + if (merge) { + attributes.reset_is_clear_context(); + } vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x6F); emit_operand(dst, src); } void Assembler::evmovdquq(Address dst, XMMRegister src, int vector_len) { + // Unmasked instruction + evmovdquq(dst, k0, src, /*merge*/ true, vector_len); +} + +void Assembler::evmovdquq(Address dst, KRegister mask, XMMRegister src, bool merge, int vector_len) { assert(VM_Version::supports_evex(), ""); assert(src != xnoreg, "sanity"); InstructionMark im(this); - InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); - attributes.reset_is_clear_context(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } attributes.set_is_evex_instruction(); vex_prefix(dst, 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x7F); @@ -2775,6 +2988,29 @@ void Assembler::movq(Address dst, XMMRegister src) { emit_operand(src, dst); } +void Assembler::movq(XMMRegister dst, XMMRegister src) { + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_rex_vex_w_reverted(); + int encode = simd_prefix_and_encode(src, xnoreg, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xD6, (0xC0 | encode)); +} + +void Assembler::movq(Register dst, XMMRegister src) { + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + // swap src/dst to get correct prefix + int encode = simd_prefix_and_encode(src, xnoreg, as_XMMRegister(dst->encoding()), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16(0x7E, (0xC0 | encode)); +} + +void Assembler::movq(XMMRegister dst, Register src) { + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, xnoreg, as_XMMRegister(src->encoding()), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16(0x6E, (0xC0 | encode)); +} + void Assembler::movsbl(Register dst, Address src) { // movsxb InstructionMark im(this); prefix(src, dst); @@ -3274,6 +3510,11 @@ void Assembler::notl(Register dst) { emit_int16((unsigned char)0xF7, (0xD0 | encode)); } +void Assembler::orw(Register dst, Register src) { + (void)prefix_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x0B, 0xC0, dst, src); +} + void Assembler::orl(Address dst, int32_t imm32) { InstructionMark im(this); prefix(dst); @@ -3312,6 +3553,34 @@ void Assembler::orb(Address dst, int imm8) { emit_int8(imm8); } +void Assembler::packsswb(XMMRegister dst, XMMRegister src) { + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16(0x63, (0xC0 | encode)); +} + +void Assembler::vpacksswb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(UseAVX > 0, "some form of AVX must be enabled"); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16(0x63, (0xC0 | encode)); +} + +void Assembler::packssdw(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse2(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16(0x6B, (0xC0 | encode)); +} + +void Assembler::vpackssdw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(UseAVX > 0, "some form of AVX must be enabled"); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16(0x6B, (0xC0 | encode)); +} + void Assembler::packuswb(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); assert((UseAVX > 0), "SSE mode requires address alignment 16 bytes"); @@ -3337,21 +3606,74 @@ void Assembler::vpackuswb(XMMRegister dst, XMMRegister nds, XMMRegister src, int emit_int16(0x67, (0xC0 | encode)); } +void Assembler::packusdw(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse4_1(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x2B, (0xC0 | encode)); +} + +void Assembler::vpackusdw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(UseAVX > 0, "some form of AVX must be enabled"); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x2B, (0xC0 | encode)); +} + void Assembler::vpermq(XMMRegister dst, XMMRegister src, int imm8, int vector_len) { assert(VM_Version::supports_avx2(), ""); + assert(vector_len != AVX_128bit, ""); + // VEX.256.66.0F3A.W1 00 /r ib InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int24(0x00, (0xC0 | encode), imm8); } void Assembler::vpermq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - assert(UseAVX > 2, "requires AVX512F"); + assert(vector_len == AVX_256bit ? VM_Version::supports_avx512vl() : + vector_len == AVX_512bit ? VM_Version::supports_evex() : false, "not supported"); InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); attributes.set_is_evex_instruction(); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int16(0x36, (0xC0 | encode)); } +void Assembler::vpermb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_avx512_vbmi(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16((unsigned char)0x8D, (0xC0 | encode)); +} + +void Assembler::vpermw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(vector_len == AVX_128bit ? VM_Version::supports_avx512vlbw() : + vector_len == AVX_256bit ? VM_Version::supports_avx512vlbw() : + vector_len == AVX_512bit ? VM_Version::supports_avx512bw() : false, "not supported"); + InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16((unsigned char)0x8D, (0xC0 | encode)); +} + +void Assembler::vpermd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(vector_len <= AVX_256bit ? VM_Version::supports_avx2() : VM_Version::supports_evex(), ""); + // VEX.NDS.256.66.0F38.W0 36 /r + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x36, (0xC0 | encode)); +} + +void Assembler::vpermd(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { + assert(vector_len <= AVX_256bit ? VM_Version::supports_avx2() : VM_Version::supports_evex(), ""); + // VEX.NDS.256.66.0F38.W0 36 /r + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int8(0x36); + emit_operand(dst, src); +} + void Assembler::vperm2i128(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8) { assert(VM_Version::supports_avx2(), ""); InstructionAttr attributes(AVX_256bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); @@ -3366,6 +3688,28 @@ void Assembler::vperm2f128(XMMRegister dst, XMMRegister nds, XMMRegister src, in emit_int24(0x06, (0xC0 | encode), imm8); } +void Assembler::vpermilps(XMMRegister dst, XMMRegister src, int imm8, int vector_len) { + assert(vector_len <= AVX_256bit ? VM_Version::supports_avx() : VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x04, (0xC0 | encode), imm8); +} + +void Assembler::vpermilpd(XMMRegister dst, XMMRegister src, int imm8, int vector_len) { + assert(vector_len <= AVX_256bit ? VM_Version::supports_avx() : VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ VM_Version::supports_evex(),/* legacy_mode */ false,/* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_rex_vex_w_reverted(); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x05, (0xC0 | encode), imm8); +} + +void Assembler::vpermpd(XMMRegister dst, XMMRegister src, int imm8, int vector_len) { + assert(vector_len <= AVX_256bit ? VM_Version::supports_avx2() : VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x01, (0xC0 | encode), imm8); +} + void Assembler::evpermi2q(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(VM_Version::supports_evex(), ""); InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); @@ -3374,7 +3718,6 @@ void Assembler::evpermi2q(XMMRegister dst, XMMRegister nds, XMMRegister src, int emit_int16(0x76, (0xC0 | encode)); } - void Assembler::pause() { emit_int16((unsigned char)0xF3, (unsigned char)0x90); } @@ -3408,9 +3751,18 @@ void Assembler::pcmpeqb(XMMRegister dst, XMMRegister src) { emit_int16(0x74, (0xC0 | encode)); } +void Assembler::vpcmpCCbwd(XMMRegister dst, XMMRegister nds, XMMRegister src, int cond_encoding, int vector_len) { + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : VM_Version::supports_avx2(), ""); + assert(vector_len <= AVX_256bit, "evex encoding is different - has k register as dest"); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16(cond_encoding, (0xC0 | encode)); +} + // In this context, the dst vector contains the components that are equal, non equal components are zeroed in dst void Assembler::vpcmpeqb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - assert(VM_Version::supports_avx(), ""); + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : VM_Version::supports_avx2(), ""); + assert(vector_len <= AVX_256bit, "evex encoding is different - has k register as dest"); InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int16(0x74, (0xC0 | encode)); @@ -3497,7 +3849,7 @@ void Assembler::evpcmpeqb(KRegister kdst, XMMRegister nds, Address src, int vect void Assembler::evpcmpeqb(KRegister kdst, KRegister mask, XMMRegister nds, Address src, int vector_len) { assert(VM_Version::supports_avx512vlbw(), ""); InstructionMark im(this); - InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_reg_mask */ false, /* uses_vl */ true); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); attributes.reset_is_clear_context(); attributes.set_embedded_opmask_register_specifier(mask); @@ -3517,7 +3869,8 @@ void Assembler::pcmpeqw(XMMRegister dst, XMMRegister src) { // In this context, the dst vector contains the components that are equal, non equal components are zeroed in dst void Assembler::vpcmpeqw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - assert(VM_Version::supports_avx(), ""); + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : VM_Version::supports_avx2(), ""); + assert(vector_len <= AVX_256bit, "evex encoding is different - has k register as dest"); InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int16(0x75, (0xC0 | encode)); @@ -3554,29 +3907,32 @@ void Assembler::pcmpeqd(XMMRegister dst, XMMRegister src) { // In this context, the dst vector contains the components that are equal, non equal components are zeroed in dst void Assembler::vpcmpeqd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - assert(VM_Version::supports_avx(), ""); - InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : VM_Version::supports_avx2(), ""); + assert(vector_len <= AVX_256bit, "evex encoding is different - has k register as dest"); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int16(0x76, (0xC0 | encode)); } // In this context, kdst is written the mask used to process the equal components -void Assembler::evpcmpeqd(KRegister kdst, XMMRegister nds, XMMRegister src, int vector_len) { +void Assembler::evpcmpeqd(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, int vector_len) { assert(VM_Version::supports_evex(), ""); - InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); attributes.set_is_evex_instruction(); attributes.reset_is_clear_context(); + attributes.set_embedded_opmask_register_specifier(mask); int encode = vex_prefix_and_encode(kdst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int16(0x76, (0xC0 | encode)); } -void Assembler::evpcmpeqd(KRegister kdst, XMMRegister nds, Address src, int vector_len) { +void Assembler::evpcmpeqd(KRegister kdst, KRegister mask, XMMRegister nds, Address src, int vector_len) { assert(VM_Version::supports_evex(), ""); InstructionMark im(this); - InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); - attributes.reset_is_clear_context(); attributes.set_is_evex_instruction(); + attributes.reset_is_clear_context(); + attributes.set_embedded_opmask_register_specifier(mask); int dst_enc = kdst->encoding(); vex_prefix(src, nds->encoding(), dst_enc, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x76); @@ -3591,6 +3947,13 @@ void Assembler::pcmpeqq(XMMRegister dst, XMMRegister src) { emit_int16(0x29, (0xC0 | encode)); } +void Assembler::vpcmpCCq(XMMRegister dst, XMMRegister nds, XMMRegister src, int cond_encoding, int vector_len) { + assert(VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(cond_encoding, (0xC0 | encode)); +} + // In this context, the dst vector contains the components that are equal, non equal components are zeroed in dst void Assembler::vpcmpeqq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(VM_Version::supports_avx(), ""); @@ -3623,11 +3986,36 @@ void Assembler::evpcmpeqq(KRegister kdst, XMMRegister nds, Address src, int vect emit_operand(as_Register(dst_enc), src); } -void Assembler::pmovmskb(Register dst, XMMRegister src) { - assert(VM_Version::supports_sse2(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); - int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); - emit_int16((unsigned char)0xD7, (0xC0 | encode)); +void Assembler::evpmovd2m(KRegister kdst, XMMRegister src, int vector_len) { + assert(UseAVX > 2 && VM_Version::supports_avx512dq(), ""); + assert(vector_len == AVX_512bit || VM_Version::supports_avx512vl(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(kdst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x39, (0xC0 | encode)); +} + +void Assembler::evpmovq2m(KRegister kdst, XMMRegister src, int vector_len) { + assert(UseAVX > 2 && VM_Version::supports_avx512dq(), ""); + assert(vector_len == AVX_512bit || VM_Version::supports_avx512vl(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(kdst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x39, (0xC0 | encode)); +} + +void Assembler::pcmpgtq(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse4_1(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x37, (0xC0 | encode)); +} + +void Assembler::pmovmskb(Register dst, XMMRegister src) { + assert(VM_Version::supports_sse2(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xD7, (0xC0 | encode)); } void Assembler::vpmovmskb(Register dst, XMMRegister src) { @@ -3639,14 +4027,14 @@ void Assembler::vpmovmskb(Register dst, XMMRegister src) { void Assembler::pextrd(Register dst, XMMRegister src, int imm8) { assert(VM_Version::supports_sse4_1(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(src, xnoreg, as_XMMRegister(dst->encoding()), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int24(0x16, (0xC0 | encode), imm8); } void Assembler::pextrd(Address dst, XMMRegister src, int imm8) { assert(VM_Version::supports_sse4_1(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); simd_prefix(src, xnoreg, dst, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x16); @@ -3656,14 +4044,14 @@ void Assembler::pextrd(Address dst, XMMRegister src, int imm8) { void Assembler::pextrq(Register dst, XMMRegister src, int imm8) { assert(VM_Version::supports_sse4_1(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(src, xnoreg, as_XMMRegister(dst->encoding()), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int24(0x16, (0xC0 | encode), imm8); } void Assembler::pextrq(Address dst, XMMRegister src, int imm8) { assert(VM_Version::supports_sse4_1(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); simd_prefix(src, xnoreg, dst, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x16); @@ -3673,14 +4061,14 @@ void Assembler::pextrq(Address dst, XMMRegister src, int imm8) { void Assembler::pextrw(Register dst, XMMRegister src, int imm8) { assert(VM_Version::supports_sse2(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int24((unsigned char)0xC5, (0xC0 | encode), imm8); } void Assembler::pextrw(Address dst, XMMRegister src, int imm8) { assert(VM_Version::supports_sse4_1(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_16bit); simd_prefix(src, xnoreg, dst, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x15); @@ -3688,9 +4076,16 @@ void Assembler::pextrw(Address dst, XMMRegister src, int imm8) { emit_int8(imm8); } +void Assembler::pextrb(Register dst, XMMRegister src, int imm8) { + assert(VM_Version::supports_sse4_1(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(src, xnoreg, as_XMMRegister(dst->encoding()), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x14, (0xC0 | encode), imm8); +} + void Assembler::pextrb(Address dst, XMMRegister src, int imm8) { assert(VM_Version::supports_sse4_1(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_8bit); simd_prefix(src, xnoreg, dst, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x14); @@ -3700,14 +4095,14 @@ void Assembler::pextrb(Address dst, XMMRegister src, int imm8) { void Assembler::pinsrd(XMMRegister dst, Register src, int imm8) { assert(VM_Version::supports_sse4_1(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int24(0x22, (0xC0 | encode), imm8); } void Assembler::pinsrd(XMMRegister dst, Address src, int imm8) { assert(VM_Version::supports_sse4_1(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x22); @@ -3715,16 +4110,23 @@ void Assembler::pinsrd(XMMRegister dst, Address src, int imm8) { emit_int8(imm8); } +void Assembler::vpinsrd(XMMRegister dst, XMMRegister nds, Register src, int imm8) { + assert(VM_Version::supports_avx(), ""); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x22, (0xC0 | encode), imm8); +} + void Assembler::pinsrq(XMMRegister dst, Register src, int imm8) { assert(VM_Version::supports_sse4_1(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int24(0x22, (0xC0 | encode), imm8); } void Assembler::pinsrq(XMMRegister dst, Address src, int imm8) { assert(VM_Version::supports_sse4_1(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x22); @@ -3732,16 +4134,23 @@ void Assembler::pinsrq(XMMRegister dst, Address src, int imm8) { emit_int8(imm8); } +void Assembler::vpinsrq(XMMRegister dst, XMMRegister nds, Register src, int imm8) { + assert(VM_Version::supports_avx(), ""); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x22, (0xC0 | encode), imm8); +} + void Assembler::pinsrw(XMMRegister dst, Register src, int imm8) { assert(VM_Version::supports_sse2(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int24((unsigned char)0xC4, (0xC0 | encode), imm8); } void Assembler::pinsrw(XMMRegister dst, Address src, int imm8) { assert(VM_Version::supports_sse2(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_16bit); simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8((unsigned char)0xC4); @@ -3749,9 +4158,16 @@ void Assembler::pinsrw(XMMRegister dst, Address src, int imm8) { emit_int8(imm8); } +void Assembler::vpinsrw(XMMRegister dst, XMMRegister nds, Register src, int imm8) { + assert(VM_Version::supports_avx(), ""); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int24((unsigned char)0xC4, (0xC0 | encode), imm8); +} + void Assembler::pinsrb(XMMRegister dst, Address src, int imm8) { assert(VM_Version::supports_sse4_1(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_8bit); simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x20); @@ -3759,6 +4175,34 @@ void Assembler::pinsrb(XMMRegister dst, Address src, int imm8) { emit_int8(imm8); } +void Assembler::pinsrb(XMMRegister dst, Register src, int imm8) { + assert(VM_Version::supports_sse4_1(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x20, (0xC0 | encode), imm8); +} + +void Assembler::vpinsrb(XMMRegister dst, XMMRegister nds, Register src, int imm8) { + assert(VM_Version::supports_avx(), ""); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x20, (0xC0 | encode), imm8); +} + +void Assembler::insertps(XMMRegister dst, XMMRegister src, int imm8) { + assert(VM_Version::supports_sse4_1(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x21, (0xC0 | encode), imm8); +} + +void Assembler::vinsertps(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8) { + assert(VM_Version::supports_avx(), ""); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x21, (0xC0 | encode), imm8); +} + void Assembler::pmovzxbw(XMMRegister dst, Address src) { assert(VM_Version::supports_sse4_1(), ""); InstructionMark im(this); @@ -3783,6 +4227,41 @@ void Assembler::pmovsxbw(XMMRegister dst, XMMRegister src) { emit_int16(0x20, (0xC0 | encode)); } +void Assembler::pmovzxdq(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse4_1(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x35, (0xC0 | encode)); +} + +void Assembler::pmovsxbd(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse4_1(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x21, (0xC0 | encode)); +} + +void Assembler::pmovzxbd(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse4_1(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x31, (0xC0 | encode)); +} + +void Assembler::pmovsxbq(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse4_1(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x22, (0xC0 | encode)); +} + +void Assembler::pmovsxwd(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse4_1(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x23, (0xC0 | encode)); +} + void Assembler::vpmovzxbw(XMMRegister dst, Address src, int vector_len) { assert(VM_Version::supports_avx(), ""); InstructionMark im(this); @@ -3816,7 +4295,7 @@ void Assembler::evpmovzxbw(XMMRegister dst, KRegister mask, Address src, int vec assert(VM_Version::supports_avx512vlbw(), ""); assert(dst != xnoreg, "sanity"); InstructionMark im(this); - InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); attributes.set_address_attributes(/* tuple_type */ EVEX_HVM, /* input_size_in_bits */ EVEX_NObit); attributes.set_embedded_opmask_register_specifier(mask); attributes.set_is_evex_instruction(); @@ -3824,6 +4303,86 @@ void Assembler::evpmovzxbw(XMMRegister dst, KRegister mask, Address src, int vec emit_int8(0x30); emit_operand(dst, src); } + +void Assembler::evpandd(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len) { + assert(VM_Version::supports_evex(), ""); + // Encoding: EVEX.NDS.XXX.66.0F.W0 DB /r + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xDB, (0xC0 | encode)); +} + +void Assembler::vpmovzxdq(XMMRegister dst, XMMRegister src, int vector_len) { + assert(vector_len > AVX_128bit ? VM_Version::supports_avx2() : VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x35, (0xC0 | encode)); +} + +void Assembler::vpmovzxbd(XMMRegister dst, XMMRegister src, int vector_len) { + assert(vector_len > AVX_128bit ? VM_Version::supports_avx2() : VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x31, (0xC0 | encode)); +} + +void Assembler::vpmovzxbq(XMMRegister dst, XMMRegister src, int vector_len) { + assert(vector_len > AVX_128bit ? VM_Version::supports_avx2() : VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x32, (0xC0 | encode)); +} + +void Assembler::vpmovsxbd(XMMRegister dst, XMMRegister src, int vector_len) { + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : + vector_len == AVX_256bit ? VM_Version::supports_avx2() : + VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x21, (0xC0 | encode)); +} + +void Assembler::vpmovsxbq(XMMRegister dst, XMMRegister src, int vector_len) { + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : + vector_len == AVX_256bit ? VM_Version::supports_avx2() : + VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x22, (0xC0 | encode)); +} + +void Assembler::vpmovsxwd(XMMRegister dst, XMMRegister src, int vector_len) { + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : + vector_len == AVX_256bit ? VM_Version::supports_avx2() : + VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x23, (0xC0 | encode)); +} + +void Assembler::vpmovsxwq(XMMRegister dst, XMMRegister src, int vector_len) { + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : + vector_len == AVX_256bit ? VM_Version::supports_avx2() : + VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x24, (0xC0 | encode)); +} + +void Assembler::vpmovsxdq(XMMRegister dst, XMMRegister src, int vector_len) { + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : + vector_len == AVX_256bit ? VM_Version::supports_avx2() : + VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x25, (0xC0 | encode)); +} + void Assembler::evpmovwb(Address dst, XMMRegister src, int vector_len) { assert(VM_Version::supports_avx512vlbw(), ""); assert(src != xnoreg, "sanity"); @@ -4050,6 +4609,14 @@ void Assembler::pshufd(XMMRegister dst, Address src, int mode) { emit_int8(mode & 0xFF); } +void Assembler::pshufhw(XMMRegister dst, XMMRegister src, int mode) { + assert(isByte(mode), "invalid value"); + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int24(0x70, (0xC0 | encode), mode & 0xFF); +} + void Assembler::pshuflw(XMMRegister dst, XMMRegister src, int mode) { assert(isByte(mode), "invalid value"); NOT_LP64(assert(VM_Version::supports_sse2(), "")); @@ -4080,6 +4647,35 @@ void Assembler::evshufi64x2(XMMRegister dst, XMMRegister nds, XMMRegister src, i emit_int24(0x43, (0xC0 | encode), imm8 & 0xFF); } +void Assembler::pshufpd(XMMRegister dst, XMMRegister src, int imm8) { + assert(isByte(imm8), "invalid value"); + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int24((unsigned char)0xC6, (0xC0 | encode), imm8 & 0xFF); +} + +void Assembler::vpshufpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8, int vector_len) { + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_rex_vex_w_reverted(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int24((unsigned char)0xC6, (0xC0 | encode), imm8 & 0xFF); +} + +void Assembler::pshufps(XMMRegister dst, XMMRegister src, int imm8) { + assert(isByte(imm8), "invalid value"); + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int24((unsigned char)0xC6, (0xC0 | encode), imm8 & 0xFF); +} + +void Assembler::vpshufps(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8, int vector_len) { + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int24((unsigned char)0xC6, (0xC0 | encode), imm8 & 0xFF); +} + void Assembler::psrldq(XMMRegister dst, int shift) { // Shift left 128 bit value in dst XMMRegister by shift number of bytes. NOT_LP64(assert(VM_Version::supports_sse2(), "")); @@ -4151,6 +4747,13 @@ void Assembler::vptest(XMMRegister dst, XMMRegister src) { emit_int16(0x17, (0xC0 | encode)); } +void Assembler::vptest(XMMRegister dst, XMMRegister src, int vector_len) { + assert(VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x17, (0xC0 | encode)); +} + void Assembler::punpcklbw(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); assert((UseAVX > 0), "SSE mode requires address alignment 16 bytes"); @@ -4881,6 +5484,11 @@ void Assembler::xorb(Register dst, Address src) { emit_operand(dst, src); } +void Assembler::xorw(Register dst, Register src) { + (void)prefix_and_encode(dst->encoding(), src->encoding()); + emit_arith(0x33, 0xC0, dst, src); +} + // AVX 3-operands scalar float-point arithmetic instructions void Assembler::vaddsd(XMMRegister dst, XMMRegister nds, Address src) { @@ -5794,6 +6402,13 @@ void Assembler::pmulld(XMMRegister dst, XMMRegister src) { emit_int16(0x40, (0xC0 | encode)); } +void Assembler::pmuludq(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse2(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xF4, (0xC0 | encode)); +} + void Assembler::vpmullw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); @@ -5816,6 +6431,13 @@ void Assembler::vpmullq(XMMRegister dst, XMMRegister nds, XMMRegister src, int v emit_int16(0x40, (0xC0 | encode)); } +void Assembler::vpmuludq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(UseAVX > 0, "requires some form of AVX"); + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xF4, (0xC0 | encode)); +} + void Assembler::vpmullw(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); InstructionMark im(this); @@ -5847,66 +6469,227 @@ void Assembler::vpmullq(XMMRegister dst, XMMRegister nds, Address src, int vecto emit_operand(dst, src); } -// Shift packed integers left by specified number of bits. -void Assembler::psllw(XMMRegister dst, int shift) { - NOT_LP64(assert(VM_Version::supports_sse2(), "")); +// Min, max +void Assembler::pminsb(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse4_1(), ""); InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); - // XMM6 is for /6 encoding: 66 0F 71 /6 ib - int encode = simd_prefix_and_encode(xmm6, dst, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); - emit_int24(0x71, (0xC0 | encode), shift & 0xFF); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x38, (0xC0 | encode)); } -void Assembler::pslld(XMMRegister dst, int shift) { - NOT_LP64(assert(VM_Version::supports_sse2(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); - // XMM6 is for /6 encoding: 66 0F 72 /6 ib - int encode = simd_prefix_and_encode(xmm6, dst, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); - emit_int24(0x72, (0xC0 | encode), shift & 0xFF); +void Assembler::vpminsb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : + (vector_len == AVX_256bit ? VM_Version::supports_avx2() : VM_Version::supports_avx512bw()), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x38, (0xC0 | encode)); } -void Assembler::psllq(XMMRegister dst, int shift) { - NOT_LP64(assert(VM_Version::supports_sse2(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); - // XMM6 is for /6 encoding: 66 0F 73 /6 ib - int encode = simd_prefix_and_encode(xmm6, dst, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); - emit_int24(0x73, (0xC0 | encode), shift & 0xFF); +void Assembler::pminsw(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse2(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xEA, (0xC0 | encode)); } -void Assembler::psllw(XMMRegister dst, XMMRegister shift) { - NOT_LP64(assert(VM_Version::supports_sse2(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); - int encode = simd_prefix_and_encode(dst, dst, shift, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); - emit_int16((unsigned char)0xF1, (0xC0 | encode)); +void Assembler::vpminsw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : + (vector_len == AVX_256bit ? VM_Version::supports_avx2() : VM_Version::supports_avx512bw()), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xEA, (0xC0 | encode)); } -void Assembler::pslld(XMMRegister dst, XMMRegister shift) { - NOT_LP64(assert(VM_Version::supports_sse2(), "")); +void Assembler::pminsd(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse4_1(), ""); InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); - int encode = simd_prefix_and_encode(dst, dst, shift, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); - emit_int16((unsigned char)0xF2, (0xC0 | encode)); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x39, (0xC0 | encode)); } -void Assembler::psllq(XMMRegister dst, XMMRegister shift) { - NOT_LP64(assert(VM_Version::supports_sse2(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); - attributes.set_rex_vex_w_reverted(); - int encode = simd_prefix_and_encode(dst, dst, shift, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); - emit_int16((unsigned char)0xF3, (0xC0 | encode)); +void Assembler::vpminsd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : + (vector_len == AVX_256bit ? VM_Version::supports_avx2() : VM_Version::supports_evex()), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x39, (0xC0 | encode)); } -void Assembler::vpsllw(XMMRegister dst, XMMRegister src, int shift, int vector_len) { - assert(UseAVX > 0, "requires some form of AVX"); - InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); - // XMM6 is for /6 encoding: 66 0F 71 /6 ib - int encode = vex_prefix_and_encode(xmm6->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); - emit_int24(0x71, (0xC0 | encode), shift & 0xFF); +void Assembler::vpminsq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(UseAVX > 2, "requires AVX512F"); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x39, (0xC0 | encode)); } -void Assembler::vpslld(XMMRegister dst, XMMRegister src, int shift, int vector_len) { - assert(UseAVX > 0, "requires some form of AVX"); - NOT_LP64(assert(VM_Version::supports_sse2(), "")); +void Assembler::minps(XMMRegister dst, XMMRegister src) { + NOT_LP64(assert(VM_Version::supports_sse(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int16(0x5D, (0xC0 | encode)); +} +void Assembler::vminps(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(vector_len >= AVX_512bit ? VM_Version::supports_evex() : VM_Version::supports_avx(), ""); InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); - // XMM6 is for /6 encoding: 66 0F 72 /6 ib + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int16(0x5D, (0xC0 | encode)); +} + +void Assembler::minpd(XMMRegister dst, XMMRegister src) { + NOT_LP64(assert(VM_Version::supports_sse(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16(0x5D, (0xC0 | encode)); +} +void Assembler::vminpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(vector_len >= AVX_512bit ? VM_Version::supports_evex() : VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16(0x5D, (0xC0 | encode)); +} + +void Assembler::pmaxsb(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse4_1(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x3C, (0xC0 | encode)); +} + +void Assembler::vpmaxsb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : + (vector_len == AVX_256bit ? VM_Version::supports_avx2() : VM_Version::supports_avx512bw()), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x3C, (0xC0 | encode)); +} + +void Assembler::pmaxsw(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse2(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xEE, (0xC0 | encode)); +} + +void Assembler::vpmaxsw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : + (vector_len == AVX_256bit ? VM_Version::supports_avx2() : VM_Version::supports_avx512bw()), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xEE, (0xC0 | encode)); +} + +void Assembler::pmaxsd(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse4_1(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x3D, (0xC0 | encode)); +} + +void Assembler::vpmaxsd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : + (vector_len == AVX_256bit ? VM_Version::supports_avx2() : VM_Version::supports_evex()), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x3D, (0xC0 | encode)); +} + +void Assembler::vpmaxsq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(UseAVX > 2, "requires AVX512F"); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x3D, (0xC0 | encode)); +} + +void Assembler::maxps(XMMRegister dst, XMMRegister src) { + NOT_LP64(assert(VM_Version::supports_sse(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int16(0x5F, (0xC0 | encode)); +} + +void Assembler::vmaxps(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(vector_len >= AVX_512bit ? VM_Version::supports_evex() : VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int16(0x5F, (0xC0 | encode)); +} + +void Assembler::maxpd(XMMRegister dst, XMMRegister src) { + NOT_LP64(assert(VM_Version::supports_sse(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16(0x5F, (0xC0 | encode)); +} + +void Assembler::vmaxpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(vector_len >= AVX_512bit ? VM_Version::supports_evex() : VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* vex_w */true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16(0x5F, (0xC0 | encode)); +} + +// Shift packed integers left by specified number of bits. +void Assembler::psllw(XMMRegister dst, int shift) { + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + // XMM6 is for /6 encoding: 66 0F 71 /6 ib + int encode = simd_prefix_and_encode(xmm6, dst, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int24(0x71, (0xC0 | encode), shift & 0xFF); +} + +void Assembler::pslld(XMMRegister dst, int shift) { + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + // XMM6 is for /6 encoding: 66 0F 72 /6 ib + int encode = simd_prefix_and_encode(xmm6, dst, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int24(0x72, (0xC0 | encode), shift & 0xFF); +} + +void Assembler::psllq(XMMRegister dst, int shift) { + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + // XMM6 is for /6 encoding: 66 0F 73 /6 ib + int encode = simd_prefix_and_encode(xmm6, dst, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int24(0x73, (0xC0 | encode), shift & 0xFF); +} + +void Assembler::psllw(XMMRegister dst, XMMRegister shift) { + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, shift, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xF1, (0xC0 | encode)); +} + +void Assembler::pslld(XMMRegister dst, XMMRegister shift) { + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, shift, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xF2, (0xC0 | encode)); +} + +void Assembler::psllq(XMMRegister dst, XMMRegister shift) { + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_rex_vex_w_reverted(); + int encode = simd_prefix_and_encode(dst, dst, shift, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xF3, (0xC0 | encode)); +} + +void Assembler::vpsllw(XMMRegister dst, XMMRegister src, int shift, int vector_len) { + assert(UseAVX > 0, "requires some form of AVX"); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + // XMM6 is for /6 encoding: 66 0F 71 /6 ib + int encode = vex_prefix_and_encode(xmm6->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int24(0x71, (0xC0 | encode), shift & 0xFF); +} + +void Assembler::vpslld(XMMRegister dst, XMMRegister src, int shift, int vector_len) { + assert(UseAVX > 0, "requires some form of AVX"); + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + // XMM6 is for /6 encoding: 66 0F 72 /6 ib int encode = vex_prefix_and_encode(xmm6->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int24(0x72, (0xC0 | encode), shift & 0xFF); } @@ -6168,13 +6951,67 @@ void Assembler::vpandq(XMMRegister dst, XMMRegister nds, XMMRegister src, int ve emit_int16((unsigned char)0xDB, (0xC0 | encode)); } +//Variable Shift packed integers logically left. +void Assembler::vpsllvd(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len) { + assert(UseAVX > 1, "requires AVX2"); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), src->encoding(), shift->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x47, (0xC0 | encode)); +} + +void Assembler::vpsllvq(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len) { + assert(UseAVX > 1, "requires AVX2"); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), src->encoding(), shift->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x47, (0xC0 | encode)); +} + +//Variable Shift packed integers logically right. +void Assembler::vpsrlvd(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len) { + assert(UseAVX > 1, "requires AVX2"); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), src->encoding(), shift->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x45, (0xC0 | encode)); +} + +void Assembler::vpsrlvq(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len) { + assert(UseAVX > 1, "requires AVX2"); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), src->encoding(), shift->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x45, (0xC0 | encode)); +} + +//Variable right Shift arithmetic packed integers . +void Assembler::vpsravd(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len) { + assert(UseAVX > 1, "requires AVX2"); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), src->encoding(), shift->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x46, (0xC0 | encode)); +} + +void Assembler::evpsravw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_avx512bw(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x11, (0xC0 | encode)); +} + +void Assembler::evpsravq(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len) { + assert(UseAVX > 2, "requires AVX512"); + assert(vector_len == Assembler::AVX_512bit || VM_Version::supports_avx512vl(), "requires AVX512VL"); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), src->encoding(), shift->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x46, (0xC0 | encode)); +} + void Assembler::vpshldvd(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len) { assert(VM_Version::supports_avx512_vbmi2(), "requires vbmi2"); InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); attributes.set_is_evex_instruction(); int encode = vex_prefix_and_encode(dst->encoding(), src->encoding(), shift->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); - emit_int8(0x71); - emit_int8((0xC0 | encode)); + emit_int16(0x71, (0xC0 | encode)); } void Assembler::vpshrdvd(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len) { @@ -6200,7 +7037,6 @@ void Assembler::vpandn(XMMRegister dst, XMMRegister nds, XMMRegister src, int ve emit_int16((unsigned char)0xDF, (0xC0 | encode)); } - void Assembler::por(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); @@ -6233,6 +7069,35 @@ void Assembler::vporq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vec } +void Assembler::evpord(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len) { + assert(VM_Version::supports_evex(), ""); + // Encoding: EVEX.NDS.XXX.66.0F.W0 EB /r + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xEB, (0xC0 | encode)); +} + +void Assembler::evpord(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int vector_len) { + assert(VM_Version::supports_evex(), ""); + // Encoding: EVEX.NDS.XXX.66.0F.W0 EB /r + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_NObit); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xEB); + emit_operand(dst, src); +} + void Assembler::pxor(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); @@ -6257,13 +7122,33 @@ void Assembler::vpxor(XMMRegister dst, XMMRegister nds, Address src, int vector_ emit_operand(dst, src); } +void Assembler::vpxorq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(UseAVX > 2, "requires some form of EVEX"); + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_rex_vex_w_reverted(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xEF, (0xC0 | encode)); +} + +void Assembler::evpxord(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len) { + assert(VM_Version::supports_evex(), ""); + // Encoding: EVEX.NDS.XXX.66.0F.W0 EF /r + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xEF, (0xC0 | encode)); +} + void Assembler::evpxorq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(VM_Version::supports_evex(), "requires EVEX support"); InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); attributes.set_is_evex_instruction(); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); - emit_int8((unsigned char)0xEF); - emit_int8((0xC0 | encode)); + emit_int16((unsigned char)0xEF, (0xC0 | encode)); } void Assembler::evpxorq(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { @@ -6960,12 +7845,67 @@ void Assembler::evpbroadcastq(XMMRegister dst, Register src, int vector_len) { int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int16(0x7C, (0xC0 | encode)); } + +void Assembler::vpgatherdd(XMMRegister dst, Address src, XMMRegister mask, int vector_len) { + assert(VM_Version::supports_avx2(), ""); + assert(vector_len == Assembler::AVX_128bit || vector_len == Assembler::AVX_256bit, ""); + assert(dst != xnoreg, "sanity"); + assert(src.isxmmindex(),"expected to be xmm index"); + assert(dst != src.xmmindex(), "instruction will #UD if dst and index are the same"); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ true); + vex_prefix(src, mask->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int8((unsigned char)0x90); + emit_operand(dst, src); +} + +void Assembler::vpgatherdq(XMMRegister dst, Address src, XMMRegister mask, int vector_len) { + assert(VM_Version::supports_avx2(), ""); + assert(vector_len == Assembler::AVX_128bit || vector_len == Assembler::AVX_256bit, ""); + assert(dst != xnoreg, "sanity"); + assert(src.isxmmindex(),"expected to be xmm index"); + assert(dst != src.xmmindex(), "instruction will #UD if dst and index are the same"); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ true); + vex_prefix(src, mask->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int8((unsigned char)0x90); + emit_operand(dst, src); +} + +void Assembler::vgatherdpd(XMMRegister dst, Address src, XMMRegister mask, int vector_len) { + assert(VM_Version::supports_avx2(), ""); + assert(vector_len == Assembler::AVX_128bit || vector_len == Assembler::AVX_256bit, ""); + assert(dst != xnoreg, "sanity"); + assert(src.isxmmindex(),"expected to be xmm index"); + assert(dst != src.xmmindex(), "instruction will #UD if dst and index are the same"); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ true); + vex_prefix(src, mask->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int8((unsigned char)0x92); + emit_operand(dst, src); +} + +void Assembler::vgatherdps(XMMRegister dst, Address src, XMMRegister mask, int vector_len) { + assert(VM_Version::supports_avx2(), ""); + assert(vector_len == Assembler::AVX_128bit || vector_len == Assembler::AVX_256bit, ""); + assert(dst != xnoreg, "sanity"); + assert(src.isxmmindex(),"expected to be xmm index"); + assert(dst != src.xmmindex(), "instruction will #UD if dst and index are the same"); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ true); + vex_prefix(src, mask->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int8((unsigned char)0x92); + emit_operand(dst, src); +} void Assembler::evpgatherdd(XMMRegister dst, KRegister mask, Address src, int vector_len) { assert(VM_Version::supports_evex(), ""); assert(dst != xnoreg, "sanity"); + assert(src.isxmmindex(),"expected to be xmm index"); + assert(dst != src.xmmindex(), "instruction will #UD if dst and index are the same"); + assert(mask != k0, "instruction will #UD if mask is in k0"); InstructionMark im(this); InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); - attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); attributes.reset_is_clear_context(); attributes.set_embedded_opmask_register_specifier(mask); attributes.set_is_evex_instruction(); @@ -6974,6 +7914,116 @@ void Assembler::evpgatherdd(XMMRegister dst, KRegister mask, Address src, int ve emit_int8((unsigned char)0x90); emit_operand(dst, src); } + +void Assembler::evpgatherdq(XMMRegister dst, KRegister mask, Address src, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(dst != xnoreg, "sanity"); + assert(src.isxmmindex(),"expected to be xmm index"); + assert(dst != src.xmmindex(), "instruction will #UD if dst and index are the same"); + assert(mask != k0, "instruction will #UD if mask is in k0"); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + attributes.reset_is_clear_context(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.set_is_evex_instruction(); + // swap src<->dst for encoding + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int8((unsigned char)0x90); + emit_operand(dst, src); +} + +void Assembler::evgatherdpd(XMMRegister dst, KRegister mask, Address src, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(dst != xnoreg, "sanity"); + assert(src.isxmmindex(),"expected to be xmm index"); + assert(dst != src.xmmindex(), "instruction will #UD if dst and index are the same"); + assert(mask != k0, "instruction will #UD if mask is in k0"); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + attributes.reset_is_clear_context(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.set_is_evex_instruction(); + // swap src<->dst for encoding + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int8((unsigned char)0x92); + emit_operand(dst, src); +} + +void Assembler::evgatherdps(XMMRegister dst, KRegister mask, Address src, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(dst != xnoreg, "sanity"); + assert(src.isxmmindex(),"expected to be xmm index"); + assert(dst != src.xmmindex(), "instruction will #UD if dst and index are the same"); + assert(mask != k0, "instruction will #UD if mask is in k0"); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + attributes.reset_is_clear_context(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.set_is_evex_instruction(); + // swap src<->dst for encoding + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int8((unsigned char)0x92); + emit_operand(dst, src); +} + +void Assembler::evpscatterdd(Address dst, KRegister mask, XMMRegister src, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(mask != k0, "instruction will #UD if mask is in k0"); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + attributes.reset_is_clear_context(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.set_is_evex_instruction(); + vex_prefix(dst, 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int8((unsigned char)0xA0); + emit_operand(src, dst); +} + +void Assembler::evpscatterdq(Address dst, KRegister mask, XMMRegister src, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(mask != k0, "instruction will #UD if mask is in k0"); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + attributes.reset_is_clear_context(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.set_is_evex_instruction(); + vex_prefix(dst, 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int8((unsigned char)0xA0); + emit_operand(src, dst); +} + +void Assembler::evscatterdps(Address dst, KRegister mask, XMMRegister src, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(mask != k0, "instruction will #UD if mask is in k0"); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + attributes.reset_is_clear_context(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.set_is_evex_instruction(); + vex_prefix(dst, 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int8((unsigned char)0xA2); + emit_operand(src, dst); +} + +void Assembler::evscatterdpd(Address dst, KRegister mask, XMMRegister src, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(mask != k0, "instruction will #UD if mask is in k0"); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + attributes.reset_is_clear_context(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.set_is_evex_instruction(); + vex_prefix(dst, 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int8((unsigned char)0xA2); + emit_operand(src, dst); +} // Carry-Less Multiplication Quadword void Assembler::pclmulqdq(XMMRegister dst, XMMRegister src, int mask) { assert(VM_Version::supports_clmul(), ""); @@ -7571,7 +8621,8 @@ void Assembler::evex_prefix(bool vex_r, bool vex_b, bool vex_x, bool evex_r, boo // fourth EVEX.L'L for vector length : 0 is 128, 1 is 256, 2 is 512, currently we do not support 1024 byte4 |= ((_attributes->get_vector_len())& 0x3) << 5; // last is EVEX.z for zero/merge actions - if (_attributes->is_no_reg_mask() == false) { + if (_attributes->is_no_reg_mask() == false && + _attributes->get_embedded_opmask_register_specifier() != 0) { byte4 |= (_attributes->is_clear_context() ? EVEX_Z : 0); } @@ -7739,7 +8790,7 @@ void Assembler::vminsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { emit_int16(0x5D, (0xC0 | encode)); } -void Assembler::cmppd(XMMRegister dst, XMMRegister nds, XMMRegister src, int cop, int vector_len) { +void Assembler::vcmppd(XMMRegister dst, XMMRegister nds, XMMRegister src, int cop, int vector_len) { assert(VM_Version::supports_avx(), ""); assert(vector_len <= AVX_256bit, ""); InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ true); @@ -7756,8 +8807,8 @@ void Assembler::blendvpb(XMMRegister dst, XMMRegister nds, XMMRegister src1, XMM emit_int24(0x4C, (0xC0 | encode), (0xF0 & src2_enc << 4)); } -void Assembler::blendvpd(XMMRegister dst, XMMRegister nds, XMMRegister src1, XMMRegister src2, int vector_len) { - assert(VM_Version::supports_avx(), ""); +void Assembler::vblendvpd(XMMRegister dst, XMMRegister nds, XMMRegister src1, XMMRegister src2, int vector_len) { + assert(UseAVX > 0 && (vector_len == AVX_128bit || vector_len == AVX_256bit), ""); assert(vector_len <= AVX_256bit, ""); InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ true); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src1->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); @@ -7765,28 +8816,330 @@ void Assembler::blendvpd(XMMRegister dst, XMMRegister nds, XMMRegister src1, XMM emit_int24(0x4B, (0xC0 | encode), (0xF0 & src2_enc << 4)); } -void Assembler::cmpps(XMMRegister dst, XMMRegister nds, XMMRegister src, int cop, int vector_len) { - assert(VM_Version::supports_avx(), ""); +void Assembler::vpblendd(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8, int vector_len) { + assert(VM_Version::supports_avx2(), ""); assert(vector_len <= AVX_256bit, ""); InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ true); - int encode = simd_prefix_and_encode(dst, nds, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); - emit_int24((unsigned char)0xC2, (0xC0 | encode), (0xF & cop)); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x02, (0xC0 | encode), (unsigned char)imm8); } -void Assembler::blendvps(XMMRegister dst, XMMRegister nds, XMMRegister src1, XMMRegister src2, int vector_len) { +void Assembler::vcmpps(XMMRegister dst, XMMRegister nds, XMMRegister src, int comparison, int vector_len) { assert(VM_Version::supports_avx(), ""); assert(vector_len <= AVX_256bit, ""); InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int24((unsigned char)0xC2, (0xC0 | encode), (unsigned char)comparison); +} + +void Assembler::evcmpps(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, + ComparisonPredicateFP comparison, int vector_len) { + assert(VM_Version::supports_evex(), ""); + // Encoding: EVEX.NDS.XXX.0F.W0 C2 /r ib + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.reset_is_clear_context(); + int encode = vex_prefix_and_encode(kdst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int24((unsigned char)0xC2, (0xC0 | encode), comparison); +} + +void Assembler::evcmppd(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, + ComparisonPredicateFP comparison, int vector_len) { + assert(VM_Version::supports_evex(), ""); + // Encoding: EVEX.NDS.XXX.66.0F.W1 C2 /r ib + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.reset_is_clear_context(); + int encode = vex_prefix_and_encode(kdst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int24((unsigned char)0xC2, (0xC0 | encode), comparison); +} + +void Assembler::blendvps(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse4_1(), ""); + assert(UseAVX <= 0, "sse encoding is inconsistent with avx encoding"); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x14, (0xC0 | encode)); +} + +void Assembler::blendvpd(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse4_1(), ""); + assert(UseAVX <= 0, "sse encoding is inconsistent with avx encoding"); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x15, (0xC0 | encode)); +} + +void Assembler::pblendvb(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_sse4_1(), ""); + assert(UseAVX <= 0, "sse encoding is inconsistent with avx encoding"); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x10, (0xC0 | encode)); +} + +void Assembler::vblendvps(XMMRegister dst, XMMRegister nds, XMMRegister src1, XMMRegister src2, int vector_len) { + assert(UseAVX > 0 && (vector_len == AVX_128bit || vector_len == AVX_256bit), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src1->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); int src2_enc = src2->encoding(); emit_int24(0x4A, (0xC0 | encode), (0xF0 & src2_enc << 4)); } -void Assembler::vpblendd(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8, int vector_len) { - assert(VM_Version::supports_avx2(), ""); - InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ true); +void Assembler::vblendps(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8, int vector_len) { + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); - emit_int24(0x02, (0xC0 | encode), (unsigned char)imm8); + emit_int24(0x0C, (0xC0 | encode), imm8); +} + +void Assembler::vpcmpgtb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : VM_Version::supports_avx2(), ""); + assert(vector_len <= AVX_256bit, "evex encoding is different - has k register as dest"); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16(0x64, (0xC0 | encode)); +} + +void Assembler::vpcmpgtw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : VM_Version::supports_avx2(), ""); + assert(vector_len <= AVX_256bit, "evex encoding is different - has k register as dest"); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16(0x65, (0xC0 | encode)); +} + +void Assembler::vpcmpgtd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : VM_Version::supports_avx2(), ""); + assert(vector_len <= AVX_256bit, "evex encoding is different - has k register as dest"); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16(0x66, (0xC0 | encode)); +} + +void Assembler::vpcmpgtq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(vector_len == AVX_128bit ? VM_Version::supports_avx() : VM_Version::supports_avx2(), ""); + assert(vector_len <= AVX_256bit, "evex encoding is different - has k register as dest"); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x37, (0xC0 | encode)); +} + +void Assembler::evpcmpd(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, + int comparison, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(comparison >= Assembler::eq && comparison <= Assembler::_true, ""); + // Encoding: EVEX.NDS.XXX.66.0F3A.W0 1F /r ib + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.reset_is_clear_context(); + int encode = vex_prefix_and_encode(kdst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x1F, (0xC0 | encode), comparison); +} + +void Assembler::evpcmpd(KRegister kdst, KRegister mask, XMMRegister nds, Address src, + int comparison, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(comparison >= Assembler::eq && comparison <= Assembler::_true, ""); + // Encoding: EVEX.NDS.XXX.66.0F3A.W0 1F /r ib + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_NObit); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.reset_is_clear_context(); + int dst_enc = kdst->encoding(); + vex_prefix(src, nds->encoding(), dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int8((unsigned char)0x1F); + emit_operand(as_Register(dst_enc), src); + emit_int8((unsigned char)comparison); +} + +void Assembler::evpcmpq(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, + int comparison, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(comparison >= Assembler::eq && comparison <= Assembler::_true, ""); + // Encoding: EVEX.NDS.XXX.66.0F3A.W1 1F /r ib + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.reset_is_clear_context(); + int encode = vex_prefix_and_encode(kdst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x1F, (0xC0 | encode), comparison); +} + +void Assembler::evpcmpq(KRegister kdst, KRegister mask, XMMRegister nds, Address src, + int comparison, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(comparison >= Assembler::eq && comparison <= Assembler::_true, ""); + // Encoding: EVEX.NDS.XXX.66.0F3A.W1 1F /r ib + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_NObit); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.reset_is_clear_context(); + int dst_enc = kdst->encoding(); + vex_prefix(src, nds->encoding(), dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int8((unsigned char)0x1F); + emit_operand(as_Register(dst_enc), src); + emit_int8((unsigned char)comparison); +} + +void Assembler::evpcmpb(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, + int comparison, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(VM_Version::supports_avx512bw(), ""); + assert(comparison >= Assembler::eq && comparison <= Assembler::_true, ""); + // Encoding: EVEX.NDS.XXX.66.0F3A.W0 3F /r ib + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.reset_is_clear_context(); + int encode = vex_prefix_and_encode(kdst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x3F, (0xC0 | encode), comparison); +} + +void Assembler::evpcmpb(KRegister kdst, KRegister mask, XMMRegister nds, Address src, + int comparison, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(VM_Version::supports_avx512bw(), ""); + assert(comparison >= Assembler::eq && comparison <= Assembler::_true, ""); + // Encoding: EVEX.NDS.XXX.66.0F3A.W0 3F /r ib + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.reset_is_clear_context(); + int dst_enc = kdst->encoding(); + vex_prefix(src, nds->encoding(), dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int8((unsigned char)0x3F); + emit_operand(as_Register(dst_enc), src); + emit_int8((unsigned char)comparison); +} + +void Assembler::evpcmpw(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, + int comparison, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(VM_Version::supports_avx512bw(), ""); + assert(comparison >= Assembler::eq && comparison <= Assembler::_true, ""); + // Encoding: EVEX.NDS.XXX.66.0F3A.W1 3F /r ib + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.reset_is_clear_context(); + int encode = vex_prefix_and_encode(kdst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x3F, (0xC0 | encode), comparison); +} + +void Assembler::evpcmpw(KRegister kdst, KRegister mask, XMMRegister nds, Address src, + int comparison, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(VM_Version::supports_avx512bw(), ""); + assert(comparison >= Assembler::eq && comparison <= Assembler::_true, ""); + // Encoding: EVEX.NDS.XXX.66.0F3A.W1 3F /r ib + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.reset_is_clear_context(); + int dst_enc = kdst->encoding(); + vex_prefix(src, nds->encoding(), dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int8((unsigned char)0x3F); + emit_operand(as_Register(dst_enc), src); + emit_int8((unsigned char)comparison); +} + +void Assembler::vpblendvb(XMMRegister dst, XMMRegister nds, XMMRegister src, XMMRegister mask, int vector_len) { + assert(VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + int mask_enc = mask->encoding(); + emit_int24(0x4C, (0xC0 | encode), 0xF0 & mask_enc << 4); +} + +void Assembler::evblendmpd(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len) { + assert(VM_Version::supports_evex(), ""); + // Encoding: EVEX.NDS.XXX.66.0F38.W1 65 /r + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x65, (0xC0 | encode)); +} + +void Assembler::evblendmps(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len) { + assert(VM_Version::supports_evex(), ""); + // Encoding: EVEX.NDS.XXX.66.0F38.W0 65 /r + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x65, (0xC0 | encode)); +} + +void Assembler::evpblendmb (XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(VM_Version::supports_avx512bw(), ""); + // Encoding: EVEX.NDS.512.66.0F38.W0 66 /r + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x66, (0xC0 | encode)); +} + +void Assembler::evpblendmw (XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(VM_Version::supports_avx512bw(), ""); + // Encoding: EVEX.NDS.512.66.0F38.W1 66 /r + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x66, (0xC0 | encode)); +} + +void Assembler::evpblendmd (XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len) { + assert(VM_Version::supports_evex(), ""); + //Encoding: EVEX.NDS.512.66.0F38.W0 64 /r + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x64, (0xC0 | encode)); +} + +void Assembler::evpblendmq (XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len) { + assert(VM_Version::supports_evex(), ""); + //Encoding: EVEX.NDS.512.66.0F38.W1 64 /r + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x64, (0xC0 | encode)); } void Assembler::shlxl(Register dst, Register src1, Register src2) { @@ -7803,6 +9156,13 @@ void Assembler::shlxq(Register dst, Register src1, Register src2) { emit_int16((unsigned char)0xF7, (0xC0 | encode)); } +void Assembler::shrxq(Register dst, Register src1, Register src2) { + assert(VM_Version::supports_bmi2(), ""); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), src2->encoding(), src1->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F_38, &attributes); + emit_int16((unsigned char)0xF7, (0xC0 | encode)); +} + #ifndef _LP64 void Assembler::incl(Register dst) { @@ -8443,7 +9803,7 @@ void Assembler::cmpq(Register dst, int32_t imm32) { void Assembler::cmpq(Address dst, Register src) { InstructionMark im(this); - emit_int16(get_prefixq(dst, src), 0x3B); + emit_int16(get_prefixq(dst, src), 0x39); emit_operand(src, dst); } diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp index 283285dc347..1d6eb41bd05 100644 --- a/src/hotspot/cpu/x86/assembler_x86.hpp +++ b/src/hotspot/cpu/x86/assembler_x86.hpp @@ -588,6 +588,7 @@ class Assembler : public AbstractAssembler { #endif }; + // Comparison predicates for integral types & FP types when using SSE enum ComparisonPredicate { eq = 0, lt = 1, @@ -599,6 +600,51 @@ class Assembler : public AbstractAssembler { _true = 7 }; + // Comparison predicates for FP types when using AVX + // O means ordered. U is unordered. When using ordered, any NaN comparison is false. Otherwise, it is true. + // S means signaling. Q means non-signaling. When signaling is true, instruction signals #IA on NaN. + enum ComparisonPredicateFP { + EQ_OQ = 0, + LT_OS = 1, + LE_OS = 2, + UNORD_Q = 3, + NEQ_UQ = 4, + NLT_US = 5, + NLE_US = 6, + ORD_Q = 7, + EQ_UQ = 8, + NGE_US = 9, + NGT_US = 0xA, + FALSE_OQ = 0XB, + NEQ_OQ = 0xC, + GE_OS = 0xD, + GT_OS = 0xE, + TRUE_UQ = 0xF, + EQ_OS = 0x10, + LT_OQ = 0x11, + LE_OQ = 0x12, + UNORD_S = 0x13, + NEQ_US = 0x14, + NLT_UQ = 0x15, + NLE_UQ = 0x16, + ORD_S = 0x17, + EQ_US = 0x18, + NGE_UQ = 0x19, + NGT_UQ = 0x1A, + FALSE_OS = 0x1B, + NEQ_OS = 0x1C, + GE_OQ = 0x1D, + GT_OQ = 0x1E, + TRUE_US =0x1F + }; + + enum Width { + B = 0, + W = 1, + D = 2, + Q = 3 + }; + //---< calculate length of instruction >--- // As instruction size can't be found out easily on x86/x64, // we just use '4' for len and maxlen. @@ -794,7 +840,6 @@ class Assembler : public AbstractAssembler { void decl(Register dst); void decl(Address dst); - void decq(Register dst); void decq(Address dst); void incl(Register dst); @@ -879,6 +924,7 @@ class Assembler : public AbstractAssembler { void popa_uncached(); #endif void vzeroupper_uncached(); + void decq(Register dst); void pusha(); void popa(); @@ -918,6 +964,7 @@ class Assembler : public AbstractAssembler { void adcq(Register dst, Register src); void addb(Address dst, int imm8); + void addw(Register dst, Register src); void addw(Address dst, int imm16); void addl(Address dst, int32_t imm32); @@ -968,6 +1015,8 @@ class Assembler : public AbstractAssembler { void vaesdec(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void vaesdeclast(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void andw(Register dst, Register src); + void andl(Address dst, int32_t imm32); void andl(Register dst, int32_t imm32); void andl(Register dst, Address src); @@ -1093,9 +1142,11 @@ class Assembler : public AbstractAssembler { // Convert Packed Signed Doubleword Integers to Packed Double-Precision Floating-Point Value void cvtdq2pd(XMMRegister dst, XMMRegister src); + void vcvtdq2pd(XMMRegister dst, XMMRegister src, int vector_len); // Convert Packed Signed Doubleword Integers to Packed Single-Precision Floating-Point Value void cvtdq2ps(XMMRegister dst, XMMRegister src); + void vcvtdq2ps(XMMRegister dst, XMMRegister src, int vector_len); // Convert Scalar Single-Precision Floating-Point Value to Scalar Double-Precision Floating-Point Value void cvtss2sd(XMMRegister dst, XMMRegister src); @@ -1111,8 +1162,25 @@ class Assembler : public AbstractAssembler { void cvttss2sil(Register dst, XMMRegister src); void cvttss2siq(Register dst, XMMRegister src); + // Convert vector double to int void cvttpd2dq(XMMRegister dst, XMMRegister src); + // Convert vector float and double + void vcvtps2pd(XMMRegister dst, XMMRegister src, int vector_len); + void vcvtpd2ps(XMMRegister dst, XMMRegister src, int vector_len); + + // Convert vector long to vector FP + void evcvtqq2ps(XMMRegister dst, XMMRegister src, int vector_len); + void evcvtqq2pd(XMMRegister dst, XMMRegister src, int vector_len); + + // Evex casts with truncation + void evpmovwb(XMMRegister dst, XMMRegister src, int vector_len); + void evpmovdw(XMMRegister dst, XMMRegister src, int vector_len); + void evpmovdb(XMMRegister dst, XMMRegister src, int vector_len); + void evpmovqd(XMMRegister dst, XMMRegister src, int vector_len); + void evpmovqb(XMMRegister dst, XMMRegister src, int vector_len); + void evpmovqw(XMMRegister dst, XMMRegister src, int vector_len); + //Abs of packed Integer values void pabsb(XMMRegister dst, XMMRegister src); void pabsw(XMMRegister dst, XMMRegister src); @@ -1472,20 +1540,30 @@ class Assembler : public AbstractAssembler { void vmovdqu(XMMRegister dst, XMMRegister src); // Move Unaligned 512bit Vector - void evmovdqub(Address dst, XMMRegister src, int vector_len); - void evmovdqub(XMMRegister dst, Address src, int vector_len); - void evmovdqub(XMMRegister dst, XMMRegister src, int vector_len); - void evmovdqub(XMMRegister dst, KRegister mask, Address src, int vector_len); - void evmovdquw(Address dst, XMMRegister src, int vector_len); - void evmovdquw(Address dst, KRegister mask, XMMRegister src, int vector_len); - void evmovdquw(XMMRegister dst, Address src, int vector_len); - void evmovdquw(XMMRegister dst, KRegister mask, Address src, int vector_len); + void evmovdqub(Address dst, XMMRegister src, bool merge, int vector_len); + void evmovdqub(XMMRegister dst, Address src, bool merge, int vector_len); + void evmovdqub(XMMRegister dst, XMMRegister src, bool merge, int vector_len); + void evmovdqub(XMMRegister dst, KRegister mask, Address src, bool merge, int vector_len); + void evmovdquw(Address dst, XMMRegister src, bool merge, int vector_len); + void evmovdquw(Address dst, KRegister mask, XMMRegister src, bool merge, int vector_len); + void evmovdquw(XMMRegister dst, Address src, bool merge, int vector_len); + void evmovdquw(XMMRegister dst, KRegister mask, Address src, bool merge, int vector_len); void evmovdqul(Address dst, XMMRegister src, int vector_len); void evmovdqul(XMMRegister dst, Address src, int vector_len); void evmovdqul(XMMRegister dst, XMMRegister src, int vector_len); + void evmovdqul(Address dst, KRegister mask, XMMRegister src, bool merge, int vector_len); + void evmovdqul(XMMRegister dst, KRegister mask, Address src, bool merge, int vector_len); + void evmovdqul(XMMRegister dst, KRegister mask, XMMRegister src, bool merge, int vector_len); void evmovdquq(Address dst, XMMRegister src, int vector_len); void evmovdquq(XMMRegister dst, Address src, int vector_len); void evmovdquq(XMMRegister dst, XMMRegister src, int vector_len); + void evmovdquq(Address dst, KRegister mask, XMMRegister src, bool merge, int vector_len); + void evmovdquq(XMMRegister dst, KRegister mask, Address src, bool merge, int vector_len); + void evmovdquq(XMMRegister dst, KRegister mask, XMMRegister src, bool merge, int vector_len); + + // Generic move instructions. + void evmovdqu(Address dst, KRegister mask, XMMRegister src, int vector_len, int type); + void evmovdqu(XMMRegister dst, KRegister mask, Address src, int vector_len, int type); // Move lower 64bit to high 64bit in 128bit register void movlhps(XMMRegister dst, XMMRegister src); @@ -1517,6 +1595,9 @@ class Assembler : public AbstractAssembler { // Move Quadword void movq(Address dst, XMMRegister src); void movq(XMMRegister dst, Address src); + void movq(XMMRegister dst, XMMRegister src); + void movq(Register dst, XMMRegister src); + void movq(XMMRegister dst, Register src); void movsbl(Register dst, Address src); void movsbl(Register dst, Register src); @@ -1597,6 +1678,8 @@ class Assembler : public AbstractAssembler { void btrq(Address dst, int imm8); #endif + void orw(Register dst, Register src); + void orl(Address dst, int32_t imm32); void orl(Register dst, int32_t imm32); void orl(Register dst, Address src); @@ -1610,17 +1693,32 @@ class Assembler : public AbstractAssembler { void orq(Register dst, Address src); void orq(Register dst, Register src); + // Pack with signed saturation + void packsswb(XMMRegister dst, XMMRegister src); + void vpacksswb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void packssdw(XMMRegister dst, XMMRegister src); + void vpackssdw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + // Pack with unsigned saturation void packuswb(XMMRegister dst, XMMRegister src); void packuswb(XMMRegister dst, Address src); + void packusdw(XMMRegister dst, XMMRegister src); void vpackuswb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void vpackusdw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); - // Pemutation of 64bit words + // Permutations void vpermq(XMMRegister dst, XMMRegister src, int imm8, int vector_len); void vpermq(XMMRegister dst, XMMRegister src, int imm8); void vpermq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void vpermb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void vpermw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void vpermd(XMMRegister dst, XMMRegister nds, Address src, int vector_len); + void vpermd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void vperm2i128(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8); void vperm2f128(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8); + void vpermilps(XMMRegister dst, XMMRegister src, int imm8, int vector_len); + void vpermilpd(XMMRegister dst, XMMRegister src, int imm8, int vector_len); + void vpermpd(XMMRegister dst, XMMRegister src, int imm8, int vector_len); void evpermi2q(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void pause(); @@ -1633,11 +1731,14 @@ class Assembler : public AbstractAssembler { void pcmpestri(XMMRegister xmm1, Address src, int imm8); void pcmpeqb(XMMRegister dst, XMMRegister src); + void vpcmpCCbwd(XMMRegister dst, XMMRegister nds, XMMRegister src, int cond_encoding, int vector_len); + void vpcmpeqb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void evpcmpeqb(KRegister kdst, XMMRegister nds, XMMRegister src, int vector_len); void evpcmpeqb(KRegister kdst, XMMRegister nds, Address src, int vector_len); void evpcmpeqb(KRegister kdst, KRegister mask, XMMRegister nds, Address src, int vector_len); + void vpcmpgtb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void evpcmpgtb(KRegister kdst, XMMRegister nds, Address src, int vector_len); void evpcmpgtb(KRegister kdst, KRegister mask, XMMRegister nds, Address src, int vector_len); @@ -1650,16 +1751,22 @@ class Assembler : public AbstractAssembler { void evpcmpeqw(KRegister kdst, XMMRegister nds, XMMRegister src, int vector_len); void evpcmpeqw(KRegister kdst, XMMRegister nds, Address src, int vector_len); + void vpcmpgtw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void pcmpeqd(XMMRegister dst, XMMRegister src); void vpcmpeqd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); - void evpcmpeqd(KRegister kdst, XMMRegister nds, XMMRegister src, int vector_len); - void evpcmpeqd(KRegister kdst, XMMRegister nds, Address src, int vector_len); + void evpcmpeqd(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, int vector_len); + void evpcmpeqd(KRegister kdst, KRegister mask, XMMRegister nds, Address src, int vector_len); void pcmpeqq(XMMRegister dst, XMMRegister src); + void vpcmpCCq(XMMRegister dst, XMMRegister nds, XMMRegister src, int cond_encoding, int vector_len); void vpcmpeqq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void evpcmpeqq(KRegister kdst, XMMRegister nds, XMMRegister src, int vector_len); void evpcmpeqq(KRegister kdst, XMMRegister nds, Address src, int vector_len); + void pcmpgtq(XMMRegister dst, XMMRegister src); + void vpcmpgtq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void pmovmskb(Register dst, XMMRegister src); void vpmovmskb(Register dst, XMMRegister src); @@ -1668,6 +1775,7 @@ class Assembler : public AbstractAssembler { void pextrq(Register dst, XMMRegister src, int imm8); void pextrd(Address dst, XMMRegister src, int imm8); void pextrq(Address dst, XMMRegister src, int imm8); + void pextrb(Register dst, XMMRegister src, int imm8); void pextrb(Address dst, XMMRegister src, int imm8); // SSE 2 extract void pextrw(Register dst, XMMRegister src, int imm8); @@ -1676,21 +1784,46 @@ class Assembler : public AbstractAssembler { // SSE 4.1 insert void pinsrd(XMMRegister dst, Register src, int imm8); void pinsrq(XMMRegister dst, Register src, int imm8); + void pinsrb(XMMRegister dst, Register src, int imm8); void pinsrd(XMMRegister dst, Address src, int imm8); void pinsrq(XMMRegister dst, Address src, int imm8); void pinsrb(XMMRegister dst, Address src, int imm8); + void insertps(XMMRegister dst, XMMRegister src, int imm8); // SSE 2 insert void pinsrw(XMMRegister dst, Register src, int imm8); void pinsrw(XMMRegister dst, Address src, int imm8); - // SSE4.1 packed move + // AVX insert + void vpinsrd(XMMRegister dst, XMMRegister nds, Register src, int imm8); + void vpinsrb(XMMRegister dst, XMMRegister nds, Register src, int imm8); + void vpinsrq(XMMRegister dst, XMMRegister nds, Register src, int imm8); + void vpinsrw(XMMRegister dst, XMMRegister nds, Register src, int imm8); + void vinsertps(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8); + + // Zero extend moves void pmovzxbw(XMMRegister dst, XMMRegister src); void pmovzxbw(XMMRegister dst, Address src); - + void pmovzxbd(XMMRegister dst, XMMRegister src); void vpmovzxbw( XMMRegister dst, Address src, int vector_len); + void pmovzxdq(XMMRegister dst, XMMRegister src); void vpmovzxbw(XMMRegister dst, XMMRegister src, int vector_len); + void vpmovzxdq(XMMRegister dst, XMMRegister src, int vector_len); + void vpmovzxbd(XMMRegister dst, XMMRegister src, int vector_len); + void vpmovzxbq(XMMRegister dst, XMMRegister src, int vector_len); void evpmovzxbw(XMMRegister dst, KRegister mask, Address src, int vector_len); + // Sign extend moves + void pmovsxbd(XMMRegister dst, XMMRegister src); + void pmovsxbq(XMMRegister dst, XMMRegister src); + void pmovsxbw(XMMRegister dst, XMMRegister src); + void pmovsxwd(XMMRegister dst, XMMRegister src); + void vpmovsxbd(XMMRegister dst, XMMRegister src, int vector_len); + void vpmovsxbq(XMMRegister dst, XMMRegister src, int vector_len); + void vpmovsxbw(XMMRegister dst, XMMRegister src, int vector_len); + void vpmovsxwd(XMMRegister dst, XMMRegister src, int vector_len); + void vpmovsxwq(XMMRegister dst, XMMRegister src, int vector_len); + void vpmovsxdq(XMMRegister dst, XMMRegister src, int vector_len); + void evpmovwb(Address dst, XMMRegister src, int vector_len); void evpmovwb(Address dst, KRegister mask, XMMRegister src, int vector_len); @@ -1698,10 +1831,6 @@ class Assembler : public AbstractAssembler { void evpmovdb(Address dst, XMMRegister src, int vector_len); - // Sign extend moves - void pmovsxbw(XMMRegister dst, XMMRegister src); - void vpmovsxbw(XMMRegister dst, XMMRegister src, int vector_len); - // Multiply add void pmaddwd(XMMRegister dst, XMMRegister src); void vpmaddwd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); @@ -1745,10 +1874,17 @@ class Assembler : public AbstractAssembler { void pshufd(XMMRegister dst, Address src, int mode); void vpshufd(XMMRegister dst, XMMRegister src, int mode, int vector_len); - // Shuffle Packed Low Words + // Shuffle Packed High/Low Words + void pshufhw(XMMRegister dst, XMMRegister src, int mode); void pshuflw(XMMRegister dst, XMMRegister src, int mode); void pshuflw(XMMRegister dst, Address src, int mode); + //shuffle floats and doubles + void pshufps(XMMRegister, XMMRegister, int); + void pshufpd(XMMRegister, XMMRegister, int); + void vpshufps(XMMRegister, XMMRegister, XMMRegister, int, int); + void vpshufpd(XMMRegister, XMMRegister, XMMRegister, int, int); + // Shuffle packed values at 128 bit granularity void evshufi64x2(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8, int vector_len); @@ -1764,6 +1900,9 @@ class Assembler : public AbstractAssembler { void vptest(XMMRegister dst, XMMRegister src); void vptest(XMMRegister dst, Address src); + // Vector compare + void vptest(XMMRegister dst, XMMRegister src, int vector_len); + // Interleave Low Bytes void punpcklbw(XMMRegister dst, XMMRegister src); void punpcklbw(XMMRegister dst, Address src); @@ -1837,6 +1976,7 @@ class Assembler : public AbstractAssembler { void evalignq(XMMRegister dst, XMMRegister nds, XMMRegister src, uint8_t imm8); void pblendw(XMMRegister dst, XMMRegister src, int imm8); + void vblendps(XMMRegister dst, XMMRegister src1, XMMRegister src2, int imm8, int vector_len); void sha1rnds4(XMMRegister dst, XMMRegister src, int imm8); void sha1nexte(XMMRegister dst, XMMRegister src); @@ -1955,6 +2095,7 @@ class Assembler : public AbstractAssembler { void xorl(Register dst, Register src); void xorb(Register dst, Address src); + void xorw(Register dst, Register src); void xorq(Register dst, Address src); void xorq(Register dst, Register src); @@ -1989,8 +2130,12 @@ class Assembler : public AbstractAssembler { void shlxl(Register dst, Register src1, Register src2); void shlxq(Register dst, Register src1, Register src2); + void shrxq(Register dst, Register src1, Register src2); + //====================VECTOR ARITHMETIC===================================== + void evpmovd2m(KRegister kdst, XMMRegister src, int vector_len); + void evpmovq2m(KRegister kdst, XMMRegister src, int vector_len); // Add Packed Floating-Point Values void addpd(XMMRegister dst, XMMRegister src); @@ -2100,13 +2245,41 @@ class Assembler : public AbstractAssembler { // Multiply packed integers (only shorts and ints) void pmullw(XMMRegister dst, XMMRegister src); void pmulld(XMMRegister dst, XMMRegister src); + void pmuludq(XMMRegister dst, XMMRegister src); void vpmullw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void vpmulld(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void vpmullq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void vpmuludq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void vpmullw(XMMRegister dst, XMMRegister nds, Address src, int vector_len); void vpmulld(XMMRegister dst, XMMRegister nds, Address src, int vector_len); void vpmullq(XMMRegister dst, XMMRegister nds, Address src, int vector_len); + // Minimum of packed integers + void pminsb(XMMRegister dst, XMMRegister src); + void vpminsb(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len); + void pminsw(XMMRegister dst, XMMRegister src); + void vpminsw(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len); + void pminsd(XMMRegister dst, XMMRegister src); + void vpminsd(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len); + void vpminsq(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len); + void minps(XMMRegister dst, XMMRegister src); + void vminps(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len); + void minpd(XMMRegister dst, XMMRegister src); + void vminpd(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len); + + // Maximum of packed integers + void pmaxsb(XMMRegister dst, XMMRegister src); + void vpmaxsb(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len); + void pmaxsw(XMMRegister dst, XMMRegister src); + void vpmaxsw(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len); + void pmaxsd(XMMRegister dst, XMMRegister src); + void vpmaxsd(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len); + void vpmaxsq(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len); + void maxps(XMMRegister dst, XMMRegister src); + void vmaxps(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len); + void maxpd(XMMRegister dst, XMMRegister src); + void vmaxpd(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len); + // Shift left packed integers void psllw(XMMRegister dst, int shift); void pslld(XMMRegister dst, int shift); @@ -2148,9 +2321,22 @@ class Assembler : public AbstractAssembler { void vpsrad(XMMRegister dst, XMMRegister src, int shift, int vector_len); void vpsraw(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len); void vpsrad(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len); + void evpsravw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void evpsraq(XMMRegister dst, XMMRegister src, int shift, int vector_len); void evpsraq(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len); + // Variable shift left packed integers + void vpsllvd(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len); + void vpsllvq(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len); + + // Variable shift right packed integers + void vpsrlvd(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len); + void vpsrlvq(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len); + + // Variable shift right arithmetic packed integers + void vpsravd(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len); + void evpsravq(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len); + void vpshldvd(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len); void vpshrdvd(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len); @@ -2158,6 +2344,7 @@ class Assembler : public AbstractAssembler { void pand(XMMRegister dst, XMMRegister src); void vpand(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void vpand(XMMRegister dst, XMMRegister nds, Address src, int vector_len); + void evpandd(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len); void vpandq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); // Andn packed integers @@ -2170,10 +2357,15 @@ class Assembler : public AbstractAssembler { void vpor(XMMRegister dst, XMMRegister nds, Address src, int vector_len); void vporq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void evpord(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len); + void evpord(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int vector_len); + // Xor packed integers void pxor(XMMRegister dst, XMMRegister src); void vpxor(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void vpxor(XMMRegister dst, XMMRegister nds, Address src, int vector_len); + void vpxorq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void evpxord(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len); void evpxorq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void evpxorq(XMMRegister dst, XMMRegister nds, Address src, int vector_len); @@ -2251,7 +2443,21 @@ class Assembler : public AbstractAssembler { void evpbroadcastd(XMMRegister dst, Register src, int vector_len); void evpbroadcastq(XMMRegister dst, Register src, int vector_len); - void evpgatherdd(XMMRegister dst, KRegister k1, Address src, int vector_len); + // Gather AVX2 and AVX3 + void vpgatherdd(XMMRegister dst, Address src, XMMRegister mask, int vector_len); + void vpgatherdq(XMMRegister dst, Address src, XMMRegister mask, int vector_len); + void vgatherdpd(XMMRegister dst, Address src, XMMRegister mask, int vector_len); + void vgatherdps(XMMRegister dst, Address src, XMMRegister mask, int vector_len); + void evpgatherdd(XMMRegister dst, KRegister mask, Address src, int vector_len); + void evpgatherdq(XMMRegister dst, KRegister mask, Address src, int vector_len); + void evgatherdpd(XMMRegister dst, KRegister mask, Address src, int vector_len); + void evgatherdps(XMMRegister dst, KRegister mask, Address src, int vector_len); + + //Scatter AVX3 only + void evpscatterdd(Address dst, KRegister mask, XMMRegister src, int vector_len); + void evpscatterdq(Address dst, KRegister mask, XMMRegister src, int vector_len); + void evscatterdps(Address dst, KRegister mask, XMMRegister src, int vector_len); + void evscatterdpd(Address dst, KRegister mask, XMMRegister src, int vector_len); // Carry-Less Multiplication Quadword void pclmulqdq(XMMRegister dst, XMMRegister src, int mask); @@ -2264,14 +2470,56 @@ class Assembler : public AbstractAssembler { // runtime code and native libraries. void vzeroupper(); - // AVX support for vectorized conditional move (float/double). The following two instructions used only coupled. + // Vector double compares + void vcmppd(XMMRegister dst, XMMRegister nds, XMMRegister src, int cop, int vector_len); + void evcmppd(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, + ComparisonPredicateFP comparison, int vector_len); + + // Vector float compares + void vcmpps(XMMRegister dst, XMMRegister nds, XMMRegister src, int comparison, int vector_len); + void evcmpps(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, + ComparisonPredicateFP comparison, int vector_len); + + // Vector integer compares + void vpcmpgtd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void evpcmpd(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, + int comparison, int vector_len); + void evpcmpd(KRegister kdst, KRegister mask, XMMRegister nds, Address src, + int comparison, int vector_len); + + // Vector long compares + void evpcmpq(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, + int comparison, int vector_len); + void evpcmpq(KRegister kdst, KRegister mask, XMMRegister nds, Address src, + int comparison, int vector_len); + + // Vector byte compares + void evpcmpb(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, + int comparison, int vector_len); + void evpcmpb(KRegister kdst, KRegister mask, XMMRegister nds, Address src, + int comparison, int vector_len); + + // Vector short compares + void evpcmpw(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, + int comparison, int vector_len); + void evpcmpw(KRegister kdst, KRegister mask, XMMRegister nds, Address src, + int comparison, int vector_len); + + // Vector blends + void blendvps(XMMRegister dst, XMMRegister src); + void blendvpd(XMMRegister dst, XMMRegister src); + void pblendvb(XMMRegister dst, XMMRegister src); void blendvpb(XMMRegister dst, XMMRegister nds, XMMRegister src1, XMMRegister src2, int vector_len); - void cmppd(XMMRegister dst, XMMRegister nds, XMMRegister src, int cop, int vector_len); - void blendvpd(XMMRegister dst, XMMRegister nds, XMMRegister src1, XMMRegister src2, int vector_len); - void cmpps(XMMRegister dst, XMMRegister nds, XMMRegister src, int cop, int vector_len); - void blendvps(XMMRegister dst, XMMRegister nds, XMMRegister src1, XMMRegister src2, int vector_len); + void vblendvps(XMMRegister dst, XMMRegister nds, XMMRegister src, XMMRegister mask, int vector_len); + void vblendvpd(XMMRegister dst, XMMRegister nds, XMMRegister src1, XMMRegister src2, int vector_len); + void vpblendvb(XMMRegister dst, XMMRegister nds, XMMRegister src, XMMRegister mask, int vector_len); void vpblendd(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8, int vector_len); - + void evblendmpd(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len); + void evblendmps(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len); + void evpblendmb(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len); + void evpblendmw(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len); + void evpblendmd(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len); + void evpblendmq(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len); protected: // Next instructions require address alignment 16 bytes SSE mode. // They should be called only from corresponding MacroAssembler instructions. @@ -2367,7 +2615,8 @@ class InstructionAttr { // Internal encoding data used in compressed immediate offset programming void set_evex_encoding(int value) { _evex_encoding = value; } - // Set the Evex.Z field to be used to clear all non directed XMM/YMM/ZMM components + // When the Evex.Z field is set (true), it is used to clear all non directed XMM/YMM/ZMM components. + // This method unsets it so that merge semantics are used instead. void reset_is_clear_context(void) { _is_clear_context = false; } // Map back to current asembler so that we can manage object level assocation diff --git a/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp b/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp index 526fe5af2fc..6853953f0eb 100644 --- a/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp +++ b/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp @@ -79,6 +79,32 @@ void ConversionStub::emit_code(LIR_Assembler* ce) { } #endif // !_LP64 +void C1SafepointPollStub::emit_code(LIR_Assembler* ce) { + __ bind(_entry); + InternalAddress safepoint_pc(ce->masm()->pc() - ce->masm()->offset() + safepoint_offset()); +#ifdef _LP64 + __ lea(rscratch1, safepoint_pc); + __ movptr(Address(r15_thread, JavaThread::saved_exception_pc_offset()), rscratch1); +#else + const Register tmp1 = rcx; + const Register tmp2 = rdx; + __ push(tmp1); + __ push(tmp2); + + __ lea(tmp1, safepoint_pc); + __ get_thread(tmp2); + __ movptr(Address(tmp2, JavaThread::saved_exception_pc_offset()), tmp1); + + __ pop(tmp2); + __ pop(tmp1); +#endif /* _LP64 */ + assert(SharedRuntime::polling_page_return_handler_blob() != NULL, + "polling page return stub not created yet"); + + address stub = SharedRuntime::polling_page_return_handler_blob()->entry_point(); + __ jump(RuntimeAddress(stub)); +} + void CounterOverflowStub::emit_code(LIR_Assembler* ce) { __ bind(_entry); Metadata *m = _method->as_constant_ptr()->as_metadata(); diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index 8a0200a18dc..bba946ec4ad 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.hpp" #include "asm/macroAssembler.inline.hpp" +#include "c1/c1_CodeStubs.hpp" #include "c1/c1_Compilation.hpp" #include "c1/c1_LIRAssembler.hpp" #include "c1/c1_MacroAssembler.hpp" @@ -517,8 +518,7 @@ int LIR_Assembler::emit_deopt_handler() { return offset; } - -void LIR_Assembler::return_op(LIR_Opr result) { +void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) { assert(result->is_illegal() || !result->is_single_cpu() || result->as_register() == rax, "word returns are in rax,"); if (!result->is_illegal() && result->is_float_kind() && !result->is_xmm_register()) { assert(result->fpu() == 0, "result must already be on TOS"); @@ -531,22 +531,18 @@ void LIR_Assembler::return_op(LIR_Opr result) { __ reserved_stack_check(); } - bool result_is_oop = result->is_valid() ? result->is_oop() : false; - // Note: we do not need to round double result; float result has the right precision // the poll sets the condition code, but no data registers #ifdef _LP64 - const Register poll_addr = rscratch1; - __ movptr(poll_addr, Address(r15_thread, Thread::polling_page_offset())); + const Register thread = r15_thread; #else - const Register poll_addr = rbx; - assert(FrameMap::is_caller_save_register(poll_addr), "will overwrite"); - __ get_thread(poll_addr); - __ movptr(poll_addr, Address(poll_addr, Thread::polling_page_offset())); + const Register thread = rbx; + __ get_thread(thread); #endif + code_stub->set_safepoint_offset(__ offset()); __ relocate(relocInfo::poll_return_type); - __ testl(rax, Address(poll_addr, 0)); + __ safepoint_poll(*code_stub->entry(), thread, true /* at_return */, true /* in_nmethod */); __ ret(0); } diff --git a/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp b/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp index 53935539a36..60347c41163 100644 --- a/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp +++ b/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp @@ -69,7 +69,7 @@ int StubAssembler::call_RT(Register oop_result1, Register metadata_result, addre push(thread); #endif // _LP64 - int call_offset; + int call_offset = -1; if (!align_stack) { set_last_Java_frame(thread, noreg, rbp, NULL); } else { @@ -133,6 +133,8 @@ int StubAssembler::call_RT(Register oop_result1, Register metadata_result, addre if (metadata_result->is_valid()) { get_vm_result_2(metadata_result, thread); } + + assert(call_offset >= 0, "Should be set"); return call_offset; } diff --git a/src/hotspot/cpu/x86/c1_globals_x86.hpp b/src/hotspot/cpu/x86/c1_globals_x86.hpp index fbf538c2cec..afd2a65cb89 100644 --- a/src/hotspot/cpu/x86/c1_globals_x86.hpp +++ b/src/hotspot/cpu/x86/c1_globals_x86.hpp @@ -33,8 +33,6 @@ #ifndef TIERED define_pd_global(bool, BackgroundCompilation, true ); -define_pd_global(bool, UseTLAB, true ); -define_pd_global(bool, ResizeTLAB, true ); define_pd_global(bool, InlineIntrinsics, true ); define_pd_global(bool, PreferInterpreterNativeStubs, false); define_pd_global(bool, ProfileTraps, false); diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index 8940b0c3c44..3aef6446f78 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -33,6 +33,21 @@ #include "runtime/objectMonitor.hpp" #include "runtime/stubRoutines.hpp" +inline Assembler::AvxVectorLen C2_MacroAssembler::vector_length_encoding(int vlen_in_bytes) { + switch (vlen_in_bytes) { + case 4: // fall-through + case 8: // fall-through + case 16: return Assembler::AVX_128bit; + case 32: return Assembler::AVX_256bit; + case 64: return Assembler::AVX_512bit; + + default: { + ShouldNotReachHere(); + return Assembler::AVX_NoVec; + } + } +} + void C2_MacroAssembler::setvectmask(Register dst, Register src) { guarantee(PostLoopMultiversioning, "must be"); Assembler::movl(dst, 1); @@ -861,6 +876,174 @@ void C2_MacroAssembler::vabsnegf(int opcode, XMMRegister dst, XMMRegister src, i } } +void C2_MacroAssembler::pminmax(int opcode, BasicType elem_bt, XMMRegister dst, XMMRegister src, XMMRegister tmp) { + assert(opcode == Op_MinV || opcode == Op_MaxV, "sanity"); + assert(tmp == xnoreg || elem_bt == T_LONG, "unused"); + + if (opcode == Op_MinV) { + if (elem_bt == T_BYTE) { + pminsb(dst, src); + } else if (elem_bt == T_SHORT) { + pminsw(dst, src); + } else if (elem_bt == T_INT) { + pminsd(dst, src); + } else { + assert(elem_bt == T_LONG, "required"); + assert(tmp == xmm0, "required"); + assert_different_registers(dst, src, tmp); + movdqu(xmm0, dst); + pcmpgtq(xmm0, src); + blendvpd(dst, src); // xmm0 as mask + } + } else { // opcode == Op_MaxV + if (elem_bt == T_BYTE) { + pmaxsb(dst, src); + } else if (elem_bt == T_SHORT) { + pmaxsw(dst, src); + } else if (elem_bt == T_INT) { + pmaxsd(dst, src); + } else { + assert(elem_bt == T_LONG, "required"); + assert(tmp == xmm0, "required"); + assert_different_registers(dst, src, tmp); + movdqu(xmm0, src); + pcmpgtq(xmm0, dst); + blendvpd(dst, src); // xmm0 as mask + } + } +} + +void C2_MacroAssembler::vpminmax(int opcode, BasicType elem_bt, + XMMRegister dst, XMMRegister src1, XMMRegister src2, + int vlen_enc) { + assert(opcode == Op_MinV || opcode == Op_MaxV, "sanity"); + + if (opcode == Op_MinV) { + if (elem_bt == T_BYTE) { + vpminsb(dst, src1, src2, vlen_enc); + } else if (elem_bt == T_SHORT) { + vpminsw(dst, src1, src2, vlen_enc); + } else if (elem_bt == T_INT) { + vpminsd(dst, src1, src2, vlen_enc); + } else { + assert(elem_bt == T_LONG, "required"); + if (UseAVX > 2 && (vlen_enc == Assembler::AVX_512bit || VM_Version::supports_avx512vl())) { + vpminsq(dst, src1, src2, vlen_enc); + } else { + assert_different_registers(dst, src1, src2); + vpcmpgtq(dst, src1, src2, vlen_enc); + vblendvpd(dst, src1, src2, dst, vlen_enc); + } + } + } else { // opcode == Op_MaxV + if (elem_bt == T_BYTE) { + vpmaxsb(dst, src1, src2, vlen_enc); + } else if (elem_bt == T_SHORT) { + vpmaxsw(dst, src1, src2, vlen_enc); + } else if (elem_bt == T_INT) { + vpmaxsd(dst, src1, src2, vlen_enc); + } else { + assert(elem_bt == T_LONG, "required"); + if (UseAVX > 2 && (vlen_enc == Assembler::AVX_512bit || VM_Version::supports_avx512vl())) { + vpmaxsq(dst, src1, src2, vlen_enc); + } else { + assert_different_registers(dst, src1, src2); + vpcmpgtq(dst, src1, src2, vlen_enc); + vblendvpd(dst, src2, src1, dst, vlen_enc); + } + } + } +} + +// Float/Double min max + +void C2_MacroAssembler::vminmax_fp(int opcode, BasicType elem_bt, + XMMRegister dst, XMMRegister a, XMMRegister b, + XMMRegister tmp, XMMRegister atmp, XMMRegister btmp, + int vlen_enc) { + assert(UseAVX > 0, "required"); + assert(opcode == Op_MinV || opcode == Op_MinReductionV || + opcode == Op_MaxV || opcode == Op_MaxReductionV, "sanity"); + assert(elem_bt == T_FLOAT || elem_bt == T_DOUBLE, "sanity"); + assert_different_registers(a, b, tmp, atmp, btmp); + + bool is_min = (opcode == Op_MinV || opcode == Op_MinReductionV); + bool is_double_word = is_double_word_type(elem_bt); + + if (!is_double_word && is_min) { + vblendvps(atmp, a, b, a, vlen_enc); + vblendvps(btmp, b, a, a, vlen_enc); + vminps(tmp, atmp, btmp, vlen_enc); + vcmpps(btmp, atmp, atmp, Assembler::UNORD_Q, vlen_enc); + vblendvps(dst, tmp, atmp, btmp, vlen_enc); + } else if (!is_double_word && !is_min) { + vblendvps(btmp, b, a, b, vlen_enc); + vblendvps(atmp, a, b, b, vlen_enc); + vmaxps(tmp, atmp, btmp, vlen_enc); + vcmpps(btmp, atmp, atmp, Assembler::UNORD_Q, vlen_enc); + vblendvps(dst, tmp, atmp, btmp, vlen_enc); + } else if (is_double_word && is_min) { + vblendvpd(atmp, a, b, a, vlen_enc); + vblendvpd(btmp, b, a, a, vlen_enc); + vminpd(tmp, atmp, btmp, vlen_enc); + vcmppd(btmp, atmp, atmp, Assembler::UNORD_Q, vlen_enc); + vblendvpd(dst, tmp, atmp, btmp, vlen_enc); + } else { + assert(is_double_word && !is_min, "sanity"); + vblendvpd(btmp, b, a, b, vlen_enc); + vblendvpd(atmp, a, b, b, vlen_enc); + vmaxpd(tmp, atmp, btmp, vlen_enc); + vcmppd(btmp, atmp, atmp, Assembler::UNORD_Q, vlen_enc); + vblendvpd(dst, tmp, atmp, btmp, vlen_enc); + } +} + +void C2_MacroAssembler::evminmax_fp(int opcode, BasicType elem_bt, + XMMRegister dst, XMMRegister a, XMMRegister b, + KRegister ktmp, XMMRegister atmp, XMMRegister btmp, + int vlen_enc) { + assert(UseAVX > 2, "required"); + assert(opcode == Op_MinV || opcode == Op_MinReductionV || + opcode == Op_MaxV || opcode == Op_MaxReductionV, "sanity"); + assert(elem_bt == T_FLOAT || elem_bt == T_DOUBLE, "sanity"); + assert_different_registers(dst, a, b, atmp, btmp); + + bool is_min = (opcode == Op_MinV || opcode == Op_MinReductionV); + bool is_double_word = is_double_word_type(elem_bt); + bool merge = true; + + if (!is_double_word && is_min) { + evpmovd2m(ktmp, a, vlen_enc); + evblendmps(atmp, ktmp, a, b, merge, vlen_enc); + evblendmps(btmp, ktmp, b, a, merge, vlen_enc); + vminps(dst, atmp, btmp, vlen_enc); + evcmpps(ktmp, k0, atmp, atmp, Assembler::UNORD_Q, vlen_enc); + evmovdqul(dst, ktmp, atmp, merge, vlen_enc); + } else if (!is_double_word && !is_min) { + evpmovd2m(ktmp, b, vlen_enc); + evblendmps(atmp, ktmp, a, b, merge, vlen_enc); + evblendmps(btmp, ktmp, b, a, merge, vlen_enc); + vmaxps(dst, atmp, btmp, vlen_enc); + evcmpps(ktmp, k0, atmp, atmp, Assembler::UNORD_Q, vlen_enc); + evmovdqul(dst, ktmp, atmp, merge, vlen_enc); + } else if (is_double_word && is_min) { + evpmovq2m(ktmp, a, vlen_enc); + evblendmpd(atmp, ktmp, a, b, merge, vlen_enc); + evblendmpd(btmp, ktmp, b, a, merge, vlen_enc); + vminpd(dst, atmp, btmp, vlen_enc); + evcmppd(ktmp, k0, atmp, atmp, Assembler::UNORD_Q, vlen_enc); + evmovdquq(dst, ktmp, atmp, merge, vlen_enc); + } else { + assert(is_double_word && !is_min, "sanity"); + evpmovq2m(ktmp, b, vlen_enc); + evblendmpd(atmp, ktmp, a, b, merge, vlen_enc); + evblendmpd(btmp, ktmp, b, a, merge, vlen_enc); + vmaxpd(dst, atmp, btmp, vlen_enc); + evcmppd(ktmp, k0, atmp, atmp, Assembler::UNORD_Q, vlen_enc); + evmovdquq(dst, ktmp, atmp, merge, vlen_enc); + } +} + void C2_MacroAssembler::vextendbw(bool sign, XMMRegister dst, XMMRegister src) { if (sign) { pmovsxbw(dst, src); @@ -877,6 +1060,22 @@ void C2_MacroAssembler::vextendbw(bool sign, XMMRegister dst, XMMRegister src, i } } +void C2_MacroAssembler::vextendbd(bool sign, XMMRegister dst, XMMRegister src, int vector_len) { + if (sign) { + vpmovsxbd(dst, src, vector_len); + } else { + vpmovzxbd(dst, src, vector_len); + } +} + +void C2_MacroAssembler::vextendwd(bool sign, XMMRegister dst, XMMRegister src, int vector_len) { + if (sign) { + vpmovsxwd(dst, src, vector_len); + } else { + vpmovzxwd(dst, src, vector_len); + } +} + void C2_MacroAssembler::vprotate_imm(int opcode, BasicType etype, XMMRegister dst, XMMRegister src, int shift, int vector_len) { if (opcode == Op_RotateLeftV) { @@ -928,14 +1127,13 @@ void C2_MacroAssembler::vshiftd_imm(int opcode, XMMRegister dst, int shift) { } } -void C2_MacroAssembler::vshiftd(int opcode, XMMRegister dst, XMMRegister src) { - if (opcode == Op_RShiftVI) { - psrad(dst, src); - } else if (opcode == Op_LShiftVI) { - pslld(dst, src); - } else { - assert((opcode == Op_URShiftVI),"opcode should be Op_URShiftVI"); - psrld(dst, src); +void C2_MacroAssembler::vshiftd(int opcode, XMMRegister dst, XMMRegister shift) { + switch (opcode) { + case Op_RShiftVI: psrad(dst, shift); break; + case Op_LShiftVI: pslld(dst, shift); break; + case Op_URShiftVI: psrld(dst, shift); break; + + default: assert(false, "%s", NodeClassNames[opcode]); } } @@ -950,47 +1148,53 @@ void C2_MacroAssembler::vshiftd_imm(int opcode, XMMRegister dst, XMMRegister nds } } -void C2_MacroAssembler::vshiftd(int opcode, XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - if (opcode == Op_RShiftVI) { - vpsrad(dst, nds, src, vector_len); - } else if (opcode == Op_LShiftVI) { - vpslld(dst, nds, src, vector_len); - } else { - assert((opcode == Op_URShiftVI),"opcode should be Op_URShiftVI"); - vpsrld(dst, nds, src, vector_len); +void C2_MacroAssembler::vshiftd(int opcode, XMMRegister dst, XMMRegister src, XMMRegister shift, int vlen_enc) { + switch (opcode) { + case Op_RShiftVI: vpsrad(dst, src, shift, vlen_enc); break; + case Op_LShiftVI: vpslld(dst, src, shift, vlen_enc); break; + case Op_URShiftVI: vpsrld(dst, src, shift, vlen_enc); break; + + default: assert(false, "%s", NodeClassNames[opcode]); } } -void C2_MacroAssembler::vshiftw(int opcode, XMMRegister dst, XMMRegister src) { - if ((opcode == Op_RShiftVS) || (opcode == Op_RShiftVB)) { - psraw(dst, src); - } else if ((opcode == Op_LShiftVS) || (opcode == Op_LShiftVB)) { - psllw(dst, src); - } else { - assert(((opcode == Op_URShiftVS) || (opcode == Op_URShiftVB)),"opcode should be one of Op_URShiftVS or Op_URShiftVB"); - psrlw(dst, src); +void C2_MacroAssembler::vshiftw(int opcode, XMMRegister dst, XMMRegister shift) { + switch (opcode) { + case Op_RShiftVB: // fall-through + case Op_RShiftVS: psraw(dst, shift); break; + + case Op_LShiftVB: // fall-through + case Op_LShiftVS: psllw(dst, shift); break; + + case Op_URShiftVS: // fall-through + case Op_URShiftVB: psrlw(dst, shift); break; + + default: assert(false, "%s", NodeClassNames[opcode]); } } -void C2_MacroAssembler::vshiftw(int opcode, XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - if ((opcode == Op_RShiftVS) || (opcode == Op_RShiftVB)) { - vpsraw(dst, nds, src, vector_len); - } else if ((opcode == Op_LShiftVS) || (opcode == Op_LShiftVB)) { - vpsllw(dst, nds, src, vector_len); - } else { - assert(((opcode == Op_URShiftVS) || (opcode == Op_URShiftVB)),"opcode should be one of Op_URShiftVS or Op_URShiftVB"); - vpsrlw(dst, nds, src, vector_len); +void C2_MacroAssembler::vshiftw(int opcode, XMMRegister dst, XMMRegister src, XMMRegister shift, int vlen_enc) { + switch (opcode) { + case Op_RShiftVB: // fall-through + case Op_RShiftVS: vpsraw(dst, src, shift, vlen_enc); break; + + case Op_LShiftVB: // fall-through + case Op_LShiftVS: vpsllw(dst, src, shift, vlen_enc); break; + + case Op_URShiftVS: // fall-through + case Op_URShiftVB: vpsrlw(dst, src, shift, vlen_enc); break; + + default: assert(false, "%s", NodeClassNames[opcode]); } } -void C2_MacroAssembler::vshiftq(int opcode, XMMRegister dst, XMMRegister src) { - if (opcode == Op_RShiftVL) { - psrlq(dst, src); // using srl to implement sra on pre-avs512 systems - } else if (opcode == Op_LShiftVL) { - psllq(dst, src); - } else { - assert((opcode == Op_URShiftVL),"opcode should be Op_URShiftVL"); - psrlq(dst, src); +void C2_MacroAssembler::vshiftq(int opcode, XMMRegister dst, XMMRegister shift) { + switch (opcode) { + case Op_RShiftVL: psrlq(dst, shift); break; // using srl to implement sra on pre-avs512 systems + case Op_LShiftVL: psllq(dst, shift); break; + case Op_URShiftVL: psrlq(dst, shift); break; + + default: assert(false, "%s", NodeClassNames[opcode]); } } @@ -1005,14 +1209,13 @@ void C2_MacroAssembler::vshiftq_imm(int opcode, XMMRegister dst, int shift) { } } -void C2_MacroAssembler::vshiftq(int opcode, XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - if (opcode == Op_RShiftVL) { - evpsraq(dst, nds, src, vector_len); - } else if (opcode == Op_LShiftVL) { - vpsllq(dst, nds, src, vector_len); - } else { - assert((opcode == Op_URShiftVL),"opcode should be Op_URShiftVL"); - vpsrlq(dst, nds, src, vector_len); +void C2_MacroAssembler::vshiftq(int opcode, XMMRegister dst, XMMRegister src, XMMRegister shift, int vlen_enc) { + switch (opcode) { + case Op_RShiftVL: evpsraq(dst, src, shift, vlen_enc); break; + case Op_LShiftVL: vpsllq(dst, src, shift, vlen_enc); break; + case Op_URShiftVL: vpsrlq(dst, src, shift, vlen_enc); break; + + default: assert(false, "%s", NodeClassNames[opcode]); } } @@ -1027,45 +1230,351 @@ void C2_MacroAssembler::vshiftq_imm(int opcode, XMMRegister dst, XMMRegister nds } } -// Reductions for vectors of ints, longs, floats, and doubles. +void C2_MacroAssembler::varshiftd(int opcode, XMMRegister dst, XMMRegister src, XMMRegister shift, int vlen_enc) { + switch (opcode) { + case Op_RShiftVB: // fall-through + case Op_RShiftVS: // fall-through + case Op_RShiftVI: vpsravd(dst, src, shift, vlen_enc); break; + + case Op_LShiftVB: // fall-through + case Op_LShiftVS: // fall-through + case Op_LShiftVI: vpsllvd(dst, src, shift, vlen_enc); break; + + case Op_URShiftVB: // fall-through + case Op_URShiftVS: // fall-through + case Op_URShiftVI: vpsrlvd(dst, src, shift, vlen_enc); break; + + default: assert(false, "%s", NodeClassNames[opcode]); + } +} + +void C2_MacroAssembler::varshiftw(int opcode, XMMRegister dst, XMMRegister src, XMMRegister shift, int vlen_enc) { + switch (opcode) { + case Op_RShiftVB: // fall-through + case Op_RShiftVS: evpsravw(dst, src, shift, vlen_enc); break; -void C2_MacroAssembler::reduce_operation_128(int opcode, XMMRegister dst, XMMRegister src) { + case Op_LShiftVB: // fall-through + case Op_LShiftVS: evpsllvw(dst, src, shift, vlen_enc); break; + + case Op_URShiftVB: // fall-through + case Op_URShiftVS: evpsrlvw(dst, src, shift, vlen_enc); break; + + default: assert(false, "%s", NodeClassNames[opcode]); + } +} + +void C2_MacroAssembler::varshiftq(int opcode, XMMRegister dst, XMMRegister src, XMMRegister shift, int vlen_enc, XMMRegister tmp) { + assert(UseAVX >= 2, "required"); + switch (opcode) { + case Op_RShiftVL: { + if (UseAVX > 2) { + assert(tmp == xnoreg, "not used"); + if (!VM_Version::supports_avx512vl()) { + vlen_enc = Assembler::AVX_512bit; + } + evpsravq(dst, src, shift, vlen_enc); + } else { + vmovdqu(tmp, ExternalAddress(StubRoutines::x86::vector_long_sign_mask())); + vpsrlvq(dst, src, shift, vlen_enc); + vpsrlvq(tmp, tmp, shift, vlen_enc); + vpxor(dst, dst, tmp, vlen_enc); + vpsubq(dst, dst, tmp, vlen_enc); + } + break; + } + case Op_LShiftVL: { + assert(tmp == xnoreg, "not used"); + vpsllvq(dst, src, shift, vlen_enc); + break; + } + case Op_URShiftVL: { + assert(tmp == xnoreg, "not used"); + vpsrlvq(dst, src, shift, vlen_enc); + break; + } + default: assert(false, "%s", NodeClassNames[opcode]); + } +} + +// Variable shift src by shift using vtmp and scratch as TEMPs giving word result in dst +void C2_MacroAssembler::varshiftbw(int opcode, XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len, XMMRegister vtmp, Register scratch) { + assert(opcode == Op_LShiftVB || + opcode == Op_RShiftVB || + opcode == Op_URShiftVB, "%s", NodeClassNames[opcode]); + bool sign = (opcode != Op_URShiftVB); + assert(vector_len == 0, "required"); + vextendbd(sign, dst, src, 1); + vpmovzxbd(vtmp, shift, 1); + varshiftd(opcode, dst, dst, vtmp, 1); + vpand(dst, dst, ExternalAddress(StubRoutines::x86::vector_int_to_byte_mask()), 1, scratch); + vextracti128_high(vtmp, dst); + vpackusdw(dst, dst, vtmp, 0); +} + +// Variable shift src by shift using vtmp and scratch as TEMPs giving byte result in dst +void C2_MacroAssembler::evarshiftb(int opcode, XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len, XMMRegister vtmp, Register scratch) { + assert(opcode == Op_LShiftVB || + opcode == Op_RShiftVB || + opcode == Op_URShiftVB, "%s", NodeClassNames[opcode]); + bool sign = (opcode != Op_URShiftVB); + int ext_vector_len = vector_len + 1; + vextendbw(sign, dst, src, ext_vector_len); + vpmovzxbw(vtmp, shift, ext_vector_len); + varshiftw(opcode, dst, dst, vtmp, ext_vector_len); + vpand(dst, dst, ExternalAddress(StubRoutines::x86::vector_short_to_byte_mask()), ext_vector_len, scratch); + if (vector_len == 0) { + vextracti128_high(vtmp, dst); + vpackuswb(dst, dst, vtmp, vector_len); + } else { + vextracti64x4_high(vtmp, dst); + vpackuswb(dst, dst, vtmp, vector_len); + vpermq(dst, dst, 0xD8, vector_len); + } +} + +void C2_MacroAssembler::insert(BasicType typ, XMMRegister dst, Register val, int idx) { + switch(typ) { + case T_BYTE: + pinsrb(dst, val, idx); + break; + case T_SHORT: + pinsrw(dst, val, idx); + break; + case T_INT: + pinsrd(dst, val, idx); + break; + case T_LONG: + pinsrq(dst, val, idx); + break; + default: + assert(false,"Should not reach here."); + break; + } +} + +void C2_MacroAssembler::vinsert(BasicType typ, XMMRegister dst, XMMRegister src, Register val, int idx) { + switch(typ) { + case T_BYTE: + vpinsrb(dst, src, val, idx); + break; + case T_SHORT: + vpinsrw(dst, src, val, idx); + break; + case T_INT: + vpinsrd(dst, src, val, idx); + break; + case T_LONG: + vpinsrq(dst, src, val, idx); + break; + default: + assert(false,"Should not reach here."); + break; + } +} + +void C2_MacroAssembler::vgather(BasicType typ, XMMRegister dst, Register base, XMMRegister idx, XMMRegister mask, int vector_len) { + switch(typ) { + case T_INT: + vpgatherdd(dst, Address(base, idx, Address::times_4), mask, vector_len); + break; + case T_FLOAT: + vgatherdps(dst, Address(base, idx, Address::times_4), mask, vector_len); + break; + case T_LONG: + vpgatherdq(dst, Address(base, idx, Address::times_8), mask, vector_len); + break; + case T_DOUBLE: + vgatherdpd(dst, Address(base, idx, Address::times_8), mask, vector_len); + break; + default: + assert(false,"Should not reach here."); + break; + } +} + +void C2_MacroAssembler::evgather(BasicType typ, XMMRegister dst, KRegister mask, Register base, XMMRegister idx, int vector_len) { + switch(typ) { + case T_INT: + evpgatherdd(dst, mask, Address(base, idx, Address::times_4), vector_len); + break; + case T_FLOAT: + evgatherdps(dst, mask, Address(base, idx, Address::times_4), vector_len); + break; + case T_LONG: + evpgatherdq(dst, mask, Address(base, idx, Address::times_8), vector_len); + break; + case T_DOUBLE: + evgatherdpd(dst, mask, Address(base, idx, Address::times_8), vector_len); + break; + default: + assert(false,"Should not reach here."); + break; + } +} + +void C2_MacroAssembler::evscatter(BasicType typ, Register base, XMMRegister idx, KRegister mask, XMMRegister src, int vector_len) { + switch(typ) { + case T_INT: + evpscatterdd(Address(base, idx, Address::times_4), mask, src, vector_len); + break; + case T_FLOAT: + evscatterdps(Address(base, idx, Address::times_4), mask, src, vector_len); + break; + case T_LONG: + evpscatterdq(Address(base, idx, Address::times_8), mask, src, vector_len); + break; + case T_DOUBLE: + evscatterdpd(Address(base, idx, Address::times_8), mask, src, vector_len); + break; + default: + assert(false,"Should not reach here."); + break; + } +} + +void C2_MacroAssembler::load_vector_mask(XMMRegister dst, XMMRegister src, int vlen_in_bytes, BasicType elem_bt) { + if (vlen_in_bytes <= 16) { + pxor (dst, dst); + psubb(dst, src); + switch (elem_bt) { + case T_BYTE: /* nothing to do */ break; + case T_SHORT: pmovsxbw(dst, dst); break; + case T_INT: pmovsxbd(dst, dst); break; + case T_FLOAT: pmovsxbd(dst, dst); break; + case T_LONG: pmovsxbq(dst, dst); break; + case T_DOUBLE: pmovsxbq(dst, dst); break; + + default: assert(false, "%s", type2name(elem_bt)); + } + } else { + int vlen_enc = vector_length_encoding(vlen_in_bytes); + + vpxor (dst, dst, dst, vlen_enc); + vpsubb(dst, dst, src, vlen_enc); + switch (elem_bt) { + case T_BYTE: /* nothing to do */ break; + case T_SHORT: vpmovsxbw(dst, dst, vlen_enc); break; + case T_INT: vpmovsxbd(dst, dst, vlen_enc); break; + case T_FLOAT: vpmovsxbd(dst, dst, vlen_enc); break; + case T_LONG: vpmovsxbq(dst, dst, vlen_enc); break; + case T_DOUBLE: vpmovsxbq(dst, dst, vlen_enc); break; + + default: assert(false, "%s", type2name(elem_bt)); + } + } +} + +void C2_MacroAssembler::load_iota_indices(XMMRegister dst, Register scratch, int vlen_in_bytes) { + ExternalAddress addr(StubRoutines::x86::vector_iota_indices()); + if (vlen_in_bytes <= 16) { + movdqu(dst, addr, scratch); + } else if (vlen_in_bytes == 32) { + vmovdqu(dst, addr, scratch); + } else { + assert(vlen_in_bytes == 64, "%d", vlen_in_bytes); + evmovdqub(dst, k0, addr, false /*merge*/, Assembler::AVX_512bit, scratch); + } +} +// Reductions for vectors of bytes, shorts, ints, longs, floats, and doubles. + +void C2_MacroAssembler::reduce_operation_128(BasicType typ, int opcode, XMMRegister dst, XMMRegister src) { int vector_len = Assembler::AVX_128bit; switch (opcode) { case Op_AndReductionV: pand(dst, src); break; case Op_OrReductionV: por (dst, src); break; case Op_XorReductionV: pxor(dst, src); break; - + case Op_MinReductionV: + switch (typ) { + case T_BYTE: pminsb(dst, src); break; + case T_SHORT: pminsw(dst, src); break; + case T_INT: pminsd(dst, src); break; + case T_LONG: assert(UseAVX > 2, "required"); + vpminsq(dst, dst, src, Assembler::AVX_128bit); break; + default: assert(false, "wrong type"); + } + break; + case Op_MaxReductionV: + switch (typ) { + case T_BYTE: pmaxsb(dst, src); break; + case T_SHORT: pmaxsw(dst, src); break; + case T_INT: pmaxsd(dst, src); break; + case T_LONG: assert(UseAVX > 2, "required"); + vpmaxsq(dst, dst, src, Assembler::AVX_128bit); break; + default: assert(false, "wrong type"); + } + break; case Op_AddReductionVF: addss(dst, src); break; case Op_AddReductionVD: addsd(dst, src); break; - case Op_AddReductionVI: paddd(dst, src); break; + case Op_AddReductionVI: + switch (typ) { + case T_BYTE: paddb(dst, src); break; + case T_SHORT: paddw(dst, src); break; + case T_INT: paddd(dst, src); break; + default: assert(false, "wrong type"); + } + break; case Op_AddReductionVL: paddq(dst, src); break; - case Op_MulReductionVF: mulss(dst, src); break; case Op_MulReductionVD: mulsd(dst, src); break; - case Op_MulReductionVI: pmulld(dst, src); break; - case Op_MulReductionVL: vpmullq(dst, dst, src, vector_len); break; - - default: assert(false, "wrong opcode"); + case Op_MulReductionVI: + switch (typ) { + case T_SHORT: pmullw(dst, src); break; + case T_INT: pmulld(dst, src); break; + default: assert(false, "wrong type"); + } + break; + case Op_MulReductionVL: assert(UseAVX > 2, "required"); + vpmullq(dst, dst, src, vector_len); break; + default: assert(false, "wrong opcode"); } } -void C2_MacroAssembler::reduce_operation_256(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2) { +void C2_MacroAssembler::reduce_operation_256(BasicType typ, int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2) { int vector_len = Assembler::AVX_256bit; switch (opcode) { case Op_AndReductionV: vpand(dst, src1, src2, vector_len); break; case Op_OrReductionV: vpor (dst, src1, src2, vector_len); break; case Op_XorReductionV: vpxor(dst, src1, src2, vector_len); break; - - case Op_AddReductionVI: vpaddd(dst, src1, src2, vector_len); break; + case Op_MinReductionV: + switch (typ) { + case T_BYTE: vpminsb(dst, src1, src2, vector_len); break; + case T_SHORT: vpminsw(dst, src1, src2, vector_len); break; + case T_INT: vpminsd(dst, src1, src2, vector_len); break; + case T_LONG: assert(UseAVX > 2, "required"); + vpminsq(dst, src1, src2, vector_len); break; + default: assert(false, "wrong type"); + } + break; + case Op_MaxReductionV: + switch (typ) { + case T_BYTE: vpmaxsb(dst, src1, src2, vector_len); break; + case T_SHORT: vpmaxsw(dst, src1, src2, vector_len); break; + case T_INT: vpmaxsd(dst, src1, src2, vector_len); break; + case T_LONG: assert(UseAVX > 2, "required"); + vpmaxsq(dst, src1, src2, vector_len); break; + default: assert(false, "wrong type"); + } + break; + case Op_AddReductionVI: + switch (typ) { + case T_BYTE: vpaddb(dst, src1, src2, vector_len); break; + case T_SHORT: vpaddw(dst, src1, src2, vector_len); break; + case T_INT: vpaddd(dst, src1, src2, vector_len); break; + default: assert(false, "wrong type"); + } + break; case Op_AddReductionVL: vpaddq(dst, src1, src2, vector_len); break; - - case Op_MulReductionVI: vpmulld(dst, src1, src2, vector_len); break; + case Op_MulReductionVI: + switch (typ) { + case T_SHORT: vpmullw(dst, src1, src2, vector_len); break; + case T_INT: vpmulld(dst, src1, src2, vector_len); break; + default: assert(false, "wrong type"); + } + break; case Op_MulReductionVL: vpmullq(dst, src1, src2, vector_len); break; - - default: assert(false, "wrong opcode"); + default: assert(false, "wrong opcode"); } } @@ -1087,9 +1596,48 @@ void C2_MacroAssembler::reduce_fp(int opcode, int vlen, } } +void C2_MacroAssembler::reduceB(int opcode, int vlen, + Register dst, Register src1, XMMRegister src2, + XMMRegister vtmp1, XMMRegister vtmp2) { + switch (vlen) { + case 8: reduce8B (opcode, dst, src1, src2, vtmp1, vtmp2); break; + case 16: reduce16B(opcode, dst, src1, src2, vtmp1, vtmp2); break; + case 32: reduce32B(opcode, dst, src1, src2, vtmp1, vtmp2); break; + case 64: reduce64B(opcode, dst, src1, src2, vtmp1, vtmp2); break; + + default: assert(false, "wrong vector length"); + } +} + +void C2_MacroAssembler::mulreduceB(int opcode, int vlen, + Register dst, Register src1, XMMRegister src2, + XMMRegister vtmp1, XMMRegister vtmp2) { + switch (vlen) { + case 8: mulreduce8B (opcode, dst, src1, src2, vtmp1, vtmp2); break; + case 16: mulreduce16B(opcode, dst, src1, src2, vtmp1, vtmp2); break; + case 32: mulreduce32B(opcode, dst, src1, src2, vtmp1, vtmp2); break; + case 64: mulreduce64B(opcode, dst, src1, src2, vtmp1, vtmp2); break; + + default: assert(false, "wrong vector length"); + } +} + +void C2_MacroAssembler::reduceS(int opcode, int vlen, + Register dst, Register src1, XMMRegister src2, + XMMRegister vtmp1, XMMRegister vtmp2) { + switch (vlen) { + case 4: reduce4S (opcode, dst, src1, src2, vtmp1, vtmp2); break; + case 8: reduce8S (opcode, dst, src1, src2, vtmp1, vtmp2); break; + case 16: reduce16S(opcode, dst, src1, src2, vtmp1, vtmp2); break; + case 32: reduce32S(opcode, dst, src1, src2, vtmp1, vtmp2); break; + + default: assert(false, "wrong vector length"); + } +} + void C2_MacroAssembler::reduceI(int opcode, int vlen, - Register dst, Register src1, XMMRegister src2, - XMMRegister vtmp1, XMMRegister vtmp2) { + Register dst, Register src1, XMMRegister src2, + XMMRegister vtmp1, XMMRegister vtmp2) { switch (vlen) { case 2: reduce2I (opcode, dst, src1, src2, vtmp1, vtmp2); break; case 4: reduce4I (opcode, dst, src1, src2, vtmp1, vtmp2); break; @@ -1102,8 +1650,8 @@ void C2_MacroAssembler::reduceI(int opcode, int vlen, #ifdef _LP64 void C2_MacroAssembler::reduceL(int opcode, int vlen, - Register dst, Register src1, XMMRegister src2, - XMMRegister vtmp1, XMMRegister vtmp2) { + Register dst, Register src1, XMMRegister src2, + XMMRegister vtmp1, XMMRegister vtmp2) { switch (vlen) { case 2: reduce2L(opcode, dst, src1, src2, vtmp1, vtmp2); break; case 4: reduce4L(opcode, dst, src1, src2, vtmp1, vtmp2); break; @@ -1158,10 +1706,10 @@ void C2_MacroAssembler::reduce2I(int opcode, Register dst, Register src1, XMMReg phaddd(vtmp1, vtmp1); } else { pshufd(vtmp1, src2, 0x1); - reduce_operation_128(opcode, vtmp1, src2); + reduce_operation_128(T_INT, opcode, vtmp1, src2); } movdl(vtmp2, src1); - reduce_operation_128(opcode, vtmp1, vtmp2); + reduce_operation_128(T_INT, opcode, vtmp1, vtmp2); movdl(dst, vtmp1); } @@ -1174,7 +1722,7 @@ void C2_MacroAssembler::reduce4I(int opcode, Register dst, Register src1, XMMReg reduce2I(opcode, dst, src1, vtmp1, vtmp1, vtmp2); } else { pshufd(vtmp2, src2, 0xE); - reduce_operation_128(opcode, vtmp2, src2); + reduce_operation_128(T_INT, opcode, vtmp2, src2); reduce2I(opcode, dst, src1, vtmp2, vtmp1, vtmp2); } } @@ -1187,51 +1735,176 @@ void C2_MacroAssembler::reduce8I(int opcode, Register dst, Register src1, XMMReg reduce2I(opcode, dst, src1, vtmp1, vtmp1, vtmp2); } else { vextracti128_high(vtmp1, src2); - reduce_operation_128(opcode, vtmp1, src2); + reduce_operation_128(T_INT, opcode, vtmp1, src2); reduce4I(opcode, dst, src1, vtmp1, vtmp1, vtmp2); } } void C2_MacroAssembler::reduce16I(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { vextracti64x4_high(vtmp2, src2); - reduce_operation_256(opcode, vtmp2, vtmp2, src2); + reduce_operation_256(T_INT, opcode, vtmp2, vtmp2, src2); reduce8I(opcode, dst, src1, vtmp2, vtmp1, vtmp2); } +void C2_MacroAssembler::reduce8B(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { + pshufd(vtmp2, src2, 0x1); + reduce_operation_128(T_BYTE, opcode, vtmp2, src2); + movdqu(vtmp1, vtmp2); + psrldq(vtmp1, 2); + reduce_operation_128(T_BYTE, opcode, vtmp1, vtmp2); + movdqu(vtmp2, vtmp1); + psrldq(vtmp2, 1); + reduce_operation_128(T_BYTE, opcode, vtmp1, vtmp2); + movdl(vtmp2, src1); + pmovsxbd(vtmp1, vtmp1); + reduce_operation_128(T_INT, opcode, vtmp1, vtmp2); + pextrb(dst, vtmp1, 0x0); + movsbl(dst, dst); +} + +void C2_MacroAssembler::reduce16B(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { + pshufd(vtmp1, src2, 0xE); + reduce_operation_128(T_BYTE, opcode, vtmp1, src2); + reduce8B(opcode, dst, src1, vtmp1, vtmp1, vtmp2); +} + +void C2_MacroAssembler::reduce32B(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { + vextracti128_high(vtmp2, src2); + reduce_operation_128(T_BYTE, opcode, vtmp2, src2); + reduce16B(opcode, dst, src1, vtmp2, vtmp1, vtmp2); +} + +void C2_MacroAssembler::reduce64B(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { + vextracti64x4_high(vtmp1, src2); + reduce_operation_256(T_BYTE, opcode, vtmp1, vtmp1, src2); + reduce32B(opcode, dst, src1, vtmp1, vtmp1, vtmp2); +} + +void C2_MacroAssembler::mulreduce8B(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { + pmovsxbw(vtmp2, src2); + reduce8S(opcode, dst, src1, vtmp2, vtmp1, vtmp2); +} + +void C2_MacroAssembler::mulreduce16B(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { + if (UseAVX > 1) { + int vector_len = Assembler::AVX_256bit; + vpmovsxbw(vtmp1, src2, vector_len); + reduce16S(opcode, dst, src1, vtmp1, vtmp1, vtmp2); + } else { + pmovsxbw(vtmp2, src2); + reduce8S(opcode, dst, src1, vtmp2, vtmp1, vtmp2); + pshufd(vtmp2, src2, 0x1); + pmovsxbw(vtmp2, src2); + reduce8S(opcode, dst, dst, vtmp2, vtmp1, vtmp2); + } +} + +void C2_MacroAssembler::mulreduce32B(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { + if (UseAVX > 2 && VM_Version::supports_avx512bw()) { + int vector_len = Assembler::AVX_512bit; + vpmovsxbw(vtmp1, src2, vector_len); + reduce32S(opcode, dst, src1, vtmp1, vtmp1, vtmp2); + } else { + assert(UseAVX >= 2,"Should not reach here."); + mulreduce16B(opcode, dst, src1, src2, vtmp1, vtmp2); + vextracti128_high(vtmp2, src2); + mulreduce16B(opcode, dst, dst, vtmp2, vtmp1, vtmp2); + } +} + +void C2_MacroAssembler::mulreduce64B(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { + mulreduce32B(opcode, dst, src1, src2, vtmp1, vtmp2); + vextracti64x4_high(vtmp2, src2); + mulreduce32B(opcode, dst, dst, vtmp2, vtmp1, vtmp2); +} + +void C2_MacroAssembler::reduce4S(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { + if (opcode == Op_AddReductionVI) { + if (vtmp1 != src2) { + movdqu(vtmp1, src2); + } + phaddw(vtmp1, vtmp1); + phaddw(vtmp1, vtmp1); + } else { + pshufd(vtmp2, src2, 0x1); + reduce_operation_128(T_SHORT, opcode, vtmp2, src2); + movdqu(vtmp1, vtmp2); + psrldq(vtmp1, 2); + reduce_operation_128(T_SHORT, opcode, vtmp1, vtmp2); + } + movdl(vtmp2, src1); + pmovsxwd(vtmp1, vtmp1); + reduce_operation_128(T_INT, opcode, vtmp1, vtmp2); + pextrw(dst, vtmp1, 0x0); + movswl(dst, dst); +} + +void C2_MacroAssembler::reduce8S(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { + if (opcode == Op_AddReductionVI) { + if (vtmp1 != src2) { + movdqu(vtmp1, src2); + } + phaddw(vtmp1, src2); + } else { + pshufd(vtmp1, src2, 0xE); + reduce_operation_128(T_SHORT, opcode, vtmp1, src2); + } + reduce4S(opcode, dst, src1, vtmp1, vtmp1, vtmp2); +} + +void C2_MacroAssembler::reduce16S(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { + if (opcode == Op_AddReductionVI) { + int vector_len = Assembler::AVX_256bit; + vphaddw(vtmp2, src2, src2, vector_len); + vpermq(vtmp2, vtmp2, 0xD8, vector_len); + } else { + vextracti128_high(vtmp2, src2); + reduce_operation_128(T_SHORT, opcode, vtmp2, src2); + } + reduce8S(opcode, dst, src1, vtmp2, vtmp1, vtmp2); +} + +void C2_MacroAssembler::reduce32S(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { + int vector_len = Assembler::AVX_256bit; + vextracti64x4_high(vtmp1, src2); + reduce_operation_256(T_SHORT, opcode, vtmp1, vtmp1, src2); + reduce16S(opcode, dst, src1, vtmp1, vtmp1, vtmp2); +} + #ifdef _LP64 void C2_MacroAssembler::reduce2L(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { pshufd(vtmp2, src2, 0xE); - reduce_operation_128(opcode, vtmp2, src2); + reduce_operation_128(T_LONG, opcode, vtmp2, src2); movdq(vtmp1, src1); - reduce_operation_128(opcode, vtmp1, vtmp2); + reduce_operation_128(T_LONG, opcode, vtmp1, vtmp2); movdq(dst, vtmp1); } void C2_MacroAssembler::reduce4L(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { vextracti128_high(vtmp1, src2); - reduce_operation_128(opcode, vtmp1, src2); + reduce_operation_128(T_LONG, opcode, vtmp1, src2); reduce2L(opcode, dst, src1, vtmp1, vtmp1, vtmp2); } void C2_MacroAssembler::reduce8L(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { vextracti64x4_high(vtmp2, src2); - reduce_operation_256(opcode, vtmp2, vtmp2, src2); + reduce_operation_256(T_LONG, opcode, vtmp2, vtmp2, src2); reduce4L(opcode, dst, src1, vtmp2, vtmp1, vtmp2); } #endif // _LP64 void C2_MacroAssembler::reduce2F(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp) { - reduce_operation_128(opcode, dst, src); + reduce_operation_128(T_FLOAT, opcode, dst, src); pshufd(vtmp, src, 0x1); - reduce_operation_128(opcode, dst, vtmp); + reduce_operation_128(T_FLOAT, opcode, dst, vtmp); } void C2_MacroAssembler::reduce4F(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp) { reduce2F(opcode, dst, src, vtmp); pshufd(vtmp, src, 0x2); - reduce_operation_128(opcode, dst, vtmp); + reduce_operation_128(T_FLOAT, opcode, dst, vtmp); pshufd(vtmp, src, 0x3); - reduce_operation_128(opcode, dst, vtmp); + reduce_operation_128(T_FLOAT, opcode, dst, vtmp); } void C2_MacroAssembler::reduce8F(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2) { @@ -1247,9 +1920,9 @@ void C2_MacroAssembler::reduce16F(int opcode, XMMRegister dst, XMMRegister src, } void C2_MacroAssembler::reduce2D(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp) { - reduce_operation_128(opcode, dst, src); + reduce_operation_128(T_DOUBLE, opcode, dst, src); pshufd(vtmp, src, 0xE); - reduce_operation_128(opcode, dst, vtmp); + reduce_operation_128(T_DOUBLE, opcode, dst, vtmp); } void C2_MacroAssembler::reduce4D(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2) { @@ -1264,6 +1937,207 @@ void C2_MacroAssembler::reduce8D(int opcode, XMMRegister dst, XMMRegister src, X reduce4D(opcode, dst, vtmp1, vtmp1, vtmp2); } +void C2_MacroAssembler::reduceFloatMinMax(int opcode, int vlen, bool is_dst_valid, + XMMRegister dst, XMMRegister src, + XMMRegister tmp, XMMRegister atmp, XMMRegister btmp, + XMMRegister xmm_0, XMMRegister xmm_1) { + int permconst[] = {1, 14}; + XMMRegister wsrc = src; + XMMRegister wdst = xmm_0; + XMMRegister wtmp = (xmm_1 == xnoreg) ? xmm_0: xmm_1; + + int vlen_enc = Assembler::AVX_128bit; + if (vlen == 16) { + vlen_enc = Assembler::AVX_256bit; + } + + for (int i = log2(vlen) - 1; i >=0; i--) { + if (i == 0 && !is_dst_valid) { + wdst = dst; + } + if (i == 3) { + vextracti64x4_high(wtmp, wsrc); + } else if (i == 2) { + vextracti128_high(wtmp, wsrc); + } else { // i = [0,1] + vpermilps(wtmp, wsrc, permconst[i], vlen_enc); + } + vminmax_fp(opcode, T_FLOAT, wdst, wtmp, wsrc, tmp, atmp, btmp, vlen_enc); + wsrc = wdst; + vlen_enc = Assembler::AVX_128bit; + } + if (is_dst_valid) { + vminmax_fp(opcode, T_FLOAT, dst, wdst, dst, tmp, atmp, btmp, Assembler::AVX_128bit); + } +} + +void C2_MacroAssembler::reduceDoubleMinMax(int opcode, int vlen, bool is_dst_valid, XMMRegister dst, XMMRegister src, + XMMRegister tmp, XMMRegister atmp, XMMRegister btmp, + XMMRegister xmm_0, XMMRegister xmm_1) { + XMMRegister wsrc = src; + XMMRegister wdst = xmm_0; + XMMRegister wtmp = (xmm_1 == xnoreg) ? xmm_0: xmm_1; + int vlen_enc = Assembler::AVX_128bit; + if (vlen == 8) { + vlen_enc = Assembler::AVX_256bit; + } + for (int i = log2(vlen) - 1; i >=0; i--) { + if (i == 0 && !is_dst_valid) { + wdst = dst; + } + if (i == 1) { + vextracti128_high(wtmp, wsrc); + } else if (i == 2) { + vextracti64x4_high(wtmp, wsrc); + } else { + assert(i == 0, "%d", i); + vpermilpd(wtmp, wsrc, 1, vlen_enc); + } + vminmax_fp(opcode, T_DOUBLE, wdst, wtmp, wsrc, tmp, atmp, btmp, vlen_enc); + wsrc = wdst; + vlen_enc = Assembler::AVX_128bit; + } + if (is_dst_valid) { + vminmax_fp(opcode, T_DOUBLE, dst, wdst, dst, tmp, atmp, btmp, Assembler::AVX_128bit); + } +} + +void C2_MacroAssembler::extract(BasicType bt, Register dst, XMMRegister src, int idx) { + switch (bt) { + case T_BYTE: pextrb(dst, src, idx); break; + case T_SHORT: pextrw(dst, src, idx); break; + case T_INT: pextrd(dst, src, idx); break; + case T_LONG: pextrq(dst, src, idx); break; + + default: + assert(false,"Should not reach here."); + break; + } +} + +XMMRegister C2_MacroAssembler::get_lane(BasicType typ, XMMRegister dst, XMMRegister src, int elemindex) { + int esize = type2aelembytes(typ); + int elem_per_lane = 16/esize; + int lane = elemindex / elem_per_lane; + int eindex = elemindex % elem_per_lane; + + if (lane >= 2) { + assert(UseAVX > 2, "required"); + vextractf32x4(dst, src, lane & 3); + return dst; + } else if (lane > 0) { + assert(UseAVX > 0, "required"); + vextractf128(dst, src, lane); + return dst; + } else { + return src; + } +} + +void C2_MacroAssembler::get_elem(BasicType typ, Register dst, XMMRegister src, int elemindex) { + int esize = type2aelembytes(typ); + int elem_per_lane = 16/esize; + int eindex = elemindex % elem_per_lane; + assert(is_integral_type(typ),"required"); + + if (eindex == 0) { + if (typ == T_LONG) { + movq(dst, src); + } else { + movdl(dst, src); + if (typ == T_BYTE) + movsbl(dst, dst); + else if (typ == T_SHORT) + movswl(dst, dst); + } + } else { + extract(typ, dst, src, eindex); + } +} + +void C2_MacroAssembler::get_elem(BasicType typ, XMMRegister dst, XMMRegister src, int elemindex, Register tmp, XMMRegister vtmp) { + int esize = type2aelembytes(typ); + int elem_per_lane = 16/esize; + int eindex = elemindex % elem_per_lane; + assert((typ == T_FLOAT || typ == T_DOUBLE),"required"); + + if (eindex == 0) { + movq(dst, src); + } else { + if (typ == T_FLOAT) { + if (UseAVX == 0) { + movdqu(dst, src); + pshufps(dst, dst, eindex); + } else { + vpshufps(dst, src, src, eindex, Assembler::AVX_128bit); + } + } else { + if (UseAVX == 0) { + movdqu(dst, src); + psrldq(dst, eindex*esize); + } else { + vpsrldq(dst, src, eindex*esize, Assembler::AVX_128bit); + } + movq(dst, dst); + } + } + // Zero upper bits + if (typ == T_FLOAT) { + if (UseAVX == 0) { + assert((vtmp != xnoreg) && (tmp != noreg), "required."); + movdqu(vtmp, ExternalAddress(StubRoutines::x86::vector_32_bit_mask()), tmp); + pand(dst, vtmp); + } else { + assert((tmp != noreg), "required."); + vpand(dst, dst, ExternalAddress(StubRoutines::x86::vector_32_bit_mask()), Assembler::AVX_128bit, tmp); + } + } +} + +void C2_MacroAssembler::evpcmp(BasicType typ, KRegister kdmask, KRegister ksmask, XMMRegister src1, AddressLiteral adr, int comparison, int vector_len, Register scratch) { + switch(typ) { + case T_BYTE: + evpcmpb(kdmask, ksmask, src1, adr, comparison, vector_len, scratch); + break; + case T_SHORT: + evpcmpw(kdmask, ksmask, src1, adr, comparison, vector_len, scratch); + break; + case T_INT: + case T_FLOAT: + evpcmpd(kdmask, ksmask, src1, adr, comparison, vector_len, scratch); + break; + case T_LONG: + case T_DOUBLE: + evpcmpq(kdmask, ksmask, src1, adr, comparison, vector_len, scratch); + break; + default: + assert(false,"Should not reach here."); + break; + } +} + +void C2_MacroAssembler::evpblend(BasicType typ, XMMRegister dst, KRegister kmask, XMMRegister src1, XMMRegister src2, bool merge, int vector_len) { + switch(typ) { + case T_BYTE: + evpblendmb(dst, kmask, src1, src2, merge, vector_len); + break; + case T_SHORT: + evpblendmw(dst, kmask, src1, src2, merge, vector_len); + break; + case T_INT: + case T_FLOAT: + evpblendmd(dst, kmask, src1, src2, merge, vector_len); + break; + case T_LONG: + case T_DOUBLE: + evpblendmq(dst, kmask, src1, src2, merge, vector_len); + break; + default: + assert(false,"Should not reach here."); + break; + } +} + //------------------------------------------------------------------------------------------- // IndexOf for constant substrings with size >= 8 chars @@ -1850,7 +2724,7 @@ void C2_MacroAssembler::string_indexof_char(Register str1, Register cnt1, Regist pmovmskb(tmp, vec3); } bsfl(ch, tmp); - addl(result, ch); + addptr(result, ch); bind(FOUND_SEQ_CHAR); subptr(result, str1); @@ -1859,6 +2733,99 @@ void C2_MacroAssembler::string_indexof_char(Register str1, Register cnt1, Regist bind(DONE_LABEL); } // string_indexof_char +void C2_MacroAssembler::stringL_indexof_char(Register str1, Register cnt1, Register ch, Register result, + XMMRegister vec1, XMMRegister vec2, XMMRegister vec3, Register tmp) { + ShortBranchVerifier sbv(this); + assert(UseSSE42Intrinsics, "SSE4.2 intrinsics are required"); + + int stride = 16; + + Label FOUND_CHAR, SCAN_TO_CHAR_INIT, SCAN_TO_CHAR_LOOP, + SCAN_TO_16_CHAR, SCAN_TO_16_CHAR_LOOP, SCAN_TO_32_CHAR_LOOP, + RET_NOT_FOUND, SCAN_TO_16_CHAR_INIT, + FOUND_SEQ_CHAR, DONE_LABEL; + + movptr(result, str1); + if (UseAVX >= 2) { + cmpl(cnt1, stride); + jcc(Assembler::less, SCAN_TO_CHAR_INIT); + cmpl(cnt1, stride*2); + jcc(Assembler::less, SCAN_TO_16_CHAR_INIT); + movdl(vec1, ch); + vpbroadcastb(vec1, vec1, Assembler::AVX_256bit); + vpxor(vec2, vec2); + movl(tmp, cnt1); + andl(tmp, 0xFFFFFFE0); //vector count (in chars) + andl(cnt1,0x0000001F); //tail count (in chars) + + bind(SCAN_TO_32_CHAR_LOOP); + vmovdqu(vec3, Address(result, 0)); + vpcmpeqb(vec3, vec3, vec1, Assembler::AVX_256bit); + vptest(vec2, vec3); + jcc(Assembler::carryClear, FOUND_CHAR); + addptr(result, 32); + subl(tmp, stride*2); + jcc(Assembler::notZero, SCAN_TO_32_CHAR_LOOP); + jmp(SCAN_TO_16_CHAR); + + bind(SCAN_TO_16_CHAR_INIT); + movdl(vec1, ch); + pxor(vec2, vec2); + pshufb(vec1, vec2); + } + + bind(SCAN_TO_16_CHAR); + cmpl(cnt1, stride); + jcc(Assembler::less, SCAN_TO_CHAR_INIT);//less than 16 entires left + if (UseAVX < 2) { + movdl(vec1, ch); + pxor(vec2, vec2); + pshufb(vec1, vec2); + } + movl(tmp, cnt1); + andl(tmp, 0xFFFFFFF0); //vector count (in bytes) + andl(cnt1,0x0000000F); //tail count (in bytes) + + bind(SCAN_TO_16_CHAR_LOOP); + movdqu(vec3, Address(result, 0)); + pcmpeqb(vec3, vec1); + ptest(vec2, vec3); + jcc(Assembler::carryClear, FOUND_CHAR); + addptr(result, 16); + subl(tmp, stride); + jcc(Assembler::notZero, SCAN_TO_16_CHAR_LOOP);//last 16 items... + + bind(SCAN_TO_CHAR_INIT); + testl(cnt1, cnt1); + jcc(Assembler::zero, RET_NOT_FOUND); + bind(SCAN_TO_CHAR_LOOP); + load_unsigned_byte(tmp, Address(result, 0)); + cmpl(ch, tmp); + jccb(Assembler::equal, FOUND_SEQ_CHAR); + addptr(result, 1); + subl(cnt1, 1); + jccb(Assembler::zero, RET_NOT_FOUND); + jmp(SCAN_TO_CHAR_LOOP); + + bind(RET_NOT_FOUND); + movl(result, -1); + jmpb(DONE_LABEL); + + bind(FOUND_CHAR); + if (UseAVX >= 2) { + vpmovmskb(tmp, vec3); + } else { + pmovmskb(tmp, vec3); + } + bsfl(ch, tmp); + addptr(result, ch); + + bind(FOUND_SEQ_CHAR); + subptr(result, str1); + + bind(DONE_LABEL); +} // stringL_indexof_char + // helper function for string_compare void C2_MacroAssembler::load_next_elements(Register elem1, Register elem2, Register str1, Register str2, Address::ScaleFactor scale, Address::ScaleFactor scale1, diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp index f16b193a21d..79ab55a75ad 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp @@ -28,6 +28,8 @@ // C2_MacroAssembler contains high-level macros for C2 public: + Assembler::AvxVectorLen vector_length_encoding(int vlen_in_bytes); + // special instructions for EVEX void setvectmask(Register dst, Register src); void restorevectmask(); @@ -71,25 +73,69 @@ void vabsnegd(int opcode, XMMRegister dst, XMMRegister src, int vector_len, Register scr); void vabsnegf(int opcode, XMMRegister dst, XMMRegister src, Register scr); void vabsnegf(int opcode, XMMRegister dst, XMMRegister src, int vector_len, Register scr); + + void pminmax(int opcode, BasicType elem_bt, XMMRegister dst, XMMRegister src, + XMMRegister tmp = xnoreg); + void vpminmax(int opcode, BasicType elem_bt, + XMMRegister dst, XMMRegister src1, XMMRegister src2, + int vlen_enc); + + void vminmax_fp(int opcode, BasicType elem_bt, + XMMRegister dst, XMMRegister a, XMMRegister b, + XMMRegister tmp, XMMRegister atmp, XMMRegister btmp, + int vlen_enc); + void evminmax_fp(int opcode, BasicType elem_bt, + XMMRegister dst, XMMRegister a, XMMRegister b, + KRegister ktmp, XMMRegister atmp, XMMRegister btmp, + int vlen_enc); + void vextendbw(bool sign, XMMRegister dst, XMMRegister src, int vector_len); void vextendbw(bool sign, XMMRegister dst, XMMRegister src); - void vshiftd(int opcode, XMMRegister dst, XMMRegister src); + void vextendbd(bool sign, XMMRegister dst, XMMRegister src, int vector_len); + void vextendwd(bool sign, XMMRegister dst, XMMRegister src, int vector_len); + + void vshiftd(int opcode, XMMRegister dst, XMMRegister shift); void vshiftd_imm(int opcode, XMMRegister dst, int shift); - void vshiftd(int opcode, XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void vshiftd(int opcode, XMMRegister dst, XMMRegister src, XMMRegister shift, int vlen_enc); void vshiftd_imm(int opcode, XMMRegister dst, XMMRegister nds, int shift, int vector_len); - void vshiftw(int opcode, XMMRegister dst, XMMRegister src); - void vshiftw(int opcode, XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); - void vshiftq(int opcode, XMMRegister dst, XMMRegister src); + void vshiftw(int opcode, XMMRegister dst, XMMRegister shift); + void vshiftw(int opcode, XMMRegister dst, XMMRegister src, XMMRegister shift, int vlen_enc); + void vshiftq(int opcode, XMMRegister dst, XMMRegister shift); void vshiftq_imm(int opcode, XMMRegister dst, int shift); - void vshiftq(int opcode, XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void vshiftq(int opcode, XMMRegister dst, XMMRegister src, XMMRegister shift, int vlen_enc); void vshiftq_imm(int opcode, XMMRegister dst, XMMRegister nds, int shift, int vector_len); void vprotate_imm(int opcode, BasicType etype, XMMRegister dst, XMMRegister src, int shift, int vector_len); void vprotate_var(int opcode, BasicType etype, XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len); - // Reductions for vectors of ints, longs, floats, and doubles. + void varshiftd(int opcode, XMMRegister dst, XMMRegister src, XMMRegister shift, int vlen_enc); + void varshiftw(int opcode, XMMRegister dst, XMMRegister src, XMMRegister shift, int vlen_enc); + void varshiftq(int opcode, XMMRegister dst, XMMRegister src, XMMRegister shift, int vlen_enc, XMMRegister vtmp = xnoreg); + void varshiftbw(int opcode, XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len, XMMRegister vtmp, Register scratch); + void evarshiftb(int opcode, XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len, XMMRegister vtmp, Register scratch); + + void insert(BasicType typ, XMMRegister dst, Register val, int idx); + void vinsert(BasicType typ, XMMRegister dst, XMMRegister src, Register val, int idx); + void vgather(BasicType typ, XMMRegister dst, Register base, XMMRegister idx, XMMRegister mask, int vector_len); + void evgather(BasicType typ, XMMRegister dst, KRegister mask, Register base, XMMRegister idx, int vector_len); + void evscatter(BasicType typ, Register base, XMMRegister idx, KRegister mask, XMMRegister src, int vector_len); + + // extract + void extract(BasicType typ, Register dst, XMMRegister src, int idx); + XMMRegister get_lane(BasicType typ, XMMRegister dst, XMMRegister src, int elemindex); + void get_elem(BasicType typ, Register dst, XMMRegister src, int elemindex); + void get_elem(BasicType typ, XMMRegister dst, XMMRegister src, int elemindex, Register tmp = noreg, XMMRegister vtmp = xnoreg); - // dst = src1 + reduce(op, src2) using vtmp as temps + // blend + void evpcmp(BasicType typ, KRegister kdmask, KRegister ksmask, XMMRegister src1, AddressLiteral adr, int comparison, int vector_len, Register scratch = rscratch1); + void evpblend(BasicType typ, XMMRegister dst, KRegister kmask, XMMRegister src1, XMMRegister src2, bool merge, int vector_len); + + void load_vector_mask(XMMRegister dst, XMMRegister src, int vlen_in_bytes, BasicType elem_bt); + void load_iota_indices(XMMRegister dst, Register scratch, int vlen_in_bytes); + + // Reductions for vectors of bytes, shorts, ints, longs, floats, and doubles. + + // dst = src1 reduce(op, src2) using vtmp as temps void reduceI(int opcode, int vlen, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); #ifdef _LP64 void reduceL(int opcode, int vlen, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); @@ -99,38 +145,71 @@ void reduce_fp(int opcode, int vlen, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2 = xnoreg); + void reduceB(int opcode, int vlen, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); + void mulreduceB(int opcode, int vlen, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); + void reduceS(int opcode, int vlen, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); + void reduceFloatMinMax(int opcode, int vlen, bool is_dst_valid, + XMMRegister dst, XMMRegister src, + XMMRegister tmp, XMMRegister atmp, XMMRegister btmp, XMMRegister xmm_0, XMMRegister xmm_1 = xnoreg); + void reduceDoubleMinMax(int opcode, int vlen, bool is_dst_valid, + XMMRegister dst, XMMRegister src, + XMMRegister tmp, XMMRegister atmp, XMMRegister btmp, XMMRegister xmm_0, XMMRegister xmm_1 = xnoreg); private: void reduceF(int opcode, int vlen, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2); void reduceD(int opcode, int vlen, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2); + // Int Reduction void reduce2I (int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); void reduce4I (int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); void reduce8I (int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); void reduce16I(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); + // Byte Reduction + void reduce8B (int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); + void reduce16B(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); + void reduce32B(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); + void reduce64B(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); + void mulreduce8B (int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); + void mulreduce16B(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); + void mulreduce32B(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); + void mulreduce64B(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); + + // Short Reduction + void reduce4S (int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); + void reduce8S (int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); + void reduce16S(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); + void reduce32S(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); + + // Long Reduction #ifdef _LP64 void reduce2L(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); void reduce4L(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); void reduce8L(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); #endif // _LP64 + // Float Reduction void reduce2F (int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp); void reduce4F (int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp); void reduce8F (int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2); void reduce16F(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2); + // Double Reduction void reduce2D(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp); void reduce4D(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2); void reduce8D(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2); - void reduce_operation_128(int opcode, XMMRegister dst, XMMRegister src); - void reduce_operation_256(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2); + // Base reduction instruction + void reduce_operation_128(BasicType typ, int opcode, XMMRegister dst, XMMRegister src); + void reduce_operation_256(BasicType typ, int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2); public: void string_indexof_char(Register str1, Register cnt1, Register ch, Register result, XMMRegister vec1, XMMRegister vec2, XMMRegister vec3, Register tmp); + void stringL_indexof_char(Register str1, Register cnt1, Register ch, Register result, + XMMRegister vec1, XMMRegister vec2, XMMRegister vec3, Register tmp); + // IndexOf strings. // Small strings are loaded through stack if they cross page boundary. void string_indexof(Register str1, Register str2, diff --git a/src/hotspot/cpu/x86/c2_globals_x86.hpp b/src/hotspot/cpu/x86/c2_globals_x86.hpp index 6513be7b53e..31e77b52568 100644 --- a/src/hotspot/cpu/x86/c2_globals_x86.hpp +++ b/src/hotspot/cpu/x86/c2_globals_x86.hpp @@ -31,8 +31,6 @@ // Sets the default values for platform dependent flags used by the server compiler. // (see c2_globals.hpp). Alpha-sorted. define_pd_global(bool, BackgroundCompilation, true); -define_pd_global(bool, UseTLAB, true); -define_pd_global(bool, ResizeTLAB, true); define_pd_global(bool, CICompileOSR, true); define_pd_global(bool, InlineIntrinsics, true); define_pd_global(bool, PreferInterpreterNativeStubs, false); diff --git a/src/hotspot/cpu/x86/c2_safepointPollStubTable_x86.cpp b/src/hotspot/cpu/x86/c2_safepointPollStubTable_x86.cpp new file mode 100644 index 00000000000..c3d4850a5db --- /dev/null +++ b/src/hotspot/cpu/x86/c2_safepointPollStubTable_x86.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "asm/macroAssembler.hpp" +#include "opto/compile.hpp" +#include "opto/node.hpp" +#include "opto/output.hpp" +#include "runtime/sharedRuntime.hpp" + +#define __ masm. +void C2SafepointPollStubTable::emit_stub_impl(MacroAssembler& masm, C2SafepointPollStub* entry) const { + assert(SharedRuntime::polling_page_return_handler_blob() != NULL, + "polling page return stub not created yet"); + address stub = SharedRuntime::polling_page_return_handler_blob()->entry_point(); + + RuntimeAddress callback_addr(stub); + + __ bind(entry->_stub_label); + InternalAddress safepoint_pc(masm.pc() - masm.offset() + entry->_safepoint_offset); +#ifdef _LP64 + __ lea(rscratch1, safepoint_pc); + __ movptr(Address(r15_thread, JavaThread::saved_exception_pc_offset()), rscratch1); +#else + const Register tmp1 = rcx; + const Register tmp2 = rdx; + __ push(tmp1); + __ push(tmp2); + + __ lea(tmp1, safepoint_pc); + __ get_thread(tmp2); + __ movptr(Address(tmp2, JavaThread::saved_exception_pc_offset()), tmp1); + + __ pop(tmp2); + __ pop(tmp1); +#endif + __ jump(callback_addr); +} +#undef __ diff --git a/src/hotspot/cpu/x86/frame_x86.cpp b/src/hotspot/cpu/x86/frame_x86.cpp index c433eabf993..1e9bf12cd2b 100644 --- a/src/hotspot/cpu/x86/frame_x86.cpp +++ b/src/hotspot/cpu/x86/frame_x86.cpp @@ -36,6 +36,7 @@ #include "runtime/monitorChunk.hpp" #include "runtime/os.inline.hpp" #include "runtime/signature.hpp" +#include "runtime/stackWatermarkSet.hpp" #include "runtime/stubCodeGenerator.hpp" #include "runtime/stubRoutines.hpp" #include "vmreg_x86.inline.hpp" @@ -469,8 +470,8 @@ frame frame::sender_for_compiled_frame(RegisterMap* map) const { //------------------------------------------------------------------------------ -// frame::sender -frame frame::sender(RegisterMap* map) const { +// frame::sender_raw +frame frame::sender_raw(RegisterMap* map) const { // Default is we done have to follow them. The sender_for_xxx will // update it accordingly map->set_include_argument_oops(false); @@ -487,6 +488,16 @@ frame frame::sender(RegisterMap* map) const { return frame(sender_sp(), link(), sender_pc()); } +frame frame::sender(RegisterMap* map) const { + frame result = sender_raw(map); + + if (map->process_frames()) { + StackWatermarkSet::on_iteration(map->thread(), result); + } + + return result; +} + bool frame::is_interpreted_frame_valid(JavaThread* thread) const { assert(is_interpreted_frame(), "Not an interpreted frame"); // These are reasonable sanity checks diff --git a/src/hotspot/cpu/x86/frame_x86.hpp b/src/hotspot/cpu/x86/frame_x86.hpp index ffe5e92275d..26dbb2aa956 100644 --- a/src/hotspot/cpu/x86/frame_x86.hpp +++ b/src/hotspot/cpu/x86/frame_x86.hpp @@ -156,4 +156,7 @@ static jint interpreter_frame_expression_stack_direction() { return -1; } + // returns the sending frame, without applying any barriers + frame sender_raw(RegisterMap* map) const; + #endif // CPU_X86_FRAME_X86_HPP diff --git a/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp index 58dcd9ed5fb..2aac0608207 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp @@ -111,7 +111,8 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt __ xchg(access.resolved_addr(), result, result, LIR_OprFact::illegalOpr); if (access.is_oop()) { - result = load_reference_barrier(access.gen(), result, LIR_OprFact::addressConst(0), false); + ShenandoahBarrierSet::AccessKind kind = ShenandoahBarrierSet::access_kind(access.decorators(), access.type()); + result = load_reference_barrier(access.gen(), result, LIR_OprFact::addressConst(0), kind); LIR_Opr tmp = gen->new_register(type); __ move(result, tmp); result = tmp; diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index 5ce3cc95e93..40f16ef2731 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -32,7 +32,6 @@ #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "interpreter/interpreter.hpp" -#include "interpreter/interp_masm.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/thread.hpp" #include "utilities/macros.hpp" @@ -44,8 +43,6 @@ #define __ masm-> -address ShenandoahBarrierSetAssembler::_shenandoah_lrb = NULL; - static void save_xmm_registers(MacroAssembler* masm) { __ subptr(rsp, 64); __ movdbl(Address(rsp, 0), xmm0); @@ -271,11 +268,14 @@ void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm, __ bind(done); } -void ShenandoahBarrierSetAssembler::load_reference_barrier_not_null(MacroAssembler* masm, Register dst, Address src) { +void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, Register dst, Address src, ShenandoahBarrierSet::AccessKind kind) { assert(ShenandoahLoadRefBarrier, "Should be enabled"); - Label done; + Label heap_stable, not_cset; + __ block_comment("load_reference_barrier { "); + + // Check if GC is active #ifdef _LP64 Register thread = r15_thread; #else @@ -289,138 +289,130 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier_not_null(MacroAssembl Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); __ testb(gc_state, ShenandoahHeap::HAS_FORWARDED); - __ jccb(Assembler::zero, done); - - // Use rsi for src address - const Register src_addr = rsi; - // Setup address parameter first, if it does not clobber oop in dst - bool need_addr_setup = (src_addr != dst); - - if (need_addr_setup) { - __ push(src_addr); - __ lea(src_addr, src); - - if (dst != rax) { - // Move obj into rax and save rax - __ push(rax); - __ movptr(rax, dst); - } - } else { - // dst == rsi - __ push(rax); - __ movptr(rax, dst); - - // we can clobber it, since it is outgoing register - __ lea(src_addr, src); - } - - save_xmm_registers(masm); - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahBarrierSetAssembler::shenandoah_lrb()))); - restore_xmm_registers(masm); - - if (need_addr_setup) { - if (dst != rax) { - __ movptr(dst, rax); - __ pop(rax); + __ jcc(Assembler::zero, heap_stable); + + Register tmp1 = noreg, tmp2 = noreg; + if (kind == ShenandoahBarrierSet::AccessKind::NORMAL) { + // Test for object in cset + // Allocate temporary registers + for (int i = 0; i < 8; i++) { + Register r = as_Register(i); + if (r != rsp && r != rbp && r != dst && r != src.base() && r != src.index()) { + if (tmp1 == noreg) { + tmp1 = r; + } else { + tmp2 = r; + break; + } + } } - __ pop(src_addr); - } else { - __ movptr(dst, rax); - __ pop(rax); + assert(tmp1 != noreg, "tmp1 allocated"); + assert(tmp2 != noreg, "tmp2 allocated"); + assert_different_registers(tmp1, tmp2, src.base(), src.index()); + assert_different_registers(tmp1, tmp2, dst); + + __ push(tmp1); + __ push(tmp2); + + // Optimized cset-test + __ movptr(tmp1, dst); + __ shrptr(tmp1, ShenandoahHeapRegion::region_size_bytes_shift_jint()); + __ movptr(tmp2, (intptr_t) ShenandoahHeap::in_cset_fast_test_addr()); + __ movbool(tmp1, Address(tmp1, tmp2, Address::times_1)); + __ testbool(tmp1); + __ jcc(Assembler::zero, not_cset); + } + + uint num_saved_regs = 4 + (dst != rax ? 1 : 0) LP64_ONLY(+4); + __ subptr(rsp, num_saved_regs * wordSize); + uint slot = num_saved_regs; + if (dst != rax) { + __ movptr(Address(rsp, (--slot) * wordSize), rax); } - - __ bind(done); - -#ifndef _LP64 - __ pop(thread); + __ movptr(Address(rsp, (--slot) * wordSize), rcx); + __ movptr(Address(rsp, (--slot) * wordSize), rdx); + __ movptr(Address(rsp, (--slot) * wordSize), rdi); + __ movptr(Address(rsp, (--slot) * wordSize), rsi); +#ifdef _LP64 + __ movptr(Address(rsp, (--slot) * wordSize), r8); + __ movptr(Address(rsp, (--slot) * wordSize), r9); + __ movptr(Address(rsp, (--slot) * wordSize), r10); + __ movptr(Address(rsp, (--slot) * wordSize), r11); + // r12-r15 are callee saved in all calling conventions #endif -} - -void ShenandoahBarrierSetAssembler::load_reference_barrier_native(MacroAssembler* masm, Register dst, Address src) { - if (!ShenandoahLoadRefBarrier) { - return; - } - - Label done; - Label not_null; - Label slow_path; - __ block_comment("load_reference_barrier_native { "); - - // null check - __ testptr(dst, dst); - __ jcc(Assembler::notZero, not_null); - __ jmp(done); - __ bind(not_null); - + assert(slot == 0, "must use all slots"); + // Shuffle registers such that dst is in c_rarg0 and addr in c_rarg1. #ifdef _LP64 - Register thread = r15_thread; + Register arg0 = c_rarg0, arg1 = c_rarg1; #else - Register thread = rcx; - if (thread == dst) { - thread = rbx; - } - __ push(thread); - __ get_thread(thread); -#endif - assert_different_registers(dst, thread); - - Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); - __ testb(gc_state, ShenandoahHeap::EVACUATION); -#ifndef _LP64 - __ pop(thread); + Register arg0 = rdi, arg1 = rsi; #endif - __ jccb(Assembler::notZero, slow_path); - __ jmp(done); - __ bind(slow_path); - - if (dst != rax) { - __ push(rax); + if (dst == arg1) { + __ lea(arg0, src); + __ xchgptr(arg1, arg0); + } else { + __ lea(arg1, src); + __ movptr(arg0, dst); } - __ push(rcx); - __ push(rdx); - __ push(rdi); - __ push(rsi); -#ifdef _LP64 - __ push(r8); - __ push(r9); - __ push(r10); - __ push(r11); - __ push(r12); - __ push(r13); - __ push(r14); - __ push(r15); -#endif - - assert_different_registers(dst, rsi); - __ lea(rsi, src); save_xmm_registers(masm); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_native), dst, rsi); + switch (kind) { + case ShenandoahBarrierSet::AccessKind::NORMAL: + if (UseCompressedOops) { + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_narrow), arg0, arg1); + } else { + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier), arg0, arg1); + } + break; + case ShenandoahBarrierSet::AccessKind::WEAK: + if (UseCompressedOops) { + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow), arg0, arg1); + } else { + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak), arg0, arg1); + } + break; + case ShenandoahBarrierSet::AccessKind::NATIVE: + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak), arg0, arg1); + break; + default: + ShouldNotReachHere(); + } restore_xmm_registers(masm); #ifdef _LP64 - __ pop(r15); - __ pop(r14); - __ pop(r13); - __ pop(r12); - __ pop(r11); - __ pop(r10); - __ pop(r9); - __ pop(r8); + __ movptr(r11, Address(rsp, (slot++) * wordSize)); + __ movptr(r10, Address(rsp, (slot++) * wordSize)); + __ movptr(r9, Address(rsp, (slot++) * wordSize)); + __ movptr(r8, Address(rsp, (slot++) * wordSize)); #endif - __ pop(rsi); - __ pop(rdi); - __ pop(rdx); - __ pop(rcx); + __ movptr(rsi, Address(rsp, (slot++) * wordSize)); + __ movptr(rdi, Address(rsp, (slot++) * wordSize)); + __ movptr(rdx, Address(rsp, (slot++) * wordSize)); + __ movptr(rcx, Address(rsp, (slot++) * wordSize)); if (dst != rax) { __ movptr(dst, rax); - __ pop(rax); + __ movptr(rax, Address(rsp, (slot++) * wordSize)); } - __ bind(done); - __ block_comment("load_reference_barrier_native { "); + assert(slot == num_saved_regs, "must use all slots"); + __ addptr(rsp, num_saved_regs * wordSize); + + __ bind(not_cset); + + if (kind == ShenandoahBarrierSet::AccessKind::NORMAL) { + __ pop(tmp2); + __ pop(tmp1); + } + + __ bind(heap_stable); + + __ block_comment("} load_reference_barrier"); + +#ifndef _LP64 + __ pop(thread); +#endif } void ShenandoahBarrierSetAssembler::storeval_barrier(MacroAssembler* masm, Register dst, Register tmp) { @@ -464,16 +456,6 @@ void ShenandoahBarrierSetAssembler::storeval_barrier_impl(MacroAssembler* masm, } } -void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, Register dst, Address src) { - if (ShenandoahLoadRefBarrier) { - Label done; - __ testptr(dst, dst); - __ jcc(Assembler::zero, done); - load_reference_barrier_not_null(masm, dst, src); - __ bind(done); - } -} - // // Arguments: // @@ -504,7 +486,7 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet d // Preserve src location for LRB if (dst == src.base() || dst == src.index()) { - // Use tmp1 for dst if possible, as it is not used in BarrierAssembler::load_at() + // Use tmp1 for dst if possible, as it is not used in BarrierAssembler::load_at() if (tmp1->is_valid() && tmp1 != src.base() && tmp1 != src.index()) { dst = tmp1; use_tmp1_for_dst = true; @@ -517,11 +499,8 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet d BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1, tmp_thread); - if (ShenandoahBarrierSet::use_load_reference_barrier_native(decorators, type)) { - load_reference_barrier_native(masm, dst, src); - } else { - load_reference_barrier(masm, dst, src); - } + ShenandoahBarrierSet::AccessKind kind = ShenandoahBarrierSet::access_kind(decorators, type); + load_reference_barrier(masm, dst, src, kind); // Move loaded oop to final destination if (dst != result_dst) { @@ -638,7 +617,8 @@ void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler* masm, bool exchange, Register tmp1, Register tmp2) { assert(ShenandoahCASBarrier, "Should only be used when CAS barrier is enabled"); assert(oldval == rax, "must be in rax for implicit use in cmpxchg"); - assert_different_registers(oldval, newval, tmp1, tmp2); + assert_different_registers(oldval, tmp1, tmp2); + assert_different_registers(newval, tmp1, tmp2); Label L_success, L_failure; @@ -870,10 +850,18 @@ void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assemble __ bind(slow_path); ce->store_parameter(res, 0); ce->store_parameter(addr, 1); - if (stub->is_native()) { - __ call(RuntimeAddress(bs->load_reference_barrier_native_rt_code_blob()->code_begin())); - } else { - __ call(RuntimeAddress(bs->load_reference_barrier_rt_code_blob()->code_begin())); + switch (stub->kind()) { + case ShenandoahBarrierSet::AccessKind::NORMAL: + __ call(RuntimeAddress(bs->load_reference_barrier_normal_rt_code_blob()->code_begin())); + break; + case ShenandoahBarrierSet::AccessKind::WEAK: + __ call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin())); + break; + case ShenandoahBarrierSet::AccessKind::NATIVE: + __ call(RuntimeAddress(bs->load_reference_barrier_native_rt_code_blob()->code_begin())); + break; + default: + ShouldNotReachHere(); } __ jmp(*stub->continuation()); } @@ -938,7 +926,7 @@ void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAss __ epilogue(); } -void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, bool is_native) { +void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, ShenandoahBarrierSet::AccessKind kind) { __ prologue("shenandoah_load_reference_barrier", false); // arg0 : object to be resolved @@ -947,20 +935,40 @@ void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_s #ifdef _LP64 __ load_parameter(0, c_rarg0); __ load_parameter(1, c_rarg1); - if (is_native) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_native), c_rarg0, c_rarg1); - } else if (UseCompressedOops) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_narrow), c_rarg0, c_rarg1); - } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier), c_rarg0, c_rarg1); + switch (kind) { + case ShenandoahBarrierSet::AccessKind::NORMAL: + if (UseCompressedOops) { + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_narrow), c_rarg0, c_rarg1); + } else { + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier), c_rarg0, c_rarg1); + } + break; + case ShenandoahBarrierSet::AccessKind::WEAK: + if (UseCompressedOops) { + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow), c_rarg0, c_rarg1); + } else { + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak), c_rarg0, c_rarg1); + } + break; + case ShenandoahBarrierSet::AccessKind::NATIVE: + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak), c_rarg0, c_rarg1); + break; + default: + ShouldNotReachHere(); } #else __ load_parameter(0, rax); __ load_parameter(1, rbx); - if (is_native) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_native), rax, rbx); - } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier), rax, rbx); + switch (kind) { + case ShenandoahBarrierSet::AccessKind::NORMAL: + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier), rax, rbx); + break; + case ShenandoahBarrierSet::AccessKind::WEAK: + case ShenandoahBarrierSet::AccessKind::NATIVE: + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak), rax, rbx); + break; + default: + ShouldNotReachHere(); } #endif @@ -972,104 +980,3 @@ void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_s #undef __ #endif // COMPILER1 - -address ShenandoahBarrierSetAssembler::shenandoah_lrb() { - assert(_shenandoah_lrb != NULL, "need load reference barrier stub"); - return _shenandoah_lrb; -} - -#define __ cgen->assembler()-> - -/* - * Incoming parameters: - * rax: oop - * rsi: load address - */ -address ShenandoahBarrierSetAssembler::generate_shenandoah_lrb(StubCodeGenerator* cgen) { - __ align(CodeEntryAlignment); - StubCodeMark mark(cgen, "StubRoutines", "shenandoah_lrb"); - address start = __ pc(); - - Label slow_path; - - // We use RDI, which also serves as argument register for slow call. - // RAX always holds the src object ptr, except after the slow call, - // then it holds the result. R8/RBX is used as temporary register. - - Register tmp1 = rdi; - Register tmp2 = LP64_ONLY(r8) NOT_LP64(rbx); - - __ push(tmp1); - __ push(tmp2); - - // Check for object being in the collection set. - __ mov(tmp1, rax); - __ shrptr(tmp1, ShenandoahHeapRegion::region_size_bytes_shift_jint()); - __ movptr(tmp2, (intptr_t) ShenandoahHeap::in_cset_fast_test_addr()); - __ movbool(tmp2, Address(tmp2, tmp1, Address::times_1)); - __ testbool(tmp2); - __ jccb(Assembler::notZero, slow_path); - __ pop(tmp2); - __ pop(tmp1); - __ ret(0); - - __ bind(slow_path); - - __ push(rcx); - __ push(rdx); - __ push(rdi); -#ifdef _LP64 - __ push(r8); - __ push(r9); - __ push(r10); - __ push(r11); - __ push(r12); - __ push(r13); - __ push(r14); - __ push(r15); -#endif - __ push(rbp); - __ movptr(rbp, rsp); - __ andptr(rsp, -StackAlignmentInBytes); - __ push_FPU_state(); - if (UseCompressedOops) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_narrow), rax, rsi); - } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier), rax, rsi); - } - __ pop_FPU_state(); - __ movptr(rsp, rbp); - __ pop(rbp); -#ifdef _LP64 - __ pop(r15); - __ pop(r14); - __ pop(r13); - __ pop(r12); - __ pop(r11); - __ pop(r10); - __ pop(r9); - __ pop(r8); -#endif - __ pop(rdi); - __ pop(rdx); - __ pop(rcx); - - __ pop(tmp2); - __ pop(tmp1); - __ ret(0); - - return start; -} - -#undef __ - -void ShenandoahBarrierSetAssembler::barrier_stubs_init() { - if (ShenandoahLoadRefBarrier) { - int stub_code_size = 4096; - ResourceMark rm; - BufferBlob* bb = BufferBlob::create("shenandoah_barrier_stubs", stub_code_size); - CodeBuffer buf(bb); - StubCodeGenerator cgen(&buf); - _shenandoah_lrb = generate_shenandoah_lrb(&cgen); - } -} diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp index 60aa3b4600d..108b5670206 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp @@ -27,6 +27,8 @@ #include "asm/macroAssembler.hpp" #include "gc/shared/barrierSetAssembler.hpp" +#include "gc/shenandoah/shenandoahBarrierSet.hpp" + #ifdef COMPILER1 class LIR_Assembler; class ShenandoahPreBarrierStub; @@ -38,8 +40,6 @@ class StubCodeGenerator; class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { private: - static address _shenandoah_lrb; - void satb_write_barrier_pre(MacroAssembler* masm, Register obj, Register pre_val, @@ -56,25 +56,18 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { bool tosca_live, bool expand_call); - void load_reference_barrier_not_null(MacroAssembler* masm, Register dst, Address src); - void storeval_barrier_impl(MacroAssembler* masm, Register dst, Register tmp); - address generate_shenandoah_lrb(StubCodeGenerator* cgen); - public: - static address shenandoah_lrb(); - void storeval_barrier(MacroAssembler* masm, Register dst, Register tmp); #ifdef COMPILER1 void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); - void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, bool is_native); + void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, ShenandoahBarrierSet::AccessKind kind); #endif - void load_reference_barrier(MacroAssembler* masm, Register dst, Address src); - void load_reference_barrier_native(MacroAssembler* masm, Register dst, Address src); + void load_reference_barrier(MacroAssembler* masm, Register dst, Address src, ShenandoahBarrierSet::AccessKind kind); void cmpxchg_oop(MacroAssembler* masm, Register res, Address addr, Register oldval, Register newval, @@ -87,8 +80,6 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Address dst, Register val, Register tmp1, Register tmp2); virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath); - virtual void barrier_stubs_init(); - }; #endif // CPU_X86_GC_SHENANDOAH_SHENANDOAHBARRIERSETASSEMBLER_X86_HPP diff --git a/src/hotspot/cpu/x86/gc/z/zGlobals_x86.hpp b/src/hotspot/cpu/x86/gc/z/zGlobals_x86.hpp index 83c8caa6a58..db558d8cb2a 100644 --- a/src/hotspot/cpu/x86/gc/z/zGlobals_x86.hpp +++ b/src/hotspot/cpu/x86/gc/z/zGlobals_x86.hpp @@ -24,10 +24,9 @@ #ifndef CPU_X86_GC_Z_ZGLOBALS_X86_HPP #define CPU_X86_GC_Z_ZGLOBALS_X86_HPP -const size_t ZPlatformGranuleSizeShift = 21; // 2MB -const size_t ZPlatformHeapViews = 3; -const size_t ZPlatformNMethodDisarmedOffset = 4; -const size_t ZPlatformCacheLineSize = 64; +const size_t ZPlatformGranuleSizeShift = 21; // 2MB +const size_t ZPlatformHeapViews = 3; +const size_t ZPlatformCacheLineSize = 64; size_t ZPlatformAddressOffsetBits(); size_t ZPlatformAddressMetadataShift(); diff --git a/src/hotspot/cpu/x86/interp_masm_x86.cpp b/src/hotspot/cpu/x86/interp_masm_x86.cpp index 738771e800a..140dcfc2f06 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.cpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp @@ -605,6 +605,10 @@ void InterpreterMacroAssembler::push_i(Register r) { push(r); } +void InterpreterMacroAssembler::push_i_or_ptr(Register r) { + push(r); +} + void InterpreterMacroAssembler::push_f(XMMRegister r) { subptr(rsp, wordSize); movflt(Address(rsp, 0), r); @@ -853,7 +857,7 @@ void InterpreterMacroAssembler::dispatch_base(TosState state, Label no_safepoint, dispatch; if (table != safepoint_table && generate_poll) { NOT_PRODUCT(block_comment("Thread-local Safepoint poll")); - testb(Address(r15_thread, Thread::polling_page_offset()), SafepointMechanism::poll_bit()); + testb(Address(r15_thread, Thread::polling_word_offset()), SafepointMechanism::poll_bit()); jccb(Assembler::zero, no_safepoint); lea(rscratch1, ExternalAddress((address)safepoint_table)); @@ -872,7 +876,7 @@ void InterpreterMacroAssembler::dispatch_base(TosState state, Label no_safepoint; const Register thread = rcx; get_thread(thread); - testb(Address(thread, Thread::polling_page_offset()), SafepointMechanism::poll_bit()); + testb(Address(thread, Thread::polling_word_offset()), SafepointMechanism::poll_bit()); jccb(Assembler::zero, no_safepoint); ArrayAddress dispatch_addr(ExternalAddress((address)safepoint_table), index); @@ -961,6 +965,7 @@ void InterpreterMacroAssembler::narrow(Register result) { // remove activation // +// Apply stack watermark barrier. // Unlock the receiver if this is a synchronized method. // Unlock any Java monitors from syncronized blocks. // Remove the activation from the stack. @@ -987,7 +992,23 @@ void InterpreterMacroAssembler::remove_activation( const Register rmon = LP64_ONLY(c_rarg1) NOT_LP64(rcx); // monitor pointers need different register // because rdx may have the result in it - NOT_LP64(get_thread(rcx);) + NOT_LP64(get_thread(rthread);) + + // The below poll is for the stack watermark barrier. It allows fixing up frames lazily, + // that would normally not be safe to use. Such bad returns into unsafe territory of + // the stack, will call InterpreterRuntime::at_unwind. + Label slow_path; + Label fast_path; + safepoint_poll(slow_path, rthread, true /* at_return */, false /* in_nmethod */); + jmp(fast_path); + bind(slow_path); + push(state); + set_last_Java_frame(rthread, noreg, rbp, (address)pc()); + super_call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::at_unwind), rthread); + NOT_LP64(get_thread(rthread);) // call_VM clobbered it, restore + reset_last_Java_frame(rthread, true); + pop(state); + bind(fast_path); // get the value of _do_not_unlock_if_synchronized into rdx const Address do_not_unlock_if_synchronized(rthread, @@ -1938,7 +1959,7 @@ void InterpreterMacroAssembler::profile_switch_case(Register index, void InterpreterMacroAssembler::_interp_verify_oop(Register reg, TosState state, const char* file, int line) { if (state == atos) { - MacroAssembler::_verify_oop(reg, "broken oop", file, line); + MacroAssembler::_verify_oop_checked(reg, "broken oop", file, line); } } diff --git a/src/hotspot/cpu/x86/interp_masm_x86.hpp b/src/hotspot/cpu/x86/interp_masm_x86.hpp index 3e2e33278a1..288b1bd1dfe 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.hpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.hpp @@ -139,9 +139,18 @@ class InterpreterMacroAssembler: public MacroAssembler { // Expression stack void pop_ptr(Register r = rax); void pop_i(Register r = rax); + + // On x86, pushing a ptr or an int is semantically identical, but we + // maintain a distinction for clarity and for making it easier to change + // semantics in the future void push_ptr(Register r = rax); void push_i(Register r = rax); + // push_i_or_ptr is provided for when explicitly allowing either a ptr or + // an int might have some advantage, while still documenting the fact that a + // ptr might be pushed to the stack. + void push_i_or_ptr(Register r = rax); + void push_f(XMMRegister r); void pop_f(XMMRegister r); void pop_d(XMMRegister r); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 8b19ddab7b8..d7fabfbbedb 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -112,6 +112,7 @@ void MacroAssembler::cmpklass(Address src1, Metadata* obj) { cmp_literal32(src1, (int32_t)obj, metadata_Relocation::spec_for_immediate()); } + void MacroAssembler::cmpklass(Register src1, Metadata* obj) { cmp_literal32(src1, (int32_t)obj, metadata_Relocation::spec_for_immediate()); } @@ -369,11 +370,6 @@ void MacroAssembler::pushptr(AddressLiteral src) { } } -void MacroAssembler::set_word_if_not_zero(Register dst) { - xorl(dst, dst); - set_byte_if_not_zero(dst); -} - static void pass_arg0(MacroAssembler* masm, Register arg) { masm->push(arg); } @@ -713,8 +709,12 @@ void MacroAssembler::movptr(Register dst, ArrayAddress src) { // src should NEVER be a real pointer. Use AddressLiteral for true pointers void MacroAssembler::movptr(Address dst, intptr_t src) { - mov64(rscratch1, src); - movq(dst, rscratch1); + if (is_simm32(src)) { + movptr(dst, checked_cast(src)); + } else { + mov64(rscratch1, src); + movq(dst, rscratch1); + } } // These are mostly for initializing NULL @@ -2495,6 +2495,7 @@ void MacroAssembler::movdqu(XMMRegister dst, Address src) { void MacroAssembler::movdqu(XMMRegister dst, XMMRegister src) { assert(((dst->encoding() < 16 && src->encoding() < 16) || VM_Version::supports_avx512vl()),"XMM register should be 0-15"); + if (dst->encoding() == src->encoding()) return; Assembler::movdqu(dst, src); } @@ -2519,6 +2520,7 @@ void MacroAssembler::vmovdqu(XMMRegister dst, Address src) { void MacroAssembler::vmovdqu(XMMRegister dst, XMMRegister src) { assert(((dst->encoding() < 16 && src->encoding() < 16) || VM_Version::supports_avx512vl()),"XMM register should be 0-15"); + if (dst->encoding() == src->encoding()) return; Assembler::vmovdqu(dst, src); } @@ -2532,6 +2534,64 @@ void MacroAssembler::vmovdqu(XMMRegister dst, AddressLiteral src, Register scrat } } + +void MacroAssembler::kmovwl(KRegister dst, AddressLiteral src, Register scratch_reg) { + if (reachable(src)) { + kmovwl(dst, as_Address(src)); + } else { + lea(scratch_reg, src); + kmovwl(dst, Address(scratch_reg, 0)); + } +} + +void MacroAssembler::evmovdqub(XMMRegister dst, KRegister mask, AddressLiteral src, bool merge, + int vector_len, Register scratch_reg) { + if (reachable(src)) { + if (mask == k0) { + Assembler::evmovdqub(dst, as_Address(src), merge, vector_len); + } else { + Assembler::evmovdqub(dst, mask, as_Address(src), merge, vector_len); + } + } else { + lea(scratch_reg, src); + if (mask == k0) { + Assembler::evmovdqub(dst, Address(scratch_reg, 0), merge, vector_len); + } else { + Assembler::evmovdqub(dst, mask, Address(scratch_reg, 0), merge, vector_len); + } + } +} + +void MacroAssembler::evmovdquw(XMMRegister dst, KRegister mask, AddressLiteral src, bool merge, + int vector_len, Register scratch_reg) { + if (reachable(src)) { + Assembler::evmovdquw(dst, mask, as_Address(src), merge, vector_len); + } else { + lea(scratch_reg, src); + Assembler::evmovdquw(dst, mask, Address(scratch_reg, 0), merge, vector_len); + } +} + +void MacroAssembler::evmovdqul(XMMRegister dst, KRegister mask, AddressLiteral src, bool merge, + int vector_len, Register scratch_reg) { + if (reachable(src)) { + Assembler::evmovdqul(dst, mask, as_Address(src), merge, vector_len); + } else { + lea(scratch_reg, src); + Assembler::evmovdqul(dst, mask, Address(scratch_reg, 0), merge, vector_len); + } +} + +void MacroAssembler::evmovdquq(XMMRegister dst, KRegister mask, AddressLiteral src, bool merge, + int vector_len, Register scratch_reg) { + if (reachable(src)) { + Assembler::evmovdquq(dst, mask, as_Address(src), merge, vector_len); + } else { + lea(scratch_reg, src); + Assembler::evmovdquq(dst, mask, Address(scratch_reg, 0), merge, vector_len); + } +} + void MacroAssembler::evmovdquq(XMMRegister dst, AddressLiteral src, int vector_len, Register rscratch) { if (reachable(src)) { Assembler::evmovdquq(dst, as_Address(src), vector_len); @@ -2699,16 +2759,15 @@ void MacroAssembler::save_rax(Register tmp) { else if (tmp != rax) mov(tmp, rax); } -void MacroAssembler::safepoint_poll(Label& slow_path, Register thread_reg, Register temp_reg) { -#ifdef _LP64 - assert(thread_reg == r15_thread, "should be"); -#else - if (thread_reg == noreg) { - thread_reg = temp_reg; - get_thread(thread_reg); +void MacroAssembler::safepoint_poll(Label& slow_path, Register thread_reg, bool at_return, bool in_nmethod) { + if (at_return) { + // Note that when in_nmethod is set, the stack pointer is incremented before the poll. Therefore, + // we may safely use rsp instead to perform the stack watermark check. + cmpptr(in_nmethod ? rsp : rbp, Address(thread_reg, Thread::polling_word_offset())); + jcc(Assembler::above, slow_path); + return; } -#endif - testb(Address(thread_reg, Thread::polling_page_offset()), SafepointMechanism::poll_bit()); + testb(Address(thread_reg, Thread::polling_word_offset()), SafepointMechanism::poll_bit()); jcc(Assembler::notZero, slow_path); // handshake bit set implies poll } @@ -3018,6 +3077,98 @@ void MacroAssembler::vpcmpeqw(XMMRegister dst, XMMRegister nds, XMMRegister src, Assembler::vpcmpeqw(dst, nds, src, vector_len); } +void MacroAssembler::evpcmpeqd(KRegister kdst, KRegister mask, XMMRegister nds, + AddressLiteral src, int vector_len, Register scratch_reg) { + if (reachable(src)) { + Assembler::evpcmpeqd(kdst, mask, nds, as_Address(src), vector_len); + } else { + lea(scratch_reg, src); + Assembler::evpcmpeqd(kdst, mask, nds, Address(scratch_reg, 0), vector_len); + } +} + +void MacroAssembler::evpcmpd(KRegister kdst, KRegister mask, XMMRegister nds, AddressLiteral src, + int comparison, int vector_len, Register scratch_reg) { + if (reachable(src)) { + Assembler::evpcmpd(kdst, mask, nds, as_Address(src), comparison, vector_len); + } else { + lea(scratch_reg, src); + Assembler::evpcmpd(kdst, mask, nds, Address(scratch_reg, 0), comparison, vector_len); + } +} + +void MacroAssembler::evpcmpq(KRegister kdst, KRegister mask, XMMRegister nds, AddressLiteral src, + int comparison, int vector_len, Register scratch_reg) { + if (reachable(src)) { + Assembler::evpcmpq(kdst, mask, nds, as_Address(src), comparison, vector_len); + } else { + lea(scratch_reg, src); + Assembler::evpcmpq(kdst, mask, nds, Address(scratch_reg, 0), comparison, vector_len); + } +} + +void MacroAssembler::evpcmpb(KRegister kdst, KRegister mask, XMMRegister nds, AddressLiteral src, + int comparison, int vector_len, Register scratch_reg) { + if (reachable(src)) { + Assembler::evpcmpb(kdst, mask, nds, as_Address(src), comparison, vector_len); + } else { + lea(scratch_reg, src); + Assembler::evpcmpb(kdst, mask, nds, Address(scratch_reg, 0), comparison, vector_len); + } +} + +void MacroAssembler::evpcmpw(KRegister kdst, KRegister mask, XMMRegister nds, AddressLiteral src, + int comparison, int vector_len, Register scratch_reg) { + if (reachable(src)) { + Assembler::evpcmpw(kdst, mask, nds, as_Address(src), comparison, vector_len); + } else { + lea(scratch_reg, src); + Assembler::evpcmpw(kdst, mask, nds, Address(scratch_reg, 0), comparison, vector_len); + } +} + +void MacroAssembler::vpcmpCC(XMMRegister dst, XMMRegister nds, XMMRegister src, int cond_encoding, Width width, int vector_len) { + if (width == Assembler::Q) { + Assembler::vpcmpCCq(dst, nds, src, cond_encoding, vector_len); + } else { + Assembler::vpcmpCCbwd(dst, nds, src, cond_encoding, vector_len); + } +} + +void MacroAssembler::vpcmpCCW(XMMRegister dst, XMMRegister nds, XMMRegister src, ComparisonPredicate cond, Width width, int vector_len, Register scratch_reg) { + int eq_cond_enc = 0x29; + int gt_cond_enc = 0x37; + if (width != Assembler::Q) { + eq_cond_enc = 0x74 + width; + gt_cond_enc = 0x64 + width; + } + switch (cond) { + case eq: + vpcmpCC(dst, nds, src, eq_cond_enc, width, vector_len); + break; + case neq: + vpcmpCC(dst, nds, src, eq_cond_enc, width, vector_len); + vpxor(dst, dst, ExternalAddress(StubRoutines::x86::vector_all_bits_set()), vector_len, scratch_reg); + break; + case le: + vpcmpCC(dst, nds, src, gt_cond_enc, width, vector_len); + vpxor(dst, dst, ExternalAddress(StubRoutines::x86::vector_all_bits_set()), vector_len, scratch_reg); + break; + case nlt: + vpcmpCC(dst, src, nds, gt_cond_enc, width, vector_len); + vpxor(dst, dst, ExternalAddress(StubRoutines::x86::vector_all_bits_set()), vector_len, scratch_reg); + break; + case lt: + vpcmpCC(dst, src, nds, gt_cond_enc, width, vector_len); + break; + case nle: + vpcmpCC(dst, nds, src, gt_cond_enc, width, vector_len); + break; + default: + assert(false, "Should not reach here"); + } +} + void MacroAssembler::vpmovzxbw(XMMRegister dst, Address src, int vector_len) { assert(((dst->encoding() < 16) || VM_Version::supports_avx512vlbw()),"XMM register should be 0-15"); Assembler::vpmovzxbw(dst, src, vector_len); @@ -3142,6 +3293,16 @@ void MacroAssembler::vandps(XMMRegister dst, XMMRegister nds, AddressLiteral src } } +void MacroAssembler::evpord(XMMRegister dst, KRegister mask, XMMRegister nds, AddressLiteral src, + bool merge, int vector_len, Register scratch_reg) { + if (reachable(src)) { + Assembler::evpord(dst, mask, nds, as_Address(src), merge, vector_len); + } else { + lea(scratch_reg, src); + Assembler::evpord(dst, mask, nds, Address(scratch_reg, 0), merge, vector_len); + } +} + void MacroAssembler::vdivsd(XMMRegister dst, XMMRegister nds, AddressLiteral src) { if (reachable(src)) { vdivsd(dst, nds, as_Address(src)); @@ -3238,7 +3399,14 @@ void MacroAssembler::vpxor(XMMRegister dst, XMMRegister nds, AddressLiteral src, } } -//------------------------------------------------------------------------------------------- +void MacroAssembler::vpermd(XMMRegister dst, XMMRegister nds, AddressLiteral src, int vector_len, Register scratch_reg) { + if (reachable(src)) { + Assembler::vpermd(dst, nds, as_Address(src), vector_len); + } else { + lea(scratch_reg, src); + Assembler::vpermd(dst, nds, Address(scratch_reg, 0), vector_len); + } +} void MacroAssembler::clear_jweak_tag(Register possibly_jweak) { const int32_t inverted_jweak_mask = ~static_cast(JNIHandles::weak_tag_mask); @@ -3761,44 +3929,6 @@ void MacroAssembler::vallones(XMMRegister dst, int vector_len) { } } -RegisterOrConstant MacroAssembler::delayed_value_impl(intptr_t* delayed_value_addr, - Register tmp, - int offset) { - intptr_t value = *delayed_value_addr; - if (value != 0) - return RegisterOrConstant(value + offset); - - // load indirectly to solve generation ordering problem - movptr(tmp, ExternalAddress((address) delayed_value_addr)); - -#ifdef ASSERT - { Label L; - testptr(tmp, tmp); - if (WizardMode) { - const char* buf = NULL; - { - ResourceMark rm; - stringStream ss; - ss.print("DelayedValue=" INTPTR_FORMAT, delayed_value_addr[1]); - buf = code_string(ss.as_string()); - } - jcc(Assembler::notZero, L); - STOP(buf); - } else { - jccb(Assembler::notZero, L); - hlt(); - } - bind(L); - } -#endif - - if (offset != 0) - addptr(tmp, offset); - - return RegisterOrConstant(tmp); -} - - Address MacroAssembler::argument_address(RegisterOrConstant arg_slot, int extra_slot_offset) { // cf. TemplateTable::prepare_invoke(), if (load_receiver). @@ -3820,7 +3950,6 @@ Address MacroAssembler::argument_address(RegisterOrConstant arg_slot, return Address(rsp, scale_reg, scale_factor, offset); } - void MacroAssembler::_verify_oop_addr(Address addr, const char* s, const char* file, int line) { if (!VerifyOops) return; @@ -3913,6 +4042,9 @@ class ControlWord { case 1: rc = "round down"; break; case 2: rc = "round up "; break; case 3: rc = "chop "; break; + default: + rc = NULL; // silence compiler warnings + fatal("Unknown rounding control: %d", rounding_control()); }; // precision control const char* pc; @@ -3921,6 +4053,9 @@ class ControlWord { case 1: pc = "reserved"; break; case 2: pc = "53 bits "; break; case 3: pc = "64 bits "; break; + default: + pc = NULL; // silence compiler warnings + fatal("Unknown precision control: %d", precision_control()); }; // flags char f[9]; @@ -5764,7 +5899,7 @@ void MacroAssembler::vectorized_mismatch(Register obja, Register objb, Register bind(VECTOR64_LOOP); // AVX512 code to compare 64 byte vectors. - evmovdqub(rymm0, Address(obja, result), Assembler::AVX_512bit); + evmovdqub(rymm0, Address(obja, result), false, Assembler::AVX_512bit); evpcmpeqb(k7, rymm0, Address(objb, result), Assembler::AVX_512bit); kortestql(k7, k7); jcc(Assembler::aboveEqual, VECTOR64_NOT_EQUAL); // mismatch @@ -5783,7 +5918,7 @@ void MacroAssembler::vectorized_mismatch(Register obja, Register objb, Register notq(tmp2); kmovql(k3, tmp2); - evmovdqub(rymm0, k3, Address(obja, result), Assembler::AVX_512bit); + evmovdqub(rymm0, k3, Address(obja, result), false, Assembler::AVX_512bit); evpcmpeqb(k7, k3, rymm0, Address(objb, result), Assembler::AVX_512bit); ktestql(k7, k3); @@ -7578,7 +7713,7 @@ void MacroAssembler::char_array_compress(Register src, Register dst, Register le notl(result); kmovdl(k3, result); - evmovdquw(tmp1Reg, k3, Address(src, 0), Assembler::AVX_512bit); + evmovdquw(tmp1Reg, k3, Address(src, 0), /*merge*/ false, Assembler::AVX_512bit); evpcmpuw(k2, k3, tmp1Reg, tmp2Reg, Assembler::le, Assembler::AVX_512bit); ktestd(k2, k3); jcc(Assembler::carryClear, return_zero); @@ -7603,7 +7738,7 @@ void MacroAssembler::char_array_compress(Register src, Register dst, Register le negptr(len); bind(copy_32_loop); - evmovdquw(tmp1Reg, Address(src, len, Address::times_2), Assembler::AVX_512bit); + evmovdquw(tmp1Reg, Address(src, len, Address::times_2), /*merge*/ false, Assembler::AVX_512bit); evpcmpuw(k2, tmp1Reg, tmp2Reg, Assembler::le, Assembler::AVX_512bit); kortestdl(k2, k2); jcc(Assembler::carryClear, return_zero); @@ -7628,7 +7763,7 @@ void MacroAssembler::char_array_compress(Register src, Register dst, Register le kmovdl(k3, result); - evmovdquw(tmp1Reg, k3, Address(src, 0), Assembler::AVX_512bit); + evmovdquw(tmp1Reg, k3, Address(src, 0), /*merge*/ false, Assembler::AVX_512bit); evpcmpuw(k2, k3, tmp1Reg, tmp2Reg, Assembler::le, Assembler::AVX_512bit); ktestd(k2, k3); jcc(Assembler::carryClear, return_zero); @@ -7773,7 +7908,7 @@ void MacroAssembler::byte_array_inflate(Register src, Register dst, Register len // inflate 32 chars per iter bind(copy_32_loop); vpmovzxbw(tmp1, Address(src, len, Address::times_1), Assembler::AVX_512bit); - evmovdquw(Address(dst, len, Address::times_2), tmp1, Assembler::AVX_512bit); + evmovdquw(Address(dst, len, Address::times_2), tmp1, /*merge*/ false, Assembler::AVX_512bit); addptr(len, 32); jcc(Assembler::notZero, copy_32_loop); @@ -7788,7 +7923,7 @@ void MacroAssembler::byte_array_inflate(Register src, Register dst, Register len notl(tmp3_aliased); kmovdl(k2, tmp3_aliased); evpmovzxbw(tmp1, k2, Address(src, 0), Assembler::AVX_512bit); - evmovdquw(Address(dst, 0), k2, tmp1, Assembler::AVX_512bit); + evmovdquw(Address(dst, 0), k2, tmp1, /*merge*/ true, Assembler::AVX_512bit); jmp(done); bind(avx3_threshold); @@ -7963,6 +8098,7 @@ void MacroAssembler::cache_wbsync(bool is_pre) sfence(); } } + #endif // _LP64 Assembler::Condition MacroAssembler::negate_condition(Assembler::Condition cond) { diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index 3d009d69945..e7419fc916b 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -583,22 +583,30 @@ class MacroAssembler: public Assembler { // method handles (JSR 292) Address argument_address(RegisterOrConstant arg_slot, int extra_slot_offset = 0); - //---- - void set_word_if_not_zero(Register reg); // sets reg to 1 if not zero, otherwise 0 - // Debugging // only if +VerifyOops void _verify_oop(Register reg, const char* s, const char* file, int line); void _verify_oop_addr(Address addr, const char* s, const char* file, int line); + void _verify_oop_checked(Register reg, const char* s, const char* file, int line) { + if (VerifyOops) { + _verify_oop(reg, s, file, line); + } + } + void _verify_oop_addr_checked(Address reg, const char* s, const char* file, int line) { + if (VerifyOops) { + _verify_oop_addr(reg, s, file, line); + } + } + // TODO: verify method and klass metadata (compare against vptr?) void _verify_method_ptr(Register reg, const char * msg, const char * file, int line) {} void _verify_klass_ptr(Register reg, const char * msg, const char * file, int line){} -#define verify_oop(reg) _verify_oop(reg, "broken oop " #reg, __FILE__, __LINE__) -#define verify_oop_msg(reg, msg) _verify_oop(reg, "broken oop " #reg ", " #msg, __FILE__, __LINE__) -#define verify_oop_addr(addr) _verify_oop_addr(addr, "broken oop addr " #addr, __FILE__, __LINE__) +#define verify_oop(reg) _verify_oop_checked(reg, "broken oop " #reg, __FILE__, __LINE__) +#define verify_oop_msg(reg, msg) _verify_oop_checked(reg, "broken oop " #reg ", " #msg, __FILE__, __LINE__) +#define verify_oop_addr(addr) _verify_oop_addr_checked(addr, "broken oop addr " #addr, __FILE__, __LINE__) #define verify_method_ptr(reg) _verify_method_ptr(reg, "broken method " #reg, __FILE__, __LINE__) #define verify_klass_ptr(reg) _verify_klass_ptr(reg, "broken klass " #reg, __FILE__, __LINE__) @@ -643,13 +651,7 @@ class MacroAssembler: public Assembler { // Check for reserved stack access in method being exited (for JIT) void reserved_stack_check(); - virtual RegisterOrConstant delayed_value_impl(intptr_t* delayed_value_addr, - Register tmp, - int offset); - - // If thread_reg is != noreg the code assumes the register passed contains - // the thread (required on 64 bit). - void safepoint_poll(Label& slow_path, Register thread_reg, Register temp_reg); + void safepoint_poll(Label& slow_path, Register thread_reg, bool at_return, bool in_nmethod); void verify_tlab(); @@ -1078,15 +1080,59 @@ class MacroAssembler: public Assembler { void movdqu(XMMRegister dst, Address src); void movdqu(XMMRegister dst, XMMRegister src); void movdqu(XMMRegister dst, AddressLiteral src, Register scratchReg = rscratch1); + + void kmovwl(KRegister dst, Register src) { Assembler::kmovwl(dst, src); } + void kmovwl(Register dst, KRegister src) { Assembler::kmovwl(dst, src); } + void kmovwl(KRegister dst, Address src) { Assembler::kmovwl(dst, src); } + void kmovwl(KRegister dst, AddressLiteral src, Register scratch_reg = rscratch1); + // AVX Unaligned forms void vmovdqu(Address dst, XMMRegister src); void vmovdqu(XMMRegister dst, Address src); void vmovdqu(XMMRegister dst, XMMRegister src); void vmovdqu(XMMRegister dst, AddressLiteral src, Register scratch_reg = rscratch1); + + // AVX512 Unaligned + void evmovdqub(Address dst, XMMRegister src, bool merge, int vector_len) { Assembler::evmovdqub(dst, src, merge, vector_len); } + void evmovdqub(XMMRegister dst, Address src, bool merge, int vector_len) { Assembler::evmovdqub(dst, src, merge, vector_len); } + void evmovdqub(XMMRegister dst, XMMRegister src, bool merge, int vector_len) { Assembler::evmovdqub(dst, src, merge, vector_len); } + void evmovdqub(XMMRegister dst, KRegister mask, Address src, bool merge, int vector_len) { Assembler::evmovdqub(dst, mask, src, merge, vector_len); } + void evmovdqub(XMMRegister dst, KRegister mask, AddressLiteral src, bool merge, int vector_len, Register scratch_reg); + + void evmovdquw(Address dst, XMMRegister src, bool merge, int vector_len) { Assembler::evmovdquw(dst, src, merge, vector_len); } + void evmovdquw(Address dst, KRegister mask, XMMRegister src, bool merge, int vector_len) { Assembler::evmovdquw(dst, mask, src, merge, vector_len); } + void evmovdquw(XMMRegister dst, Address src, bool merge, int vector_len) { Assembler::evmovdquw(dst, src, merge, vector_len); } + void evmovdquw(XMMRegister dst, KRegister mask, Address src, bool merge, int vector_len) { Assembler::evmovdquw(dst, mask, src, merge, vector_len); } + void evmovdquw(XMMRegister dst, KRegister mask, AddressLiteral src, bool merge, int vector_len, Register scratch_reg); + + void evmovdqul(Address dst, XMMRegister src, int vector_len) { Assembler::evmovdqul(dst, src, vector_len); } + void evmovdqul(XMMRegister dst, Address src, int vector_len) { Assembler::evmovdqul(dst, src, vector_len); } + void evmovdqul(XMMRegister dst, XMMRegister src, int vector_len) { + if (dst->encoding() == src->encoding()) return; + Assembler::evmovdqul(dst, src, vector_len); + } + void evmovdqul(Address dst, KRegister mask, XMMRegister src, bool merge, int vector_len) { Assembler::evmovdqul(dst, mask, src, merge, vector_len); } + void evmovdqul(XMMRegister dst, KRegister mask, Address src, bool merge, int vector_len) { Assembler::evmovdqul(dst, mask, src, merge, vector_len); } + void evmovdqul(XMMRegister dst, KRegister mask, XMMRegister src, bool merge, int vector_len) { + if (dst->encoding() == src->encoding() && mask == k0) return; + Assembler::evmovdqul(dst, mask, src, merge, vector_len); + } + void evmovdqul(XMMRegister dst, KRegister mask, AddressLiteral src, bool merge, int vector_len, Register scratch_reg); + void evmovdquq(XMMRegister dst, Address src, int vector_len) { Assembler::evmovdquq(dst, src, vector_len); } - void evmovdquq(XMMRegister dst, XMMRegister src, int vector_len) { Assembler::evmovdquq(dst, src, vector_len); } void evmovdquq(Address dst, XMMRegister src, int vector_len) { Assembler::evmovdquq(dst, src, vector_len); } void evmovdquq(XMMRegister dst, AddressLiteral src, int vector_len, Register rscratch); + void evmovdquq(XMMRegister dst, XMMRegister src, int vector_len) { + if (dst->encoding() == src->encoding()) return; + Assembler::evmovdquq(dst, src, vector_len); + } + void evmovdquq(Address dst, KRegister mask, XMMRegister src, bool merge, int vector_len) { Assembler::evmovdquq(dst, mask, src, merge, vector_len); } + void evmovdquq(XMMRegister dst, KRegister mask, Address src, bool merge, int vector_len) { Assembler::evmovdquq(dst, mask, src, merge, vector_len); } + void evmovdquq(XMMRegister dst, KRegister mask, XMMRegister src, bool merge, int vector_len) { + if (dst->encoding() == src->encoding() && mask == k0) return; + Assembler::evmovdquq(dst, mask, src, merge, vector_len); + } + void evmovdquq(XMMRegister dst, KRegister mask, AddressLiteral src, bool merge, int vector_len, Register scratch_reg); // Move Aligned Double Quadword void movdqa(XMMRegister dst, Address src) { Assembler::movdqa(dst, src); } @@ -1208,6 +1254,30 @@ class MacroAssembler: public Assembler { void vpcmpeqb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void vpcmpeqw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void evpcmpeqd(KRegister kdst, KRegister mask, XMMRegister nds, AddressLiteral src, int vector_len, Register scratch_reg); + + // Vector compares + void evpcmpd(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, + int comparison, int vector_len) { Assembler::evpcmpd(kdst, mask, nds, src, comparison, vector_len); } + void evpcmpd(KRegister kdst, KRegister mask, XMMRegister nds, AddressLiteral src, + int comparison, int vector_len, Register scratch_reg); + void evpcmpq(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, + int comparison, int vector_len) { Assembler::evpcmpq(kdst, mask, nds, src, comparison, vector_len); } + void evpcmpq(KRegister kdst, KRegister mask, XMMRegister nds, AddressLiteral src, + int comparison, int vector_len, Register scratch_reg); + void evpcmpb(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, + int comparison, int vector_len) { Assembler::evpcmpb(kdst, mask, nds, src, comparison, vector_len); } + void evpcmpb(KRegister kdst, KRegister mask, XMMRegister nds, AddressLiteral src, + int comparison, int vector_len, Register scratch_reg); + void evpcmpw(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, + int comparison, int vector_len) { Assembler::evpcmpw(kdst, mask, nds, src, comparison, vector_len); } + void evpcmpw(KRegister kdst, KRegister mask, XMMRegister nds, AddressLiteral src, + int comparison, int vector_len, Register scratch_reg); + + + // Emit comparison instruction for the specified comparison predicate. + void vpcmpCCW(XMMRegister dst, XMMRegister nds, XMMRegister src, ComparisonPredicate cond, Width width, int vector_len, Register scratch_reg); + void vpcmpCC(XMMRegister dst, XMMRegister nds, XMMRegister src, int cond_encoding, Width width, int vector_len); void vpmovzxbw(XMMRegister dst, Address src, int vector_len); void vpmovzxbw(XMMRegister dst, XMMRegister src, int vector_len) { Assembler::vpmovzxbw(dst, src, vector_len); } @@ -1236,6 +1306,7 @@ class MacroAssembler: public Assembler { void vpsllw(XMMRegister dst, XMMRegister nds, int shift, int vector_len); void vptest(XMMRegister dst, XMMRegister src); + void vptest(XMMRegister dst, XMMRegister src, int vector_len) { Assembler::vptest(dst, src, vector_len); } void punpcklbw(XMMRegister dst, XMMRegister src); void punpcklbw(XMMRegister dst, Address src) { Assembler::punpcklbw(dst, src); } @@ -1254,6 +1325,8 @@ class MacroAssembler: public Assembler { void vandps(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { Assembler::vandps(dst, nds, src, vector_len); } void vandps(XMMRegister dst, XMMRegister nds, AddressLiteral src, int vector_len, Register scratch_reg = rscratch1); + void evpord(XMMRegister dst, KRegister mask, XMMRegister nds, AddressLiteral src, bool merge, int vector_len, Register scratch_reg); + void vdivsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { Assembler::vdivsd(dst, nds, src); } void vdivsd(XMMRegister dst, XMMRegister nds, Address src) { Assembler::vdivsd(dst, nds, src); } void vdivsd(XMMRegister dst, XMMRegister nds, AddressLiteral src); @@ -1309,6 +1382,9 @@ class MacroAssembler: public Assembler { void vpxor(XMMRegister dst, XMMRegister src) { Assembler::vpxor(dst, dst, src, true); } void vpxor(XMMRegister dst, Address src) { Assembler::vpxor(dst, dst, src, true); } + void vpermd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { Assembler::vpermd(dst, nds, src, vector_len); } + void vpermd(XMMRegister dst, XMMRegister nds, AddressLiteral src, int vector_len, Register scratch_reg); + void vinserti128(XMMRegister dst, XMMRegister nds, XMMRegister src, uint8_t imm8) { if (UseAVX > 2 && VM_Version::supports_avx512novl()) { Assembler::vinserti32x4(dst, dst, src, imm8); @@ -1727,6 +1803,35 @@ class MacroAssembler: public Assembler { void cache_wb(Address line); void cache_wbsync(bool is_pre); + +#if COMPILER2_OR_JVMCI + void arraycopy_avx3_special_cases(XMMRegister xmm, KRegister mask, Register from, + Register to, Register count, int shift, + Register index, Register temp, + bool use64byteVector, Label& L_entry, Label& L_exit); + + void arraycopy_avx3_special_cases_conjoint(XMMRegister xmm, KRegister mask, Register from, + Register to, Register start_index, Register end_index, + Register count, int shift, Register temp, + bool use64byteVector, Label& L_entry, Label& L_exit); + + void copy64_masked_avx(Register dst, Register src, XMMRegister xmm, + KRegister mask, Register length, Register index, + Register temp, int shift = Address::times_1, int offset = 0, + bool use64byteVector = false); + + void copy32_masked_avx(Register dst, Register src, XMMRegister xmm, + KRegister mask, Register length, Register index, + Register temp, int shift = Address::times_1, int offset = 0); + + void copy32_avx(Register dst, Register src, Register index, XMMRegister xmm, + int shift = Address::times_1, int offset = 0); + + void copy64_avx(Register dst, Register src, Register index, XMMRegister xmm, + bool conjoint, int shift = Address::times_1, int offset = 0, + bool use64byteVector = false); +#endif // COMPILER2_OR_JVMCI + #endif // _LP64 void vallones(XMMRegister dst, int vector_len); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86_arrayCopy_avx3.cpp b/src/hotspot/cpu/x86/macroAssembler_x86_arrayCopy_avx3.cpp new file mode 100644 index 00000000000..4368dee7329 --- /dev/null +++ b/src/hotspot/cpu/x86/macroAssembler_x86_arrayCopy_avx3.cpp @@ -0,0 +1,253 @@ +/* +* Copyright (c) 2020, Intel Corporation. +* +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* This code is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 2 only, as +* published by the Free Software Foundation. +* +* This code is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +* version 2 for more details (a copy is included in the LICENSE file that +* accompanied this code). +* +* You should have received a copy of the GNU General Public License version +* 2 along with this work; if not, write to the Free Software Foundation, +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +* +* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +* or visit www.oracle.com if you need additional information or have any +* questions. +* +*/ + +#include "precompiled.hpp" +#include "asm/macroAssembler.hpp" +#include "asm/macroAssembler.inline.hpp" + +#ifdef PRODUCT +#define BLOCK_COMMENT(str) /* nothing */ +#else +#define BLOCK_COMMENT(str) block_comment(str) +#endif + +#define BIND(label) bind(label); BLOCK_COMMENT(#label ":") + +#ifdef _LP64 + +#if COMPILER2_OR_JVMCI + +void MacroAssembler::arraycopy_avx3_special_cases(XMMRegister xmm, KRegister mask, Register from, + Register to, Register count, int shift, + Register index, Register temp, + bool use64byteVector, Label& L_entry, Label& L_exit) { + Label L_entry_64, L_entry_96, L_entry_128; + Label L_entry_160, L_entry_192; + + int size_mat[][6] = { + /* T_BYTE */ {32 , 64, 96 , 128 , 160 , 192 }, + /* T_SHORT*/ {16 , 32, 48 , 64 , 80 , 96 }, + /* T_INT */ {8 , 16, 24 , 32 , 40 , 48 }, + /* T_LONG */ {4 , 8, 12 , 16 , 20 , 24 } + }; + + // Case A) Special case for length less than equal to 32 bytes. + cmpq(count, size_mat[shift][0]); + jccb(Assembler::greater, L_entry_64); + copy32_masked_avx(to, from, xmm, mask, count, index, temp, shift); + jmp(L_exit); + + // Case B) Special case for length less than equal to 64 bytes. + BIND(L_entry_64); + cmpq(count, size_mat[shift][1]); + jccb(Assembler::greater, L_entry_96); + copy64_masked_avx(to, from, xmm, mask, count, index, temp, shift, 0, use64byteVector); + jmp(L_exit); + + // Case C) Special case for length less than equal to 96 bytes. + BIND(L_entry_96); + cmpq(count, size_mat[shift][2]); + jccb(Assembler::greater, L_entry_128); + copy64_avx(to, from, index, xmm, false, shift, 0, use64byteVector); + subq(count, 64 >> shift); + copy32_masked_avx(to, from, xmm, mask, count, index, temp, shift, 64); + jmp(L_exit); + + // Case D) Special case for length less than equal to 128 bytes. + BIND(L_entry_128); + cmpq(count, size_mat[shift][3]); + jccb(Assembler::greater, L_entry_160); + copy64_avx(to, from, index, xmm, false, shift, 0, use64byteVector); + copy32_avx(to, from, index, xmm, shift, 64); + subq(count, 96 >> shift); + copy32_masked_avx(to, from, xmm, mask, count, index, temp, shift, 96); + jmp(L_exit); + + // Case E) Special case for length less than equal to 160 bytes. + BIND(L_entry_160); + cmpq(count, size_mat[shift][4]); + jccb(Assembler::greater, L_entry_192); + copy64_avx(to, from, index, xmm, false, shift, 0, use64byteVector); + copy64_avx(to, from, index, xmm, false, shift, 64, use64byteVector); + subq(count, 128 >> shift); + copy32_masked_avx(to, from, xmm, mask, count, index, temp, shift, 128); + jmp(L_exit); + + // Case F) Special case for length less than equal to 192 bytes. + BIND(L_entry_192); + cmpq(count, size_mat[shift][5]); + jcc(Assembler::greater, L_entry); + copy64_avx(to, from, index, xmm, false, shift, 0, use64byteVector); + copy64_avx(to, from, index, xmm, false, shift, 64, use64byteVector); + copy32_avx(to, from, index, xmm, shift, 128); + subq(count, 160 >> shift); + copy32_masked_avx(to, from, xmm, mask, count, index, temp, shift, 160); + jmp(L_exit); +} + +void MacroAssembler::arraycopy_avx3_special_cases_conjoint(XMMRegister xmm, KRegister mask, Register from, + Register to, Register start_index, Register end_index, + Register count, int shift, Register temp, + bool use64byteVector, Label& L_entry, Label& L_exit) { + Label L_entry_64, L_entry_96, L_entry_128; + Label L_entry_160, L_entry_192; + bool avx3 = MaxVectorSize > 32 && AVX3Threshold == 0; + + int size_mat[][6] = { + /* T_BYTE */ {32 , 64, 96 , 128 , 160 , 192 }, + /* T_SHORT*/ {16 , 32, 48 , 64 , 80 , 96 }, + /* T_INT */ {8 , 16, 24 , 32 , 40 , 48 }, + /* T_LONG */ {4 , 8, 12 , 16 , 20 , 24 } + }; + + // Case A) Special case for length less than equal to 32 bytes. + cmpq(count, size_mat[shift][0]); + jccb(Assembler::greater, L_entry_64); + copy32_masked_avx(to, from, xmm, mask, count, start_index, temp, shift); + jmp(L_exit); + + // Case B) Special case for length less than equal to 64 bytes. + BIND(L_entry_64); + cmpq(count, size_mat[shift][1]); + jccb(Assembler::greater, L_entry_96); + if (avx3) { + copy64_masked_avx(to, from, xmm, mask, count, start_index, temp, shift, 0, true); + } else { + copy32_avx(to, from, end_index, xmm, shift, -32); + subq(count, 32 >> shift); + copy32_masked_avx(to, from, xmm, mask, count, start_index, temp, shift); + } + jmp(L_exit); + + // Case C) Special case for length less than equal to 96 bytes. + BIND(L_entry_96); + cmpq(count, size_mat[shift][2]); + jccb(Assembler::greater, L_entry_128); + copy64_avx(to, from, end_index, xmm, true, shift, -64, use64byteVector); + subq(count, 64 >> shift); + copy32_masked_avx(to, from, xmm, mask, count, start_index, temp, shift); + jmp(L_exit); + + // Case D) Special case for length less than equal to 128 bytes. + BIND(L_entry_128); + cmpq(count, size_mat[shift][3]); + jccb(Assembler::greater, L_entry_160); + copy64_avx(to, from, end_index, xmm, true, shift, -64, use64byteVector); + copy32_avx(to, from, end_index, xmm, shift, -96); + subq(count, 96 >> shift); + copy32_masked_avx(to, from, xmm, mask, count, start_index, temp, shift); + jmp(L_exit); + + // Case E) Special case for length less than equal to 160 bytes. + BIND(L_entry_160); + cmpq(count, size_mat[shift][4]); + jccb(Assembler::greater, L_entry_192); + copy64_avx(to, from, end_index, xmm, true, shift, -64, use64byteVector); + copy64_avx(to, from, end_index, xmm, true, shift, -128, use64byteVector); + subq(count, 128 >> shift); + copy32_masked_avx(to, from, xmm, mask, count, start_index, temp, shift); + jmp(L_exit); + + // Case F) Special case for length less than equal to 192 bytes. + BIND(L_entry_192); + cmpq(count, size_mat[shift][5]); + jcc(Assembler::greater, L_entry); + copy64_avx(to, from, end_index, xmm, true, shift, -64, use64byteVector); + copy64_avx(to, from, end_index, xmm, true, shift, -128, use64byteVector); + copy32_avx(to, from, end_index, xmm, shift, -160); + subq(count, 160 >> shift); + copy32_masked_avx(to, from, xmm, mask, count, start_index, temp, shift); + jmp(L_exit); +} + +void MacroAssembler::copy64_masked_avx(Register dst, Register src, XMMRegister xmm, + KRegister mask, Register length, Register index, + Register temp, int shift, int offset, + bool use64byteVector) { + BasicType type[] = { T_BYTE, T_SHORT, T_INT, T_LONG}; + assert(MaxVectorSize >= 32, "vector length should be >= 32"); + if (!use64byteVector) { + copy32_avx(dst, src, index, xmm, shift, offset); + subptr(length, 32 >> shift); + copy32_masked_avx(dst, src, xmm, mask, length, index, temp, shift, offset+32); + } else { + Address::ScaleFactor scale = (Address::ScaleFactor)(shift); + assert(MaxVectorSize == 64, "vector length != 64"); + negptr(length); + addq(length, 64); + mov64(temp, -1); + shrxq(temp, temp, length); + kmovql(mask, temp); + evmovdqu(xmm, mask, Address(src, index, scale, offset), Assembler::AVX_512bit, type[shift]); + evmovdqu(Address(dst, index, scale, offset), mask, xmm, Assembler::AVX_512bit, type[shift]); + } +} + + +void MacroAssembler::copy32_masked_avx(Register dst, Register src, XMMRegister xmm, + KRegister mask, Register length, Register index, + Register temp, int shift, int offset) { + assert(MaxVectorSize >= 32, "vector length should be >= 32"); + BasicType type[] = { T_BYTE, T_SHORT, T_INT, T_LONG}; + Address::ScaleFactor scale = (Address::ScaleFactor)(shift); + mov64(temp, 1); + shlxq(temp, temp, length); + decq(temp); + kmovql(mask, temp); + evmovdqu(xmm, mask, Address(src, index, scale, offset), Assembler::AVX_256bit, type[shift]); + evmovdqu(Address(dst, index, scale, offset), mask, xmm, Assembler::AVX_256bit, type[shift]); +} + + +void MacroAssembler::copy32_avx(Register dst, Register src, Register index, XMMRegister xmm, + int shift, int offset) { + assert(MaxVectorSize >= 32, "vector length should be >= 32"); + Address::ScaleFactor scale = (Address::ScaleFactor)(shift); + vmovdqu(xmm, Address(src, index, scale, offset)); + vmovdqu(Address(dst, index, scale, offset), xmm); +} + + +void MacroAssembler::copy64_avx(Register dst, Register src, Register index, XMMRegister xmm, + bool conjoint, int shift, int offset, bool use64byteVector) { + assert(MaxVectorSize == 64 || MaxVectorSize == 32, "vector length mismatch"); + if (!use64byteVector) { + if (conjoint) { + copy32_avx(dst, src, index, xmm, shift, offset+32); + copy32_avx(dst, src, index, xmm, shift, offset); + } else { + copy32_avx(dst, src, index, xmm, shift, offset); + copy32_avx(dst, src, index, xmm, shift, offset+32); + } + } else { + Address::ScaleFactor scale = (Address::ScaleFactor)(shift); + evmovdquq(xmm, Address(src, index, scale, offset), Assembler::AVX_512bit); + evmovdquq(Address(dst, index, scale, offset), xmm, Assembler::AVX_512bit); + } +} + +#endif // COMPILER2_OR_JVMCI + +#endif diff --git a/src/hotspot/cpu/x86/methodHandles_x86.hpp b/src/hotspot/cpu/x86/methodHandles_x86.hpp index bb333781a62..444d0495666 100644 --- a/src/hotspot/cpu/x86/methodHandles_x86.hpp +++ b/src/hotspot/cpu/x86/methodHandles_x86.hpp @@ -27,7 +27,7 @@ // Adapters enum /* platform_dependent_constants */ { - adapter_code_size = NOT_LP64(16000 DEBUG_ONLY(+ 25000)) LP64_ONLY(32000 DEBUG_ONLY(+ 150000)) + adapter_code_size = 4000 DEBUG_ONLY(+ 6000) }; // Additional helper methods for MethodHandles code generation: diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp index 066d1ae98cb..3e2b3a118c7 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp @@ -37,6 +37,7 @@ #include "memory/resourceArea.hpp" #include "oops/compiledICHolder.hpp" #include "oops/klass.inline.hpp" +#include "prims/methodHandles.hpp" #include "runtime/safepointMechanism.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/vframeArray.hpp" @@ -1213,265 +1214,6 @@ void SharedRuntime::restore_native_result(MacroAssembler *masm, BasicType ret_ty } } - -static void save_or_restore_arguments(MacroAssembler* masm, - const int stack_slots, - const int total_in_args, - const int arg_save_area, - OopMap* map, - VMRegPair* in_regs, - BasicType* in_sig_bt) { - // if map is non-NULL then the code should store the values, - // otherwise it should load them. - int handle_index = 0; - // Save down double word first - for ( int i = 0; i < total_in_args; i++) { - if (in_regs[i].first()->is_XMMRegister() && in_sig_bt[i] == T_DOUBLE) { - int slot = handle_index * VMRegImpl::slots_per_word + arg_save_area; - int offset = slot * VMRegImpl::stack_slot_size; - handle_index += 2; - assert(handle_index <= stack_slots, "overflow"); - if (map != NULL) { - __ movdbl(Address(rsp, offset), in_regs[i].first()->as_XMMRegister()); - } else { - __ movdbl(in_regs[i].first()->as_XMMRegister(), Address(rsp, offset)); - } - } - if (in_regs[i].first()->is_Register() && in_sig_bt[i] == T_LONG) { - int slot = handle_index * VMRegImpl::slots_per_word + arg_save_area; - int offset = slot * VMRegImpl::stack_slot_size; - handle_index += 2; - assert(handle_index <= stack_slots, "overflow"); - if (map != NULL) { - __ movl(Address(rsp, offset), in_regs[i].first()->as_Register()); - if (in_regs[i].second()->is_Register()) { - __ movl(Address(rsp, offset + 4), in_regs[i].second()->as_Register()); - } - } else { - __ movl(in_regs[i].first()->as_Register(), Address(rsp, offset)); - if (in_regs[i].second()->is_Register()) { - __ movl(in_regs[i].second()->as_Register(), Address(rsp, offset + 4)); - } - } - } - } - // Save or restore single word registers - for ( int i = 0; i < total_in_args; i++) { - if (in_regs[i].first()->is_Register()) { - int slot = handle_index++ * VMRegImpl::slots_per_word + arg_save_area; - int offset = slot * VMRegImpl::stack_slot_size; - assert(handle_index <= stack_slots, "overflow"); - if (in_sig_bt[i] == T_ARRAY && map != NULL) { - map->set_oop(VMRegImpl::stack2reg(slot));; - } - - // Value is in an input register pass we must flush it to the stack - const Register reg = in_regs[i].first()->as_Register(); - switch (in_sig_bt[i]) { - case T_ARRAY: - if (map != NULL) { - __ movptr(Address(rsp, offset), reg); - } else { - __ movptr(reg, Address(rsp, offset)); - } - break; - case T_BOOLEAN: - case T_CHAR: - case T_BYTE: - case T_SHORT: - case T_INT: - if (map != NULL) { - __ movl(Address(rsp, offset), reg); - } else { - __ movl(reg, Address(rsp, offset)); - } - break; - case T_OBJECT: - default: ShouldNotReachHere(); - } - } else if (in_regs[i].first()->is_XMMRegister()) { - if (in_sig_bt[i] == T_FLOAT) { - int slot = handle_index++ * VMRegImpl::slots_per_word + arg_save_area; - int offset = slot * VMRegImpl::stack_slot_size; - assert(handle_index <= stack_slots, "overflow"); - if (map != NULL) { - __ movflt(Address(rsp, offset), in_regs[i].first()->as_XMMRegister()); - } else { - __ movflt(in_regs[i].first()->as_XMMRegister(), Address(rsp, offset)); - } - } - } else if (in_regs[i].first()->is_stack()) { - if (in_sig_bt[i] == T_ARRAY && map != NULL) { - int offset_in_older_frame = in_regs[i].first()->reg2stack() + SharedRuntime::out_preserve_stack_slots(); - map->set_oop(VMRegImpl::stack2reg(offset_in_older_frame + stack_slots)); - } - } - } -} - -// Registers need to be saved for runtime call -static Register caller_saved_registers[] = { - rcx, rdx, rsi, rdi -}; - -// Save caller saved registers except r1 and r2 -static void save_registers_except(MacroAssembler* masm, Register r1, Register r2) { - int reg_len = (int)(sizeof(caller_saved_registers) / sizeof(Register)); - for (int index = 0; index < reg_len; index ++) { - Register this_reg = caller_saved_registers[index]; - if (this_reg != r1 && this_reg != r2) { - __ push(this_reg); - } - } -} - -// Restore caller saved registers except r1 and r2 -static void restore_registers_except(MacroAssembler* masm, Register r1, Register r2) { - int reg_len = (int)(sizeof(caller_saved_registers) / sizeof(Register)); - for (int index = reg_len - 1; index >= 0; index --) { - Register this_reg = caller_saved_registers[index]; - if (this_reg != r1 && this_reg != r2) { - __ pop(this_reg); - } - } -} - -// Pin object, return pinned object or null in rax -static void gen_pin_object(MacroAssembler* masm, - Register thread, VMRegPair reg) { - __ block_comment("gen_pin_object {"); - - Label is_null; - Register tmp_reg = rax; - VMRegPair tmp(tmp_reg->as_VMReg()); - if (reg.first()->is_stack()) { - // Load the arg up from the stack - simple_move32(masm, reg, tmp); - reg = tmp; - } else { - __ movl(tmp_reg, reg.first()->as_Register()); - } - __ testptr(reg.first()->as_Register(), reg.first()->as_Register()); - __ jccb(Assembler::equal, is_null); - - // Save registers that may be used by runtime call - Register arg = reg.first()->is_Register() ? reg.first()->as_Register() : noreg; - save_registers_except(masm, arg, thread); - - __ call_VM_leaf( - CAST_FROM_FN_PTR(address, SharedRuntime::pin_object), - thread, reg.first()->as_Register()); - - // Restore saved registers - restore_registers_except(masm, arg, thread); - - __ bind(is_null); - __ block_comment("} gen_pin_object"); -} - -// Unpin object -static void gen_unpin_object(MacroAssembler* masm, - Register thread, VMRegPair reg) { - __ block_comment("gen_unpin_object {"); - Label is_null; - - // temp register - __ push(rax); - Register tmp_reg = rax; - VMRegPair tmp(tmp_reg->as_VMReg()); - - simple_move32(masm, reg, tmp); - - __ testptr(rax, rax); - __ jccb(Assembler::equal, is_null); - - // Save registers that may be used by runtime call - Register arg = reg.first()->is_Register() ? reg.first()->as_Register() : noreg; - save_registers_except(masm, arg, thread); - - __ call_VM_leaf( - CAST_FROM_FN_PTR(address, SharedRuntime::unpin_object), - thread, rax); - - // Restore saved registers - restore_registers_except(masm, arg, thread); - __ bind(is_null); - __ pop(rax); - __ block_comment("} gen_unpin_object"); -} - -// Check GCLocker::needs_gc and enter the runtime if it's true. This -// keeps a new JNI critical region from starting until a GC has been -// forced. Save down any oops in registers and describe them in an -// OopMap. -static void check_needs_gc_for_critical_native(MacroAssembler* masm, - Register thread, - int stack_slots, - int total_c_args, - int total_in_args, - int arg_save_area, - OopMapSet* oop_maps, - VMRegPair* in_regs, - BasicType* in_sig_bt) { - __ block_comment("check GCLocker::needs_gc"); - Label cont; - __ cmp8(ExternalAddress((address)GCLocker::needs_gc_address()), false); - __ jcc(Assembler::equal, cont); - - // Save down any incoming oops and call into the runtime to halt for a GC - - OopMap* map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); - - save_or_restore_arguments(masm, stack_slots, total_in_args, - arg_save_area, map, in_regs, in_sig_bt); - - address the_pc = __ pc(); - oop_maps->add_gc_map( __ offset(), map); - __ set_last_Java_frame(thread, rsp, noreg, the_pc); - - __ block_comment("block_for_jni_critical"); - __ push(thread); - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::block_for_jni_critical))); - __ increment(rsp, wordSize); - - __ get_thread(thread); - __ reset_last_Java_frame(thread, false); - - save_or_restore_arguments(masm, stack_slots, total_in_args, - arg_save_area, NULL, in_regs, in_sig_bt); - - __ bind(cont); -#ifdef ASSERT - if (StressCriticalJNINatives) { - // Stress register saving - OopMap* map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); - save_or_restore_arguments(masm, stack_slots, total_in_args, - arg_save_area, map, in_regs, in_sig_bt); - // Destroy argument registers - for (int i = 0; i < total_in_args - 1; i++) { - if (in_regs[i].first()->is_Register()) { - const Register reg = in_regs[i].first()->as_Register(); - __ xorptr(reg, reg); - } else if (in_regs[i].first()->is_XMMRegister()) { - __ xorpd(in_regs[i].first()->as_XMMRegister(), in_regs[i].first()->as_XMMRegister()); - } else if (in_regs[i].first()->is_FloatRegister()) { - ShouldNotReachHere(); - } else if (in_regs[i].first()->is_stack()) { - // Nothing to do - } else { - ShouldNotReachHere(); - } - if (in_sig_bt[i] == T_LONG || in_sig_bt[i] == T_DOUBLE) { - i++; - } - } - - save_or_restore_arguments(masm, stack_slots, total_in_args, - arg_save_area, NULL, in_regs, in_sig_bt); - } -#endif -} - // Unpack an array argument into a pointer to the body and the length // if the array is non-null, otherwise pass 0 for both. static void unpack_array_argument(MacroAssembler* masm, VMRegPair reg, BasicType in_elem_type, VMRegPair body_arg, VMRegPair length_arg) { @@ -1596,24 +1338,12 @@ static void gen_special_dispatch(MacroAssembler* masm, // Critical native functions are a shorthand for the use of // GetPrimtiveArrayCritical and disallow the use of any other JNI // functions. The wrapper is expected to unpack the arguments before -// passing them to the callee and perform checks before and after the -// native call to ensure that they GCLocker -// lock_critical/unlock_critical semantics are followed. Some other -// parts of JNI setup are skipped like the tear down of the JNI handle +// passing them to the callee. Critical native functions leave the state _in_Java, +// since they cannot stop for GC. +// Some other parts of JNI setup are skipped like the tear down of the JNI handle // block and the check for pending exceptions it's impossible for them // to be thrown. // -// They are roughly structured like this: -// if (GCLocker::needs_gc()) -// SharedRuntime::block_for_jni_critical(); -// tranistion to thread_in_native -// unpack arrray arguments and call native entry point -// check for safepoint in progress -// check if any thread suspend flags are set -// call into JVM and possible unlock the JNI critical -// if a GC was suppressed while in the critical native. -// transition back to thread_in_Java -// return to caller // nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, const methodHandle& method, @@ -1925,11 +1655,6 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ get_thread(thread); - if (is_critical_native && !Universe::heap()->supports_object_pinning()) { - check_needs_gc_for_critical_native(masm, thread, stack_slots, total_c_args, total_in_args, - oop_handle_offset, oop_maps, in_regs, in_sig_bt); - } - // // We immediately shuffle the arguments so that any vm call we have to // make from here on out (sync slow path, jvmti, etc.) we will have @@ -1963,11 +1688,6 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // OopMap* map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); - // Inbound arguments that need to be pinned for critical natives - GrowableArray pinned_args(total_in_args); - // Current stack slot for storing register based array argument - int pinned_slot = oop_handle_offset; - // Mark location of rbp, // map->set_callee_saved(VMRegImpl::stack2reg( stack_slots - 2), stack_slots * 2, 0, rbp->as_VMReg()); @@ -1980,26 +1700,6 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, case T_ARRAY: if (is_critical_native) { VMRegPair in_arg = in_regs[i]; - if (Universe::heap()->supports_object_pinning()) { - // gen_pin_object handles save and restore - // of any clobbered registers - gen_pin_object(masm, thread, in_arg); - pinned_args.append(i); - - // rax has pinned array - VMRegPair result_reg(rax->as_VMReg()); - if (!in_arg.first()->is_stack()) { - assert(pinned_slot <= stack_slots, "overflow"); - simple_move32(masm, result_reg, VMRegImpl::stack2reg(pinned_slot)); - pinned_slot += VMRegImpl::slots_per_word; - } else { - // Write back pinned value, it will be used to unpin this argument - __ movptr(Address(rbp, reg2offset_in(in_arg.first())), result_reg.first()->as_Register()); - } - // We have the array in register, use it - in_arg = result_reg; - } - unpack_array_argument(masm, in_arg, in_elem_bt[i], out_regs[c_arg + 1], out_regs[c_arg]); c_arg++; break; @@ -2154,15 +1854,14 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Finally just about ready to make the JNI call - // get JNIEnv* which is first argument to native if (!is_critical_native) { __ lea(rdx, Address(thread, in_bytes(JavaThread::jni_environment_offset()))); __ movptr(Address(rsp, 0), rdx); - } - // Now set thread in native - __ movl(Address(thread, JavaThread::thread_state_offset()), _thread_in_native); + // Now set thread in native + __ movl(Address(thread, JavaThread::thread_state_offset()), _thread_in_native); + } __ call(RuntimeAddress(native_func)); @@ -2193,24 +1892,17 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, default : ShouldNotReachHere(); } - // unpin pinned arguments - pinned_slot = oop_handle_offset; - if (pinned_args.length() > 0) { - // save return value that may be overwritten otherwise. - save_native_result(masm, ret_type, stack_slots); - for (int index = 0; index < pinned_args.length(); index ++) { - int i = pinned_args.at(index); - assert(pinned_slot <= stack_slots, "overflow"); - if (!in_regs[i].first()->is_stack()) { - int offset = pinned_slot * VMRegImpl::stack_slot_size; - __ movl(in_regs[i].first()->as_Register(), Address(rsp, offset)); - pinned_slot += VMRegImpl::slots_per_word; - } - // gen_pin_object handles save and restore - // of any other clobbered registers - gen_unpin_object(masm, thread, in_regs[i]); - } - restore_native_result(masm, ret_type, stack_slots); + Label after_transition; + + // If this is a critical native, check for a safepoint or suspend request after the call. + // If a safepoint is needed, transition to native, then to native_trans to handle + // safepoints like the native methods that are not critical natives. + if (is_critical_native) { + Label needs_safepoint; + __ safepoint_poll(needs_safepoint, thread, false /* at_return */, false /* in_nmethod */); + __ cmpl(Address(thread, JavaThread::suspend_flags_offset()), 0); + __ jcc(Assembler::equal, after_transition); + __ bind(needs_safepoint); } // Switch thread to "native transition" state before reading the synchronization state. @@ -2232,12 +1924,10 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ fldcw(ExternalAddress(StubRoutines::addr_fpu_cntrl_wrd_std())); } - Label after_transition; - // check for safepoint operation in progress and/or pending suspend requests { Label Continue, slow_path; - __ safepoint_poll(slow_path, thread, noreg); + __ safepoint_poll(slow_path, thread, true /* at_return */, false /* in_nmethod */); __ cmpl(Address(thread, JavaThread::suspend_flags_offset()), 0); __ jcc(Assembler::equal, Continue); @@ -2253,23 +1943,11 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, save_native_result(masm, ret_type, stack_slots); __ push(thread); - if (!is_critical_native) { - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans))); - } else { - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, - JavaThread::check_special_condition_for_native_trans_and_transition))); - } __ increment(rsp, wordSize); // Restore any method result value restore_native_result(masm, ret_type, stack_slots); - - if (is_critical_native) { - // The call above performed the transition to thread_in_Java so - // skip the transition logic below. - __ jmpb(after_transition); - } - __ bind(Continue); } @@ -2510,10 +2188,6 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, in_ByteSize(lock_slot_offset*VMRegImpl::stack_slot_size), oop_maps); - if (is_critical_native) { - nm->set_lazy_critical_native(true); - } - return nm; } diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index 1f96dc6ecfe..b238b0e0d35 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -42,6 +42,7 @@ #include "memory/universe.hpp" #include "oops/compiledICHolder.hpp" #include "oops/klass.inline.hpp" +#include "prims/methodHandles.hpp" #include "runtime/safepointMechanism.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/vframeArray.hpp" @@ -1377,222 +1378,6 @@ static void restore_args(MacroAssembler *masm, int arg_count, int first_arg, VMR } } - -static void save_or_restore_arguments(MacroAssembler* masm, - const int stack_slots, - const int total_in_args, - const int arg_save_area, - OopMap* map, - VMRegPair* in_regs, - BasicType* in_sig_bt) { - // if map is non-NULL then the code should store the values, - // otherwise it should load them. - int slot = arg_save_area; - // Save down double word first - for ( int i = 0; i < total_in_args; i++) { - if (in_regs[i].first()->is_XMMRegister() && in_sig_bt[i] == T_DOUBLE) { - int offset = slot * VMRegImpl::stack_slot_size; - slot += VMRegImpl::slots_per_word; - assert(slot <= stack_slots, "overflow"); - if (map != NULL) { - __ movdbl(Address(rsp, offset), in_regs[i].first()->as_XMMRegister()); - } else { - __ movdbl(in_regs[i].first()->as_XMMRegister(), Address(rsp, offset)); - } - } - if (in_regs[i].first()->is_Register() && - (in_sig_bt[i] == T_LONG || in_sig_bt[i] == T_ARRAY)) { - int offset = slot * VMRegImpl::stack_slot_size; - if (map != NULL) { - __ movq(Address(rsp, offset), in_regs[i].first()->as_Register()); - if (in_sig_bt[i] == T_ARRAY) { - map->set_oop(VMRegImpl::stack2reg(slot));; - } - } else { - __ movq(in_regs[i].first()->as_Register(), Address(rsp, offset)); - } - slot += VMRegImpl::slots_per_word; - } - } - // Save or restore single word registers - for ( int i = 0; i < total_in_args; i++) { - if (in_regs[i].first()->is_Register()) { - int offset = slot * VMRegImpl::stack_slot_size; - slot++; - assert(slot <= stack_slots, "overflow"); - - // Value is in an input register pass we must flush it to the stack - const Register reg = in_regs[i].first()->as_Register(); - switch (in_sig_bt[i]) { - case T_BOOLEAN: - case T_CHAR: - case T_BYTE: - case T_SHORT: - case T_INT: - if (map != NULL) { - __ movl(Address(rsp, offset), reg); - } else { - __ movl(reg, Address(rsp, offset)); - } - break; - case T_ARRAY: - case T_LONG: - // handled above - break; - case T_OBJECT: - default: ShouldNotReachHere(); - } - } else if (in_regs[i].first()->is_XMMRegister()) { - if (in_sig_bt[i] == T_FLOAT) { - int offset = slot * VMRegImpl::stack_slot_size; - slot++; - assert(slot <= stack_slots, "overflow"); - if (map != NULL) { - __ movflt(Address(rsp, offset), in_regs[i].first()->as_XMMRegister()); - } else { - __ movflt(in_regs[i].first()->as_XMMRegister(), Address(rsp, offset)); - } - } - } else if (in_regs[i].first()->is_stack()) { - if (in_sig_bt[i] == T_ARRAY && map != NULL) { - int offset_in_older_frame = in_regs[i].first()->reg2stack() + SharedRuntime::out_preserve_stack_slots(); - map->set_oop(VMRegImpl::stack2reg(offset_in_older_frame + stack_slots)); - } - } - } -} - -// Pin object, return pinned object or null in rax -static void gen_pin_object(MacroAssembler* masm, - VMRegPair reg) { - __ block_comment("gen_pin_object {"); - - // rax always contains oop, either incoming or - // pinned. - Register tmp_reg = rax; - - Label is_null; - VMRegPair tmp; - VMRegPair in_reg = reg; - - tmp.set_ptr(tmp_reg->as_VMReg()); - if (reg.first()->is_stack()) { - // Load the arg up from the stack - move_ptr(masm, reg, tmp); - reg = tmp; - } else { - __ movptr(rax, reg.first()->as_Register()); - } - __ testptr(reg.first()->as_Register(), reg.first()->as_Register()); - __ jccb(Assembler::equal, is_null); - - if (reg.first()->as_Register() != c_rarg1) { - __ movptr(c_rarg1, reg.first()->as_Register()); - } - - __ call_VM_leaf( - CAST_FROM_FN_PTR(address, SharedRuntime::pin_object), - r15_thread, c_rarg1); - - __ bind(is_null); - __ block_comment("} gen_pin_object"); -} - -// Unpin object -static void gen_unpin_object(MacroAssembler* masm, - VMRegPair reg) { - __ block_comment("gen_unpin_object {"); - Label is_null; - - if (reg.first()->is_stack()) { - __ movptr(c_rarg1, Address(rbp, reg2offset_in(reg.first()))); - } else if (reg.first()->as_Register() != c_rarg1) { - __ movptr(c_rarg1, reg.first()->as_Register()); - } - - __ testptr(c_rarg1, c_rarg1); - __ jccb(Assembler::equal, is_null); - - __ call_VM_leaf( - CAST_FROM_FN_PTR(address, SharedRuntime::unpin_object), - r15_thread, c_rarg1); - - __ bind(is_null); - __ block_comment("} gen_unpin_object"); -} - -// Check GCLocker::needs_gc and enter the runtime if it's true. This -// keeps a new JNI critical region from starting until a GC has been -// forced. Save down any oops in registers and describe them in an -// OopMap. -static void check_needs_gc_for_critical_native(MacroAssembler* masm, - int stack_slots, - int total_c_args, - int total_in_args, - int arg_save_area, - OopMapSet* oop_maps, - VMRegPair* in_regs, - BasicType* in_sig_bt) { - __ block_comment("check GCLocker::needs_gc"); - Label cont; - __ cmp8(ExternalAddress((address)GCLocker::needs_gc_address()), false); - __ jcc(Assembler::equal, cont); - - // Save down any incoming oops and call into the runtime to halt for a GC - - OopMap* map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); - save_or_restore_arguments(masm, stack_slots, total_in_args, - arg_save_area, map, in_regs, in_sig_bt); - - address the_pc = __ pc(); - oop_maps->add_gc_map( __ offset(), map); - __ set_last_Java_frame(rsp, noreg, the_pc); - - __ block_comment("block_for_jni_critical"); - __ movptr(c_rarg0, r15_thread); - __ mov(r12, rsp); // remember sp - __ subptr(rsp, frame::arg_reg_save_area_bytes); // windows - __ andptr(rsp, -16); // align stack as required by ABI - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::block_for_jni_critical))); - __ mov(rsp, r12); // restore sp - __ reinit_heapbase(); - - __ reset_last_Java_frame(false); - - save_or_restore_arguments(masm, stack_slots, total_in_args, - arg_save_area, NULL, in_regs, in_sig_bt); - __ bind(cont); -#ifdef ASSERT - if (StressCriticalJNINatives) { - // Stress register saving - OopMap* map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); - save_or_restore_arguments(masm, stack_slots, total_in_args, - arg_save_area, map, in_regs, in_sig_bt); - // Destroy argument registers - for (int i = 0; i < total_in_args - 1; i++) { - if (in_regs[i].first()->is_Register()) { - const Register reg = in_regs[i].first()->as_Register(); - __ xorptr(reg, reg); - } else if (in_regs[i].first()->is_XMMRegister()) { - __ xorpd(in_regs[i].first()->as_XMMRegister(), in_regs[i].first()->as_XMMRegister()); - } else if (in_regs[i].first()->is_FloatRegister()) { - ShouldNotReachHere(); - } else if (in_regs[i].first()->is_stack()) { - // Nothing to do - } else { - ShouldNotReachHere(); - } - if (in_sig_bt[i] == T_LONG || in_sig_bt[i] == T_DOUBLE) { - i++; - } - } - - save_or_restore_arguments(masm, stack_slots, total_in_args, - arg_save_area, NULL, in_regs, in_sig_bt); - } -#endif -} - // Unpack an array argument into a pointer to the body and the length // if the array is non-null, otherwise pass 0 for both. static void unpack_array_argument(MacroAssembler* masm, VMRegPair reg, BasicType in_elem_type, VMRegPair body_arg, VMRegPair length_arg) { @@ -1897,25 +1682,12 @@ static void gen_special_dispatch(MacroAssembler* masm, // Critical native functions are a shorthand for the use of // GetPrimtiveArrayCritical and disallow the use of any other JNI // functions. The wrapper is expected to unpack the arguments before -// passing them to the callee and perform checks before and after the -// native call to ensure that they GCLocker -// lock_critical/unlock_critical semantics are followed. Some other -// parts of JNI setup are skipped like the tear down of the JNI handle +// passing them to the callee. Critical native functions leave the state _in_Java, +// since they cannot stop for GC. +// Some other parts of JNI setup are skipped like the tear down of the JNI handle // block and the check for pending exceptions it's impossible for them // to be thrown. // -// They are roughly structured like this: -// if (GCLocker::needs_gc()) -// SharedRuntime::block_for_jni_critical(); -// tranistion to thread_in_native -// unpack arrray arguments and call native entry point -// check for safepoint in progress -// check if any thread suspend flags are set -// call into JVM and possible unlock the JNI critical -// if a GC was suppressed while in the critical native. -// transition back to thread_in_Java -// return to caller -// nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, const methodHandle& method, int compile_id, @@ -2216,11 +1988,6 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, const Register oop_handle_reg = r14; - if (is_critical_native && !Universe::heap()->supports_object_pinning()) { - check_needs_gc_for_critical_native(masm, stack_slots, total_c_args, total_in_args, - oop_handle_offset, oop_maps, in_regs, in_sig_bt); - } - // // We immediately shuffle the arguments so that any vm call we have to // make from here on out (sync slow path, jvmti, etc.) we will have @@ -2273,10 +2040,6 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // the incoming and outgoing registers are offset upwards and for // critical natives they are offset down. GrowableArray arg_order(2 * total_in_args); - // Inbound arguments that need to be pinned for critical natives - GrowableArray pinned_args(total_in_args); - // Current stack slot for storing register based array argument - int pinned_slot = oop_handle_offset; VMRegPair tmp_vmreg; tmp_vmreg.set2(rbx->as_VMReg()); @@ -2325,23 +2088,6 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, switch (in_sig_bt[i]) { case T_ARRAY: if (is_critical_native) { - // pin before unpack - if (Universe::heap()->supports_object_pinning()) { - save_args(masm, total_c_args, 0, out_regs); - gen_pin_object(masm, in_regs[i]); - pinned_args.append(i); - restore_args(masm, total_c_args, 0, out_regs); - - // rax has pinned array - VMRegPair result_reg; - result_reg.set_ptr(rax->as_VMReg()); - move_ptr(masm, result_reg, in_regs[i]); - if (!in_regs[i].first()->is_stack()) { - assert(pinned_slot <= stack_slots, "overflow"); - move_ptr(masm, result_reg, VMRegImpl::stack2reg(pinned_slot)); - pinned_slot += VMRegImpl::slots_per_word; - } - } unpack_array_argument(masm, in_regs[i], in_elem_bt[i], out_regs[c_arg + 1], out_regs[c_arg]); c_arg++; #ifdef ASSERT @@ -2520,17 +2266,15 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ bind(lock_done); } - // Finally just about ready to make the JNI call - // get JNIEnv* which is first argument to native if (!is_critical_native) { __ lea(c_rarg0, Address(r15_thread, in_bytes(JavaThread::jni_environment_offset()))); - } - // Now set thread in native - __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native); + // Now set thread in native + __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native); + } __ call(RuntimeAddress(native_func)); @@ -2556,22 +2300,17 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, default : ShouldNotReachHere(); } - // unpin pinned arguments - pinned_slot = oop_handle_offset; - if (pinned_args.length() > 0) { - // save return value that may be overwritten otherwise. - save_native_result(masm, ret_type, stack_slots); - for (int index = 0; index < pinned_args.length(); index ++) { - int i = pinned_args.at(index); - assert(pinned_slot <= stack_slots, "overflow"); - if (!in_regs[i].first()->is_stack()) { - int offset = pinned_slot * VMRegImpl::stack_slot_size; - __ movq(in_regs[i].first()->as_Register(), Address(rsp, offset)); - pinned_slot += VMRegImpl::slots_per_word; - } - gen_unpin_object(masm, in_regs[i]); - } - restore_native_result(masm, ret_type, stack_slots); + Label after_transition; + + // If this is a critical native, check for a safepoint or suspend request after the call. + // If a safepoint is needed, transition to native, then to native_trans to handle + // safepoints like the native methods that are not critical natives. + if (is_critical_native) { + Label needs_safepoint; + __ safepoint_poll(needs_safepoint, r15_thread, false /* at_return */, false /* in_nmethod */); + __ cmpl(Address(r15_thread, JavaThread::suspend_flags_offset()), 0); + __ jcc(Assembler::equal, after_transition); + __ bind(needs_safepoint); } // Switch thread to "native transition" state before reading the synchronization state. @@ -2588,14 +2327,12 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, Assembler::LoadLoad | Assembler::LoadStore | Assembler::StoreLoad | Assembler::StoreStore)); - Label after_transition; - // check for safepoint operation in progress and/or pending suspend requests { Label Continue; Label slow_path; - __ safepoint_poll(slow_path, r15_thread, rscratch1); + __ safepoint_poll(slow_path, r15_thread, true /* at_return */, false /* in_nmethod */); __ cmpl(Address(r15_thread, JavaThread::suspend_flags_offset()), 0); __ jcc(Assembler::equal, Continue); @@ -2613,22 +2350,11 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ mov(r12, rsp); // remember sp __ subptr(rsp, frame::arg_reg_save_area_bytes); // windows __ andptr(rsp, -16); // align stack as required by ABI - if (!is_critical_native) { - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans))); - } else { - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans_and_transition))); - } + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans))); __ mov(rsp, r12); // restore sp __ reinit_heapbase(); // Restore any method result value restore_native_result(masm, ret_type, stack_slots); - - if (is_critical_native) { - // The call above performed the transition to thread_in_Java so - // skip the transition logic below. - __ jmpb(after_transition); - } - __ bind(Continue); } @@ -2852,12 +2578,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, in_ByteSize(lock_slot_offset*VMRegImpl::stack_slot_size), oop_maps); - if (is_critical_native) { - nm->set_lazy_critical_native(true); - } - return nm; - } // this function returns the adjust size (in number of words) to a c2i adapter diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp index c5d0effae0f..4bc3b0340b5 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp @@ -587,6 +587,29 @@ class StubGenerator: public StubCodeGenerator { return start; } + address generate_iota_indices(const char *stub_name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", stub_name); + address start = __ pc(); + __ emit_data(0x03020100, relocInfo::none, 0); + __ emit_data(0x07060504, relocInfo::none, 0); + __ emit_data(0x0B0A0908, relocInfo::none, 0); + __ emit_data(0x0F0E0D0C, relocInfo::none, 0); + __ emit_data(0x13121110, relocInfo::none, 0); + __ emit_data(0x17161514, relocInfo::none, 0); + __ emit_data(0x1B1A1918, relocInfo::none, 0); + __ emit_data(0x1F1E1D1C, relocInfo::none, 0); + __ emit_data(0x23222120, relocInfo::none, 0); + __ emit_data(0x27262524, relocInfo::none, 0); + __ emit_data(0x2B2A2928, relocInfo::none, 0); + __ emit_data(0x2F2E2D2C, relocInfo::none, 0); + __ emit_data(0x33323130, relocInfo::none, 0); + __ emit_data(0x37363534, relocInfo::none, 0); + __ emit_data(0x3B3A3938, relocInfo::none, 0); + __ emit_data(0x3F3E3D3C, relocInfo::none, 0); + return start; + } + address generate_vector_mask_long_double(const char *stub_name, int32_t maskhi, int32_t masklo) { __ align(CodeEntryAlignment); StubCodeMark mark(this, "StubRoutines", stub_name); @@ -627,6 +650,40 @@ class StubGenerator: public StubCodeGenerator { return start; } + address generate_vector_custom_i32(const char *stub_name, Assembler::AvxVectorLen len, + int32_t val0, int32_t val1, int32_t val2, int32_t val3, + int32_t val4 = 0, int32_t val5 = 0, int32_t val6 = 0, int32_t val7 = 0, + int32_t val8 = 0, int32_t val9 = 0, int32_t val10 = 0, int32_t val11 = 0, + int32_t val12 = 0, int32_t val13 = 0, int32_t val14 = 0, int32_t val15 = 0) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", stub_name); + address start = __ pc(); + + assert(len != Assembler::AVX_NoVec, "vector len must be specified"); + __ emit_data(val0, relocInfo::none, 0); + __ emit_data(val1, relocInfo::none, 0); + __ emit_data(val2, relocInfo::none, 0); + __ emit_data(val3, relocInfo::none, 0); + if (len >= Assembler::AVX_256bit) { + __ emit_data(val4, relocInfo::none, 0); + __ emit_data(val5, relocInfo::none, 0); + __ emit_data(val6, relocInfo::none, 0); + __ emit_data(val7, relocInfo::none, 0); + if (len >= Assembler::AVX_512bit) { + __ emit_data(val8, relocInfo::none, 0); + __ emit_data(val9, relocInfo::none, 0); + __ emit_data(val10, relocInfo::none, 0); + __ emit_data(val11, relocInfo::none, 0); + __ emit_data(val12, relocInfo::none, 0); + __ emit_data(val13, relocInfo::none, 0); + __ emit_data(val14, relocInfo::none, 0); + __ emit_data(val15, relocInfo::none, 0); + } + } + + return start; + } + //---------------------------------------------------------------------------------------------------- // Non-destructive plausibility checks for oops @@ -3612,7 +3669,7 @@ class StubGenerator: public StubCodeGenerator { __ pusha(); // xmm0 and xmm1 may be used for passing float/double arguments - const int xmm_size = wordSize * 2; + const int xmm_size = wordSize * 4; const int xmm_spill_size = xmm_size * 2; __ subptr(rsp, xmm_spill_size); __ movdqu(Address(rsp, xmm_size * 1), xmm1); @@ -3902,8 +3959,19 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::x86::_vector_double_sign_mask = generate_vector_mask_long_double("vector_double_sign_mask", 0x7FFFFFFF, 0xFFFFFFFF); StubRoutines::x86::_vector_double_sign_flip = generate_vector_mask_long_double("vector_double_sign_flip", 0x80000000, 0x00000000); StubRoutines::x86::_vector_short_to_byte_mask = generate_vector_mask("vector_short_to_byte_mask", 0x00ff00ff); + StubRoutines::x86::_vector_int_to_byte_mask = generate_vector_mask("vector_int_to_byte_mask", 0x000000ff); + StubRoutines::x86::_vector_int_to_short_mask = generate_vector_mask("vector_int_to_short_mask", 0x0000ffff); + StubRoutines::x86::_vector_32_bit_mask = generate_vector_custom_i32("vector_32_bit_mask", Assembler::AVX_512bit, + 0xFFFFFFFF, 0, 0, 0); + StubRoutines::x86::_vector_64_bit_mask = generate_vector_custom_i32("vector_64_bit_mask", Assembler::AVX_512bit, + 0xFFFFFFFF, 0xFFFFFFFF, 0, 0); + StubRoutines::x86::_vector_int_shuffle_mask = generate_vector_mask("vector_int_shuffle_mask", 0x03020100); + StubRoutines::x86::_vector_short_shuffle_mask = generate_vector_mask("vector_short_shuffle_mask", 0x01000100); + StubRoutines::x86::_vector_long_shuffle_mask = generate_vector_mask_long_double("vector_long_shuffle_mask", 0x00000001, 0x0); StubRoutines::x86::_vector_byte_perm_mask = generate_vector_byte_perm_mask("vector_byte_perm_mask"); StubRoutines::x86::_vector_long_sign_mask = generate_vector_mask_long_double("vector_long_sign_mask", 0x80000000, 0x00000000); + StubRoutines::x86::_vector_all_bits_set = generate_vector_mask("vector_all_bits_set", 0xFFFFFFFF); + StubRoutines::x86::_vector_iota_indices = generate_iota_indices("iota_indices"); // support for verify_oop (must happen after universe_init) StubRoutines::_verify_oop_subroutine_entry = generate_verify_oop(); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index 3d2c7671304..b028e6a9c9b 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -809,6 +809,21 @@ class StubGenerator: public StubCodeGenerator { return start; } + address generate_iota_indices(const char *stub_name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", stub_name); + address start = __ pc(); + __ emit_data64(0x0706050403020100, relocInfo::none); + __ emit_data64(0x0F0E0D0C0B0A0908, relocInfo::none); + __ emit_data64(0x1716151413121110, relocInfo::none); + __ emit_data64(0x1F1E1D1C1B1A1918, relocInfo::none); + __ emit_data64(0x2726252423222120, relocInfo::none); + __ emit_data64(0x2F2E2D2C2B2A2928, relocInfo::none); + __ emit_data64(0x3736353433323130, relocInfo::none); + __ emit_data64(0x3F3E3D3C3B3A3938, relocInfo::none); + return start; + } + address generate_fp_mask(const char *stub_name, int64_t mask) { __ align(CodeEntryAlignment); StubCodeMark mark(this, "StubRoutines", stub_name); @@ -854,6 +869,57 @@ class StubGenerator: public StubCodeGenerator { return start; } + address generate_vector_fp_mask(const char *stub_name, int64_t mask) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", stub_name); + address start = __ pc(); + + __ emit_data64(mask, relocInfo::none); + __ emit_data64(mask, relocInfo::none); + __ emit_data64(mask, relocInfo::none); + __ emit_data64(mask, relocInfo::none); + __ emit_data64(mask, relocInfo::none); + __ emit_data64(mask, relocInfo::none); + __ emit_data64(mask, relocInfo::none); + __ emit_data64(mask, relocInfo::none); + + return start; + } + + address generate_vector_custom_i32(const char *stub_name, Assembler::AvxVectorLen len, + int32_t val0, int32_t val1, int32_t val2, int32_t val3, + int32_t val4 = 0, int32_t val5 = 0, int32_t val6 = 0, int32_t val7 = 0, + int32_t val8 = 0, int32_t val9 = 0, int32_t val10 = 0, int32_t val11 = 0, + int32_t val12 = 0, int32_t val13 = 0, int32_t val14 = 0, int32_t val15 = 0) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", stub_name); + address start = __ pc(); + + assert(len != Assembler::AVX_NoVec, "vector len must be specified"); + __ emit_data(val0, relocInfo::none, 0); + __ emit_data(val1, relocInfo::none, 0); + __ emit_data(val2, relocInfo::none, 0); + __ emit_data(val3, relocInfo::none, 0); + if (len >= Assembler::AVX_256bit) { + __ emit_data(val4, relocInfo::none, 0); + __ emit_data(val5, relocInfo::none, 0); + __ emit_data(val6, relocInfo::none, 0); + __ emit_data(val7, relocInfo::none, 0); + if (len >= Assembler::AVX_512bit) { + __ emit_data(val8, relocInfo::none, 0); + __ emit_data(val9, relocInfo::none, 0); + __ emit_data(val10, relocInfo::none, 0); + __ emit_data(val11, relocInfo::none, 0); + __ emit_data(val12, relocInfo::none, 0); + __ emit_data(val13, relocInfo::none, 0); + __ emit_data(val14, relocInfo::none, 0); + __ emit_data(val15, relocInfo::none, 0); + } + } + + return start; + } + // Non-destructive plausibility checks for oops // // Arguments: @@ -1124,59 +1190,28 @@ class StubGenerator: public StubCodeGenerator { __ align(OptoLoopAlignment); if (UseUnalignedLoadStores) { Label L_end; - // Copy 64-bytes per iteration - if (UseAVX > 2) { - Label L_loop_avx512, L_loop_avx2, L_32_byte_head, L_above_threshold, L_below_threshold; - - __ BIND(L_copy_bytes); - __ cmpptr(qword_count, (-1 * AVX3Threshold / 8)); - __ jccb(Assembler::less, L_above_threshold); - __ jmpb(L_below_threshold); - - __ bind(L_loop_avx512); - __ evmovdqul(xmm0, Address(end_from, qword_count, Address::times_8, -56), Assembler::AVX_512bit); - __ evmovdqul(Address(end_to, qword_count, Address::times_8, -56), xmm0, Assembler::AVX_512bit); - __ bind(L_above_threshold); - __ addptr(qword_count, 8); - __ jcc(Assembler::lessEqual, L_loop_avx512); - __ jmpb(L_32_byte_head); - - __ bind(L_loop_avx2); + __ BIND(L_loop); + if (UseAVX >= 2) { __ vmovdqu(xmm0, Address(end_from, qword_count, Address::times_8, -56)); __ vmovdqu(Address(end_to, qword_count, Address::times_8, -56), xmm0); __ vmovdqu(xmm1, Address(end_from, qword_count, Address::times_8, -24)); __ vmovdqu(Address(end_to, qword_count, Address::times_8, -24), xmm1); - __ bind(L_below_threshold); - __ addptr(qword_count, 8); - __ jcc(Assembler::lessEqual, L_loop_avx2); - - __ bind(L_32_byte_head); - __ subptr(qword_count, 4); // sub(8) and add(4) - __ jccb(Assembler::greater, L_end); } else { - __ BIND(L_loop); - if (UseAVX == 2) { - __ vmovdqu(xmm0, Address(end_from, qword_count, Address::times_8, -56)); - __ vmovdqu(Address(end_to, qword_count, Address::times_8, -56), xmm0); - __ vmovdqu(xmm1, Address(end_from, qword_count, Address::times_8, -24)); - __ vmovdqu(Address(end_to, qword_count, Address::times_8, -24), xmm1); - } else { - __ movdqu(xmm0, Address(end_from, qword_count, Address::times_8, -56)); - __ movdqu(Address(end_to, qword_count, Address::times_8, -56), xmm0); - __ movdqu(xmm1, Address(end_from, qword_count, Address::times_8, -40)); - __ movdqu(Address(end_to, qword_count, Address::times_8, -40), xmm1); - __ movdqu(xmm2, Address(end_from, qword_count, Address::times_8, -24)); - __ movdqu(Address(end_to, qword_count, Address::times_8, -24), xmm2); - __ movdqu(xmm3, Address(end_from, qword_count, Address::times_8, - 8)); - __ movdqu(Address(end_to, qword_count, Address::times_8, - 8), xmm3); - } - - __ BIND(L_copy_bytes); - __ addptr(qword_count, 8); - __ jcc(Assembler::lessEqual, L_loop); - __ subptr(qword_count, 4); // sub(8) and add(4) - __ jccb(Assembler::greater, L_end); + __ movdqu(xmm0, Address(end_from, qword_count, Address::times_8, -56)); + __ movdqu(Address(end_to, qword_count, Address::times_8, -56), xmm0); + __ movdqu(xmm1, Address(end_from, qword_count, Address::times_8, -40)); + __ movdqu(Address(end_to, qword_count, Address::times_8, -40), xmm1); + __ movdqu(xmm2, Address(end_from, qword_count, Address::times_8, -24)); + __ movdqu(Address(end_to, qword_count, Address::times_8, -24), xmm2); + __ movdqu(xmm3, Address(end_from, qword_count, Address::times_8, - 8)); + __ movdqu(Address(end_to, qword_count, Address::times_8, - 8), xmm3); } + + __ BIND(L_copy_bytes); + __ addptr(qword_count, 8); + __ jcc(Assembler::lessEqual, L_loop); + __ subptr(qword_count, 4); // sub(8) and add(4) + __ jccb(Assembler::greater, L_end); // Copy trailing 32 bytes if (UseAVX >= 2) { __ vmovdqu(xmm0, Address(end_from, qword_count, Address::times_8, -24)); @@ -1232,60 +1267,29 @@ class StubGenerator: public StubCodeGenerator { __ align(OptoLoopAlignment); if (UseUnalignedLoadStores) { Label L_end; - // Copy 64-bytes per iteration - if (UseAVX > 2) { - Label L_loop_avx512, L_loop_avx2, L_32_byte_head, L_above_threshold, L_below_threshold; - - __ BIND(L_copy_bytes); - __ cmpptr(qword_count, (AVX3Threshold / 8)); - __ jccb(Assembler::greater, L_above_threshold); - __ jmpb(L_below_threshold); - - __ BIND(L_loop_avx512); - __ evmovdqul(xmm0, Address(from, qword_count, Address::times_8, 0), Assembler::AVX_512bit); - __ evmovdqul(Address(dest, qword_count, Address::times_8, 0), xmm0, Assembler::AVX_512bit); - __ bind(L_above_threshold); - __ subptr(qword_count, 8); - __ jcc(Assembler::greaterEqual, L_loop_avx512); - __ jmpb(L_32_byte_head); - - __ bind(L_loop_avx2); + __ BIND(L_loop); + if (UseAVX >= 2) { __ vmovdqu(xmm0, Address(from, qword_count, Address::times_8, 32)); __ vmovdqu(Address(dest, qword_count, Address::times_8, 32), xmm0); - __ vmovdqu(xmm1, Address(from, qword_count, Address::times_8, 0)); - __ vmovdqu(Address(dest, qword_count, Address::times_8, 0), xmm1); - __ bind(L_below_threshold); - __ subptr(qword_count, 8); - __ jcc(Assembler::greaterEqual, L_loop_avx2); - - __ bind(L_32_byte_head); - __ addptr(qword_count, 4); // add(8) and sub(4) - __ jccb(Assembler::less, L_end); + __ vmovdqu(xmm1, Address(from, qword_count, Address::times_8, 0)); + __ vmovdqu(Address(dest, qword_count, Address::times_8, 0), xmm1); } else { - __ BIND(L_loop); - if (UseAVX == 2) { - __ vmovdqu(xmm0, Address(from, qword_count, Address::times_8, 32)); - __ vmovdqu(Address(dest, qword_count, Address::times_8, 32), xmm0); - __ vmovdqu(xmm1, Address(from, qword_count, Address::times_8, 0)); - __ vmovdqu(Address(dest, qword_count, Address::times_8, 0), xmm1); - } else { - __ movdqu(xmm0, Address(from, qword_count, Address::times_8, 48)); - __ movdqu(Address(dest, qword_count, Address::times_8, 48), xmm0); - __ movdqu(xmm1, Address(from, qword_count, Address::times_8, 32)); - __ movdqu(Address(dest, qword_count, Address::times_8, 32), xmm1); - __ movdqu(xmm2, Address(from, qword_count, Address::times_8, 16)); - __ movdqu(Address(dest, qword_count, Address::times_8, 16), xmm2); - __ movdqu(xmm3, Address(from, qword_count, Address::times_8, 0)); - __ movdqu(Address(dest, qword_count, Address::times_8, 0), xmm3); - } + __ movdqu(xmm0, Address(from, qword_count, Address::times_8, 48)); + __ movdqu(Address(dest, qword_count, Address::times_8, 48), xmm0); + __ movdqu(xmm1, Address(from, qword_count, Address::times_8, 32)); + __ movdqu(Address(dest, qword_count, Address::times_8, 32), xmm1); + __ movdqu(xmm2, Address(from, qword_count, Address::times_8, 16)); + __ movdqu(Address(dest, qword_count, Address::times_8, 16), xmm2); + __ movdqu(xmm3, Address(from, qword_count, Address::times_8, 0)); + __ movdqu(Address(dest, qword_count, Address::times_8, 0), xmm3); + } - __ BIND(L_copy_bytes); - __ subptr(qword_count, 8); - __ jcc(Assembler::greaterEqual, L_loop); + __ BIND(L_copy_bytes); + __ subptr(qword_count, 8); + __ jcc(Assembler::greaterEqual, L_loop); - __ addptr(qword_count, 4); // add(8) and sub(4) - __ jccb(Assembler::less, L_end); - } + __ addptr(qword_count, 4); // add(8) and sub(4) + __ jccb(Assembler::less, L_end); // Copy trailing 32 bytes if (UseAVX >= 2) { __ vmovdqu(xmm0, Address(from, qword_count, Address::times_8, 0)); @@ -1323,6 +1327,444 @@ class StubGenerator: public StubCodeGenerator { __ jcc(Assembler::greater, L_copy_8_bytes); // Copy trailing qwords } +#ifndef PRODUCT + int& get_profile_ctr(int shift) { + if ( 0 == shift) + return SharedRuntime::_jbyte_array_copy_ctr; + else if(1 == shift) + return SharedRuntime::_jshort_array_copy_ctr; + else if(2 == shift) + return SharedRuntime::_jint_array_copy_ctr; + else + return SharedRuntime::_jlong_array_copy_ctr; + } +#endif + + void setup_argument_regs(BasicType type) { + if (type == T_BYTE || type == T_SHORT) { + setup_arg_regs(); // from => rdi, to => rsi, count => rdx + // r9 and r10 may be used to save non-volatile registers + } else { + setup_arg_regs_using_thread(); // from => rdi, to => rsi, count => rdx + // r9 is used to save r15_thread + } + } + + void restore_argument_regs(BasicType type) { + if (type == T_BYTE || type == T_SHORT) { + restore_arg_regs(); + } else { + restore_arg_regs_using_thread(); + } + } + +#if COMPILER2_OR_JVMCI + // Note: Following rules apply to AVX3 optimized arraycopy stubs:- + // - If target supports AVX3 features (BW+VL+F) then implementation uses 32 byte vectors (YMMs) + // for both special cases (various small block sizes) and aligned copy loop. This is the + // default configuration. + // - If copy length is above AVX3Threshold, then implementation use 64 byte vectors (ZMMs) + // for main copy loop (and subsequent tail) since bulk of the cycles will be consumed in it. + // - If user forces MaxVectorSize=32 then above 4096 bytes its seen that REP MOVs shows a + // better performance for disjoint copies. For conjoint/backward copy vector based + // copy performs better. + // - If user sets AVX3Threshold=0, then special cases for small blocks sizes operate over + // 64 byte vector registers (ZMMs). + + // Inputs: + // c_rarg0 - source array address + // c_rarg1 - destination array address + // c_rarg2 - element count, treated as ssize_t, can be zero + // + // + // Side Effects: + // disjoint_copy_avx3_masked is set to the no-overlap entry point + // used by generate_conjoint_[byte/int/short/long]_copy(). + // + + address generate_disjoint_copy_avx3_masked(address* entry, const char *name, int shift, + bool aligned, bool is_oop, bool dest_uninitialized) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + bool use64byteVector = MaxVectorSize > 32 && AVX3Threshold == 0; + Label L_main_loop, L_main_loop_64bytes, L_tail, L_tail64, L_exit, L_entry; + Label L_repmovs, L_main_pre_loop, L_main_pre_loop_64bytes, L_pre_main_post_64; + const Register from = rdi; // source array address + const Register to = rsi; // destination array address + const Register count = rdx; // elements count + const Register temp1 = r8; + const Register temp2 = r11; + const Register temp3 = rax; + const Register temp4 = rcx; + // End pointers are inclusive, and if count is not zero they point + // to the last unit copied: end_to[0] := end_from[0] + + __ enter(); // required for proper stackwalking of RuntimeStub frame + assert_clean_int(c_rarg2, rax); // Make sure 'count' is clean int. + + if (entry != NULL) { + *entry = __ pc(); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + BLOCK_COMMENT("Entry:"); + } + + BasicType type_vec[] = { T_BYTE, T_SHORT, T_INT, T_LONG}; + BasicType type = is_oop ? T_OBJECT : type_vec[shift]; + + setup_argument_regs(type); + + DecoratorSet decorators = IN_HEAP | IS_ARRAY | ARRAYCOPY_DISJOINT; + if (dest_uninitialized) { + decorators |= IS_DEST_UNINITIALIZED; + } + if (aligned) { + decorators |= ARRAYCOPY_ALIGNED; + } + BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler(); + bs->arraycopy_prologue(_masm, decorators, type, from, to, count); + + { + // Type(shift) byte(0), short(1), int(2), long(3) + int loop_size[] = { 192, 96, 48, 24}; + int threshold[] = { 4096, 2048, 1024, 512}; + + // UnsafeCopyMemory page error: continue after ucm + UnsafeCopyMemoryMark ucmm(this, !is_oop && !aligned, true); + // 'from', 'to' and 'count' are now valid + + // temp1 holds remaining count and temp4 holds running count used to compute + // next address offset for start of to/from addresses (temp4 * scale). + __ mov64(temp4, 0); + __ movq(temp1, count); + + // Zero length check. + __ BIND(L_tail); + __ cmpq(temp1, 0); + __ jcc(Assembler::lessEqual, L_exit); + + // Special cases using 32 byte [masked] vector copy operations. + __ arraycopy_avx3_special_cases(xmm1, k2, from, to, temp1, shift, + temp4, temp3, use64byteVector, L_entry, L_exit); + + // PRE-MAIN-POST loop for aligned copy. + __ BIND(L_entry); + + if (AVX3Threshold != 0) { + __ cmpq(count, threshold[shift]); + if (MaxVectorSize == 64) { + // Copy using 64 byte vectors. + __ jcc(Assembler::greaterEqual, L_pre_main_post_64); + } else { + assert(MaxVectorSize < 64, "vector size should be < 64 bytes"); + // REP MOVS offer a faster copy path. + __ jcc(Assembler::greaterEqual, L_repmovs); + } + } + + if (MaxVectorSize < 64 || AVX3Threshold != 0) { + // Partial copy to make dst address 32 byte aligned. + __ movq(temp2, to); + __ andq(temp2, 31); + __ jcc(Assembler::equal, L_main_pre_loop); + + __ negptr(temp2); + __ addq(temp2, 32); + if (shift) { + __ shrq(temp2, shift); + } + __ movq(temp3, temp2); + __ copy32_masked_avx(to, from, xmm1, k2, temp3, temp4, temp1, shift); + __ movq(temp4, temp2); + __ movq(temp1, count); + __ subq(temp1, temp2); + + __ cmpq(temp1, loop_size[shift]); + __ jcc(Assembler::less, L_tail); + + __ BIND(L_main_pre_loop); + __ subq(temp1, loop_size[shift]); + + // Main loop with aligned copy block size of 192 bytes at 32 byte granularity. + __ BIND(L_main_loop); + __ copy64_avx(to, from, temp4, xmm1, false, shift, 0); + __ copy64_avx(to, from, temp4, xmm1, false, shift, 64); + __ copy64_avx(to, from, temp4, xmm1, false, shift, 128); + __ addptr(temp4, loop_size[shift]); + __ subq(temp1, loop_size[shift]); + __ jcc(Assembler::greater, L_main_loop); + + __ addq(temp1, loop_size[shift]); + + // Tail loop. + __ jmp(L_tail); + + __ BIND(L_repmovs); + __ movq(temp2, temp1); + // Swap to(RSI) and from(RDI) addresses to comply with REP MOVs semantics. + __ movq(temp3, to); + __ movq(to, from); + __ movq(from, temp3); + // Save to/from for restoration post rep_mov. + __ movq(temp1, to); + __ movq(temp3, from); + if(shift < 3) { + __ shrq(temp2, 3-shift); // quad word count + } + __ movq(temp4 , temp2); // move quad ward count into temp4(RCX). + __ rep_mov(); + __ shlq(temp2, 3); // convert quad words into byte count. + if(shift) { + __ shrq(temp2, shift); // type specific count. + } + // Restore original addresses in to/from. + __ movq(to, temp3); + __ movq(from, temp1); + __ movq(temp4, temp2); + __ movq(temp1, count); + __ subq(temp1, temp2); // tailing part (less than a quad ward size). + __ jmp(L_tail); + } + + if (MaxVectorSize > 32) { + __ BIND(L_pre_main_post_64); + // Partial copy to make dst address 64 byte aligned. + __ movq(temp2, to); + __ andq(temp2, 63); + __ jcc(Assembler::equal, L_main_pre_loop_64bytes); + + __ negptr(temp2); + __ addq(temp2, 64); + if (shift) { + __ shrq(temp2, shift); + } + __ movq(temp3, temp2); + __ copy64_masked_avx(to, from, xmm1, k2, temp3, temp4, temp1, shift, 0 , true); + __ movq(temp4, temp2); + __ movq(temp1, count); + __ subq(temp1, temp2); + + __ cmpq(temp1, loop_size[shift]); + __ jcc(Assembler::less, L_tail64); + + __ BIND(L_main_pre_loop_64bytes); + __ subq(temp1, loop_size[shift]); + + // Main loop with aligned copy block size of 192 bytes at + // 64 byte copy granularity. + __ BIND(L_main_loop_64bytes); + __ copy64_avx(to, from, temp4, xmm1, false, shift, 0 , true); + __ copy64_avx(to, from, temp4, xmm1, false, shift, 64, true); + __ copy64_avx(to, from, temp4, xmm1, false, shift, 128, true); + __ addptr(temp4, loop_size[shift]); + __ subq(temp1, loop_size[shift]); + __ jcc(Assembler::greater, L_main_loop_64bytes); + + __ addq(temp1, loop_size[shift]); + // Zero length check. + __ jcc(Assembler::lessEqual, L_exit); + + __ BIND(L_tail64); + + // Tail handling using 64 byte [masked] vector copy operations. + use64byteVector = true; + __ arraycopy_avx3_special_cases(xmm1, k2, from, to, temp1, shift, + temp4, temp3, use64byteVector, L_entry, L_exit); + } + __ BIND(L_exit); + } + + address ucme_exit_pc = __ pc(); + // When called from generic_arraycopy r11 contains specific values + // used during arraycopy epilogue, re-initializing r11. + if (is_oop) { + __ movq(r11, shift == 3 ? count : to); + } + bs->arraycopy_epilogue(_masm, decorators, type, from, to, count); + restore_argument_regs(type); + inc_counter_np(get_profile_ctr(shift)); // Update counter after rscratch1 is free + __ xorptr(rax, rax); // return 0 + __ vzeroupper(); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + return start; + } + + // Inputs: + // c_rarg0 - source array address + // c_rarg1 - destination array address + // c_rarg2 - element count, treated as ssize_t, can be zero + // + // + address generate_conjoint_copy_avx3_masked(address* entry, const char *name, int shift, + address nooverlap_target, bool aligned, bool is_oop, + bool dest_uninitialized) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + bool use64byteVector = MaxVectorSize > 32 && AVX3Threshold == 0; + + Label L_main_pre_loop, L_main_pre_loop_64bytes, L_pre_main_post_64; + Label L_main_loop, L_main_loop_64bytes, L_tail, L_tail64, L_exit, L_entry; + const Register from = rdi; // source array address + const Register to = rsi; // destination array address + const Register count = rdx; // elements count + const Register temp1 = r8; + const Register temp2 = rcx; + const Register temp3 = r11; + const Register temp4 = rax; + // End pointers are inclusive, and if count is not zero they point + // to the last unit copied: end_to[0] := end_from[0] + + __ enter(); // required for proper stackwalking of RuntimeStub frame + assert_clean_int(c_rarg2, rax); // Make sure 'count' is clean int. + + if (entry != NULL) { + *entry = __ pc(); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + BLOCK_COMMENT("Entry:"); + } + + array_overlap_test(nooverlap_target, (Address::ScaleFactor)(shift)); + + BasicType type_vec[] = { T_BYTE, T_SHORT, T_INT, T_LONG}; + BasicType type = is_oop ? T_OBJECT : type_vec[shift]; + + setup_argument_regs(type); + + DecoratorSet decorators = IN_HEAP | IS_ARRAY; + if (dest_uninitialized) { + decorators |= IS_DEST_UNINITIALIZED; + } + if (aligned) { + decorators |= ARRAYCOPY_ALIGNED; + } + BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler(); + bs->arraycopy_prologue(_masm, decorators, type, from, to, count); + { + // Type(shift) byte(0), short(1), int(2), long(3) + int loop_size[] = { 192, 96, 48, 24}; + int threshold[] = { 4096, 2048, 1024, 512}; + + // UnsafeCopyMemory page error: continue after ucm + UnsafeCopyMemoryMark ucmm(this, !is_oop && !aligned, true); + // 'from', 'to' and 'count' are now valid + + // temp1 holds remaining count. + __ movq(temp1, count); + + // Zero length check. + __ BIND(L_tail); + __ cmpq(temp1, 0); + __ jcc(Assembler::lessEqual, L_exit); + + __ mov64(temp2, 0); + __ movq(temp3, temp1); + // Special cases using 32 byte [masked] vector copy operations. + __ arraycopy_avx3_special_cases_conjoint(xmm1, k2, from, to, temp2, temp3, temp1, shift, + temp4, use64byteVector, L_entry, L_exit); + + // PRE-MAIN-POST loop for aligned copy. + __ BIND(L_entry); + + if (MaxVectorSize > 32 && AVX3Threshold != 0) { + __ cmpq(temp1, threshold[shift]); + __ jcc(Assembler::greaterEqual, L_pre_main_post_64); + } + + if (MaxVectorSize < 64 || AVX3Threshold != 0) { + // Partial copy to make dst address 32 byte aligned. + __ leaq(temp2, Address(to, temp1, (Address::ScaleFactor)(shift), 0)); + __ andq(temp2, 31); + __ jcc(Assembler::equal, L_main_pre_loop); + + if (shift) { + __ shrq(temp2, shift); + } + __ subq(temp1, temp2); + __ copy32_masked_avx(to, from, xmm1, k2, temp2, temp1, temp3, shift); + + __ cmpq(temp1, loop_size[shift]); + __ jcc(Assembler::less, L_tail); + + __ BIND(L_main_pre_loop); + + // Main loop with aligned copy block size of 192 bytes at 32 byte granularity. + __ BIND(L_main_loop); + __ copy64_avx(to, from, temp1, xmm1, true, shift, -64); + __ copy64_avx(to, from, temp1, xmm1, true, shift, -128); + __ copy64_avx(to, from, temp1, xmm1, true, shift, -192); + __ subptr(temp1, loop_size[shift]); + __ cmpq(temp1, loop_size[shift]); + __ jcc(Assembler::greater, L_main_loop); + + // Tail loop. + __ jmp(L_tail); + } + + if (MaxVectorSize > 32) { + __ BIND(L_pre_main_post_64); + // Partial copy to make dst address 64 byte aligned. + __ leaq(temp2, Address(to, temp1, (Address::ScaleFactor)(shift), 0)); + __ andq(temp2, 63); + __ jcc(Assembler::equal, L_main_pre_loop_64bytes); + + if (shift) { + __ shrq(temp2, shift); + } + __ subq(temp1, temp2); + __ copy64_masked_avx(to, from, xmm1, k2, temp2, temp1, temp3, shift, 0 , true); + + __ cmpq(temp1, loop_size[shift]); + __ jcc(Assembler::less, L_tail64); + + __ BIND(L_main_pre_loop_64bytes); + + // Main loop with aligned copy block size of 192 bytes at + // 64 byte copy granularity. + __ BIND(L_main_loop_64bytes); + __ copy64_avx(to, from, temp1, xmm1, true, shift, -64 , true); + __ copy64_avx(to, from, temp1, xmm1, true, shift, -128, true); + __ copy64_avx(to, from, temp1, xmm1, true, shift, -192, true); + __ subq(temp1, loop_size[shift]); + __ cmpq(temp1, loop_size[shift]); + __ jcc(Assembler::greater, L_main_loop_64bytes); + + // Zero length check. + __ cmpq(temp1, 0); + __ jcc(Assembler::lessEqual, L_exit); + + __ BIND(L_tail64); + + // Tail handling using 64 byte [masked] vector copy operations. + use64byteVector = true; + __ mov64(temp2, 0); + __ movq(temp3, temp1); + __ arraycopy_avx3_special_cases_conjoint(xmm1, k2, from, to, temp2, temp3, temp1, shift, + temp4, use64byteVector, L_entry, L_exit); + } + __ BIND(L_exit); + } + address ucme_exit_pc = __ pc(); + // When called from generic_arraycopy r11 contains specific values + // used during arraycopy epilogue, re-initializing r11. + if(is_oop) { + __ movq(r11, count); + } + bs->arraycopy_epilogue(_masm, decorators, type, from, to, count); + restore_argument_regs(type); + inc_counter_np(get_profile_ctr(shift)); // Update counter after rscratch1 is free + __ xorptr(rax, rax); // return 0 + __ vzeroupper(); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + return start; + } +#endif // COMPILER2_OR_JVMCI + + // Arguments: // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary // ignored @@ -1343,6 +1785,12 @@ class StubGenerator: public StubCodeGenerator { // used by generate_conjoint_byte_copy(). // address generate_disjoint_byte_copy(bool aligned, address* entry, const char *name) { +#if COMPILER2_OR_JVMCI + if (VM_Version::supports_avx512vlbw() && MaxVectorSize >= 32) { + return generate_disjoint_copy_avx3_masked(entry, "jbyte_disjoint_arraycopy_avx3", 0, + aligned, false, false); + } +#endif __ align(CodeEntryAlignment); StubCodeMark mark(this, "StubRoutines", name); address start = __ pc(); @@ -1453,6 +1901,12 @@ class StubGenerator: public StubCodeGenerator { // address generate_conjoint_byte_copy(bool aligned, address nooverlap_target, address* entry, const char *name) { +#if COMPILER2_OR_JVMCI + if (VM_Version::supports_avx512vlbw() && MaxVectorSize >= 32) { + return generate_conjoint_copy_avx3_masked(entry, "jbyte_conjoint_arraycopy_avx3", 0, + nooverlap_target, aligned, false, false); + } +#endif __ align(CodeEntryAlignment); StubCodeMark mark(this, "StubRoutines", name); address start = __ pc(); @@ -1558,6 +2012,13 @@ class StubGenerator: public StubCodeGenerator { // used by generate_conjoint_short_copy(). // address generate_disjoint_short_copy(bool aligned, address *entry, const char *name) { +#if COMPILER2_OR_JVMCI + if (VM_Version::supports_avx512vlbw() && MaxVectorSize >= 32) { + return generate_disjoint_copy_avx3_masked(entry, "jshort_disjoint_arraycopy_avx3", 1, + aligned, false, false); + } +#endif + __ align(CodeEntryAlignment); StubCodeMark mark(this, "StubRoutines", name); address start = __ pc(); @@ -1682,6 +2143,12 @@ class StubGenerator: public StubCodeGenerator { // address generate_conjoint_short_copy(bool aligned, address nooverlap_target, address *entry, const char *name) { +#if COMPILER2_OR_JVMCI + if (VM_Version::supports_avx512vlbw() && MaxVectorSize >= 32) { + return generate_conjoint_copy_avx3_masked(entry, "jshort_conjoint_arraycopy_avx3", 1, + nooverlap_target, aligned, false, false); + } +#endif __ align(CodeEntryAlignment); StubCodeMark mark(this, "StubRoutines", name); address start = __ pc(); @@ -1780,6 +2247,13 @@ class StubGenerator: public StubCodeGenerator { // address generate_disjoint_int_oop_copy(bool aligned, bool is_oop, address* entry, const char *name, bool dest_uninitialized = false) { +#if COMPILER2_OR_JVMCI + if (VM_Version::supports_avx512vlbw() && MaxVectorSize >= 32) { + return generate_disjoint_copy_avx3_masked(entry, "jint_disjoint_arraycopy_avx3", 2, + aligned, is_oop, dest_uninitialized); + } +#endif + __ align(CodeEntryAlignment); StubCodeMark mark(this, "StubRoutines", name); address start = __ pc(); @@ -1884,6 +2358,12 @@ class StubGenerator: public StubCodeGenerator { address generate_conjoint_int_oop_copy(bool aligned, bool is_oop, address nooverlap_target, address *entry, const char *name, bool dest_uninitialized = false) { +#if COMPILER2_OR_JVMCI + if (VM_Version::supports_avx512vlbw() && MaxVectorSize >= 32) { + return generate_conjoint_copy_avx3_masked(entry, "jint_conjoint_arraycopy_avx3", 2, + nooverlap_target, aligned, is_oop, dest_uninitialized); + } +#endif __ align(CodeEntryAlignment); StubCodeMark mark(this, "StubRoutines", name); address start = __ pc(); @@ -1991,6 +2471,12 @@ class StubGenerator: public StubCodeGenerator { // address generate_disjoint_long_oop_copy(bool aligned, bool is_oop, address *entry, const char *name, bool dest_uninitialized = false) { +#if COMPILER2_OR_JVMCI + if (VM_Version::supports_avx512vlbw() && MaxVectorSize >= 32) { + return generate_disjoint_copy_avx3_masked(entry, "jlong_disjoint_arraycopy_avx3", 3, + aligned, is_oop, dest_uninitialized); + } +#endif __ align(CodeEntryAlignment); StubCodeMark mark(this, "StubRoutines", name); address start = __ pc(); @@ -2095,6 +2581,12 @@ class StubGenerator: public StubCodeGenerator { address generate_conjoint_long_oop_copy(bool aligned, bool is_oop, address nooverlap_target, address *entry, const char *name, bool dest_uninitialized = false) { +#if COMPILER2_OR_JVMCI + if (VM_Version::supports_avx512vlbw() && MaxVectorSize >= 32) { + return generate_conjoint_copy_avx3_masked(entry, "jlong_conjoint_arraycopy_avx3", 3, + nooverlap_target, aligned, is_oop, dest_uninitialized); + } +#endif __ align(CodeEntryAlignment); StubCodeMark mark(this, "StubRoutines", name); address start = __ pc(); @@ -2513,7 +3005,7 @@ class StubGenerator: public StubCodeGenerator { address long_copy_entry, address checkcast_copy_entry) { Label L_failed, L_failed_0, L_objArray; - Label L_copy_bytes, L_copy_shorts, L_copy_ints, L_copy_longs; + Label L_copy_shorts, L_copy_ints, L_copy_longs; // Input registers const Register src = c_rarg0; // source array oop @@ -2524,7 +3016,7 @@ class StubGenerator: public StubCodeGenerator { const Register length = c_rarg4; const Register rklass_tmp = r9; // load_klass #else - const Address length(rsp, 6 * wordSize); // elements count is on stack on Win64 + const Address length(rsp, 7 * wordSize); // elements count is on stack on Win64 const Register rklass_tmp = rdi; // load_klass #endif @@ -2546,6 +3038,10 @@ class StubGenerator: public StubCodeGenerator { __ enter(); // required for proper stackwalking of RuntimeStub frame +#ifdef _WIN64 + __ push(rklass_tmp); // rdi is callee-save on Windows +#endif + // bump this on entry, not on exit: inc_counter_np(SharedRuntime::_generic_array_copy_ctr); @@ -2676,6 +3172,10 @@ class StubGenerator: public StubCodeGenerator { BLOCK_COMMENT("choose copy loop based on element size"); __ andl(rax_lh, Klass::_lh_log2_element_size_mask); // rax_lh -> rax_elsize +#ifdef _WIN64 + __ pop(rklass_tmp); // Restore callee-save rdi +#endif + // next registers should be set before the jump to corresponding stub const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -2684,7 +3184,6 @@ class StubGenerator: public StubCodeGenerator { // 'from', 'to', 'count' registers should be set in such order // since they are the same as 'src', 'src_pos', 'dst'. - __ BIND(L_copy_bytes); __ cmpl(rax_elsize, 0); __ jccb(Assembler::notEqual, L_copy_shorts); __ lea(from, Address(src, src_pos, Address::times_1, 0));// src_addr @@ -2745,6 +3244,9 @@ class StubGenerator: public StubCodeGenerator { arrayOopDesc::base_offset_in_bytes(T_OBJECT))); // dst_addr __ movl2ptr(count, r11_length); // length __ BIND(L_plain_copy); +#ifdef _WIN64 + __ pop(rklass_tmp); // Restore callee-save rdi +#endif __ jump(RuntimeAddress(oop_copy_entry)); __ BIND(L_checkcast_copy); @@ -2784,6 +3286,10 @@ class StubGenerator: public StubCodeGenerator { __ movl( sco_temp, Address(r11_dst_klass, sco_offset)); assert_clean_int(sco_temp, rax); +#ifdef _WIN64 + __ pop(rklass_tmp); // Restore callee-save rdi +#endif + // the checkcast_copy loop needs two extra arguments: assert(c_rarg3 == sco_temp, "#3 already in place"); // Set up arguments for checkcast_copy_entry. @@ -2793,6 +3299,9 @@ class StubGenerator: public StubCodeGenerator { } __ BIND(L_failed); +#ifdef _WIN64 + __ pop(rklass_tmp); // Restore callee-save rdi +#endif __ xorptr(rax, rax); __ notptr(rax); // return -1 __ leave(); // required for proper stackwalking of RuntimeStub frame @@ -6326,12 +6835,25 @@ address generate_avx_ghash_processBlocks() { StubRoutines::x86::_vector_float_sign_flip = generate_vector_mask("vector_float_sign_flip", 0x8000000080000000); StubRoutines::x86::_vector_double_sign_mask = generate_vector_mask("vector_double_sign_mask", 0x7FFFFFFFFFFFFFFF); StubRoutines::x86::_vector_double_sign_flip = generate_vector_mask("vector_double_sign_flip", 0x8000000000000000); + StubRoutines::x86::_vector_all_bits_set = generate_vector_mask("vector_all_bits_set", 0xFFFFFFFFFFFFFFFF); StubRoutines::x86::_vector_short_to_byte_mask = generate_vector_mask("vector_short_to_byte_mask", 0x00ff00ff00ff00ff); StubRoutines::x86::_vector_byte_perm_mask = generate_vector_byte_perm_mask("vector_byte_perm_mask"); + StubRoutines::x86::_vector_int_to_byte_mask = generate_vector_mask("vector_int_to_byte_mask", 0x000000ff000000ff); + StubRoutines::x86::_vector_int_to_short_mask = generate_vector_mask("vector_int_to_short_mask", 0x0000ffff0000ffff); + StubRoutines::x86::_vector_32_bit_mask = generate_vector_custom_i32("vector_32_bit_mask", Assembler::AVX_512bit, + 0xFFFFFFFF, 0, 0, 0); + StubRoutines::x86::_vector_64_bit_mask = generate_vector_custom_i32("vector_64_bit_mask", Assembler::AVX_512bit, + 0xFFFFFFFF, 0xFFFFFFFF, 0, 0); + StubRoutines::x86::_vector_int_shuffle_mask = generate_vector_mask("vector_int_shuffle_mask", 0x0302010003020100); + StubRoutines::x86::_vector_short_shuffle_mask = generate_vector_mask("vector_short_shuffle_mask", 0x0100010001000100); + StubRoutines::x86::_vector_long_shuffle_mask = generate_vector_mask("vector_long_shuffle_mask", 0x0000000100000000); StubRoutines::x86::_vector_long_sign_mask = generate_vector_mask("vector_long_sign_mask", 0x8000000000000000); + StubRoutines::x86::_vector_iota_indices = generate_iota_indices("iota_indices"); // support for verify_oop (must happen after universe_init) - StubRoutines::_verify_oop_subroutine_entry = generate_verify_oop(); + if (VerifyOops) { + StubRoutines::_verify_oop_subroutine_entry = generate_verify_oop(); + } // data cache line writeback StubRoutines::_data_cache_writeback = generate_data_cache_writeback(); diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.cpp b/src/hotspot/cpu/x86/stubRoutines_x86.cpp index 5d93d118e7b..45762902db2 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.cpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.cpp @@ -44,12 +44,21 @@ address StubRoutines::x86::_upper_word_mask_addr = NULL; address StubRoutines::x86::_shuffle_byte_flip_mask_addr = NULL; address StubRoutines::x86::_k256_adr = NULL; address StubRoutines::x86::_vector_short_to_byte_mask = NULL; +address StubRoutines::x86::_vector_int_to_byte_mask = NULL; +address StubRoutines::x86::_vector_int_to_short_mask = NULL; +address StubRoutines::x86::_vector_all_bits_set = NULL; +address StubRoutines::x86::_vector_short_shuffle_mask = NULL; +address StubRoutines::x86::_vector_int_shuffle_mask = NULL; +address StubRoutines::x86::_vector_long_shuffle_mask = NULL; address StubRoutines::x86::_vector_float_sign_mask = NULL; address StubRoutines::x86::_vector_float_sign_flip = NULL; address StubRoutines::x86::_vector_double_sign_mask = NULL; address StubRoutines::x86::_vector_double_sign_flip = NULL; address StubRoutines::x86::_vector_byte_perm_mask = NULL; address StubRoutines::x86::_vector_long_sign_mask = NULL; +address StubRoutines::x86::_vector_iota_indices = NULL; +address StubRoutines::x86::_vector_32_bit_mask = NULL; +address StubRoutines::x86::_vector_64_bit_mask = NULL; #ifdef _LP64 address StubRoutines::x86::_k256_W_adr = NULL; address StubRoutines::x86::_k512_W_addr = NULL; diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.hpp b/src/hotspot/cpu/x86/stubRoutines_x86.hpp index a23ee3666a6..fa4c34016a5 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.hpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.hpp @@ -33,7 +33,7 @@ static bool returns_to_call_stub(address return_pc) { return return_pc == _call_ enum platform_dependent_constants { code_size1 = 20000 LP64_ONLY(+10000), // simply increase if too small (assembler will crash if too small) - code_size2 = 35300 LP64_ONLY(+11400) // simply increase if too small (assembler will crash if too small) + code_size2 = 35300 LP64_ONLY(+25000) // simply increase if too small (assembler will crash if too small) }; class x86 { @@ -146,8 +146,17 @@ class x86 { static address _vector_float_sign_flip; static address _vector_double_sign_mask; static address _vector_double_sign_flip; - static address _vector_byte_perm_mask; static address _vector_long_sign_mask; + static address _vector_all_bits_set; + static address _vector_byte_perm_mask; + static address _vector_int_to_byte_mask; + static address _vector_int_to_short_mask; + static address _vector_32_bit_mask; + static address _vector_64_bit_mask; + static address _vector_int_shuffle_mask; + static address _vector_short_shuffle_mask; + static address _vector_long_shuffle_mask; + static address _vector_iota_indices; #ifdef _LP64 static juint _k256_W[]; static address _k256_W_adr; @@ -248,13 +257,50 @@ class x86 { return _vector_double_sign_flip; } + static address vector_all_bits_set() { + return _vector_all_bits_set; + } + static address vector_byte_perm_mask() { return _vector_byte_perm_mask; } + static address vector_int_to_byte_mask() { + return _vector_int_to_byte_mask; + } + + static address vector_int_to_short_mask() { + return _vector_int_to_short_mask; + } + + static address vector_32_bit_mask() { + return _vector_32_bit_mask; + } + + static address vector_64_bit_mask() { + return _vector_64_bit_mask; + } + + static address vector_int_shuffle_mask() { + return _vector_int_shuffle_mask; + } + + static address vector_short_shuffle_mask() { + return _vector_short_shuffle_mask; + } + + static address vector_long_shuffle_mask() { + return _vector_long_shuffle_mask; + } + static address vector_long_sign_mask() { return _vector_long_sign_mask; } + + static address vector_iota_indices() { + return _vector_iota_indices; + } + #ifdef _LP64 static address k256_W_addr() { return _k256_W_adr; } static address k512_W_addr() { return _k512_W_addr; } diff --git a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp index e6b4b3c699b..072b3d144fa 100644 --- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp @@ -1106,11 +1106,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { Label Continue; Label slow_path; -#ifndef _LP64 - __ safepoint_poll(slow_path, thread, noreg); -#else - __ safepoint_poll(slow_path, r15_thread, rscratch1); -#endif + __ safepoint_poll(slow_path, thread, true /* at_return */, false /* in_nmethod */); __ cmpl(Address(thread, JavaThread::suspend_flags_offset()), 0); __ jcc(Assembler::equal, Continue); @@ -1765,9 +1761,6 @@ void TemplateInterpreterGenerator::set_vtos_entry_points(Template* t, address& vep) { assert(t->is_valid() && t->tos_in() == vtos, "illegal template"); Label L; - aep = __ pc(); // atos entry point - __ push_ptr(); - __ jmp(L); #ifndef _LP64 fep = __ pc(); // ftos entry point __ push(ftos); @@ -1786,8 +1779,8 @@ void TemplateInterpreterGenerator::set_vtos_entry_points(Template* t, lep = __ pc(); // ltos entry point __ push_l(); __ jmp(L); - bep = cep = sep = iep = __ pc(); // [bcsi]tos entry point - __ push_i(); + aep = bep = cep = sep = iep = __ pc(); // [abcsi]tos entry point + __ push_i_or_ptr(); vep = __ pc(); // vtos entry point __ bind(L); generate_and_dispatch(t); diff --git a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_32.cpp b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_32.cpp index 0fc994422fa..ad76dd4f8b6 100644 --- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_32.cpp +++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_32.cpp @@ -62,7 +62,8 @@ address TemplateInterpreterGenerator::generate_CRC32_update_entry() { Label slow_path; // If we need a safepoint check, generate full interpreter entry. - __ safepoint_poll(slow_path, noreg, rdi); + __ get_thread(rdi); + __ safepoint_poll(slow_path, rdi, false /* at_return */, false /* in_nmethod */); // We don't generate local frame and don't align stack because // we call stub code and there is no safepoint on this path. @@ -111,7 +112,8 @@ address TemplateInterpreterGenerator::generate_CRC32_updateBytes_entry(AbstractI Label slow_path; // If we need a safepoint check, generate full interpreter entry. - __ safepoint_poll(slow_path, noreg, rdi); + __ get_thread(rdi); + __ safepoint_poll(slow_path, rdi, false /* at_return */, false /* in_nmethod */); // We don't generate local frame and don't align stack because // we call stub code and there is no safepoint on this path. diff --git a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp index 03def319620..664bf7bfa1c 100644 --- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp @@ -191,7 +191,7 @@ address TemplateInterpreterGenerator::generate_CRC32_update_entry() { // c_rarg1: scratch (rsi on non-Win64, rdx on Win64) Label slow_path; - __ safepoint_poll(slow_path, r15_thread, rscratch1); + __ safepoint_poll(slow_path, r15_thread, true /* at_return */, false /* in_nmethod */); // We don't generate local frame and don't align stack because // we call stub code and there is no safepoint on this path. @@ -237,7 +237,7 @@ address TemplateInterpreterGenerator::generate_CRC32_updateBytes_entry(AbstractI // r13: senderSP must preserved for slow path, set SP to it on fast path Label slow_path; - __ safepoint_poll(slow_path, r15_thread, rscratch1); + __ safepoint_poll(slow_path, r15_thread, false /* at_return */, false /* in_nmethod */); // We don't generate local frame and don't align stack because // we call stub code and there is no safepoint on this path. diff --git a/src/hotspot/cpu/x86/templateTable_x86.cpp b/src/hotspot/cpu/x86/templateTable_x86.cpp index 2f1cda2d5f9..9f0e5a8694d 100644 --- a/src/hotspot/cpu/x86/templateTable_x86.cpp +++ b/src/hotspot/cpu/x86/templateTable_x86.cpp @@ -2296,7 +2296,7 @@ void TemplateTable::branch(bool is_jsr, bool is_wide) { __ dispatch_only(vtos, true); if (UseLoopCounter) { - if (ProfileInterpreter) { + if (ProfileInterpreter && !TieredCompilation) { // Out-of-line code to allocate method data oop. __ bind(profile_method); __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::profile_method)); @@ -2658,16 +2658,16 @@ void TemplateTable::_return(TosState state) { Label no_safepoint; NOT_PRODUCT(__ block_comment("Thread-local Safepoint poll")); #ifdef _LP64 - __ testb(Address(r15_thread, Thread::polling_page_offset()), SafepointMechanism::poll_bit()); + __ testb(Address(r15_thread, Thread::polling_word_offset()), SafepointMechanism::poll_bit()); #else const Register thread = rdi; __ get_thread(thread); - __ testb(Address(thread, Thread::polling_page_offset()), SafepointMechanism::poll_bit()); + __ testb(Address(thread, Thread::polling_word_offset()), SafepointMechanism::poll_bit()); #endif __ jcc(Assembler::zero, no_safepoint); __ push(state); __ call_VM(noreg, CAST_FROM_FN_PTR(address, - InterpreterRuntime::at_safepoint)); + InterpreterRuntime::at_safepoint)); __ pop(state); __ bind(no_safepoint); } diff --git a/src/hotspot/cpu/x86/vm_version_ext_x86.cpp b/src/hotspot/cpu/x86/vm_version_ext_x86.cpp index 8042a8f1c69..35d07d71e46 100644 --- a/src/hotspot/cpu/x86/vm_version_ext_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_ext_x86.cpp @@ -256,7 +256,7 @@ const size_t VM_Version_Ext::CPU_EBS_MAX_LENGTH = (3 * 4 * 4 + 1); const size_t VM_Version_Ext::CPU_TYPE_DESC_BUF_SIZE = 256; const size_t VM_Version_Ext::CPU_DETAILED_DESC_BUF_SIZE = 4096; char* VM_Version_Ext::_cpu_brand_string = NULL; -jlong VM_Version_Ext::_max_qualified_cpu_frequency = 0; +int64_t VM_Version_Ext::_max_qualified_cpu_frequency = 0; int VM_Version_Ext::_no_of_threads = 0; int VM_Version_Ext::_no_of_cores = 0; @@ -644,56 +644,50 @@ const char* VM_Version_Ext::cpu_description(void) { } /** - * See Intel Application note 485 (chapter 10) for details - * on frequency extraction from cpu brand string. - * http://www.intel.com/content/dam/www/public/us/en/documents/application-notes/processor-identification-cpuid-instruction-note.pdf + * For information about extracting the frequency from the cpu brand string, please see: * + * Intel Processor Identification and the CPUID Instruction + * Application Note 485 + * May 2012 + * + * The return value is the frequency in Hz. */ -jlong VM_Version_Ext::max_qualified_cpu_freq_from_brand_string(void) { - // get brand string +int64_t VM_Version_Ext::max_qualified_cpu_freq_from_brand_string(void) { const char* const brand_string = cpu_brand_string(); if (brand_string == NULL) { return 0; } - - const u8 MEGA = 1000000; - u8 multiplier = 0; - jlong frequency = 0; - - // the frequency information in the cpu brand string - // is given in either of two formats "x.xxyHz" or "xxxxyHz", - // where y=M,G,T and x is digits - const char* Hz_location = strchr(brand_string, 'H'); - - if (Hz_location != NULL) { - if (*(Hz_location + 1) == 'z') { - // switch on y in "yHz" - switch(*(Hz_location - 1)) { - case 'M' : - // Set multiplier to frequency is in Hz - multiplier = MEGA; - break; - case 'G' : - multiplier = MEGA * 1000; - break; - case 'T' : - multiplier = MEGA * 1000 * 1000; - break; + const int64_t MEGA = 1000000; + int64_t multiplier = 0; + int64_t frequency = 0; + uint8_t idx = 0; + // The brand string buffer is at most 48 bytes. + // -2 is to prevent buffer overrun when looking for y in yHz, as z is +2 from y. + for (; idx < 48-2; ++idx) { + // Format is either "x.xxyHz" or "xxxxyHz", where y=M, G, T and x are digits. + // Search brand string for "yHz" where y is M, G, or T. + if (brand_string[idx+1] == 'H' && brand_string[idx+2] == 'z') { + if (brand_string[idx] == 'M') { + multiplier = MEGA; + } else if (brand_string[idx] == 'G') { + multiplier = MEGA * 1000; + } else if (brand_string[idx] == 'T') { + multiplier = MEGA * MEGA; } + break; } } - if (multiplier > 0) { - // compute frequency (in Hz) from brand string - if (*(Hz_location - 4) == '.') { // if format is "x.xx" - frequency = (jlong)(*(Hz_location - 5) - '0') * (multiplier); - frequency += (jlong)(*(Hz_location - 3) - '0') * (multiplier / 10); - frequency += (jlong)(*(Hz_location - 2) - '0') * (multiplier / 100); + // Compute freqency (in Hz) from brand string. + if (brand_string[idx-3] == '.') { // if format is "x.xx" + frequency = (brand_string[idx-4] - '0') * multiplier; + frequency += (brand_string[idx-2] - '0') * multiplier / 10; + frequency += (brand_string[idx-1] - '0') * multiplier / 100; } else { // format is "xxxx" - frequency = (jlong)(*(Hz_location - 5) - '0') * 1000; - frequency += (jlong)(*(Hz_location - 4) - '0') * 100; - frequency += (jlong)(*(Hz_location - 3) - '0') * 10; - frequency += (jlong)(*(Hz_location - 2) - '0'); + frequency = (brand_string[idx-4] - '0') * 1000; + frequency += (brand_string[idx-3] - '0') * 100; + frequency += (brand_string[idx-2] - '0') * 10; + frequency += (brand_string[idx-1] - '0'); frequency *= multiplier; } } @@ -701,7 +695,7 @@ jlong VM_Version_Ext::max_qualified_cpu_freq_from_brand_string(void) { } -jlong VM_Version_Ext::maximum_qualified_cpu_frequency(void) { +int64_t VM_Version_Ext::maximum_qualified_cpu_frequency(void) { if (_max_qualified_cpu_frequency == 0) { _max_qualified_cpu_frequency = max_qualified_cpu_freq_from_brand_string(); } diff --git a/src/hotspot/cpu/x86/vm_version_ext_x86.hpp b/src/hotspot/cpu/x86/vm_version_ext_x86.hpp index 1a2134d8561..2d318dd390e 100644 --- a/src/hotspot/cpu/x86/vm_version_ext_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_ext_x86.hpp @@ -55,7 +55,7 @@ class VM_Version_Ext : public VM_Version { static int _no_of_cores; static int _no_of_packages; static char* _cpu_brand_string; - static jlong _max_qualified_cpu_frequency; + static int64_t _max_qualified_cpu_frequency; static const char* cpu_family_description(void); static const char* cpu_model_description(void); @@ -72,7 +72,7 @@ class VM_Version_Ext : public VM_Version { // Returns bytes written excluding termninating null byte. static size_t cpu_write_support_string(char* const buf, size_t buf_len); static void resolve_cpu_information_details(void); - static jlong max_qualified_cpu_freq_from_brand_string(void); + static int64_t max_qualified_cpu_freq_from_brand_string(void); public: // Offsets for cpuid asm stub brand string @@ -93,7 +93,7 @@ class VM_Version_Ext : public VM_Version { static int number_of_cores(void); static int number_of_sockets(void); - static jlong maximum_qualified_cpu_frequency(void); + static int64_t maximum_qualified_cpu_frequency(void); static bool supports_tscinv_ext(void); diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 089d720e88e..d44560438a0 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -763,6 +763,8 @@ void VM_Version::get_processor_features() { if (is_intel()) { // Intel cpus specific settings if (is_knights_family()) { _features &= ~CPU_VZEROUPPER; + _features &= ~CPU_AVX512BW; + _features &= ~CPU_AVX512VL; } } @@ -979,6 +981,11 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseSHA512Intrinsics, false); } + if (UseSHA3Intrinsics) { + warning("Intrinsics for SHA3-224, SHA3-256, SHA3-384 and SHA3-512 crypto hash functions not available on this CPU."); + FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); + } + if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics)) { FLAG_SET_DEFAULT(UseSHA, false); } diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index e2cf0e08489..5d91280e616 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -27,6 +27,7 @@ #include "memory/universe.hpp" #include "runtime/abstract_vm_version.hpp" +#include "utilities/macros.hpp" class VM_Version : public Abstract_VM_Version { friend class VMStructs; @@ -1021,6 +1022,10 @@ enum Extended_Family { return LP64_ONLY(true) NOT_LP64(false); // not implemented on x86_32 } + constexpr static bool supports_stack_watermark_barrier() { + return true; + } + // there are several insns to force cache line sync to memory which // we can use to ensure mapped non-volatile memory is up to date with // pending in-cache changes. diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index 89ca2468a21..7551dfaa0fc 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -1097,6 +1097,7 @@ reg_class vectorz_reg_legacy(XMM0, XMM0b, XMM0c, XMM0d, XMM0e, XMM0f, XMM0 reg_class_dynamic vectorz_reg (vectorz_reg_evex, vectorz_reg_legacy, %{ VM_Version::supports_evex() %} ); reg_class_dynamic vectorz_reg_vl(vectorz_reg_evex, vectorz_reg_legacy, %{ VM_Version::supports_evex() && VM_Version::supports_avx512vl() %} ); +reg_class xmm0_reg(XMM0, XMM0b, XMM0c, XMM0d); %} @@ -1165,6 +1166,64 @@ class HandlerImpl { #endif }; + +inline uint vector_length(const Node* n) { + const TypeVect* vt = n->bottom_type()->is_vect(); + return vt->length(); +} + +inline uint vector_length(const MachNode* use, MachOper* opnd) { + uint def_idx = use->operand_index(opnd); + Node* def = use->in(def_idx); + return def->bottom_type()->is_vect()->length(); +} + +inline uint vector_length_in_bytes(const Node* n) { + const TypeVect* vt = n->bottom_type()->is_vect(); + return vt->length_in_bytes(); +} + +inline uint vector_length_in_bytes(const MachNode* use, MachOper* opnd) { + uint def_idx = use->operand_index(opnd); + Node* def = use->in(def_idx); + return def->bottom_type()->is_vect()->length_in_bytes(); +} + +inline BasicType vector_element_basic_type(const Node *n) { + return n->bottom_type()->is_vect()->element_basic_type(); +} + +inline BasicType vector_element_basic_type(const MachNode *use, MachOper* opnd) { + uint def_idx = use->operand_index(opnd); + Node* def = use->in(def_idx); + return def->bottom_type()->is_vect()->element_basic_type(); +} + +inline Assembler::AvxVectorLen vector_length_encoding(int bytes) { + switch(bytes) { + case 4: // fall-through + case 8: // fall-through + case 16: return Assembler::AVX_128bit; + case 32: return Assembler::AVX_256bit; + case 64: return Assembler::AVX_512bit; + + default: { + ShouldNotReachHere(); + return Assembler::AVX_NoVec; + } + } +} + +static inline Assembler::AvxVectorLen vector_length_encoding(const Node* n) { + return vector_length_encoding(vector_length_in_bytes(n)); +} + +static inline Assembler::AvxVectorLen vector_length_encoding(const MachNode* use, MachOper* opnd) { + uint def_idx = use->operand_index(opnd); + Node* def = use->in(def_idx); + return vector_length_encoding(def); +} + class Node::PD { public: enum NodeFlags { @@ -1262,6 +1321,18 @@ int HandlerImpl::emit_deopt_handler(CodeBuffer& cbuf) { return offset; } +Assembler::Width widthForType(BasicType bt) { + if (bt == T_BYTE) { + return Assembler::B; + } else if (bt == T_SHORT) { + return Assembler::W; + } else if (bt == T_INT) { + return Assembler::D; + } else { + assert(bt == T_LONG, "not a long: %s", type2name(bt)); + return Assembler::Q; + } +} //============================================================================= @@ -1278,8 +1349,16 @@ int HandlerImpl::emit_deopt_handler(CodeBuffer& cbuf) { static address double_signflip() { return (address)double_signflip_pool; } #endif static address vector_short_to_byte_mask() { return StubRoutines::x86::vector_short_to_byte_mask(); } + static address vector_int_to_byte_mask() { return StubRoutines::x86::vector_int_to_byte_mask(); } static address vector_byte_perm_mask() { return StubRoutines::x86::vector_byte_perm_mask(); } static address vector_long_sign_mask() { return StubRoutines::x86::vector_long_sign_mask(); } + static address vector_all_bits_set() { return StubRoutines::x86::vector_all_bits_set(); } + static address vector_int_to_short_mask() { return StubRoutines::x86::vector_int_to_short_mask(); } + static address vector_short_shufflemask() { return StubRoutines::x86::vector_short_shuffle_mask(); } + static address vector_int_shufflemask() { return StubRoutines::x86::vector_int_shuffle_mask(); } + static address vector_long_shufflemask() { return StubRoutines::x86::vector_long_shuffle_mask(); } + static address vector_32_bit_mask() { return StubRoutines::x86::vector_32_bit_mask(); } + static address vector_64_bit_mask() { return StubRoutines::x86::vector_64_bit_mask(); } //============================================================================= const bool Matcher::match_rule_supported(int opcode) { @@ -1288,6 +1367,7 @@ const bool Matcher::match_rule_supported(int opcode) { } switch (opcode) { case Op_AbsVL: + case Op_StoreVectorScatter: if (UseAVX < 3) { return false; } @@ -1309,11 +1389,20 @@ const bool Matcher::match_rule_supported(int opcode) { } break; case Op_MulVL: + if (UseSSE < 4) { // only with SSE4_1 or AVX + return false; + } + break; case Op_MulReductionVL: if (VM_Version::supports_avx512dq() == false) { return false; } break; + case Op_AddReductionVL: + if (UseSSE < 2) { // requires at least SSE2 + return false; + } + break; case Op_AbsVB: case Op_AbsVS: case Op_AbsVI: @@ -1325,6 +1414,8 @@ const bool Matcher::match_rule_supported(int opcode) { return false; } break; + case Op_VectorLoadShuffle: + case Op_VectorRearrange: case Op_MulReductionVI: if (UseSSE < 4) { // requires at least SSE4 return false; @@ -1332,6 +1423,13 @@ const bool Matcher::match_rule_supported(int opcode) { break; case Op_SqrtVD: case Op_SqrtVF: + case Op_VectorMaskCmp: + case Op_VectorCastB2X: + case Op_VectorCastS2X: + case Op_VectorCastI2X: + case Op_VectorCastL2X: + case Op_VectorCastF2X: + case Op_VectorCastD2X: if (UseAVX < 1) { // enabled for AVX only return false; } @@ -1346,7 +1444,7 @@ const bool Matcher::match_rule_supported(int opcode) { break; case Op_CMoveVF: case Op_CMoveVD: - if (UseAVX < 1 || UseAVX > 2) { + if (UseAVX < 1) { // enabled for AVX only return false; } break; @@ -1369,6 +1467,10 @@ const bool Matcher::match_rule_supported(int opcode) { case Op_LShiftVB: case Op_RShiftVB: case Op_URShiftVB: + case Op_VectorInsert: + case Op_VectorLoadMask: + case Op_VectorStoreMask: + case Op_VectorBlend: if (UseSSE < 4) { return false; } @@ -1390,6 +1492,9 @@ const bool Matcher::match_rule_supported(int opcode) { return false; } break; + case Op_ExtractB: + case Op_ExtractL: + case Op_ExtractI: case Op_RoundDoubleMode: if (UseSSE < 4) { return false; @@ -1400,6 +1505,17 @@ const bool Matcher::match_rule_supported(int opcode) { return false; // 128bit vroundpd is not available } break; + case Op_LoadVectorGather: + if (UseAVX < 2) { + return false; + } + break; + case Op_FmaVD: + case Op_FmaVF: + if (!UseFMA) { + return false; + } + break; case Op_MacroLogicV: if (UseAVX < 3 || !UseVectorMacroLogic) { return false; @@ -1460,8 +1576,9 @@ const bool Matcher::match_rule_supported_vector(int opcode, int vlen, BasicType break; case Op_AbsVD: case Op_NegVD: + case Op_MulVL: if ((vlen == 8) && (VM_Version::supports_avx512dq() == false)) { - return false; // 512bit vandpd and vxorpd are not available + return false; // 512bit vpmullq, vandpd and vxorpd are not available } break; case Op_CMoveVF: @@ -1482,6 +1599,142 @@ const bool Matcher::match_rule_supported_vector(int opcode, int vlen, BasicType return false; // implementation limitation (only vcmov4D_reg is present) } break; + case Op_MaxV: + case Op_MinV: + if (UseSSE < 4 && is_integral_type(bt)) { + return false; + } + if ((bt == T_FLOAT || bt == T_DOUBLE)) { + // Float/Double intrinsics are enabled for AVX family currently. + if (UseAVX == 0) { + return false; + } + if (UseAVX > 2 && (!VM_Version::supports_avx512dq() && size_in_bits == 512)) { // 512 bit Float/Double intrinsics need AVX512DQ + return false; + } + } + break; + case Op_AddReductionVI: + if (bt == T_INT && (UseSSE < 3 || !VM_Version::supports_ssse3())) { + return false; + } + // fallthrough + case Op_AndReductionV: + case Op_OrReductionV: + case Op_XorReductionV: + if (is_subword_type(bt) && (UseSSE < 4)) { + return false; + } +#ifndef _LP64 + if (bt == T_BYTE || bt == T_LONG) { + return false; + } +#endif + break; +#ifndef _LP64 + case Op_VectorInsert: + if (bt == T_LONG || bt == T_DOUBLE) { + return false; + } + break; +#endif + case Op_MinReductionV: + case Op_MaxReductionV: + if ((bt == T_INT || is_subword_type(bt)) && UseSSE < 4) { + return false; + } else if (bt == T_LONG && (UseAVX < 3 || !VM_Version::supports_avx512vlbwdq())) { + return false; + } + // Float/Double intrinsics enabled for AVX family. + if (UseAVX == 0 && (bt == T_FLOAT || bt == T_DOUBLE)) { + return false; + } + if (UseAVX > 2 && (!VM_Version::supports_avx512dq() && size_in_bits == 512)) { + return false; + } +#ifndef _LP64 + if (bt == T_BYTE || bt == T_LONG) { + return false; + } +#endif + break; + case Op_VectorTest: + if (UseSSE < 4) { + return false; // Implementation limitation + } else if (size_in_bits < 128) { + return false; // Implementation limitation + } else if (size_in_bits == 512 && (VM_Version::supports_avx512bw() == false)) { + return false; // Implementation limitation + } + break; + case Op_VectorLoadShuffle: + case Op_VectorRearrange: + if(vlen == 2) { + return false; // Implementation limitation due to how shuffle is loaded + } else if (size_in_bits == 256 && UseAVX < 2) { + return false; // Implementation limitation + } else if (bt == T_BYTE && size_in_bits >= 256 && !VM_Version::supports_avx512_vbmi()) { + return false; // Implementation limitation + } else if (bt == T_SHORT && size_in_bits >= 256 && !VM_Version::supports_avx512bw()) { + return false; // Implementation limitation + } + break; + case Op_VectorLoadMask: + if (size_in_bits == 256 && UseAVX < 2) { + return false; // Implementation limitation + } + // fallthrough + case Op_VectorStoreMask: + if (vlen == 2) { + return false; // Implementation limitation + } + break; + case Op_VectorCastB2X: + if (size_in_bits == 256 && UseAVX < 2) { + return false; // Implementation limitation + } + break; + case Op_VectorCastS2X: + if (is_integral_type(bt) && size_in_bits == 256 && UseAVX < 2) { + return false; + } + break; + case Op_VectorCastI2X: + if (is_integral_type(bt) && size_in_bits == 256 && UseAVX < 2) { + return false; + } + break; + case Op_VectorCastL2X: + if (is_integral_type(bt) && size_in_bits == 256 && UseAVX < 2) { + return false; + } else if (!is_integral_type(bt) && !VM_Version::supports_avx512dq()) { + return false; + } + break; + case Op_VectorCastF2X: + case Op_VectorCastD2X: + if (is_integral_type(bt)) { + // Casts from FP to integral types require special fixup logic not easily + // implementable with vectors. + return false; // Implementation limitation + } + case Op_MulReductionVI: + if (bt == T_BYTE && size_in_bits == 512 && !VM_Version::supports_avx512bw()) { + return false; + } + break; + case Op_StoreVectorScatter: + if(bt == T_BYTE || bt == T_SHORT) { + return false; + } else if (size_in_bits < 512 && !VM_Version::supports_avx512vl()) { + return false; + } + // fallthrough + case Op_LoadVectorGather: + if (size_in_bits == 64 ) { + return false; + } + break; } return true; // Per default match rules are supported. } @@ -1540,6 +1793,10 @@ bool Matcher::is_generic_vector(MachOper* opnd) { //------------------------------------------------------------------------ +bool Matcher::supports_vector_variable_shifts(void) { + return (UseAVX >= 2); +} + const bool Matcher::has_predicated_vectors(void) { bool ret_value = false; if (UseAVX > 2) { @@ -1831,40 +2088,28 @@ bool Matcher::pd_clone_address_expressions(AddPNode* m, Matcher::MStack& mstack, void Compile::reshape_address(AddPNode* addp) { } -static inline uint vector_length(const MachNode* n) { - const TypeVect* vt = n->bottom_type()->is_vect(); - return vt->length(); -} - -static inline uint vector_length(const MachNode* use, MachOper* opnd) { - uint def_idx = use->operand_index(opnd); - Node* def = use->in(def_idx); - return def->bottom_type()->is_vect()->length(); -} - -static inline uint vector_length_in_bytes(const MachNode* n) { - const TypeVect* vt = n->bottom_type()->is_vect(); - return vt->length_in_bytes(); -} - -static inline uint vector_length_in_bytes(const MachNode* use, MachOper* opnd) { - uint def_idx = use->operand_index(opnd); - Node* def = use->in(def_idx); - return def->bottom_type()->is_vect()->length_in_bytes(); +static inline Assembler::ComparisonPredicate booltest_pred_to_comparison_pred(int bt) { + switch (bt) { + case BoolTest::eq: return Assembler::eq; + case BoolTest::ne: return Assembler::neq; + case BoolTest::le: return Assembler::le; + case BoolTest::ge: return Assembler::nlt; + case BoolTest::lt: return Assembler::lt; + case BoolTest::gt: return Assembler::nle; + default : ShouldNotReachHere(); return Assembler::_false; + } } -static inline Assembler::AvxVectorLen vector_length_encoding(const MachNode* n) { - switch(vector_length_in_bytes(n)) { - case 4: // fall-through - case 8: // fall-through - case 16: return Assembler::AVX_128bit; - case 32: return Assembler::AVX_256bit; - case 64: return Assembler::AVX_512bit; - - default: { - ShouldNotReachHere(); - return Assembler::AVX_NoVec; - } +static inline Assembler::ComparisonPredicateFP booltest_pred_to_comparison_pred_fp(int bt) { + switch (bt) { + case BoolTest::eq: return Assembler::EQ_OQ; // ordered non-signaling + // As per JLS 15.21.1, != of NaNs is true. Thus use unordered compare. + case BoolTest::ne: return Assembler::NEQ_UQ; // unordered non-signaling + case BoolTest::le: return Assembler::LE_OQ; // ordered non-signaling + case BoolTest::ge: return Assembler::GE_OQ; // ordered non-signaling + case BoolTest::lt: return Assembler::LT_OQ; // ordered non-signaling + case BoolTest::gt: return Assembler::GT_OQ; // ordered non-signaling + default: ShouldNotReachHere(); return Assembler::FALSE_OS; } } @@ -2191,6 +2436,13 @@ encode %{ %} +// Operands for bound floating pointer register arguments +operand rxmm0() %{ + constraint(ALLOC_IN_RC(xmm0_reg)); + match(VecX); + format%{%} + interface(REG_INTER); +%} //----------OPERANDS----------------------------------------------------------- // Operand definitions must precede instruction definitions for correct parsing @@ -2957,9 +3209,9 @@ instruct absF_reg_reg(vlRegF dst, vlRegF src) %{ ins_cost(150); format %{ "vandps $dst, $src, [0x7fffffff]\t# abs float by sign masking" %} ins_encode %{ - int vector_len = 0; + int vlen_enc = Assembler::AVX_128bit; __ vandps($dst$$XMMRegister, $src$$XMMRegister, - ExternalAddress(float_signmask()), vector_len); + ExternalAddress(float_signmask()), vlen_enc); %} ins_pipe(pipe_slow); %} @@ -2983,9 +3235,9 @@ instruct absD_reg_reg(vlRegD dst, vlRegD src) %{ format %{ "vandpd $dst, $src, [0x7fffffffffffffff]\t" "# abs double by sign masking" %} ins_encode %{ - int vector_len = 0; + int vlen_enc = Assembler::AVX_128bit; __ vandpd($dst$$XMMRegister, $src$$XMMRegister, - ExternalAddress(double_signmask()), vector_len); + ExternalAddress(double_signmask()), vlen_enc); %} ins_pipe(pipe_slow); %} @@ -3109,6 +3361,93 @@ instruct sqrtD_imm(regD dst, immD con) %{ ins_pipe(pipe_slow); %} +// ---------------------------------------- VectorReinterpret ------------------------------------ + +instruct reinterpret(vec dst) %{ + predicate(vector_length_in_bytes(n) == vector_length_in_bytes(n->in(1))); // dst == src + match(Set dst (VectorReinterpret dst)); + ins_cost(125); + format %{ "vector_reinterpret $dst\t!" %} + ins_encode %{ + // empty + %} + ins_pipe( pipe_slow ); +%} + +instruct reinterpret_expand(vec dst, vec src, rRegP scratch) %{ + predicate(UseAVX == 0 && + (vector_length_in_bytes(n->in(1)) < vector_length_in_bytes(n))); // src < dst + match(Set dst (VectorReinterpret src)); + ins_cost(125); + effect(TEMP dst, TEMP scratch); + format %{ "vector_reinterpret_expand $dst,$src\t! using $scratch as TEMP" %} + ins_encode %{ + assert(vector_length_in_bytes(this) <= 16, "required"); + assert(vector_length_in_bytes(this, $src) <= 8, "required"); + + int src_vlen_in_bytes = vector_length_in_bytes(this, $src); + if (src_vlen_in_bytes == 4) { + __ movdqu($dst$$XMMRegister, ExternalAddress(vector_32_bit_mask()), $scratch$$Register); + } else { + assert(src_vlen_in_bytes == 8, ""); + __ movdqu($dst$$XMMRegister, ExternalAddress(vector_64_bit_mask()), $scratch$$Register); + } + __ pand($dst$$XMMRegister, $src$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct vreinterpret_expand4(legVec dst, vec src, rRegP scratch) %{ + predicate(UseAVX > 0 && + (vector_length_in_bytes(n->in(1)) == 4) && // src + (vector_length_in_bytes(n->in(1)) < vector_length_in_bytes(n))); // src < dst + match(Set dst (VectorReinterpret src)); + ins_cost(125); + effect(TEMP scratch); + format %{ "vector_reinterpret_expand $dst,$src\t! using $scratch as TEMP" %} + ins_encode %{ + __ vpand($dst$$XMMRegister, $src$$XMMRegister, ExternalAddress(vector_32_bit_mask()), 0, $scratch$$Register); + %} + ins_pipe( pipe_slow ); +%} + + +instruct vreinterpret_expand(legVec dst, vec src) %{ + predicate(UseAVX > 0 && + (vector_length_in_bytes(n->in(1)) > 4) && // src + (vector_length_in_bytes(n->in(1)) < vector_length_in_bytes(n))); // src < dst + match(Set dst (VectorReinterpret src)); + ins_cost(125); + format %{ "vector_reinterpret_expand $dst,$src\t!" %} + ins_encode %{ + switch (vector_length_in_bytes(this, $src)) { + case 8: __ movq ($dst$$XMMRegister, $src$$XMMRegister); break; + case 16: __ movdqu ($dst$$XMMRegister, $src$$XMMRegister); break; + case 32: __ vmovdqu($dst$$XMMRegister, $src$$XMMRegister); break; + default: ShouldNotReachHere(); + } + %} + ins_pipe( pipe_slow ); +%} + +instruct reinterpret_shrink(vec dst, legVec src) %{ + predicate(vector_length_in_bytes(n->in(1)) > vector_length_in_bytes(n)); // src > dst + match(Set dst (VectorReinterpret src)); + ins_cost(125); + format %{ "vector_reinterpret_shrink $dst,$src\t!" %} + ins_encode %{ + switch (vector_length_in_bytes(this)) { + case 4: __ movflt ($dst$$XMMRegister, $src$$XMMRegister); break; + case 8: __ movq ($dst$$XMMRegister, $src$$XMMRegister); break; + case 16: __ movdqu ($dst$$XMMRegister, $src$$XMMRegister); break; + case 32: __ vmovdqu($dst$$XMMRegister, $src$$XMMRegister); break; + default: ShouldNotReachHere(); + } + %} + ins_pipe( pipe_slow ); +%} + +// ---------------------------------------------------------------------------------------------------- #ifdef _LP64 instruct roundD_reg(legRegD dst, legRegD src, immU8 rmode) %{ @@ -3146,19 +3485,19 @@ instruct roundD_imm(legRegD dst, immD con, immU8 rmode, rRegI scratch_reg) %{ %} instruct vroundD_reg(legVec dst, legVec src, immU8 rmode) %{ - predicate(n->as_Vector()->length() < 8); + predicate(vector_length(n) < 8); match(Set dst (RoundDoubleModeV src rmode)); format %{ "vroundpd $dst,$src,$rmode\t! round packedD" %} ins_encode %{ assert(UseAVX > 0, "required"); - int vector_len = vector_length_encoding(this); - __ vroundpd($dst$$XMMRegister, $src$$XMMRegister, $rmode$$constant, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vroundpd($dst$$XMMRegister, $src$$XMMRegister, $rmode$$constant, vlen_enc); %} ins_pipe( pipe_slow ); %} instruct vround8D_reg(vec dst, vec src, immU8 rmode) %{ - predicate(n->as_Vector()->length() == 8); + predicate(vector_length(n) == 8); match(Set dst (RoundDoubleModeV src rmode)); format %{ "vrndscalepd $dst,$src,$rmode\t! round packed8D" %} ins_encode %{ @@ -3169,19 +3508,19 @@ instruct vround8D_reg(vec dst, vec src, immU8 rmode) %{ %} instruct vroundD_mem(legVec dst, memory mem, immU8 rmode) %{ - predicate(n->as_Vector()->length() < 8); + predicate(vector_length(n) < 8); match(Set dst (RoundDoubleModeV (LoadVector mem) rmode)); format %{ "vroundpd $dst, $mem, $rmode\t! round packedD" %} ins_encode %{ assert(UseAVX > 0, "required"); - int vector_len = vector_length_encoding(this); - __ vroundpd($dst$$XMMRegister, $mem$$Address, $rmode$$constant, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vroundpd($dst$$XMMRegister, $mem$$Address, $rmode$$constant, vlen_enc); %} ins_pipe( pipe_slow ); %} instruct vround8D_mem(vec dst, memory mem, immU8 rmode) %{ - predicate(n->as_Vector()->length() == 8); + predicate(vector_length(n) == 8); match(Set dst (RoundDoubleModeV (LoadVector mem) rmode)); format %{ "vrndscalepd $dst,$mem,$rmode\t! round packed8D" %} ins_encode %{ @@ -3253,7 +3592,7 @@ instruct MoveLeg2Vec(vec dst, legVec src) %{ // ============================================================================ -// Load vectors +// Load vectors generic operand pattern instruct loadV(vec dst, memory mem) %{ match(Set dst (LoadVector mem)); ins_cost(125); @@ -3289,6 +3628,81 @@ instruct storeV(memory mem, vec src) %{ ins_pipe( pipe_slow ); %} +// ---------------------------------------- Gather ------------------------------------ + +// Gather INT, LONG, FLOAT, DOUBLE + +instruct gather(legVec dst, memory mem, legVec idx, rRegP tmp, legVec mask) %{ + predicate(vector_length_in_bytes(n) <= 32); + match(Set dst (LoadVectorGather mem idx)); + effect(TEMP dst, TEMP tmp, TEMP mask); + format %{ "load_vector_gather $dst, $mem, $idx\t! using $tmp and $mask as TEMP" %} + ins_encode %{ + assert(UseAVX >= 2, "sanity"); + + int vlen_enc = vector_length_encoding(this); + BasicType elem_bt = vector_element_basic_type(this); + + assert(vector_length_in_bytes(this) >= 16, "sanity"); + assert(!is_subword_type(elem_bt), "sanity"); // T_INT, T_LONG, T_FLOAT, T_DOUBLE + + if (vlen_enc == Assembler::AVX_128bit) { + __ movdqu($mask$$XMMRegister, ExternalAddress(vector_all_bits_set())); + } else { + __ vmovdqu($mask$$XMMRegister, ExternalAddress(vector_all_bits_set())); + } + __ lea($tmp$$Register, $mem$$Address); + __ vgather(elem_bt, $dst$$XMMRegister, $tmp$$Register, $idx$$XMMRegister, $mask$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct evgather(vec dst, memory mem, vec idx, rRegP tmp) %{ + predicate(vector_length_in_bytes(n) == 64); + match(Set dst (LoadVectorGather mem idx)); + effect(TEMP dst, TEMP tmp); + format %{ "load_vector_gather $dst, $mem, $idx\t! using $tmp and k2 as TEMP" %} + ins_encode %{ + assert(UseAVX > 2, "sanity"); + + int vlen_enc = vector_length_encoding(this); + BasicType elem_bt = vector_element_basic_type(this); + + assert(!is_subword_type(elem_bt), "sanity"); // T_INT, T_LONG, T_FLOAT, T_DOUBLE + + KRegister ktmp = k2; + __ kmovwl(k2, ExternalAddress(vector_all_bits_set()), $tmp$$Register); + __ lea($tmp$$Register, $mem$$Address); + __ evgather(elem_bt, $dst$$XMMRegister, ktmp, $tmp$$Register, $idx$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +// ====================Scatter======================================= + +// Scatter INT, LONG, FLOAT, DOUBLE + +instruct scatter(memory mem, vec src, vec idx, rRegP tmp) %{ + match(Set mem (StoreVectorScatter mem (Binary src idx))); + effect(TEMP tmp); + format %{ "store_vector_scatter $mem, $idx, $src\t! using k2 and $tmp as TEMP" %} + ins_encode %{ + assert(UseAVX > 2, "sanity"); + + int vlen_enc = vector_length_encoding(this, $src); + BasicType elem_bt = vector_element_basic_type(this, $src); + + assert(vector_length_in_bytes(this, $src) >= 16, "sanity"); + assert(!is_subword_type(elem_bt), "sanity"); // T_INT, T_LONG, T_FLOAT, T_DOUBLE + + KRegister ktmp = k2; + __ kmovwl(k2, ExternalAddress(vector_all_bits_set()), $tmp$$Register); + __ lea($tmp$$Register, $mem$$Address); + __ evscatter(elem_bt, $tmp$$Register, $idx$$XMMRegister, ktmp, $src$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + // ====================REPLICATE======================================= // Replicate byte scalar to be vector @@ -3322,8 +3736,8 @@ instruct ReplB_mem(vec dst, memory mem) %{ match(Set dst (ReplicateB (LoadB mem))); format %{ "replicateB $dst,$mem" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpbroadcastb($dst$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpbroadcastb($dst$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -3353,7 +3767,7 @@ instruct ReplB_imm(vec dst, immI con) %{ %} // Replicate byte scalar zero to be vector -instruct ReplB_zero(vec dst, immI0 zero) %{ +instruct ReplB_zero(vec dst, immI_0 zero) %{ match(Set dst (ReplicateB zero)); format %{ "replicateB $dst,$zero" %} ins_encode %{ @@ -3430,7 +3844,7 @@ instruct ReplS_imm(vec dst, immI con) %{ ins_pipe( fpu_reg_reg ); %} -instruct ReplS_zero(vec dst, immI0 zero) %{ +instruct ReplS_zero(vec dst, immI_0 zero) %{ match(Set dst (ReplicateS zero)); format %{ "replicateS $dst,$zero" %} ins_encode %{ @@ -3477,8 +3891,8 @@ instruct ReplI_mem(vec dst, memory mem) %{ __ pshufd($dst$$XMMRegister, $dst$$XMMRegister, 0x00); } else { assert(VM_Version::supports_avx2(), "sanity"); - int vector_len = vector_length_encoding(this); - __ vpbroadcastd($dst$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpbroadcastd($dst$$XMMRegister, $mem$$Address, vlen_enc); } %} ins_pipe( pipe_slow ); @@ -3497,16 +3911,16 @@ instruct ReplI_imm(vec dst, immI con) %{ } } else { assert(VM_Version::supports_avx2(), "sanity"); - int vector_len = vector_length_encoding(this); + int vlen_enc = vector_length_encoding(this); __ movq($dst$$XMMRegister, const_addr); - __ vpbroadcastd($dst$$XMMRegister, $dst$$XMMRegister, vector_len); + __ vpbroadcastd($dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); } %} ins_pipe( pipe_slow ); %} // Replicate integer (4 byte) scalar zero to be vector -instruct ReplI_zero(vec dst, immI0 zero) %{ +instruct ReplI_zero(vec dst, immI_0 zero) %{ match(Set dst (ReplicateI zero)); format %{ "replicateI $dst,$zero" %} ins_encode %{ @@ -3562,7 +3976,7 @@ instruct ReplL_reg(vec dst, rRegL src) %{ #else // _LP64 // Replicate long (8 byte) scalar to be vector instruct ReplL_reg(vec dst, eRegL src, vec tmp) %{ - predicate(n->as_Vector()->length() <= 4); + predicate(vector_length(n) <= 4); match(Set dst (ReplicateL src)); effect(TEMP dst, USE src, TEMP tmp); format %{ "replicateL $dst,$src" %} @@ -3574,11 +3988,11 @@ instruct ReplL_reg(vec dst, eRegL src, vec tmp) %{ __ punpckldq($dst$$XMMRegister, $tmp$$XMMRegister); __ punpcklqdq($dst$$XMMRegister, $dst$$XMMRegister); } else if (VM_Version::supports_avx512vl()) { // AVX512VL for <512bit operands - int vector_len = Assembler::AVX_256bit; + int vlen_enc = Assembler::AVX_256bit; __ movdl($dst$$XMMRegister, $src$$Register); __ movdl($tmp$$XMMRegister, HIGH_FROM_LOW($src$$Register)); __ punpckldq($dst$$XMMRegister, $tmp$$XMMRegister); - __ vpbroadcastq($dst$$XMMRegister, $dst$$XMMRegister, vector_len); + __ vpbroadcastq($dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); } else { __ movdl($dst$$XMMRegister, $src$$Register); __ movdl($tmp$$XMMRegister, HIGH_FROM_LOW($src$$Register)); @@ -3591,7 +4005,7 @@ instruct ReplL_reg(vec dst, eRegL src, vec tmp) %{ %} instruct ReplL_reg_leg(legVec dst, eRegL src, legVec tmp) %{ - predicate(n->as_Vector()->length() == 8); + predicate(vector_length(n) == 8); match(Set dst (ReplicateL src)); effect(TEMP dst, USE src, TEMP tmp); format %{ "replicateL $dst,$src" %} @@ -3604,11 +4018,11 @@ instruct ReplL_reg_leg(legVec dst, eRegL src, legVec tmp) %{ __ vinserti128_high($dst$$XMMRegister, $dst$$XMMRegister); __ vinserti64x4($dst$$XMMRegister, $dst$$XMMRegister, $dst$$XMMRegister, 0x1); } else { - int vector_len = Assembler::AVX_512bit; + int vlen_enc = Assembler::AVX_512bit; __ movdl($dst$$XMMRegister, $src$$Register); __ movdl($tmp$$XMMRegister, HIGH_FROM_LOW($src$$Register)); __ punpckldq($dst$$XMMRegister, $tmp$$XMMRegister); - __ vpbroadcastq($dst$$XMMRegister, $dst$$XMMRegister, vector_len); + __ vpbroadcastq($dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); } %} ins_pipe( pipe_slow ); @@ -3689,8 +4103,8 @@ instruct ReplF_reg(vec dst, vlRegF src) %{ if (vlen <= 4) { __ pshufd($dst$$XMMRegister, $src$$XMMRegister, 0x00); } else if (VM_Version::supports_avx2()) { - int vector_len = vector_length_encoding(this); - __ vbroadcastss($dst$$XMMRegister, $src$$XMMRegister, vector_len); // reg-to-reg variant requires AVX2 + int vlen_enc = vector_length_encoding(this); + __ vbroadcastss($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); // reg-to-reg variant requires AVX2 } else { assert(vlen == 8, "sanity"); __ pshufd($dst$$XMMRegister, $src$$XMMRegister, 0x00); @@ -3710,8 +4124,8 @@ instruct ReplF_mem(vec dst, memory mem) %{ __ pshufd($dst$$XMMRegister, $dst$$XMMRegister, 0x00); } else { assert(VM_Version::supports_avx(), "sanity"); - int vector_len = vector_length_encoding(this); - __ vbroadcastss($dst$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vbroadcastss($dst$$XMMRegister, $mem$$Address, vlen_enc); } %} ins_pipe( pipe_slow ); @@ -3743,8 +4157,8 @@ instruct ReplD_reg(vec dst, vlRegD src) %{ if (vlen == 2) { __ pshufd($dst$$XMMRegister, $src$$XMMRegister, 0x44); } else if (VM_Version::supports_avx2()) { - int vector_len = vector_length_encoding(this); - __ vbroadcastsd($dst$$XMMRegister, $src$$XMMRegister, vector_len); // reg-to-reg variant requires AVX2 + int vlen_enc = vector_length_encoding(this); + __ vbroadcastsd($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); // reg-to-reg variant requires AVX2 } else { assert(vlen == 4, "sanity"); __ pshufd($dst$$XMMRegister, $src$$XMMRegister, 0x44); @@ -3764,8 +4178,8 @@ instruct ReplD_mem(vec dst, memory mem) %{ __ pshufd($dst$$XMMRegister, $dst$$XMMRegister, 0x44); } else { assert(VM_Version::supports_avx(), "sanity"); - int vector_len = vector_length_encoding(this); - __ vbroadcastsd($dst$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vbroadcastsd($dst$$XMMRegister, $mem$$Address, vlen_enc); } %} ins_pipe( pipe_slow ); @@ -3786,35 +4200,239 @@ instruct ReplD_zero(vec dst, immD0 zero) %{ ins_pipe( fpu_reg_reg ); %} -// ====================REDUCTION ARITHMETIC======================================= -// =======================Int Reduction========================================== +// ====================VECTOR INSERT======================================= -instruct reductionI(rRegI dst, rRegI src1, vec src2, vec vtmp1, vec vtmp2) %{ - predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT && - n->in(2)->bottom_type()->is_vect()->length() < 16); - match(Set dst (AddReductionVI src1 src2)); - match(Set dst (MulReductionVI src1 src2)); - match(Set dst (AndReductionV src1 src2)); - match(Set dst ( OrReductionV src1 src2)); - match(Set dst (XorReductionV src1 src2)); - effect(TEMP vtmp1, TEMP vtmp2); - format %{ "vector_reduction_int $dst,$src1,$src2 ; using $vtmp1, $vtmp2 as TEMP" %} +instruct insert(vec dst, rRegI val, immU8 idx) %{ + predicate(vector_length_in_bytes(n) < 32); + match(Set dst (VectorInsert (Binary dst val) idx)); + format %{ "vector_insert $dst,$val,$idx" %} ins_encode %{ - int opcode = this->ideal_Opcode(); - int vlen = vector_length(this, $src2); - __ reduceI(opcode, vlen, $dst$$Register, $src1$$Register, $src2$$XMMRegister, $vtmp1$$XMMRegister, $vtmp2$$XMMRegister); + assert(UseSSE >= 4, "required"); + assert(vector_length_in_bytes(this) >= 8, "required"); + + BasicType elem_bt = vector_element_basic_type(this); + + assert(is_integral_type(elem_bt), ""); + assert($idx$$constant < (int)vector_length(this), "out of bounds"); + + __ insert(elem_bt, $dst$$XMMRegister, $val$$Register, $idx$$constant); + %} + ins_pipe( pipe_slow ); +%} + +instruct insert32(vec dst, vec src, rRegI val, immU8 idx, vec vtmp) %{ + predicate(vector_length_in_bytes(n) == 32); + match(Set dst (VectorInsert (Binary src val) idx)); + effect(TEMP vtmp); + format %{ "vector_insert $dst,$src,$val,$idx\t!using $vtmp as TEMP" %} + ins_encode %{ + int vlen_enc = Assembler::AVX_256bit; + BasicType elem_bt = vector_element_basic_type(this); + int elem_per_lane = 16/type2aelembytes(elem_bt); + int log2epr = log2(elem_per_lane); + + assert(is_integral_type(elem_bt), "sanity"); + assert($idx$$constant < (int)vector_length(this), "out of bounds"); + + uint x_idx = $idx$$constant & right_n_bits(log2epr); + uint y_idx = ($idx$$constant >> log2epr) & 1; + __ vextracti128($vtmp$$XMMRegister, $src$$XMMRegister, y_idx); + __ vinsert(elem_bt, $vtmp$$XMMRegister, $vtmp$$XMMRegister, $val$$Register, x_idx); + __ vinserti128($dst$$XMMRegister, $src$$XMMRegister, $vtmp$$XMMRegister, y_idx); + %} + ins_pipe( pipe_slow ); +%} + +instruct insert64(vec dst, vec src, rRegI val, immU8 idx, legVec vtmp) %{ + predicate(vector_length_in_bytes(n) == 64); + match(Set dst (VectorInsert (Binary src val) idx)); + effect(TEMP vtmp); + format %{ "vector_insert $dst,$src,$val,$idx\t!using $vtmp as TEMP" %} + ins_encode %{ + assert(UseAVX > 2, "sanity"); + + BasicType elem_bt = vector_element_basic_type(this); + int elem_per_lane = 16/type2aelembytes(elem_bt); + int log2epr = log2(elem_per_lane); + + assert(is_integral_type(elem_bt), ""); + assert($idx$$constant < (int)vector_length(this), "out of bounds"); + + uint x_idx = $idx$$constant & right_n_bits(log2epr); + uint y_idx = ($idx$$constant >> log2epr) & 3; + __ vextracti32x4($vtmp$$XMMRegister, $src$$XMMRegister, y_idx); + __ vinsert(elem_bt, $vtmp$$XMMRegister, $vtmp$$XMMRegister, $val$$Register, x_idx); + __ vinserti32x4($dst$$XMMRegister, $src$$XMMRegister, $vtmp$$XMMRegister, y_idx); + %} + ins_pipe( pipe_slow ); +%} + +#ifdef _LP64 +instruct insert2L(vec dst, rRegL val, immU8 idx) %{ + predicate(vector_length(n) == 2); + match(Set dst (VectorInsert (Binary dst val) idx)); + format %{ "vector_insert $dst,$val,$idx" %} + ins_encode %{ + assert(UseSSE >= 4, "required"); + assert(vector_element_basic_type(this) == T_LONG, ""); + assert($idx$$constant < (int)vector_length(this), "out of bounds"); + + __ pinsrq($dst$$XMMRegister, $val$$Register, $idx$$constant); + %} + ins_pipe( pipe_slow ); +%} + +instruct insert4L(vec dst, vec src, rRegL val, immU8 idx, vec vtmp) %{ + predicate(vector_length(n) == 4); + match(Set dst (VectorInsert (Binary src val) idx)); + effect(TEMP vtmp); + format %{ "vector_insert $dst,$src,$val,$idx\t!using $vtmp as TEMP" %} + ins_encode %{ + assert(vector_element_basic_type(this) == T_LONG, ""); + assert($idx$$constant < (int)vector_length(this), "out of bounds"); + + uint x_idx = $idx$$constant & right_n_bits(1); + uint y_idx = ($idx$$constant >> 1) & 1; + int vlen_enc = Assembler::AVX_256bit; + __ vextracti128($vtmp$$XMMRegister, $src$$XMMRegister, y_idx); + __ vpinsrq($vtmp$$XMMRegister, $vtmp$$XMMRegister, $val$$Register, x_idx); + __ vinserti128($dst$$XMMRegister, $src$$XMMRegister, $vtmp$$XMMRegister, y_idx); + %} + ins_pipe( pipe_slow ); +%} + +instruct insert8L(vec dst, vec src, rRegL val, immU8 idx, legVec vtmp) %{ + predicate(vector_length(n) == 8); + match(Set dst (VectorInsert (Binary src val) idx)); + effect(TEMP vtmp); + format %{ "vector_insert $dst,$src,$val,$idx\t!using $vtmp as TEMP" %} + ins_encode %{ + assert(vector_element_basic_type(this) == T_LONG, "sanity"); + assert($idx$$constant < (int)vector_length(this), "out of bounds"); + + uint x_idx = $idx$$constant & right_n_bits(1); + uint y_idx = ($idx$$constant >> 1) & 3; + __ vextracti32x4($vtmp$$XMMRegister, $src$$XMMRegister, y_idx); + __ vpinsrq($vtmp$$XMMRegister, $vtmp$$XMMRegister, $val$$Register, x_idx); + __ vinserti32x4($dst$$XMMRegister, $src$$XMMRegister, $vtmp$$XMMRegister, y_idx); + %} + ins_pipe( pipe_slow ); +%} +#endif + +instruct insertF(vec dst, regF val, immU8 idx) %{ + predicate(vector_length(n) < 8); + match(Set dst (VectorInsert (Binary dst val) idx)); + format %{ "vector_insert $dst,$val,$idx" %} + ins_encode %{ + assert(UseSSE >= 4, "sanity"); + + assert(vector_element_basic_type(this) == T_FLOAT, "sanity"); + assert($idx$$constant < (int)vector_length(this), "out of bounds"); + + __ insertps($dst$$XMMRegister, $val$$XMMRegister, $idx$$constant); + %} + ins_pipe( pipe_slow ); +%} + +instruct vinsertF(vec dst, vec src, regF val, immU8 idx, vec vtmp) %{ + predicate(vector_length(n) >= 8); + match(Set dst (VectorInsert (Binary src val) idx)); + effect(TEMP vtmp); + format %{ "vector_insert $dst,$src,$val,$idx\t!using $vtmp as TEMP" %} + ins_encode %{ + assert(vector_element_basic_type(this) == T_FLOAT, "sanity"); + assert($idx$$constant < (int)vector_length(this), "out of bounds"); + + int vlen = vector_length(this); + uint x_idx = $idx$$constant & right_n_bits(2); + if (vlen == 8) { + uint y_idx = ($idx$$constant >> 2) & 1; + int vlen_enc = Assembler::AVX_256bit; + __ vextracti128($vtmp$$XMMRegister, $src$$XMMRegister, y_idx); + __ vinsertps($vtmp$$XMMRegister, $vtmp$$XMMRegister, $val$$XMMRegister, x_idx); + __ vinserti128($dst$$XMMRegister, $src$$XMMRegister, $vtmp$$XMMRegister, y_idx); + } else { + assert(vlen == 16, "sanity"); + uint y_idx = ($idx$$constant >> 2) & 3; + __ vextracti32x4($vtmp$$XMMRegister, $src$$XMMRegister, y_idx); + __ vinsertps($vtmp$$XMMRegister, $vtmp$$XMMRegister, $val$$XMMRegister, x_idx); + __ vinserti32x4($dst$$XMMRegister, $src$$XMMRegister, $vtmp$$XMMRegister, y_idx); + } + %} + ins_pipe( pipe_slow ); +%} + +#ifdef _LP64 +instruct insert2D(vec dst, regD val, immU8 idx, rRegL tmp) %{ + predicate(vector_length(n) == 2); + match(Set dst (VectorInsert (Binary dst val) idx)); + effect(TEMP tmp); + format %{ "vector_insert $dst,$val,$idx\t!using $tmp as TEMP" %} + ins_encode %{ + assert(UseSSE >= 4, "sanity"); + assert(vector_element_basic_type(this) == T_DOUBLE, "sanity"); + assert($idx$$constant < (int)vector_length(this), "out of bounds"); + + __ movq($tmp$$Register, $val$$XMMRegister); + __ pinsrq($dst$$XMMRegister, $tmp$$Register, $idx$$constant); + %} + ins_pipe( pipe_slow ); +%} + +instruct insert4D(vec dst, vec src, regD val, immU8 idx, rRegL tmp, vec vtmp) %{ + predicate(vector_length(n) == 4); + match(Set dst (VectorInsert (Binary src val) idx)); + effect(TEMP vtmp, TEMP tmp); + format %{ "vector_insert $dst,$src,$val,$idx\t!using $tmp, $vtmp as TEMP" %} + ins_encode %{ + assert(vector_element_basic_type(this) == T_DOUBLE, "sanity"); + assert($idx$$constant < (int)vector_length(this), "out of bounds"); + + uint x_idx = $idx$$constant & right_n_bits(1); + uint y_idx = ($idx$$constant >> 1) & 1; + int vlen_enc = Assembler::AVX_256bit; + __ movq($tmp$$Register, $val$$XMMRegister); + __ vextracti128($vtmp$$XMMRegister, $src$$XMMRegister, y_idx); + __ vpinsrq($vtmp$$XMMRegister, $vtmp$$XMMRegister, $tmp$$Register, x_idx); + __ vinserti128($dst$$XMMRegister, $src$$XMMRegister, $vtmp$$XMMRegister, y_idx); + %} + ins_pipe( pipe_slow ); +%} + +instruct insert8D(vec dst, vec src, regD val, immI idx, rRegL tmp, legVec vtmp) %{ + predicate(vector_length(n) == 8); + match(Set dst (VectorInsert (Binary src val) idx)); + effect(TEMP tmp, TEMP vtmp); + format %{ "vector_insert $dst,$src,$val,$idx\t!using $vtmp as TEMP" %} + ins_encode %{ + assert(vector_element_basic_type(this) == T_DOUBLE, "sanity"); + assert($idx$$constant < (int)vector_length(this), "out of bounds"); + + uint x_idx = $idx$$constant & right_n_bits(1); + uint y_idx = ($idx$$constant >> 1) & 3; + __ movq($tmp$$Register, $val$$XMMRegister); + __ vextracti32x4($vtmp$$XMMRegister, $src$$XMMRegister, y_idx); + __ vpinsrq($vtmp$$XMMRegister, $vtmp$$XMMRegister, $tmp$$Register, x_idx); + __ vinserti32x4($dst$$XMMRegister, $src$$XMMRegister, $vtmp$$XMMRegister, y_idx); %} ins_pipe( pipe_slow ); %} +#endif -instruct reduction16I(rRegI dst, rRegI src1, legVec src2, legVec vtmp1, legVec vtmp2) %{ - predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_INT && - n->in(2)->bottom_type()->is_vect()->length() == 16); +// ====================REDUCTION ARITHMETIC======================================= + +// =======================Int Reduction========================================== + +instruct reductionI(rRegI dst, rRegI src1, legVec src2, legVec vtmp1, legVec vtmp2) %{ + predicate(vector_element_basic_type(n->in(2)) == T_INT); // src2 match(Set dst (AddReductionVI src1 src2)); match(Set dst (MulReductionVI src1 src2)); match(Set dst (AndReductionV src1 src2)); match(Set dst ( OrReductionV src1 src2)); match(Set dst (XorReductionV src1 src2)); + match(Set dst (MinReductionV src1 src2)); + match(Set dst (MaxReductionV src1 src2)); effect(TEMP vtmp1, TEMP vtmp2); format %{ "vector_reduction_int $dst,$src1,$src2 ; using $vtmp1, $vtmp2 as TEMP" %} ins_encode %{ @@ -3828,14 +4446,15 @@ instruct reduction16I(rRegI dst, rRegI src1, legVec src2, legVec vtmp1, legVec v // =======================Long Reduction========================================== #ifdef _LP64 -instruct reductionL(rRegL dst, rRegL src1, vec src2, vec vtmp1, vec vtmp2) %{ - predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_LONG && - n->in(2)->bottom_type()->is_vect()->length() < 8); +instruct reductionL(rRegL dst, rRegL src1, legVec src2, legVec vtmp1, legVec vtmp2) %{ + predicate(vector_element_basic_type(n->in(2)) == T_LONG && !VM_Version::supports_avx512dq()); match(Set dst (AddReductionVL src1 src2)); match(Set dst (MulReductionVL src1 src2)); match(Set dst (AndReductionV src1 src2)); match(Set dst ( OrReductionV src1 src2)); match(Set dst (XorReductionV src1 src2)); + match(Set dst (MinReductionV src1 src2)); + match(Set dst (MaxReductionV src1 src2)); effect(TEMP vtmp1, TEMP vtmp2); format %{ "vector_reduction_long $dst,$src1,$src2 ; using $vtmp1, $vtmp2 as TEMP" %} ins_encode %{ @@ -3846,14 +4465,15 @@ instruct reductionL(rRegL dst, rRegL src1, vec src2, vec vtmp1, vec vtmp2) %{ ins_pipe( pipe_slow ); %} -instruct reduction8L(rRegL dst, rRegL src1, legVec src2, legVec vtmp1, legVec vtmp2) %{ - predicate(n->in(2)->bottom_type()->is_vect()->element_basic_type() == T_LONG && - n->in(2)->bottom_type()->is_vect()->length() == 8); +instruct reductionL_avx512dq(rRegL dst, rRegL src1, vec src2, vec vtmp1, vec vtmp2) %{ + predicate(vector_element_basic_type(n->in(2)) == T_LONG && VM_Version::supports_avx512dq()); match(Set dst (AddReductionVL src1 src2)); match(Set dst (MulReductionVL src1 src2)); match(Set dst (AndReductionV src1 src2)); match(Set dst ( OrReductionV src1 src2)); match(Set dst (XorReductionV src1 src2)); + match(Set dst (MinReductionV src1 src2)); + match(Set dst (MaxReductionV src1 src2)); effect(TEMP vtmp1, TEMP vtmp2); format %{ "vector_reduction_long $dst,$src1,$src2 ; using $vtmp1, $vtmp2 as TEMP" %} ins_encode %{ @@ -3868,11 +4488,11 @@ instruct reduction8L(rRegL dst, rRegL src1, legVec src2, legVec vtmp1, legVec vt // =======================Float Reduction========================================== instruct reductionF128(regF dst, vec src, vec vtmp) %{ - predicate(n->in(2)->bottom_type()->is_vect()->length() <= 4); + predicate(vector_length(n->in(2)) <= 4); // src match(Set dst (AddReductionVF dst src)); match(Set dst (MulReductionVF dst src)); effect(TEMP dst, TEMP vtmp); - format %{ "vector_reduction_fp $dst,$src ; using $vtmp as TEMP" %} + format %{ "vector_reduction_float $dst,$src ; using $vtmp as TEMP" %} ins_encode %{ int opcode = this->ideal_Opcode(); int vlen = vector_length(this, $src); @@ -3882,7 +4502,7 @@ instruct reductionF128(regF dst, vec src, vec vtmp) %{ %} instruct reduction8F(regF dst, vec src, vec vtmp1, vec vtmp2) %{ - predicate(n->in(2)->bottom_type()->is_vect()->length() == 8); + predicate(vector_length(n->in(2)) == 8); // src match(Set dst (AddReductionVF dst src)); match(Set dst (MulReductionVF dst src)); effect(TEMP dst, TEMP vtmp1, TEMP vtmp2); @@ -3896,7 +4516,7 @@ instruct reduction8F(regF dst, vec src, vec vtmp1, vec vtmp2) %{ %} instruct reduction16F(regF dst, legVec src, legVec vtmp1, legVec vtmp2) %{ - predicate(n->in(2)->bottom_type()->is_vect()->length() == 16); + predicate(vector_length(n->in(2)) == 16); // src match(Set dst (AddReductionVF dst src)); match(Set dst (MulReductionVF dst src)); effect(TEMP dst, TEMP vtmp1, TEMP vtmp2); @@ -3912,7 +4532,7 @@ instruct reduction16F(regF dst, legVec src, legVec vtmp1, legVec vtmp2) %{ // =======================Double Reduction========================================== instruct reduction2D(regD dst, vec src, vec vtmp) %{ - predicate(n->in(2)->bottom_type()->is_vect()->length() == 2); + predicate(vector_length(n->in(2)) == 2); // src match(Set dst (AddReductionVD dst src)); match(Set dst (MulReductionVD dst src)); effect(TEMP dst, TEMP vtmp); @@ -3921,12 +4541,12 @@ instruct reduction2D(regD dst, vec src, vec vtmp) %{ int opcode = this->ideal_Opcode(); int vlen = vector_length(this, $src); __ reduce_fp(opcode, vlen, $dst$$XMMRegister, $src$$XMMRegister, $vtmp$$XMMRegister); - %} +%} ins_pipe( pipe_slow ); %} instruct reduction4D(regD dst, vec src, vec vtmp1, vec vtmp2) %{ - predicate(n->in(2)->bottom_type()->is_vect()->length() == 4); + predicate(vector_length(n->in(2)) == 4); // src match(Set dst (AddReductionVD dst src)); match(Set dst (MulReductionVD dst src)); effect(TEMP dst, TEMP vtmp1, TEMP vtmp2); @@ -3940,7 +4560,7 @@ instruct reduction4D(regD dst, vec src, vec vtmp1, vec vtmp2) %{ %} instruct reduction8D(regD dst, legVec src, legVec vtmp1, legVec vtmp2) %{ - predicate(n->in(2)->bottom_type()->is_vect()->length() == 8); + predicate(vector_length(n->in(2)) == 8); // src match(Set dst (AddReductionVD dst src)); match(Set dst (MulReductionVD dst src)); effect(TEMP dst, TEMP vtmp1, TEMP vtmp2); @@ -3953,6 +4573,267 @@ instruct reduction8D(regD dst, legVec src, legVec vtmp1, legVec vtmp2) %{ ins_pipe( pipe_slow ); %} +// =======================Byte Reduction========================================== + +#ifdef _LP64 +instruct reductionB(rRegI dst, rRegI src1, legVec src2, legVec vtmp1, legVec vtmp2) %{ + predicate(vector_element_basic_type(n->in(2)) == T_BYTE && !VM_Version::supports_avx512bw()); + match(Set dst (AddReductionVI src1 src2)); + match(Set dst (AndReductionV src1 src2)); + match(Set dst ( OrReductionV src1 src2)); + match(Set dst (XorReductionV src1 src2)); + match(Set dst (MinReductionV src1 src2)); + match(Set dst (MaxReductionV src1 src2)); + effect(TEMP vtmp1, TEMP vtmp2); + format %{ "vector_reduction_byte $dst,$src1,$src2 ; using $vtmp1, $vtmp2 as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = vector_length(this, $src2); + __ reduceB(opcode, vlen, $dst$$Register, $src1$$Register, $src2$$XMMRegister, $vtmp1$$XMMRegister, $vtmp2$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct reductionB_avx512bw(rRegI dst, rRegI src1, vec src2, vec vtmp1, vec vtmp2) %{ + predicate(vector_element_basic_type(n->in(2)) == T_BYTE && VM_Version::supports_avx512bw()); + match(Set dst (AddReductionVI src1 src2)); + match(Set dst (AndReductionV src1 src2)); + match(Set dst ( OrReductionV src1 src2)); + match(Set dst (XorReductionV src1 src2)); + match(Set dst (MinReductionV src1 src2)); + match(Set dst (MaxReductionV src1 src2)); + effect(TEMP vtmp1, TEMP vtmp2); + format %{ "vector_reduction_byte $dst,$src1,$src2 ; using $vtmp1, $vtmp2 as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = vector_length(this, $src2); + __ reduceB(opcode, vlen, $dst$$Register, $src1$$Register, $src2$$XMMRegister, $vtmp1$$XMMRegister, $vtmp2$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} +#endif + +// =======================Short Reduction========================================== + +instruct reductionS(rRegI dst, rRegI src1, legVec src2, legVec vtmp1, legVec vtmp2) %{ + predicate(vector_element_basic_type(n->in(2)) == T_SHORT); // src2 + match(Set dst (AddReductionVI src1 src2)); + match(Set dst (MulReductionVI src1 src2)); + match(Set dst (AndReductionV src1 src2)); + match(Set dst ( OrReductionV src1 src2)); + match(Set dst (XorReductionV src1 src2)); + match(Set dst (MinReductionV src1 src2)); + match(Set dst (MaxReductionV src1 src2)); + effect(TEMP vtmp1, TEMP vtmp2); + format %{ "vector_reduction_short $dst,$src1,$src2 ; using $vtmp1, $vtmp2 as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = vector_length(this, $src2); + __ reduceS(opcode, vlen, $dst$$Register, $src1$$Register, $src2$$XMMRegister, $vtmp1$$XMMRegister, $vtmp2$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +// =======================Mul Reduction========================================== + +instruct mul_reductionB(rRegI dst, rRegI src1, vec src2, vec vtmp1, vec vtmp2) %{ + predicate(vector_element_basic_type(n->in(2)) == T_BYTE && + vector_length(n->in(2)) <= 32); // src2 + match(Set dst (MulReductionVI src1 src2)); + effect(TEMP dst, TEMP vtmp1, TEMP vtmp2); + format %{ "vector_mul_reduction_byte $dst,$src1,$src2; using $vtmp1, $vtmp2 as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = vector_length(this, $src2); + __ mulreduceB(opcode, vlen, $dst$$Register, $src1$$Register, $src2$$XMMRegister, $vtmp1$$XMMRegister, $vtmp2$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct mul_reduction64B(rRegI dst, rRegI src1, legVec src2, legVec vtmp1, legVec vtmp2) %{ + predicate(vector_element_basic_type(n->in(2)) == T_BYTE && + vector_length(n->in(2)) == 64); // src2 + match(Set dst (MulReductionVI src1 src2)); + effect(TEMP dst, TEMP vtmp1, TEMP vtmp2); + format %{ "vector_mul_reduction_byte $dst,$src1,$src2; using $vtmp1, $vtmp2 as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = vector_length(this, $src2); + __ mulreduceB(opcode, vlen, $dst$$Register, $src1$$Register, $src2$$XMMRegister, $vtmp1$$XMMRegister, $vtmp2$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +//--------------------Min/Max Float Reduction -------------------- +// Float Min Reduction +instruct minmax_reduction2F(legRegF dst, immF src1, legVec src2, legVec tmp, + legVec atmp, legVec btmp, legVec xmm_1, rFlagsReg cr) %{ + predicate(vector_element_basic_type(n->in(2)) == T_FLOAT && + ((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeF::POS_INF) || + (n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeF::NEG_INF)) && + vector_length(n->in(2)) == 2); + match(Set dst (MinReductionV src1 src2)); + match(Set dst (MaxReductionV src1 src2)); + effect(TEMP dst, TEMP tmp, TEMP atmp, TEMP btmp, TEMP xmm_1, KILL cr); + format %{ "vector_minmax2F_reduction $dst,$src1,$src2 ; using $tmp, $atmp, $btmp, $xmm_1 as TEMP" %} + ins_encode %{ + assert(UseAVX > 0, "sanity"); + + int opcode = this->ideal_Opcode(); + int vlen = vector_length(this, $src2); + __ reduceFloatMinMax(opcode, vlen, false, $dst$$XMMRegister, $src2$$XMMRegister, $tmp$$XMMRegister, + $atmp$$XMMRegister, $btmp$$XMMRegister, $xmm_1$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct minmax_reductionF(legRegF dst, immF src1, legVec src2, legVec tmp, legVec atmp, + legVec btmp, legVec xmm_0, legVec xmm_1, rFlagsReg cr) %{ + predicate(vector_element_basic_type(n->in(2)) == T_FLOAT && + ((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeF::POS_INF) || + (n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeF::NEG_INF)) && + vector_length(n->in(2)) >= 4); + match(Set dst (MinReductionV src1 src2)); + match(Set dst (MaxReductionV src1 src2)); + effect(TEMP dst, TEMP tmp, TEMP atmp, TEMP btmp, TEMP xmm_0, TEMP xmm_1, KILL cr); + format %{ "vector_minmaxF_reduction $dst,$src1,$src2 ; using $tmp, $atmp, $btmp, $xmm_0, $xmm_1 as TEMP" %} + ins_encode %{ + assert(UseAVX > 0, "sanity"); + + int opcode = this->ideal_Opcode(); + int vlen = vector_length(this, $src2); + __ reduceFloatMinMax(opcode, vlen, false, $dst$$XMMRegister, $src2$$XMMRegister, $tmp$$XMMRegister, + $atmp$$XMMRegister, $btmp$$XMMRegister, $xmm_0$$XMMRegister, $xmm_1$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct minmax_reduction2F_av(legRegF dst, legVec src, legVec tmp, + legVec atmp, legVec btmp, legVec xmm_1, rFlagsReg cr) %{ + predicate(vector_element_basic_type(n->in(2)) == T_FLOAT && + vector_length(n->in(2)) == 2); + match(Set dst (MinReductionV dst src)); + match(Set dst (MaxReductionV dst src)); + effect(TEMP dst, TEMP tmp, TEMP atmp, TEMP btmp, TEMP xmm_1, KILL cr); + format %{ "vector_minmax2F_reduction $dst,$src ; using $tmp, $atmp, $btmp, $xmm_1 as TEMP" %} + ins_encode %{ + assert(UseAVX > 0, "sanity"); + + int opcode = this->ideal_Opcode(); + int vlen = vector_length(this, $src); + __ reduceFloatMinMax(opcode, vlen, true, $dst$$XMMRegister, $src$$XMMRegister, $tmp$$XMMRegister, + $atmp$$XMMRegister, $btmp$$XMMRegister, $xmm_1$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + + +instruct minmax_reductionF_av(legRegF dst, legVec src, legVec tmp, + legVec atmp, legVec btmp, legVec xmm_0, legVec xmm_1, rFlagsReg cr) %{ + predicate(vector_element_basic_type(n->in(2)) == T_FLOAT && + vector_length(n->in(2)) >= 4); + match(Set dst (MinReductionV dst src)); + match(Set dst (MaxReductionV dst src)); + effect(TEMP dst, TEMP tmp, TEMP atmp, TEMP btmp, TEMP xmm_0, TEMP xmm_1, KILL cr); + format %{ "vector_minmaxF_reduction $dst,$src ; using $tmp, $atmp, $btmp, $xmm_0, $xmm_1 as TEMP" %} + ins_encode %{ + assert(UseAVX > 0, "sanity"); + + int opcode = this->ideal_Opcode(); + int vlen = vector_length(this, $src); + __ reduceFloatMinMax(opcode, vlen, true, $dst$$XMMRegister, $src$$XMMRegister, $tmp$$XMMRegister, + $atmp$$XMMRegister, $btmp$$XMMRegister, $xmm_0$$XMMRegister, $xmm_1$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + + +//--------------------Min Double Reduction -------------------- +instruct minmax_reduction2D(legRegD dst, immD src1, legVec src2, + legVec tmp1, legVec tmp2, legVec tmp3, legVec tmp4, // TEMPs + rFlagsReg cr) %{ + predicate(vector_element_basic_type(n->in(2)) == T_DOUBLE && + ((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeD::POS_INF) || + (n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeD::NEG_INF)) && + vector_length(n->in(2)) == 2); + match(Set dst (MinReductionV src1 src2)); + match(Set dst (MaxReductionV src1 src2)); + effect(TEMP dst, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr); + format %{ "vector_minmax2D_reduction $dst,$src1,$src2 ; using $tmp1, $tmp2, $tmp3, $tmp4 as TEMP" %} + ins_encode %{ + assert(UseAVX > 0, "sanity"); + + int opcode = this->ideal_Opcode(); + int vlen = vector_length(this, $src2); + __ reduceDoubleMinMax(opcode, vlen, false, $dst$$XMMRegister, $src2$$XMMRegister, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, $tmp4$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct minmax_reductionD(legRegD dst, immD src1, legVec src2, + legVec tmp1, legVec tmp2, legVec tmp3, legVec tmp4, legVec tmp5, // TEMPs + rFlagsReg cr) %{ + predicate(vector_element_basic_type(n->in(2)) == T_DOUBLE && + ((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeD::POS_INF) || + (n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeD::NEG_INF)) && + vector_length(n->in(2)) >= 4); + match(Set dst (MinReductionV src1 src2)); + match(Set dst (MaxReductionV src1 src2)); + effect(TEMP dst, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, KILL cr); + format %{ "vector_minmaxD_reduction $dst,$src1,$src2 ; using $tmp1, $tmp2, $tmp3, $tmp4, $tmp5 as TEMP" %} + ins_encode %{ + assert(UseAVX > 0, "sanity"); + + int opcode = this->ideal_Opcode(); + int vlen = vector_length(this, $src2); + __ reduceDoubleMinMax(opcode, vlen, false, $dst$$XMMRegister, $src2$$XMMRegister, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, $tmp4$$XMMRegister, $tmp5$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + + +instruct minmax_reduction2D_av(legRegD dst, legVec src, + legVec tmp1, legVec tmp2, legVec tmp3, legVec tmp4, // TEMPs + rFlagsReg cr) %{ + predicate(vector_element_basic_type(n->in(2)) == T_DOUBLE && + vector_length(n->in(2)) == 2); + match(Set dst (MinReductionV dst src)); + match(Set dst (MaxReductionV dst src)); + effect(TEMP dst, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr); + format %{ "vector_minmax2D_reduction $dst,$src ; using $tmp1, $tmp2, $tmp3, $tmp4 as TEMP" %} + ins_encode %{ + assert(UseAVX > 0, "sanity"); + + int opcode = this->ideal_Opcode(); + int vlen = vector_length(this, $src); + __ reduceDoubleMinMax(opcode, vlen, true, $dst$$XMMRegister, $src$$XMMRegister, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, $tmp4$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct minmax_reductionD_av(legRegD dst, legVec src, + legVec tmp1, legVec tmp2, legVec tmp3, legVec tmp4, legVec tmp5, // TEMPs + rFlagsReg cr) %{ + predicate(vector_element_basic_type(n->in(2)) == T_DOUBLE && + vector_length(n->in(2)) >= 4); + match(Set dst (MinReductionV dst src)); + match(Set dst (MaxReductionV dst src)); + effect(TEMP dst, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, KILL cr); + format %{ "vector_minmaxD_reduction $dst,$src ; using $tmp1, $tmp2, $tmp3, $tmp4, $tmp5 as TEMP" %} + ins_encode %{ + assert(UseAVX > 0, "sanity"); + + int opcode = this->ideal_Opcode(); + int vlen = vector_length(this, $src); + __ reduceDoubleMinMax(opcode, vlen, true, $dst$$XMMRegister, $src$$XMMRegister, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, $tmp4$$XMMRegister, $tmp5$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + // ====================VECTOR ARITHMETIC======================================= // --------------------------------- ADD -------------------------------------- @@ -3973,8 +4854,8 @@ instruct vaddB_reg(vec dst, vec src1, vec src2) %{ match(Set dst (AddVB src1 src2)); format %{ "vpaddb $dst,$src1,$src2\t! add packedB" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpaddb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpaddb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -3984,8 +4865,8 @@ instruct vaddB_mem(vec dst, vec src, memory mem) %{ match(Set dst (AddVB src (LoadVector mem))); format %{ "vpaddb $dst,$src,$mem\t! add packedB" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpaddb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpaddb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4006,8 +4887,8 @@ instruct vaddS_reg(vec dst, vec src1, vec src2) %{ match(Set dst (AddVS src1 src2)); format %{ "vpaddw $dst,$src1,$src2\t! add packedS" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpaddw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpaddw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4017,8 +4898,8 @@ instruct vaddS_mem(vec dst, vec src, memory mem) %{ match(Set dst (AddVS src (LoadVector mem))); format %{ "vpaddw $dst,$src,$mem\t! add packedS" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpaddw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpaddw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4039,8 +4920,8 @@ instruct vaddI_reg(vec dst, vec src1, vec src2) %{ match(Set dst (AddVI src1 src2)); format %{ "vpaddd $dst,$src1,$src2\t! add packedI" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpaddd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpaddd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4051,8 +4932,8 @@ instruct vaddI_mem(vec dst, vec src, memory mem) %{ match(Set dst (AddVI src (LoadVector mem))); format %{ "vpaddd $dst,$src,$mem\t! add packedI" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpaddd($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpaddd($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4073,8 +4954,8 @@ instruct vaddL_reg(vec dst, vec src1, vec src2) %{ match(Set dst (AddVL src1 src2)); format %{ "vpaddq $dst,$src1,$src2\t! add packedL" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpaddq($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpaddq($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4084,8 +4965,8 @@ instruct vaddL_mem(vec dst, vec src, memory mem) %{ match(Set dst (AddVL src (LoadVector mem))); format %{ "vpaddq $dst,$src,$mem\t! add packedL" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpaddq($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpaddq($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4106,8 +4987,8 @@ instruct vaddF_reg(vec dst, vec src1, vec src2) %{ match(Set dst (AddVF src1 src2)); format %{ "vaddps $dst,$src1,$src2\t! add packedF" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vaddps($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vaddps($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4117,8 +4998,8 @@ instruct vaddF_mem(vec dst, vec src, memory mem) %{ match(Set dst (AddVF src (LoadVector mem))); format %{ "vaddps $dst,$src,$mem\t! add packedF" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vaddps($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vaddps($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4139,8 +5020,8 @@ instruct vaddD_reg(vec dst, vec src1, vec src2) %{ match(Set dst (AddVD src1 src2)); format %{ "vaddpd $dst,$src1,$src2\t! add packedD" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vaddpd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vaddpd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4150,8 +5031,8 @@ instruct vaddD_mem(vec dst, vec src, memory mem) %{ match(Set dst (AddVD src (LoadVector mem))); format %{ "vaddpd $dst,$src,$mem\t! add packedD" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vaddpd($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vaddpd($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4174,8 +5055,8 @@ instruct vsubB_reg(vec dst, vec src1, vec src2) %{ match(Set dst (SubVB src1 src2)); format %{ "vpsubb $dst,$src1,$src2\t! sub packedB" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpsubb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpsubb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4185,8 +5066,8 @@ instruct vsubB_mem(vec dst, vec src, memory mem) %{ match(Set dst (SubVB src (LoadVector mem))); format %{ "vpsubb $dst,$src,$mem\t! sub packedB" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpsubb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpsubb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4208,8 +5089,8 @@ instruct vsubS_reg(vec dst, vec src1, vec src2) %{ match(Set dst (SubVS src1 src2)); format %{ "vpsubw $dst,$src1,$src2\t! sub packedS" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpsubw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpsubw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4219,8 +5100,8 @@ instruct vsubS_mem(vec dst, vec src, memory mem) %{ match(Set dst (SubVS src (LoadVector mem))); format %{ "vpsubw $dst,$src,$mem\t! sub packedS" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpsubw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpsubw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4241,8 +5122,8 @@ instruct vsubI_reg(vec dst, vec src1, vec src2) %{ match(Set dst (SubVI src1 src2)); format %{ "vpsubd $dst,$src1,$src2\t! sub packedI" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpsubd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpsubd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4252,8 +5133,8 @@ instruct vsubI_mem(vec dst, vec src, memory mem) %{ match(Set dst (SubVI src (LoadVector mem))); format %{ "vpsubd $dst,$src,$mem\t! sub packedI" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpsubd($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpsubd($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4274,8 +5155,8 @@ instruct vsubL_reg(vec dst, vec src1, vec src2) %{ match(Set dst (SubVL src1 src2)); format %{ "vpsubq $dst,$src1,$src2\t! sub packedL" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpsubq($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpsubq($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4286,8 +5167,8 @@ instruct vsubL_mem(vec dst, vec src, memory mem) %{ match(Set dst (SubVL src (LoadVector mem))); format %{ "vpsubq $dst,$src,$mem\t! sub packedL" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpsubq($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpsubq($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4308,8 +5189,8 @@ instruct vsubF_reg(vec dst, vec src1, vec src2) %{ match(Set dst (SubVF src1 src2)); format %{ "vsubps $dst,$src1,$src2\t! sub packedF" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vsubps($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vsubps($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4319,8 +5200,8 @@ instruct vsubF_mem(vec dst, vec src, memory mem) %{ match(Set dst (SubVF src (LoadVector mem))); format %{ "vsubps $dst,$src,$mem\t! sub packedF" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vsubps($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vsubps($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4341,8 +5222,8 @@ instruct vsubD_reg(vec dst, vec src1, vec src2) %{ match(Set dst (SubVD src1 src2)); format %{ "vsubpd $dst,$src1,$src2\t! sub packedD" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vsubpd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vsubpd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4352,8 +5233,8 @@ instruct vsubD_mem(vec dst, vec src, memory mem) %{ match(Set dst (SubVD src (LoadVector mem))); format %{ "vsubpd $dst,$src,$mem\t! sub packedD" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vsubpd($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vsubpd($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4362,8 +5243,8 @@ instruct vsubD_mem(vec dst, vec src, memory mem) %{ // Byte vector mul instruct mulB_reg(vec dst, vec src1, vec src2, vec tmp, rRegI scratch) %{ - predicate(n->as_Vector()->length() == 4 || - n->as_Vector()->length() == 8); + predicate(vector_length(n) == 4 || + vector_length(n) == 8); match(Set dst (MulVB src1 src2)); effect(TEMP dst, TEMP tmp, TEMP scratch); format %{"vector_mulB $dst,$src1,$src2" %} @@ -4380,7 +5261,7 @@ instruct mulB_reg(vec dst, vec src1, vec src2, vec tmp, rRegI scratch) %{ %} instruct mul16B_reg(vec dst, vec src1, vec src2, vec tmp1, vec tmp2, rRegI scratch) %{ - predicate(n->as_Vector()->length() == 16 && UseAVX <= 1); + predicate(vector_length(n) == 16 && UseAVX <= 1); match(Set dst (MulVB src1 src2)); effect(TEMP dst, TEMP tmp1, TEMP tmp2, TEMP scratch); format %{"vector_mulB $dst,$src1,$src2" %} @@ -4403,17 +5284,17 @@ instruct mul16B_reg(vec dst, vec src1, vec src2, vec tmp1, vec tmp2, rRegI scrat %} instruct vmul16B_reg_avx(vec dst, vec src1, vec src2, vec tmp, rRegI scratch) %{ - predicate(n->as_Vector()->length() == 16 && UseAVX > 1); + predicate(vector_length(n) == 16 && UseAVX > 1); match(Set dst (MulVB src1 src2)); effect(TEMP dst, TEMP tmp, TEMP scratch); format %{"vector_mulB $dst,$src1,$src2" %} ins_encode %{ - int vector_len = Assembler::AVX_256bit; - __ vpmovsxbw($tmp$$XMMRegister, $src1$$XMMRegister, vector_len); - __ vpmovsxbw($dst$$XMMRegister, $src2$$XMMRegister, vector_len); - __ vpmullw($tmp$$XMMRegister, $tmp$$XMMRegister, $dst$$XMMRegister, vector_len); + int vlen_enc = Assembler::AVX_256bit; + __ vpmovsxbw($tmp$$XMMRegister, $src1$$XMMRegister, vlen_enc); + __ vpmovsxbw($dst$$XMMRegister, $src2$$XMMRegister, vlen_enc); + __ vpmullw($tmp$$XMMRegister, $tmp$$XMMRegister, $dst$$XMMRegister, vlen_enc); __ vmovdqu($dst$$XMMRegister, ExternalAddress(vector_short_to_byte_mask()), $scratch$$Register); - __ vpand($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister, vector_len); + __ vpand($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister, vlen_enc); __ vextracti128_high($tmp$$XMMRegister, $dst$$XMMRegister); __ vpackuswb($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister, 0); %} @@ -4421,54 +5302,54 @@ instruct vmul16B_reg_avx(vec dst, vec src1, vec src2, vec tmp, rRegI scratch) %{ %} instruct vmul32B_reg_avx(vec dst, vec src1, vec src2, vec tmp1, vec tmp2, rRegI scratch) %{ - predicate(n->as_Vector()->length() == 32); + predicate(vector_length(n) == 32); match(Set dst (MulVB src1 src2)); effect(TEMP dst, TEMP tmp1, TEMP tmp2, TEMP scratch); format %{"vector_mulB $dst,$src1,$src2" %} ins_encode %{ assert(UseAVX > 1, "required"); - int vector_len = Assembler::AVX_256bit; + int vlen_enc = Assembler::AVX_256bit; __ vextracti128_high($tmp1$$XMMRegister, $src1$$XMMRegister); __ vextracti128_high($dst$$XMMRegister, $src2$$XMMRegister); - __ vpmovsxbw($tmp1$$XMMRegister, $tmp1$$XMMRegister, vector_len); - __ vpmovsxbw($dst$$XMMRegister, $dst$$XMMRegister, vector_len); - __ vpmullw($tmp1$$XMMRegister, $tmp1$$XMMRegister, $dst$$XMMRegister, vector_len); - __ vpmovsxbw($tmp2$$XMMRegister, $src1$$XMMRegister, vector_len); - __ vpmovsxbw($dst$$XMMRegister, $src2$$XMMRegister, vector_len); - __ vpmullw($tmp2$$XMMRegister, $tmp2$$XMMRegister, $dst$$XMMRegister, vector_len); + __ vpmovsxbw($tmp1$$XMMRegister, $tmp1$$XMMRegister, vlen_enc); + __ vpmovsxbw($dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpmullw($tmp1$$XMMRegister, $tmp1$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpmovsxbw($tmp2$$XMMRegister, $src1$$XMMRegister, vlen_enc); + __ vpmovsxbw($dst$$XMMRegister, $src2$$XMMRegister, vlen_enc); + __ vpmullw($tmp2$$XMMRegister, $tmp2$$XMMRegister, $dst$$XMMRegister, vlen_enc); __ vmovdqu($dst$$XMMRegister, ExternalAddress(vector_short_to_byte_mask()), $scratch$$Register); - __ vpbroadcastd($dst$$XMMRegister, $dst$$XMMRegister, vector_len); - __ vpand($tmp1$$XMMRegister, $tmp1$$XMMRegister, $dst$$XMMRegister, vector_len); - __ vpand($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister, vector_len); - __ vpackuswb($dst$$XMMRegister, $dst$$XMMRegister, $tmp1$$XMMRegister, vector_len); - __ vpermq($dst$$XMMRegister, $dst$$XMMRegister, 0xD8, vector_len); + __ vpbroadcastd($dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpand($tmp1$$XMMRegister, $tmp1$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpand($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister, vlen_enc); + __ vpackuswb($dst$$XMMRegister, $dst$$XMMRegister, $tmp1$$XMMRegister, vlen_enc); + __ vpermq($dst$$XMMRegister, $dst$$XMMRegister, 0xD8, vlen_enc); %} ins_pipe( pipe_slow ); %} instruct vmul64B_reg_avx(vec dst, vec src1, vec src2, vec tmp1, vec tmp2, rRegI scratch) %{ - predicate(n->as_Vector()->length() == 64); + predicate(vector_length(n) == 64); match(Set dst (MulVB src1 src2)); effect(TEMP dst, TEMP tmp1, TEMP tmp2, TEMP scratch); format %{"vector_mulB $dst,$src1,$src2\n\t" %} ins_encode %{ assert(UseAVX > 2, "required"); - int vector_len = Assembler::AVX_512bit; + int vlen_enc = Assembler::AVX_512bit; __ vextracti64x4_high($tmp1$$XMMRegister, $src1$$XMMRegister); __ vextracti64x4_high($dst$$XMMRegister, $src2$$XMMRegister); - __ vpmovsxbw($tmp1$$XMMRegister, $tmp1$$XMMRegister, vector_len); - __ vpmovsxbw($dst$$XMMRegister, $dst$$XMMRegister, vector_len); - __ vpmullw($tmp1$$XMMRegister, $tmp1$$XMMRegister, $dst$$XMMRegister, vector_len); - __ vpmovsxbw($tmp2$$XMMRegister, $src1$$XMMRegister, vector_len); - __ vpmovsxbw($dst$$XMMRegister, $src2$$XMMRegister, vector_len); - __ vpmullw($tmp2$$XMMRegister, $tmp2$$XMMRegister, $dst$$XMMRegister, vector_len); + __ vpmovsxbw($tmp1$$XMMRegister, $tmp1$$XMMRegister, vlen_enc); + __ vpmovsxbw($dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpmullw($tmp1$$XMMRegister, $tmp1$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpmovsxbw($tmp2$$XMMRegister, $src1$$XMMRegister, vlen_enc); + __ vpmovsxbw($dst$$XMMRegister, $src2$$XMMRegister, vlen_enc); + __ vpmullw($tmp2$$XMMRegister, $tmp2$$XMMRegister, $dst$$XMMRegister, vlen_enc); __ vmovdqu($dst$$XMMRegister, ExternalAddress(vector_short_to_byte_mask()), $scratch$$Register); - __ vpbroadcastd($dst$$XMMRegister, $dst$$XMMRegister, vector_len); - __ vpand($tmp1$$XMMRegister, $tmp1$$XMMRegister, $dst$$XMMRegister, vector_len); - __ vpand($tmp2$$XMMRegister, $tmp2$$XMMRegister, $dst$$XMMRegister, vector_len); - __ vpackuswb($dst$$XMMRegister, $tmp1$$XMMRegister, $tmp2$$XMMRegister, vector_len); - __ evmovdquq($tmp2$$XMMRegister, ExternalAddress(vector_byte_perm_mask()), vector_len, $scratch$$Register); - __ vpermq($dst$$XMMRegister, $tmp2$$XMMRegister, $dst$$XMMRegister, vector_len); + __ vpbroadcastd($dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpand($tmp1$$XMMRegister, $tmp1$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpand($tmp2$$XMMRegister, $tmp2$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpackuswb($dst$$XMMRegister, $tmp1$$XMMRegister, $tmp2$$XMMRegister, vlen_enc); + __ evmovdquq($tmp2$$XMMRegister, ExternalAddress(vector_byte_perm_mask()), vlen_enc, $scratch$$Register); + __ vpermq($dst$$XMMRegister, $tmp2$$XMMRegister, $dst$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4489,8 +5370,8 @@ instruct vmulS_reg(vec dst, vec src1, vec src2) %{ match(Set dst (MulVS src1 src2)); format %{ "vpmullw $dst,$src1,$src2\t! mul packedS" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpmullw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpmullw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4500,8 +5381,8 @@ instruct vmulS_mem(vec dst, vec src, memory mem) %{ match(Set dst (MulVS src (LoadVector mem))); format %{ "vpmullw $dst,$src,$mem\t! mul packedS" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpmullw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpmullw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4523,8 +5404,8 @@ instruct vmulI_reg(vec dst, vec src1, vec src2) %{ match(Set dst (MulVI src1 src2)); format %{ "vpmulld $dst,$src1,$src2\t! mul packedI" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpmulld($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpmulld($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4534,31 +5415,84 @@ instruct vmulI_mem(vec dst, vec src, memory mem) %{ match(Set dst (MulVI src (LoadVector mem))); format %{ "vpmulld $dst,$src,$mem\t! mul packedI" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpmulld($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpmulld($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} // Longs vector mul instruct vmulL_reg(vec dst, vec src1, vec src2) %{ + predicate(VM_Version::supports_avx512dq()); match(Set dst (MulVL src1 src2)); format %{ "vpmullq $dst,$src1,$src2\t! mul packedL" %} ins_encode %{ assert(UseAVX > 2, "required"); - int vector_len = vector_length_encoding(this); - __ vpmullq($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpmullq($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} instruct vmulL_mem(vec dst, vec src, memory mem) %{ + predicate(VM_Version::supports_avx512dq()); match(Set dst (MulVL src (LoadVector mem))); format %{ "vpmullq $dst,$src,$mem\t! mul packedL" %} ins_encode %{ assert(UseAVX > 2, "required"); - int vector_len = vector_length_encoding(this); - __ vpmullq($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpmullq($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct mul2L_reg(vec dst, vec src2, legVec tmp) %{ + predicate(vector_length(n) == 2 && !VM_Version::supports_avx512dq()); + match(Set dst (MulVL dst src2)); + effect(TEMP dst, TEMP tmp); + format %{ "pshufd $tmp,$src2, 177\n\t" + "pmulld $tmp,$dst\n\t" + "phaddd $tmp,$tmp\n\t" + "pmovzxdq $tmp,$tmp\n\t" + "psllq $tmp, 32\n\t" + "pmuludq $dst,$src2\n\t" + "paddq $dst,$tmp\n\t! mul packed2L" %} + + ins_encode %{ + assert(VM_Version::supports_sse4_1(), "required"); + int vlen_enc = Assembler::AVX_128bit; + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 177); + __ pmulld($tmp$$XMMRegister, $dst$$XMMRegister); + __ phaddd($tmp$$XMMRegister, $tmp$$XMMRegister); + __ pmovzxdq($tmp$$XMMRegister, $tmp$$XMMRegister); + __ psllq($tmp$$XMMRegister, 32); + __ pmuludq($dst$$XMMRegister, $src2$$XMMRegister); + __ paddq($dst$$XMMRegister, $tmp$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct vmul4L_reg_avx(vec dst, vec src1, vec src2, legVec tmp, legVec tmp1) %{ + predicate(vector_length(n) == 4 && !VM_Version::supports_avx512dq()); + match(Set dst (MulVL src1 src2)); + effect(TEMP tmp1, TEMP tmp); + format %{ "vpshufd $tmp,$src2\n\t" + "vpmulld $tmp,$src1,$tmp\n\t" + "vphaddd $tmp,$tmp,$tmp\n\t" + "vpmovzxdq $tmp,$tmp\n\t" + "vpsllq $tmp,$tmp\n\t" + "vpmuludq $tmp1,$src1,$src2\n\t" + "vpaddq $dst,$tmp,$tmp1\t! mul packed4L" %} + ins_encode %{ + int vlen_enc = Assembler::AVX_256bit; + __ vpshufd($tmp$$XMMRegister, $src2$$XMMRegister, 177, vlen_enc); + __ vpmulld($tmp$$XMMRegister, $src1$$XMMRegister, $tmp$$XMMRegister, vlen_enc); + __ vextracti128_high($tmp1$$XMMRegister, $tmp$$XMMRegister); + __ vphaddd($tmp$$XMMRegister, $tmp$$XMMRegister, $tmp1$$XMMRegister, vlen_enc); + __ vpmovzxdq($tmp$$XMMRegister, $tmp$$XMMRegister, vlen_enc); + __ vpsllq($tmp$$XMMRegister, $tmp$$XMMRegister, 32, vlen_enc); + __ vpmuludq($tmp1$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); + __ vpaddq($dst$$XMMRegister, $tmp$$XMMRegister, $tmp1$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4579,8 +5513,8 @@ instruct vmulF_reg(vec dst, vec src1, vec src2) %{ match(Set dst (MulVF src1 src2)); format %{ "vmulps $dst,$src1,$src2\t! mul packedF" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vmulps($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vmulps($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4590,8 +5524,8 @@ instruct vmulF_mem(vec dst, vec src, memory mem) %{ match(Set dst (MulVF src (LoadVector mem))); format %{ "vmulps $dst,$src,$mem\t! mul packedF" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vmulps($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vmulps($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4612,8 +5546,8 @@ instruct vmulD_reg(vec dst, vec src1, vec src2) %{ match(Set dst (MulVD src1 src2)); format %{ "vmulpd $dst,$src1,$src2\t! mul packedD" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vmulpd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vmulpd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4623,40 +5557,44 @@ instruct vmulD_mem(vec dst, vec src, memory mem) %{ match(Set dst (MulVD src (LoadVector mem))); format %{ "vmulpd $dst,$src,$mem\t! mul packedD" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vmulpd($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vmulpd($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} instruct vcmov8F_reg(legVec dst, legVec src1, legVec src2, immI8 cop, cmpOp_vcmppd copnd) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 8); + predicate(vector_length(n) == 8); match(Set dst (CMoveVF (Binary copnd cop) (Binary src1 src2))); effect(TEMP dst, USE src1, USE src2); format %{ "cmpps.$copnd $dst, $src1, $src2 ! vcmovevf, cond=$cop\n\t" "blendvps $dst,$src1,$src2,$dst ! vcmovevf\n\t" %} ins_encode %{ - int vector_len = 1; + assert(UseAVX > 0, "required"); + + int vlen_enc = Assembler::AVX_256bit; int cond = (Assembler::Condition)($copnd$$cmpcode); - __ cmpps($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, cond, vector_len); - __ blendvps($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, $dst$$XMMRegister, vector_len); + __ vcmpps($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, cond, vlen_enc); + __ vblendvps($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, $dst$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} instruct vcmov4D_reg(legVec dst, legVec src1, legVec src2, immI8 cop, cmpOp_vcmppd copnd) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 4); + predicate(vector_length(n) == 4); match(Set dst (CMoveVD (Binary copnd cop) (Binary src1 src2))); effect(TEMP dst, USE src1, USE src2); format %{ "cmppd.$copnd $dst, $src1, $src2 ! vcmovevd, cond=$cop\n\t" - "blendvpd $dst,$src1,$src2,$dst ! vcmovevd\n\t" + "vblendvpd $dst,$src1,$src2,$dst ! vcmovevd\n\t" %} ins_encode %{ - int vector_len = 1; + assert(UseAVX > 0, "required"); + + int vlen_enc = Assembler::AVX_256bit; int cond = (Assembler::Condition)($copnd$$cmpcode); - __ cmppd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, cond, vector_len); - __ blendvpd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, $dst$$XMMRegister, vector_len); + __ vcmppd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, cond, vlen_enc); + __ vblendvpd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, $dst$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4679,8 +5617,8 @@ instruct vdivF_reg(vec dst, vec src1, vec src2) %{ match(Set dst (DivVF src1 src2)); format %{ "vdivps $dst,$src1,$src2\t! div packedF" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vdivps($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vdivps($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4690,8 +5628,8 @@ instruct vdivF_mem(vec dst, vec src, memory mem) %{ match(Set dst (DivVF src (LoadVector mem))); format %{ "vdivps $dst,$src,$mem\t! div packedF" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vdivps($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vdivps($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4712,8 +5650,8 @@ instruct vdivD_reg(vec dst, vec src1, vec src2) %{ match(Set dst (DivVD src1 src2)); format %{ "vdivpd $dst,$src1,$src2\t! div packedD" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vdivpd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vdivpd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4723,8 +5661,145 @@ instruct vdivD_mem(vec dst, vec src, memory mem) %{ match(Set dst (DivVD src (LoadVector mem))); format %{ "vdivpd $dst,$src,$mem\t! div packedD" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vdivpd($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vdivpd($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +// ------------------------------ MinMax --------------------------------------- + +// Byte, Short, Int vector Min/Max +instruct minmax_reg_sse(vec dst, vec src) %{ + predicate(is_integral_type(vector_element_basic_type(n)) && vector_element_basic_type(n) != T_LONG && // T_BYTE, T_SHORT, T_INT + UseAVX == 0); + match(Set dst (MinV dst src)); + match(Set dst (MaxV dst src)); + format %{ "vector_minmax $dst,$src\t! " %} + ins_encode %{ + assert(UseSSE >= 4, "required"); + + int opcode = this->ideal_Opcode(); + BasicType elem_bt = vector_element_basic_type(this); + __ pminmax(opcode, elem_bt, $dst$$XMMRegister, $src$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct vminmax_reg(vec dst, vec src1, vec src2) %{ + predicate(is_integral_type(vector_element_basic_type(n)) && vector_element_basic_type(n) != T_LONG && // T_BYTE, T_SHORT, T_INT + UseAVX > 0); + match(Set dst (MinV src1 src2)); + match(Set dst (MaxV src1 src2)); + format %{ "vector_minmax $dst,$src1,$src2\t! " %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen_enc = vector_length_encoding(this); + BasicType elem_bt = vector_element_basic_type(this); + + __ vpminmax(opcode, elem_bt, $dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +// Long vector Min/Max +instruct minmaxL_reg_sse(vec dst, vec src, rxmm0 tmp) %{ + predicate(vector_length_in_bytes(n) == 16 && vector_element_basic_type(n) == T_LONG && + UseAVX == 0); + match(Set dst (MinV dst src)); + match(Set dst (MaxV src dst)); + effect(TEMP dst, TEMP tmp); + format %{ "vector_minmaxL $dst,$src\t!using $tmp as TEMP" %} + ins_encode %{ + assert(UseSSE >= 4, "required"); + + int opcode = this->ideal_Opcode(); + BasicType elem_bt = vector_element_basic_type(this); + assert(elem_bt == T_LONG, "sanity"); + + __ pminmax(opcode, elem_bt, $dst$$XMMRegister, $src$$XMMRegister, $tmp$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct vminmaxL_reg_avx(legVec dst, legVec src1, legVec src2) %{ + predicate(vector_length_in_bytes(n) <= 32 && vector_element_basic_type(n) == T_LONG && + UseAVX > 0 && !VM_Version::supports_avx512vl()); + match(Set dst (MinV src1 src2)); + match(Set dst (MaxV src1 src2)); + effect(TEMP dst); + format %{ "vector_minmaxL $dst,$src1,$src2\t! " %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this); + int opcode = this->ideal_Opcode(); + BasicType elem_bt = vector_element_basic_type(this); + assert(elem_bt == T_LONG, "sanity"); + + __ vpminmax(opcode, elem_bt, $dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct vminmaxL_reg_evex(vec dst, vec src1, vec src2) %{ + predicate((vector_length_in_bytes(n) == 64 || VM_Version::supports_avx512vl()) && + vector_element_basic_type(n) == T_LONG); + match(Set dst (MinV src1 src2)); + match(Set dst (MaxV src1 src2)); + format %{ "vector_minmaxL $dst,$src1,src2\t! " %} + ins_encode %{ + assert(UseAVX > 2, "required"); + + int vlen_enc = vector_length_encoding(this); + int opcode = this->ideal_Opcode(); + BasicType elem_bt = vector_element_basic_type(this); + assert(elem_bt == T_LONG, "sanity"); + + __ vpminmax(opcode, elem_bt, $dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +// Float/Double vector Min/Max +instruct minmaxFP_reg(legVec dst, legVec a, legVec b, legVec tmp, legVec atmp, legVec btmp) %{ + predicate(vector_length_in_bytes(n) <= 32 && + is_floating_point_type(vector_element_basic_type(n)) && // T_FLOAT, T_DOUBLE + UseAVX > 0); + match(Set dst (MinV a b)); + match(Set dst (MaxV a b)); + effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); + format %{ "vector_minmaxFP $dst,$a,$b\t!using $tmp, $atmp, $btmp as TEMP" %} + ins_encode %{ + assert(UseAVX > 0, "required"); + + int opcode = this->ideal_Opcode(); + int vlen_enc = vector_length_encoding(this); + BasicType elem_bt = vector_element_basic_type(this); + + __ vminmax_fp(opcode, elem_bt, + $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, + $tmp$$XMMRegister, $atmp$$XMMRegister , $btmp$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct evminmaxFP_reg_eavx(vec dst, vec a, vec b, vec atmp, vec btmp) %{ + predicate(vector_length_in_bytes(n) == 64 && + is_floating_point_type(vector_element_basic_type(n))); // T_FLOAT, T_DOUBLE + match(Set dst (MinV a b)); + match(Set dst (MaxV a b)); + effect(TEMP dst, USE a, USE b, TEMP atmp, TEMP btmp); + format %{ "vector_minmaxFP $dst,$a,$b\t!using $atmp, $btmp as TEMP" %} + ins_encode %{ + assert(UseAVX > 2, "required"); + + int opcode = this->ideal_Opcode(); + int vlen_enc = vector_length_encoding(this); + BasicType elem_bt = vector_element_basic_type(this); + + KRegister ktmp = k1; + __ evminmax_fp(opcode, elem_bt, + $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, + ktmp, $atmp$$XMMRegister , $btmp$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4736,8 +5811,8 @@ instruct vsqrtF_reg(vec dst, vec src) %{ format %{ "vsqrtps $dst,$src\t! sqrt packedF" %} ins_encode %{ assert(UseAVX > 0, "required"); - int vector_len = vector_length_encoding(this); - __ vsqrtps($dst$$XMMRegister, $src$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vsqrtps($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4747,8 +5822,8 @@ instruct vsqrtF_mem(vec dst, memory mem) %{ format %{ "vsqrtps $dst,$mem\t! sqrt packedF" %} ins_encode %{ assert(UseAVX > 0, "required"); - int vector_len = vector_length_encoding(this); - __ vsqrtps($dst$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vsqrtps($dst$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4759,8 +5834,8 @@ instruct vsqrtD_reg(vec dst, vec src) %{ format %{ "vsqrtpd $dst,$src\t! sqrt packedD" %} ins_encode %{ assert(UseAVX > 0, "required"); - int vector_len = vector_length_encoding(this); - __ vsqrtpd($dst$$XMMRegister, $src$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vsqrtpd($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4770,8 +5845,8 @@ instruct vsqrtD_mem(vec dst, memory mem) %{ format %{ "vsqrtpd $dst,$mem\t! sqrt packedD" %} ins_encode %{ assert(UseAVX > 0, "required"); - int vector_len = vector_length_encoding(this); - __ vsqrtpd($dst$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vsqrtpd($dst$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4792,16 +5867,17 @@ instruct vshiftcnt(vec dst, rRegI cnt) %{ // Byte vector shift instruct vshiftB(vec dst, vec src, vec shift, vec tmp, rRegI scratch) %{ - predicate(n->as_Vector()->length() <= 8); - match(Set dst (LShiftVB src shift)); - match(Set dst (RShiftVB src shift)); + predicate(vector_length(n) <= 8 && VectorNode::is_vshift_cnt(n->in(2))); + match(Set dst ( LShiftVB src shift)); + match(Set dst ( RShiftVB src shift)); match(Set dst (URShiftVB src shift)); effect(TEMP dst, USE src, USE shift, TEMP tmp, TEMP scratch); format %{"vector_byte_shift $dst,$src,$shift" %} ins_encode %{ assert(UseSSE > 3, "required"); int opcode = this->ideal_Opcode(); - __ vextendbw(opcode, $tmp$$XMMRegister, $src$$XMMRegister); + bool sign = (opcode != Op_URShiftVB); + __ vextendbw(sign, $tmp$$XMMRegister, $src$$XMMRegister); __ vshiftw(opcode, $tmp$$XMMRegister, $shift$$XMMRegister); __ movdqu($dst$$XMMRegister, ExternalAddress(vector_short_to_byte_mask()), $scratch$$Register); __ pand($dst$$XMMRegister, $tmp$$XMMRegister); @@ -4811,20 +5887,21 @@ instruct vshiftB(vec dst, vec src, vec shift, vec tmp, rRegI scratch) %{ %} instruct vshift16B(vec dst, vec src, vec shift, vec tmp1, vec tmp2, rRegI scratch) %{ - predicate(n->as_Vector()->length() == 16 && UseAVX <= 1); - match(Set dst (LShiftVB src shift)); - match(Set dst (RShiftVB src shift)); + predicate(vector_length(n) == 16 && VectorNode::is_vshift_cnt(n->in(2)) && + UseAVX <= 1); + match(Set dst ( LShiftVB src shift)); + match(Set dst ( RShiftVB src shift)); match(Set dst (URShiftVB src shift)); effect(TEMP dst, USE src, USE shift, TEMP tmp1, TEMP tmp2, TEMP scratch); format %{"vector_byte_shift $dst,$src,$shift" %} ins_encode %{ assert(UseSSE > 3, "required"); int opcode = this->ideal_Opcode(); - - __ vextendbw(opcode, $tmp1$$XMMRegister, $src$$XMMRegister); + bool sign = (opcode != Op_URShiftVB); + __ vextendbw(sign, $tmp1$$XMMRegister, $src$$XMMRegister); __ vshiftw(opcode, $tmp1$$XMMRegister, $shift$$XMMRegister); __ pshufd($tmp2$$XMMRegister, $src$$XMMRegister, 0xE); - __ vextendbw(opcode, $tmp2$$XMMRegister, $tmp2$$XMMRegister); + __ vextendbw(sign, $tmp2$$XMMRegister, $tmp2$$XMMRegister); __ vshiftw(opcode, $tmp2$$XMMRegister, $shift$$XMMRegister); __ movdqu($dst$$XMMRegister, ExternalAddress(vector_short_to_byte_mask()), $scratch$$Register); __ pand($tmp2$$XMMRegister, $dst$$XMMRegister); @@ -4835,18 +5912,20 @@ instruct vshift16B(vec dst, vec src, vec shift, vec tmp1, vec tmp2, rRegI scratc %} instruct vshift16B_avx(vec dst, vec src, vec shift, vec tmp, rRegI scratch) %{ - predicate(n->as_Vector()->length() == 16 && UseAVX > 1); - match(Set dst (LShiftVB src shift)); - match(Set dst (RShiftVB src shift)); + predicate(vector_length(n) == 16 && VectorNode::is_vshift_cnt(n->in(2)) && + UseAVX > 1); + match(Set dst ( LShiftVB src shift)); + match(Set dst ( RShiftVB src shift)); match(Set dst (URShiftVB src shift)); effect(TEMP dst, TEMP tmp, TEMP scratch); format %{"vector_byte_shift $dst,$src,$shift" %} ins_encode %{ int opcode = this->ideal_Opcode(); - int vector_len = Assembler::AVX_256bit; - __ vextendbw(opcode, $tmp$$XMMRegister, $src$$XMMRegister, vector_len); - __ vshiftw(opcode, $tmp$$XMMRegister, $tmp$$XMMRegister, $shift$$XMMRegister, vector_len); - __ vpand($tmp$$XMMRegister, $tmp$$XMMRegister, ExternalAddress(vector_short_to_byte_mask()), vector_len, $scratch$$Register); + bool sign = (opcode != Op_URShiftVB); + int vlen_enc = Assembler::AVX_256bit; + __ vextendbw(sign, $tmp$$XMMRegister, $src$$XMMRegister, vlen_enc); + __ vshiftw(opcode, $tmp$$XMMRegister, $tmp$$XMMRegister, $shift$$XMMRegister, vlen_enc); + __ vpand($tmp$$XMMRegister, $tmp$$XMMRegister, ExternalAddress(vector_short_to_byte_mask()), vlen_enc, $scratch$$Register); __ vextracti128_high($dst$$XMMRegister, $tmp$$XMMRegister); __ vpackuswb($dst$$XMMRegister, $tmp$$XMMRegister, $dst$$XMMRegister, 0); %} @@ -4854,52 +5933,54 @@ instruct vshift16B_avx(vec dst, vec src, vec shift, vec tmp, rRegI scratch) %{ %} instruct vshift32B_avx(vec dst, vec src, vec shift, vec tmp, rRegI scratch) %{ - predicate(n->as_Vector()->length() == 32); - match(Set dst (LShiftVB src shift)); - match(Set dst (RShiftVB src shift)); + predicate(vector_length(n) == 32 && VectorNode::is_vshift_cnt(n->in(2))); + match(Set dst ( LShiftVB src shift)); + match(Set dst ( RShiftVB src shift)); match(Set dst (URShiftVB src shift)); effect(TEMP dst, TEMP tmp, TEMP scratch); format %{"vector_byte_shift $dst,$src,$shift" %} ins_encode %{ assert(UseAVX > 1, "required"); int opcode = this->ideal_Opcode(); - int vector_len = Assembler::AVX_256bit; + bool sign = (opcode != Op_URShiftVB); + int vlen_enc = Assembler::AVX_256bit; __ vextracti128_high($tmp$$XMMRegister, $src$$XMMRegister); - __ vextendbw(opcode, $tmp$$XMMRegister, $tmp$$XMMRegister, vector_len); - __ vextendbw(opcode, $dst$$XMMRegister, $src$$XMMRegister, vector_len); - __ vshiftw(opcode, $tmp$$XMMRegister, $tmp$$XMMRegister, $shift$$XMMRegister, vector_len); - __ vshiftw(opcode, $dst$$XMMRegister, $dst$$XMMRegister, $shift$$XMMRegister, vector_len); - __ vpand($tmp$$XMMRegister, $tmp$$XMMRegister, ExternalAddress(vector_short_to_byte_mask()), vector_len, $scratch$$Register); - __ vpand($dst$$XMMRegister, $dst$$XMMRegister, ExternalAddress(vector_short_to_byte_mask()), vector_len, $scratch$$Register); - __ vpackuswb($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister, vector_len); - __ vpermq($dst$$XMMRegister, $dst$$XMMRegister, 0xD8, vector_len); + __ vextendbw(sign, $tmp$$XMMRegister, $tmp$$XMMRegister, vlen_enc); + __ vextendbw(sign, $dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + __ vshiftw(opcode, $tmp$$XMMRegister, $tmp$$XMMRegister, $shift$$XMMRegister, vlen_enc); + __ vshiftw(opcode, $dst$$XMMRegister, $dst$$XMMRegister, $shift$$XMMRegister, vlen_enc); + __ vpand($tmp$$XMMRegister, $tmp$$XMMRegister, ExternalAddress(vector_short_to_byte_mask()), vlen_enc, $scratch$$Register); + __ vpand($dst$$XMMRegister, $dst$$XMMRegister, ExternalAddress(vector_short_to_byte_mask()), vlen_enc, $scratch$$Register); + __ vpackuswb($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister, vlen_enc); + __ vpermq($dst$$XMMRegister, $dst$$XMMRegister, 0xD8, vlen_enc); %} ins_pipe( pipe_slow ); %} instruct vshift64B_avx(vec dst, vec src, vec shift, vec tmp1, vec tmp2, rRegI scratch) %{ - predicate(n->as_Vector()->length() == 64); - match(Set dst (LShiftVB src shift)); - match(Set dst (RShiftVB src shift)); + predicate(vector_length(n) == 64 && VectorNode::is_vshift_cnt(n->in(2))); + match(Set dst ( LShiftVB src shift)); + match(Set dst (RShiftVB src shift)); match(Set dst (URShiftVB src shift)); effect(TEMP dst, TEMP tmp1, TEMP tmp2, TEMP scratch); format %{"vector_byte_shift $dst,$src,$shift" %} ins_encode %{ assert(UseAVX > 2, "required"); int opcode = this->ideal_Opcode(); - int vector_len = Assembler::AVX_512bit; + bool sign = (opcode != Op_URShiftVB); + int vlen_enc = Assembler::AVX_512bit; __ vextracti64x4($tmp1$$XMMRegister, $src$$XMMRegister, 1); - __ vextendbw(opcode, $tmp1$$XMMRegister, $tmp1$$XMMRegister, vector_len); - __ vextendbw(opcode, $tmp2$$XMMRegister, $src$$XMMRegister, vector_len); - __ vshiftw(opcode, $tmp1$$XMMRegister, $tmp1$$XMMRegister, $shift$$XMMRegister, vector_len); - __ vshiftw(opcode, $tmp2$$XMMRegister, $tmp2$$XMMRegister, $shift$$XMMRegister, vector_len); + __ vextendbw(sign, $tmp1$$XMMRegister, $tmp1$$XMMRegister, vlen_enc); + __ vextendbw(sign, $tmp2$$XMMRegister, $src$$XMMRegister, vlen_enc); + __ vshiftw(opcode, $tmp1$$XMMRegister, $tmp1$$XMMRegister, $shift$$XMMRegister, vlen_enc); + __ vshiftw(opcode, $tmp2$$XMMRegister, $tmp2$$XMMRegister, $shift$$XMMRegister, vlen_enc); __ vmovdqu($dst$$XMMRegister, ExternalAddress(vector_short_to_byte_mask()), $scratch$$Register); - __ vpbroadcastd($dst$$XMMRegister, $dst$$XMMRegister, vector_len); - __ vpand($tmp1$$XMMRegister, $tmp1$$XMMRegister, $dst$$XMMRegister, vector_len); - __ vpand($tmp2$$XMMRegister, $tmp2$$XMMRegister, $dst$$XMMRegister, vector_len); - __ vpackuswb($dst$$XMMRegister, $tmp1$$XMMRegister, $tmp2$$XMMRegister, vector_len); - __ evmovdquq($tmp2$$XMMRegister, ExternalAddress(vector_byte_perm_mask()), vector_len, $scratch$$Register); - __ vpermq($dst$$XMMRegister, $tmp2$$XMMRegister, $dst$$XMMRegister, vector_len); + __ vpbroadcastd($dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpand($tmp1$$XMMRegister, $tmp1$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpand($tmp2$$XMMRegister, $tmp2$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpackuswb($dst$$XMMRegister, $tmp1$$XMMRegister, $tmp2$$XMMRegister, vlen_enc); + __ evmovdquq($tmp2$$XMMRegister, ExternalAddress(vector_byte_perm_mask()), vlen_enc, $scratch$$Register); + __ vpermq($dst$$XMMRegister, $tmp2$$XMMRegister, $dst$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -4910,8 +5991,9 @@ instruct vshift64B_avx(vec dst, vec src, vec shift, vec tmp1, vec tmp2, rRegI sc // unsigned values. // Shorts/Chars vector left shift instruct vshiftS(vec dst, vec src, vec shift) %{ - match(Set dst (LShiftVS src shift)); - match(Set dst (RShiftVS src shift)); + predicate(VectorNode::is_vshift_cnt(n->in(2))); + match(Set dst ( LShiftVS src shift)); + match(Set dst ( RShiftVS src shift)); match(Set dst (URShiftVS src shift)); effect(TEMP dst, USE src, USE shift); format %{ "vshiftw $dst,$src,$shift\t! shift packedS" %} @@ -4940,16 +6022,17 @@ instruct vshiftS(vec dst, vec src, vec shift) %{ // Integers vector left shift instruct vshiftI(vec dst, vec src, vec shift) %{ - match(Set dst (LShiftVI src shift)); - match(Set dst (RShiftVI src shift)); + predicate(VectorNode::is_vshift_cnt(n->in(2))); + match(Set dst ( LShiftVI src shift)); + match(Set dst ( RShiftVI src shift)); match(Set dst (URShiftVI src shift)); effect(TEMP dst, USE src, USE shift); format %{ "vshiftd $dst,$src,$shift\t! shift packedI" %} ins_encode %{ int opcode = this->ideal_Opcode(); if (UseAVX > 0) { - int vector_len = vector_length_encoding(this); - __ vshiftd(opcode, $dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vshiftd(opcode, $dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vlen_enc); } else { int vlen = vector_length(this); if (vlen == 2) { @@ -4994,15 +6077,16 @@ instruct vshiftI_imm(vec dst, vec src, immI8 shift) %{ // Longs vector shift instruct vshiftL(vec dst, vec src, vec shift) %{ - match(Set dst (LShiftVL src shift)); + predicate(VectorNode::is_vshift_cnt(n->in(2))); + match(Set dst ( LShiftVL src shift)); match(Set dst (URShiftVL src shift)); effect(TEMP dst, USE src, USE shift); format %{ "vshiftq $dst,$src,$shift\t! shift packedL" %} ins_encode %{ int opcode = this->ideal_Opcode(); if (UseAVX > 0) { - int vector_len = vector_length_encoding(this); - __ vshiftq(opcode, $dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vshiftq(opcode, $dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vlen_enc); } else { assert(vector_length(this) == 2, ""); __ movdqu($dst$$XMMRegister, $src$$XMMRegister); @@ -5035,7 +6119,7 @@ instruct vshiftL_imm(vec dst, vec src, immI8 shift) %{ // -------------------ArithmeticRightShift ----------------------------------- // Long vector arithmetic right shift instruct vshiftL_arith_reg(vec dst, vec src, vec shift, vec tmp, rRegI scratch) %{ - predicate(UseAVX <= 2); + predicate(VectorNode::is_vshift_cnt(n->in(2)) && UseAVX <= 2); match(Set dst (RShiftVL src shift)); effect(TEMP dst, TEMP tmp, TEMP scratch); format %{ "vshiftq $dst,$src,$shift" %} @@ -5052,24 +6136,297 @@ instruct vshiftL_arith_reg(vec dst, vec src, vec shift, vec tmp, rRegI scratch) } else { assert(vlen == 4, "sanity"); assert(UseAVX > 1, "required"); - int vector_len = Assembler::AVX_256bit; - __ vpsrlq($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + int vlen_enc = Assembler::AVX_256bit; + __ vpsrlq($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vlen_enc); __ vmovdqu($tmp$$XMMRegister, ExternalAddress(vector_long_sign_mask()), $scratch$$Register); - __ vpsrlq($tmp$$XMMRegister, $tmp$$XMMRegister, $shift$$XMMRegister, vector_len); - __ vpxor($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister, vector_len); - __ vpsubq($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister, vector_len); + __ vpsrlq($tmp$$XMMRegister, $tmp$$XMMRegister, $shift$$XMMRegister, vlen_enc); + __ vpxor($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister, vlen_enc); + __ vpsubq($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister, vlen_enc); } %} ins_pipe( pipe_slow ); %} instruct vshiftL_arith_reg_evex(vec dst, vec src, vec shift) %{ - predicate(UseAVX > 2); + predicate(VectorNode::is_vshift_cnt(n->in(2)) && UseAVX > 2); match(Set dst (RShiftVL src shift)); format %{ "vshiftq $dst,$src,$shift" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ evpsraq($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ evpsraq($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +// ------------------- Variable Shift ----------------------------- +// Byte variable shift +instruct vshift8B_var_nobw(vec dst, vec src, vec shift, vec vtmp, rRegP scratch) %{ + predicate(vector_length(n) <= 8 && + !VectorNode::is_vshift_cnt(n->in(2)) && + !VM_Version::supports_avx512bw()); + match(Set dst ( LShiftVB src shift)); + match(Set dst ( RShiftVB src shift)); + match(Set dst (URShiftVB src shift)); + effect(TEMP dst, TEMP vtmp, TEMP scratch); + format %{ "vector_varshift_byte $dst, $src, $shift\n\t! using $vtmp, $scratch as TEMP" %} + ins_encode %{ + assert(UseAVX >= 2, "required"); + + int opcode = this->ideal_Opcode(); + int vlen_enc = Assembler::AVX_128bit; + __ varshiftbw(opcode, $dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vlen_enc, $vtmp$$XMMRegister, $scratch$$Register); + __ vpackuswb($dst$$XMMRegister, $dst$$XMMRegister, $dst$$XMMRegister, 0); + %} + ins_pipe( pipe_slow ); +%} + +instruct vshift16B_var_nobw(vec dst, vec src, vec shift, vec vtmp1, vec vtmp2, rRegP scratch) %{ + predicate(vector_length(n) == 16 && + !VectorNode::is_vshift_cnt(n->in(2)) && + !VM_Version::supports_avx512bw()); + match(Set dst ( LShiftVB src shift)); + match(Set dst ( RShiftVB src shift)); + match(Set dst (URShiftVB src shift)); + effect(TEMP dst, TEMP vtmp1, TEMP vtmp2, TEMP scratch); + format %{ "vector_varshift_byte $dst, $src, $shift\n\t! using $vtmp1, $vtmp2 and $scratch as TEMP" %} + ins_encode %{ + assert(UseAVX >= 2, "required"); + + int opcode = this->ideal_Opcode(); + int vlen_enc = Assembler::AVX_128bit; + // Shift lower half and get word result in dst + __ varshiftbw(opcode, $dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vlen_enc, $vtmp1$$XMMRegister, $scratch$$Register); + + // Shift upper half and get word result in vtmp1 + __ vpshufd($vtmp1$$XMMRegister, $src$$XMMRegister, 0xE, 0); + __ vpshufd($vtmp2$$XMMRegister, $shift$$XMMRegister, 0xE, 0); + __ varshiftbw(opcode, $vtmp1$$XMMRegister, $vtmp1$$XMMRegister, $vtmp2$$XMMRegister, vlen_enc, $vtmp2$$XMMRegister, $scratch$$Register); + + // Merge and down convert the two word results to byte in dst + __ vpackuswb($dst$$XMMRegister, $dst$$XMMRegister, $vtmp1$$XMMRegister, 0); + %} + ins_pipe( pipe_slow ); +%} + +instruct vshift32B_var_nobw(vec dst, vec src, vec shift, vec vtmp1, vec vtmp2, vec vtmp3, vec vtmp4, rRegP scratch) %{ + predicate(vector_length(n) == 32 && + !VectorNode::is_vshift_cnt(n->in(2)) && + !VM_Version::supports_avx512bw()); + match(Set dst ( LShiftVB src shift)); + match(Set dst ( RShiftVB src shift)); + match(Set dst (URShiftVB src shift)); + effect(TEMP dst, TEMP vtmp1, TEMP vtmp2, TEMP vtmp3, TEMP vtmp4, TEMP scratch); + format %{ "vector_varshift_byte $dst, $src, $shift\n\t using $vtmp1, $vtmp2, $vtmp3, $vtmp4 and $scratch as TEMP" %} + ins_encode %{ + assert(UseAVX >= 2, "required"); + + int opcode = this->ideal_Opcode(); + int vlen_enc = Assembler::AVX_128bit; + // Process lower 128 bits and get result in dst + __ varshiftbw(opcode, $dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vlen_enc, $vtmp1$$XMMRegister, $scratch$$Register); + __ vpshufd($vtmp1$$XMMRegister, $src$$XMMRegister, 0xE, 0); + __ vpshufd($vtmp2$$XMMRegister, $shift$$XMMRegister, 0xE, 0); + __ varshiftbw(opcode, $vtmp1$$XMMRegister, $vtmp1$$XMMRegister, $vtmp2$$XMMRegister, vlen_enc, $vtmp2$$XMMRegister, $scratch$$Register); + __ vpackuswb($dst$$XMMRegister, $dst$$XMMRegister, $vtmp1$$XMMRegister, 0); + + // Process higher 128 bits and get result in vtmp3 + __ vextracti128_high($vtmp1$$XMMRegister, $src$$XMMRegister); + __ vextracti128_high($vtmp2$$XMMRegister, $shift$$XMMRegister); + __ varshiftbw(opcode, $vtmp3$$XMMRegister, $vtmp1$$XMMRegister, $vtmp2$$XMMRegister, vlen_enc, $vtmp4$$XMMRegister, $scratch$$Register); + __ vpshufd($vtmp1$$XMMRegister, $vtmp1$$XMMRegister, 0xE, 0); + __ vpshufd($vtmp2$$XMMRegister, $vtmp2$$XMMRegister, 0xE, 0); + __ varshiftbw(opcode, $vtmp1$$XMMRegister, $vtmp1$$XMMRegister, $vtmp2$$XMMRegister, vlen_enc, $vtmp2$$XMMRegister, $scratch$$Register); + __ vpackuswb($vtmp1$$XMMRegister, $vtmp3$$XMMRegister, $vtmp1$$XMMRegister, 0); + + // Merge the two results in dst + __ vinserti128($dst$$XMMRegister, $dst$$XMMRegister, $vtmp1$$XMMRegister, 0x1); + %} + ins_pipe( pipe_slow ); +%} + +instruct vshiftB_var_evex_bw(vec dst, vec src, vec shift, vec vtmp, rRegP scratch) %{ + predicate(vector_length(n) <= 32 && + !VectorNode::is_vshift_cnt(n->in(2)) && + VM_Version::supports_avx512bw()); + match(Set dst ( LShiftVB src shift)); + match(Set dst ( RShiftVB src shift)); + match(Set dst (URShiftVB src shift)); + effect(TEMP dst, TEMP vtmp, TEMP scratch); + format %{ "vector_varshift_byte $dst, $src, $shift\n\t! using $vtmp, $scratch as TEMP" %} + ins_encode %{ + assert(UseAVX > 2, "required"); + + int opcode = this->ideal_Opcode(); + int vlen_enc = vector_length_encoding(this); + __ evarshiftb(opcode, $dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vlen_enc, $vtmp$$XMMRegister, $scratch$$Register); + %} + ins_pipe( pipe_slow ); +%} + +instruct vshift64B_var_evex_bw(vec dst, vec src, vec shift, vec vtmp1, vec vtmp2, rRegP scratch) %{ + predicate(vector_length(n) == 64 && + !VectorNode::is_vshift_cnt(n->in(2)) && + VM_Version::supports_avx512bw()); + match(Set dst ( LShiftVB src shift)); + match(Set dst ( RShiftVB src shift)); + match(Set dst (URShiftVB src shift)); + effect(TEMP dst, TEMP vtmp1, TEMP vtmp2, TEMP scratch); + format %{ "vector_varshift_byte $dst, $src, $shift\n\t! using $vtmp1, $vtmp2 and $scratch as TEMP" %} + ins_encode %{ + assert(UseAVX > 2, "required"); + + int opcode = this->ideal_Opcode(); + int vlen_enc = Assembler::AVX_256bit; + __ evarshiftb(opcode, $dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vlen_enc, $vtmp1$$XMMRegister, $scratch$$Register); + __ vextracti64x4_high($vtmp1$$XMMRegister, $src$$XMMRegister); + __ vextracti64x4_high($vtmp2$$XMMRegister, $shift$$XMMRegister); + __ evarshiftb(opcode, $vtmp1$$XMMRegister, $vtmp1$$XMMRegister, $vtmp2$$XMMRegister, vlen_enc, $vtmp2$$XMMRegister, $scratch$$Register); + __ vinserti64x4($dst$$XMMRegister, $dst$$XMMRegister, $vtmp1$$XMMRegister, 0x1); + %} + ins_pipe( pipe_slow ); +%} + +// Short variable shift +instruct vshift8S_var_nobw(vec dst, vec src, vec shift, vec vtmp, rRegP scratch) %{ + predicate(vector_length(n) <= 8 && + !VectorNode::is_vshift_cnt(n->in(2)) && + !VM_Version::supports_avx512bw()); + match(Set dst ( LShiftVS src shift)); + match(Set dst ( RShiftVS src shift)); + match(Set dst (URShiftVS src shift)); + effect(TEMP dst, TEMP vtmp, TEMP scratch); + format %{ "vector_var_shift_left_short $dst, $src, $shift\n\t" %} + ins_encode %{ + assert(UseAVX >= 2, "required"); + + int opcode = this->ideal_Opcode(); + bool sign = (opcode != Op_URShiftVS); + int vlen_enc = Assembler::AVX_256bit; + __ vextendwd(sign, $dst$$XMMRegister, $src$$XMMRegister, 1); + __ vpmovzxwd($vtmp$$XMMRegister, $shift$$XMMRegister, 1); + __ varshiftd(opcode, $dst$$XMMRegister, $dst$$XMMRegister, $vtmp$$XMMRegister, vlen_enc); + __ vpand($dst$$XMMRegister, $dst$$XMMRegister, ExternalAddress(vector_int_to_short_mask()), vlen_enc, $scratch$$Register); + __ vextracti128_high($vtmp$$XMMRegister, $dst$$XMMRegister); + __ vpackusdw($dst$$XMMRegister, $dst$$XMMRegister, $vtmp$$XMMRegister, 0); + %} + ins_pipe( pipe_slow ); +%} + +instruct vshift16S_var_nobw(vec dst, vec src, vec shift, vec vtmp1, vec vtmp2, rRegP scratch) %{ + predicate(vector_length(n) == 16 && + !VectorNode::is_vshift_cnt(n->in(2)) && + !VM_Version::supports_avx512bw()); + match(Set dst ( LShiftVS src shift)); + match(Set dst ( RShiftVS src shift)); + match(Set dst (URShiftVS src shift)); + effect(TEMP dst, TEMP vtmp1, TEMP vtmp2, TEMP scratch); + format %{ "vector_var_shift_left_short $dst, $src, $shift\n\t" %} + ins_encode %{ + assert(UseAVX >= 2, "required"); + + int opcode = this->ideal_Opcode(); + bool sign = (opcode != Op_URShiftVS); + int vlen_enc = Assembler::AVX_256bit; + // Shift lower half, with result in vtmp2 usign vtmp1 as TEMP + __ vextendwd(sign, $vtmp2$$XMMRegister, $src$$XMMRegister, vlen_enc); + __ vpmovzxwd($vtmp1$$XMMRegister, $shift$$XMMRegister, vlen_enc); + __ varshiftd(opcode, $vtmp2$$XMMRegister, $vtmp2$$XMMRegister, $vtmp1$$XMMRegister, vlen_enc); + __ vpand($vtmp2$$XMMRegister, $vtmp2$$XMMRegister, ExternalAddress(vector_int_to_short_mask()), vlen_enc, $scratch$$Register); + + // Shift upper half, with result in dst usign vtmp1 as TEMP + __ vextracti128_high($dst$$XMMRegister, $src$$XMMRegister); + __ vextracti128_high($vtmp1$$XMMRegister, $shift$$XMMRegister); + __ vextendwd(sign, $dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpmovzxwd($vtmp1$$XMMRegister, $vtmp1$$XMMRegister, vlen_enc); + __ varshiftd(opcode, $dst$$XMMRegister, $dst$$XMMRegister, $vtmp1$$XMMRegister, vlen_enc); + __ vpand($dst$$XMMRegister, $dst$$XMMRegister, ExternalAddress(vector_int_to_short_mask()), vlen_enc, $scratch$$Register); + + // Merge lower and upper half result into dst + __ vpackusdw($dst$$XMMRegister, $vtmp2$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpermq($dst$$XMMRegister, $dst$$XMMRegister, 0xD8, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct vshift16S_var_evex_bw(vec dst, vec src, vec shift) %{ + predicate(!VectorNode::is_vshift_cnt(n->in(2)) && + VM_Version::supports_avx512bw()); + match(Set dst ( LShiftVS src shift)); + match(Set dst ( RShiftVS src shift)); + match(Set dst (URShiftVS src shift)); + format %{ "vector_varshift_short $dst,$src,$shift\t!" %} + ins_encode %{ + assert(UseAVX > 2, "required"); + + int opcode = this->ideal_Opcode(); + int vlen_enc = vector_length_encoding(this); + if (!VM_Version::supports_avx512vl()) { + vlen_enc = Assembler::AVX_512bit; + } + __ varshiftw(opcode, $dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +//Integer variable shift +instruct vshiftI_var(vec dst, vec src, vec shift) %{ + predicate(!VectorNode::is_vshift_cnt(n->in(2))); + match(Set dst ( LShiftVI src shift)); + match(Set dst ( RShiftVI src shift)); + match(Set dst (URShiftVI src shift)); + format %{ "vector_varshift_int $dst,$src,$shift\t!" %} + ins_encode %{ + assert(UseAVX >= 2, "required"); + + int opcode = this->ideal_Opcode(); + int vlen_enc = vector_length_encoding(this); + __ varshiftd(opcode, $dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +//Long variable shift +instruct vshiftL_var(vec dst, vec src, vec shift) %{ + predicate(!VectorNode::is_vshift_cnt(n->in(2))); + match(Set dst ( LShiftVL src shift)); + match(Set dst (URShiftVL src shift)); + format %{ "vector_varshift_long $dst,$src,$shift\t!" %} + ins_encode %{ + assert(UseAVX >= 2, "required"); + + int opcode = this->ideal_Opcode(); + int vlen_enc = vector_length_encoding(this); + __ varshiftq(opcode, $dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +//Long variable right shift arithmetic +instruct vshiftL_arith_var(vec dst, vec src, vec shift, vec vtmp) %{ + predicate(vector_length(n) <= 4 && + !VectorNode::is_vshift_cnt(n->in(2)) && + UseAVX == 2); + match(Set dst (RShiftVL src shift)); + effect(TEMP dst, TEMP vtmp); + format %{ "vector_varshift_long $dst,$src,$shift\n\t! using $vtmp as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen_enc = vector_length_encoding(this); + __ varshiftq(opcode, $dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vlen_enc, + $vtmp$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct vshiftL_arith_var_evex(vec dst, vec src, vec shift) %{ + predicate(!VectorNode::is_vshift_cnt(n->in(2)) && + UseAVX > 2); + match(Set dst (RShiftVL src shift)); + format %{ "vector_varfshift_long $dst,$src,$shift\t!" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen_enc = vector_length_encoding(this); + __ varshiftq(opcode, $dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -5091,8 +6448,8 @@ instruct vand_reg(vec dst, vec src1, vec src2) %{ match(Set dst (AndV src1 src2)); format %{ "vpand $dst,$src1,$src2\t! and vectors" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpand($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpand($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -5102,8 +6459,8 @@ instruct vand_mem(vec dst, vec src, memory mem) %{ match(Set dst (AndV src (LoadVector mem))); format %{ "vpand $dst,$src,$mem\t! and vectors" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpand($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpand($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -5125,8 +6482,8 @@ instruct vor_reg(vec dst, vec src1, vec src2) %{ match(Set dst (OrV src1 src2)); format %{ "vpor $dst,$src1,$src2\t! or vectors" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpor($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpor($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -5136,8 +6493,8 @@ instruct vor_mem(vec dst, vec src, memory mem) %{ match(Set dst (OrV src (LoadVector mem))); format %{ "vpor $dst,$src,$mem\t! or vectors" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpor($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpor($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -5159,8 +6516,8 @@ instruct vxor_reg(vec dst, vec src1, vec src2) %{ match(Set dst (XorV src1 src2)); format %{ "vpxor $dst,$src1,$src2\t! xor vectors" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpxor($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpxor($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -5170,19 +6527,617 @@ instruct vxor_mem(vec dst, vec src, memory mem) %{ match(Set dst (XorV src (LoadVector mem))); format %{ "vpxor $dst,$src,$mem\t! xor vectors" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpxor($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpxor($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vlen_enc); %} ins_pipe( pipe_slow ); %} -// --------------------------------- ABS -------------------------------------- -// a = |a| -instruct vabsB_reg(vec dst, vec src) %{ - match(Set dst (AbsVB src)); - format %{ "vabsb $dst,$src\t# $dst = |$src| abs packedB" %} +// --------------------------------- VectorCast -------------------------------------- + +instruct vcastBtoX(vec dst, vec src) %{ + match(Set dst (VectorCastB2X src)); + format %{ "vector_cast_b2x $dst,$src\t!" %} ins_encode %{ - uint vlen = vector_length(this); + assert(UseAVX > 0, "required"); + + BasicType to_elem_bt = vector_element_basic_type(this); + int vlen_enc = vector_length_encoding(this); + switch (to_elem_bt) { + case T_SHORT: + __ vpmovsxbw($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + break; + case T_INT: + __ vpmovsxbd($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + break; + case T_FLOAT: + __ vpmovsxbd($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + __ vcvtdq2ps($dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + break; + case T_LONG: + __ vpmovsxbq($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + break; + case T_DOUBLE: + __ vpmovsxbd($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + __ vcvtdq2pd($dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + break; + + default: assert(false, "%s", type2name(to_elem_bt)); + } + %} + ins_pipe( pipe_slow ); +%} + +instruct castStoX(vec dst, vec src, rRegP scratch) %{ + predicate(UseAVX <= 2 && + vector_length(n->in(1)) <= 8 && // src + vector_element_basic_type(n) == T_BYTE); + effect(TEMP scratch); + match(Set dst (VectorCastS2X src)); + format %{ "vector_cast_s2x $dst,$src\t! using $scratch as TEMP" %} + ins_encode %{ + assert(UseAVX > 0, "required"); + + __ vpand($dst$$XMMRegister, $src$$XMMRegister, ExternalAddress(vector_short_to_byte_mask()), 0, $scratch$$Register); + __ vpackuswb($dst$$XMMRegister, $dst$$XMMRegister, $dst$$XMMRegister, 0); + %} + ins_pipe( pipe_slow ); +%} + +instruct vcastStoX(vec dst, vec src, vec vtmp, rRegP scratch) %{ + predicate(UseAVX <= 2 && + vector_length(n->in(1)) == 16 && // src + vector_element_basic_type(n) == T_BYTE); + effect(TEMP dst, TEMP vtmp, TEMP scratch); + match(Set dst (VectorCastS2X src)); + format %{ "vector_cast_s2x $dst,$src\t! using $vtmp, $scratch as TEMP" %} + ins_encode %{ + assert(UseAVX > 0, "required"); + + int vlen_enc = vector_length_encoding(vector_length_in_bytes(this, $src)); + __ vpand($dst$$XMMRegister, $src$$XMMRegister, ExternalAddress(vector_short_to_byte_mask()), vlen_enc, $scratch$$Register); + __ vextracti128($vtmp$$XMMRegister, $dst$$XMMRegister, 0x1); + __ vpackuswb($dst$$XMMRegister, $dst$$XMMRegister, $vtmp$$XMMRegister, 0); + %} + ins_pipe( pipe_slow ); +%} + +instruct vcastStoX_evex(vec dst, vec src) %{ + predicate(UseAVX > 2 || + (vector_length_in_bytes(n) >= vector_length_in_bytes(n->in(1)))); // dst >= src + match(Set dst (VectorCastS2X src)); + format %{ "vector_cast_s2x $dst,$src\t!" %} + ins_encode %{ + BasicType to_elem_bt = vector_element_basic_type(this); + int src_vlen_enc = vector_length_encoding(this, $src); + int vlen_enc = vector_length_encoding(this); + switch (to_elem_bt) { + case T_BYTE: + if (!VM_Version::supports_avx512vl()) { + vlen_enc = Assembler::AVX_512bit; + } + __ evpmovwb($dst$$XMMRegister, $src$$XMMRegister, src_vlen_enc); + break; + case T_INT: + __ vpmovsxwd($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + break; + case T_FLOAT: + __ vpmovsxwd($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + __ vcvtdq2ps($dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + break; + case T_LONG: + __ vpmovsxwq($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + break; + case T_DOUBLE: + __ vpmovsxwd($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + __ vcvtdq2pd($dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + break; + default: + ShouldNotReachHere(); + } + %} + ins_pipe( pipe_slow ); +%} + +instruct castItoX(vec dst, vec src, rRegP scratch) %{ + predicate(UseAVX <= 2 && + (vector_length_in_bytes(n->in(1)) <= 16) && + (vector_length_in_bytes(n) < vector_length_in_bytes(n->in(1)))); // dst < src + match(Set dst (VectorCastI2X src)); + format %{ "vector_cast_i2x $dst,$src\t! using $scratch as TEMP" %} + effect(TEMP scratch); + ins_encode %{ + assert(UseAVX > 0, "required"); + + BasicType to_elem_bt = vector_element_basic_type(this); + int vlen_enc = vector_length_encoding(this, $src); + + if (to_elem_bt == T_BYTE) { + __ vpand($dst$$XMMRegister, $src$$XMMRegister, ExternalAddress(vector_int_to_byte_mask()), vlen_enc, $scratch$$Register); + __ vpackusdw($dst$$XMMRegister, $dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpackuswb($dst$$XMMRegister, $dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + } else { + assert(to_elem_bt == T_SHORT, "%s", type2name(to_elem_bt)); + __ vpand($dst$$XMMRegister, $src$$XMMRegister, ExternalAddress(vector_int_to_short_mask()), vlen_enc, $scratch$$Register); + __ vpackusdw($dst$$XMMRegister, $dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + } + %} + ins_pipe( pipe_slow ); +%} + +instruct vcastItoX(vec dst, vec src, vec vtmp, rRegP scratch) %{ + predicate(UseAVX <= 2 && + (vector_length_in_bytes(n->in(1)) == 32) && + (vector_length_in_bytes(n) < vector_length_in_bytes(n->in(1)))); // dst < src + match(Set dst (VectorCastI2X src)); + format %{ "vector_cast_i2x $dst,$src\t! using $vtmp and $scratch as TEMP" %} + effect(TEMP dst, TEMP vtmp, TEMP scratch); + ins_encode %{ + assert(UseAVX > 0, "required"); + + BasicType to_elem_bt = vector_element_basic_type(this); + int vlen_enc = vector_length_encoding(this, $src); + + if (to_elem_bt == T_BYTE) { + __ vpand($vtmp$$XMMRegister, $src$$XMMRegister, ExternalAddress(vector_int_to_byte_mask()), vlen_enc, $scratch$$Register); + __ vextracti128($dst$$XMMRegister, $vtmp$$XMMRegister, 0x1); + __ vpackusdw($dst$$XMMRegister, $vtmp$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpackuswb($dst$$XMMRegister, $dst$$XMMRegister, $dst$$XMMRegister, Assembler::AVX_128bit); + } else { + assert(to_elem_bt == T_SHORT, "%s", type2name(to_elem_bt)); + __ vpand($vtmp$$XMMRegister, $src$$XMMRegister, ExternalAddress(vector_int_to_short_mask()), vlen_enc, $scratch$$Register); + __ vextracti128($dst$$XMMRegister, $vtmp$$XMMRegister, 0x1); + __ vpackusdw($dst$$XMMRegister, $vtmp$$XMMRegister, $dst$$XMMRegister, vlen_enc); + } + %} + ins_pipe( pipe_slow ); +%} + +instruct vcastItoX_evex(vec dst, vec src) %{ + predicate(UseAVX > 2 || + (vector_length_in_bytes(n) >= vector_length_in_bytes(n->in(1)))); // dst >= src + match(Set dst (VectorCastI2X src)); + format %{ "vector_cast_i2x $dst,$src\t!" %} + ins_encode %{ + assert(UseAVX > 0, "required"); + + BasicType dst_elem_bt = vector_element_basic_type(this); + int src_vlen_enc = vector_length_encoding(this, $src); + int dst_vlen_enc = vector_length_encoding(this); + switch (dst_elem_bt) { + case T_BYTE: + if (!VM_Version::supports_avx512vl()) { + src_vlen_enc = Assembler::AVX_512bit; + } + __ evpmovdb($dst$$XMMRegister, $src$$XMMRegister, src_vlen_enc); + break; + case T_SHORT: + if (!VM_Version::supports_avx512vl()) { + src_vlen_enc = Assembler::AVX_512bit; + } + __ evpmovdw($dst$$XMMRegister, $src$$XMMRegister, src_vlen_enc); + break; + case T_FLOAT: + __ vcvtdq2ps($dst$$XMMRegister, $dst$$XMMRegister, dst_vlen_enc); + break; + case T_LONG: + __ vpmovsxdq($dst$$XMMRegister, $src$$XMMRegister, dst_vlen_enc); + break; + case T_DOUBLE: + __ vcvtdq2pd($dst$$XMMRegister, $dst$$XMMRegister, dst_vlen_enc); + break; + default: + ShouldNotReachHere(); + } + %} + ins_pipe( pipe_slow ); +%} + +instruct vcastLtoBS(vec dst, vec src, rRegP scratch) %{ + predicate((vector_element_basic_type(n) == T_BYTE || vector_element_basic_type(n) == T_SHORT) && + UseAVX <= 2); + match(Set dst (VectorCastL2X src)); + effect(TEMP scratch); + format %{ "vector_cast_l2x $dst,$src\t! using $scratch as TEMP" %} + ins_encode %{ + assert(UseAVX > 0, "required"); + + int vlen = vector_length_in_bytes(this, $src); + BasicType to_elem_bt = vector_element_basic_type(this); + AddressLiteral mask_addr = (to_elem_bt == T_BYTE) ? ExternalAddress(vector_int_to_byte_mask()) + : ExternalAddress(vector_int_to_short_mask()); + if (vlen <= 16) { + __ vpshufd($dst$$XMMRegister, $src$$XMMRegister, 8, Assembler::AVX_128bit); + __ vpand($dst$$XMMRegister, $dst$$XMMRegister, mask_addr, Assembler::AVX_128bit, $scratch$$Register); + __ vpackusdw($dst$$XMMRegister, $dst$$XMMRegister, $dst$$XMMRegister, Assembler::AVX_128bit); + } else { + assert(vlen <= 32, "required"); + __ vpermilps($dst$$XMMRegister, $src$$XMMRegister, 8, Assembler::AVX_256bit); + __ vpermpd($dst$$XMMRegister, $dst$$XMMRegister, 8, Assembler::AVX_256bit); + __ vpand($dst$$XMMRegister, $dst$$XMMRegister, mask_addr, Assembler::AVX_128bit, $scratch$$Register); + __ vpackusdw($dst$$XMMRegister, $dst$$XMMRegister, $dst$$XMMRegister, Assembler::AVX_128bit); + } + if (to_elem_bt == T_BYTE) { + __ vpackuswb($dst$$XMMRegister, $dst$$XMMRegister, $dst$$XMMRegister, Assembler::AVX_128bit); + } + %} + ins_pipe( pipe_slow ); +%} + +instruct vcastLtoX_evex(vec dst, vec src) %{ + predicate(UseAVX > 2 || + (vector_element_basic_type(n) == T_INT || + vector_element_basic_type(n) == T_FLOAT || + vector_element_basic_type(n) == T_DOUBLE)); + match(Set dst (VectorCastL2X src)); + format %{ "vector_cast_l2x $dst,$src\t!" %} + ins_encode %{ + BasicType to_elem_bt = vector_element_basic_type(this); + int vlen = vector_length_in_bytes(this, $src); + int vlen_enc = vector_length_encoding(this, $src); + switch (to_elem_bt) { + case T_BYTE: + if (UseAVX > 2 && !VM_Version::supports_avx512vl()) { + vlen_enc = Assembler::AVX_512bit; + } + __ evpmovqb($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + break; + case T_SHORT: + if (UseAVX > 2 && !VM_Version::supports_avx512vl()) { + vlen_enc = Assembler::AVX_512bit; + } + __ evpmovqw($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + break; + case T_INT: + if (vlen == 8) { + if ($dst$$XMMRegister != $src$$XMMRegister) { + __ movflt($dst$$XMMRegister, $src$$XMMRegister); + } + } else if (vlen == 16) { + __ pshufd($dst$$XMMRegister, $src$$XMMRegister, 8); + } else if (vlen == 32) { + if (UseAVX > 2) { + if (!VM_Version::supports_avx512vl()) { + vlen_enc = Assembler::AVX_512bit; + } + __ evpmovqd($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + } else { + __ vpermilps($dst$$XMMRegister, $src$$XMMRegister, 8, vlen_enc); + __ vpermpd($dst$$XMMRegister, $dst$$XMMRegister, 8, vlen_enc); + } + } else { // vlen == 64 + __ evpmovqd($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + } + break; + case T_FLOAT: + assert(UseAVX > 2 && VM_Version::supports_avx512dq(), "required"); + __ evcvtqq2ps($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + break; + case T_DOUBLE: + assert(UseAVX > 2 && VM_Version::supports_avx512dq(), "required"); + __ evcvtqq2pd($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + break; + + default: assert(false, "%s", type2name(to_elem_bt)); + } + %} + ins_pipe( pipe_slow ); +%} + +instruct vcastFtoD_reg(vec dst, vec src) %{ + predicate(vector_element_basic_type(n) == T_DOUBLE); + match(Set dst (VectorCastF2X src)); + format %{ "vector_cast_f2x $dst,$src\t!" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this); + __ vcvtps2pd($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct vcastDtoF_reg(vec dst, vec src) %{ + predicate(vector_element_basic_type(n) == T_FLOAT); + match(Set dst (VectorCastD2X src)); + format %{ "vector_cast_d2x $dst,$src\t!" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this, $src); + __ vcvtpd2ps($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +// --------------------------------- VectorMaskCmp -------------------------------------- + +instruct vcmpFD(legVec dst, legVec src1, legVec src2, immI8 cond) %{ + predicate(vector_length_in_bytes(n->in(1)->in(1)) >= 8 && // src1 + vector_length_in_bytes(n->in(1)->in(1)) <= 32 && // src1 + is_floating_point_type(vector_element_basic_type(n->in(1)->in(1)))); // src1 T_FLOAT, T_DOUBLE + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + format %{ "vector_compare $dst,$src1,$src2,$cond\t!" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this, $src1); + Assembler::ComparisonPredicateFP cmp = booltest_pred_to_comparison_pred_fp($cond$$constant); + if (vector_element_basic_type(this, $src1) == T_FLOAT) { + __ vcmpps($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, cmp, vlen_enc); + } else { + __ vcmppd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, cmp, vlen_enc); + } + %} + ins_pipe( pipe_slow ); +%} + +instruct evcmpFD(vec dst, vec src1, vec src2, immI8 cond, rRegP scratch) %{ + predicate(vector_length_in_bytes(n->in(1)->in(1)) == 64 && // src1 + is_floating_point_type(vector_element_basic_type(n->in(1)->in(1)))); // src1 T_FLOAT, T_DOUBLE + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + effect(TEMP scratch); + format %{ "vector_compare $dst,$src1,$src2,$cond\t! using $scratch as TEMP" %} + ins_encode %{ + int vlen_enc = Assembler::AVX_512bit; + Assembler::ComparisonPredicateFP cmp = booltest_pred_to_comparison_pred_fp($cond$$constant); + KRegister ktmp = k2; // Use a hardcoded temp due to no k register allocation. + KRegister mask = k0; // The comparison itself is not being masked. + if (vector_element_basic_type(this, $src1) == T_FLOAT) { + __ evcmpps(ktmp, mask, $src1$$XMMRegister, $src2$$XMMRegister, cmp, vlen_enc); + __ evmovdqul($dst$$XMMRegister, ktmp, ExternalAddress(vector_all_bits_set()), false, vlen_enc, $scratch$$Register); + } else { + __ evcmppd(ktmp, mask, $src1$$XMMRegister, $src2$$XMMRegister, cmp, vlen_enc); + __ evmovdquq($dst$$XMMRegister, ktmp, ExternalAddress(vector_all_bits_set()), false, vlen_enc, $scratch$$Register); + } + %} + ins_pipe( pipe_slow ); +%} + +instruct vcmp(legVec dst, legVec src1, legVec src2, immI8 cond, rRegP scratch) %{ + predicate(vector_length_in_bytes(n->in(1)->in(1)) >= 8 && // src1 + vector_length_in_bytes(n->in(1)->in(1)) <= 32 && // src1 + is_integral_type(vector_element_basic_type(n->in(1)->in(1)))); // src1 + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + effect(TEMP scratch); + format %{ "vector_compare $dst,$src1,$src2,$cond\t! using $scratch as TEMP" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this, $src1); + Assembler::ComparisonPredicate cmp = booltest_pred_to_comparison_pred($cond$$constant); + Assembler::Width ww = widthForType(vector_element_basic_type(this, $src1)); + __ vpcmpCCW($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, cmp, ww, vlen_enc, $scratch$$Register); + %} + ins_pipe( pipe_slow ); +%} + +instruct evcmp(vec dst, vec src1, vec src2, immI8 cond, rRegP scratch) %{ + predicate(vector_length_in_bytes(n->in(1)->in(1)) == 64 && // src1 + is_integral_type(vector_element_basic_type(n->in(1)->in(1)))); // src1 + match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); + effect(TEMP scratch); + format %{ "vector_compare $dst,$src1,$src2,$cond\t! using $scratch as TEMP" %} + ins_encode %{ + assert(UseAVX > 2, "required"); + + int vlen_enc = Assembler::AVX_512bit; + Assembler::ComparisonPredicate cmp = booltest_pred_to_comparison_pred($cond$$constant); + KRegister ktmp = k2; // Use a hardcoded temp due to no k register allocation. + KRegister mask = k0; // The comparison itself is not being masked. + bool merge = false; + BasicType src1_elem_bt = vector_element_basic_type(this, $src1); + + switch (src1_elem_bt) { + case T_BYTE: { + __ evpcmpb(ktmp, mask, $src1$$XMMRegister, $src2$$XMMRegister, cmp, vlen_enc); + __ evmovdqub($dst$$XMMRegister, ktmp, ExternalAddress(vector_all_bits_set()), merge, vlen_enc, $scratch$$Register); + break; + } + case T_SHORT: { + __ evpcmpw(ktmp, mask, $src1$$XMMRegister, $src2$$XMMRegister, cmp, vlen_enc); + __ evmovdquw($dst$$XMMRegister, ktmp, ExternalAddress(vector_all_bits_set()), merge, vlen_enc, $scratch$$Register); + break; + } + case T_INT: { + __ evpcmpd(ktmp, mask, $src1$$XMMRegister, $src2$$XMMRegister, cmp, vlen_enc); + __ evmovdqul($dst$$XMMRegister, ktmp, ExternalAddress(vector_all_bits_set()), merge, vlen_enc, $scratch$$Register); + break; + } + case T_LONG: { + __ evpcmpq(ktmp, mask, $src1$$XMMRegister, $src2$$XMMRegister, cmp, vlen_enc); + __ evmovdquq($dst$$XMMRegister, ktmp, ExternalAddress(vector_all_bits_set()), merge, vlen_enc, $scratch$$Register); + break; + } + + default: assert(false, "%s", type2name(src1_elem_bt)); + } + %} + ins_pipe( pipe_slow ); +%} + +// Extract + +instruct extractI(rRegI dst, legVec src, immU8 idx) %{ + predicate(vector_length_in_bytes(n->in(1)) <= 16); // src + match(Set dst (ExtractI src idx)); + match(Set dst (ExtractS src idx)); +#ifdef _LP64 + match(Set dst (ExtractB src idx)); +#endif + format %{ "extractI $dst,$src,$idx\t!" %} + ins_encode %{ + assert($idx$$constant < (int)vector_length(this, $src), "out of bounds"); + + BasicType elem_bt = vector_element_basic_type(this, $src); + __ get_elem(elem_bt, $dst$$Register, $src$$XMMRegister, $idx$$constant); + %} + ins_pipe( pipe_slow ); +%} + +instruct vextractI(rRegI dst, legVec src, immI idx, legVec vtmp) %{ + predicate(vector_length_in_bytes(n->in(1)) == 32 || // src + vector_length_in_bytes(n->in(1)) == 64); // src + match(Set dst (ExtractI src idx)); + match(Set dst (ExtractS src idx)); +#ifdef _LP64 + match(Set dst (ExtractB src idx)); +#endif + effect(TEMP vtmp); + format %{ "vextractI $dst,$src,$idx\t! using $vtmp as TEMP" %} + ins_encode %{ + assert($idx$$constant < (int)vector_length(this, $src), "out of bounds"); + + BasicType elem_bt = vector_element_basic_type(this, $src); + XMMRegister lane_xmm = __ get_lane(elem_bt, $vtmp$$XMMRegister, $src$$XMMRegister, $idx$$constant); + __ get_elem(elem_bt, $dst$$Register, lane_xmm, $idx$$constant); + %} + ins_pipe( pipe_slow ); +%} + +#ifdef _LP64 +instruct extractL(rRegL dst, legVec src, immU8 idx) %{ + predicate(vector_length(n->in(1)) <= 2); // src + match(Set dst (ExtractL src idx)); + format %{ "extractL $dst,$src,$idx\t!" %} + ins_encode %{ + assert(UseSSE >= 4, "required"); + assert($idx$$constant < (int)vector_length(this, $src), "out of bounds"); + + __ get_elem(T_LONG, $dst$$Register, $src$$XMMRegister, $idx$$constant); + %} + ins_pipe( pipe_slow ); +%} + +instruct vextractL(rRegL dst, legVec src, immU8 idx, legVec vtmp) %{ + predicate(vector_length(n->in(1)) == 4 || // src + vector_length(n->in(1)) == 8); // src + match(Set dst (ExtractL src idx)); + effect(TEMP vtmp); + format %{ "vextractL $dst,$src,$idx\t! using $vtmp as TEMP" %} + ins_encode %{ + assert($idx$$constant < (int)vector_length(this, $src), "out of bounds"); + + XMMRegister lane_reg = __ get_lane(T_LONG, $vtmp$$XMMRegister, $src$$XMMRegister, $idx$$constant); + __ get_elem(T_LONG, $dst$$Register, lane_reg, $idx$$constant); + %} + ins_pipe( pipe_slow ); +%} +#endif + +instruct extractF(legRegF dst, legVec src, immU8 idx, rRegI tmp, legVec vtmp) %{ + predicate(vector_length(n->in(1)) <= 4); + match(Set dst (ExtractF src idx)); + effect(TEMP dst, TEMP tmp, TEMP vtmp); + format %{ "extractF $dst,$src,$idx\t! using $tmp, $vtmp as TEMP" %} + ins_encode %{ + assert($idx$$constant < (int)vector_length(this, $src), "out of bounds"); + + __ get_elem(T_FLOAT, $dst$$XMMRegister, $src$$XMMRegister, $idx$$constant, $tmp$$Register, $vtmp$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct vextractF(legRegF dst, legVec src, immU8 idx, rRegI tmp, legVec vtmp) %{ + predicate(vector_length(n->in(1)/*src*/) == 8 || + vector_length(n->in(1)/*src*/) == 16); + match(Set dst (ExtractF src idx)); + effect(TEMP tmp, TEMP vtmp); + format %{ "vextractF $dst,$src,$idx\t! using $tmp, $vtmp as TEMP" %} + ins_encode %{ + assert($idx$$constant < (int)vector_length(this, $src), "out of bounds"); + + XMMRegister lane_reg = __ get_lane(T_FLOAT, $vtmp$$XMMRegister, $src$$XMMRegister, $idx$$constant); + __ get_elem(T_FLOAT, $dst$$XMMRegister, lane_reg, $idx$$constant, $tmp$$Register); + %} + ins_pipe( pipe_slow ); +%} + +instruct extractD(legRegD dst, legVec src, immU8 idx) %{ + predicate(vector_length(n->in(1)) == 2); // src + match(Set dst (ExtractD src idx)); + format %{ "extractD $dst,$src,$idx\t!" %} + ins_encode %{ + assert($idx$$constant < (int)vector_length(this, $src), "out of bounds"); + + __ get_elem(T_DOUBLE, $dst$$XMMRegister, $src$$XMMRegister, $idx$$constant); + %} + ins_pipe( pipe_slow ); +%} + +instruct vextractD(legRegD dst, legVec src, immU8 idx, legVec vtmp) %{ + predicate(vector_length(n->in(1)) == 4 || // src + vector_length(n->in(1)) == 8); // src + match(Set dst (ExtractD src idx)); + effect(TEMP vtmp); + format %{ "vextractD $dst,$src,$idx\t! using $vtmp as TEMP" %} + ins_encode %{ + assert($idx$$constant < (int)vector_length(this, $src), "out of bounds"); + + XMMRegister lane_reg = __ get_lane(T_DOUBLE, $vtmp$$XMMRegister, $src$$XMMRegister, $idx$$constant); + __ get_elem(T_DOUBLE, $dst$$XMMRegister, lane_reg, $idx$$constant); + %} + ins_pipe( pipe_slow ); +%} + +// --------------------------------- Vector Blend -------------------------------------- + +instruct blendvp(vec dst, vec src, vec mask, rxmm0 tmp) %{ + predicate(UseAVX == 0); + match(Set dst (VectorBlend (Binary dst src) mask)); + format %{ "vector_blend $dst,$src,$mask\t! using $tmp as TEMP" %} + effect(TEMP tmp); + ins_encode %{ + assert(UseSSE >= 4, "required"); + + if ($mask$$XMMRegister != $tmp$$XMMRegister) { + __ movdqu($tmp$$XMMRegister, $mask$$XMMRegister); + } + __ pblendvb($dst$$XMMRegister, $src$$XMMRegister); // uses xmm0 as mask + %} + ins_pipe( pipe_slow ); +%} + +instruct vblendvpI(legVec dst, legVec src1, legVec src2, legVec mask) %{ + predicate(UseAVX > 0 && + vector_length_in_bytes(n) <= 32 && + is_integral_type(vector_element_basic_type(n))); + match(Set dst (VectorBlend (Binary src1 src2) mask)); + format %{ "vector_blend $dst,$src1,$src2,$mask\t!" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this); + __ vpblendvb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, $mask$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct vblendvpFD(legVec dst, legVec src1, legVec src2, legVec mask) %{ + predicate(UseAVX > 0 && + vector_length_in_bytes(n) <= 32 && + !is_integral_type(vector_element_basic_type(n))); + match(Set dst (VectorBlend (Binary src1 src2) mask)); + format %{ "vector_blend $dst,$src1,$src2,$mask\t!" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this); + __ vblendvps($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, $mask$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct evblendvp64(vec dst, vec src1, vec src2, vec mask, rRegP scratch) %{ + predicate(vector_length_in_bytes(n) == 64); + match(Set dst (VectorBlend (Binary src1 src2) mask)); + format %{ "vector_blend $dst,$src1,$src2,$mask\t! using $scratch and k2 as TEMP" %} + effect(TEMP scratch); + ins_encode %{ + int vlen_enc = Assembler::AVX_512bit; + BasicType elem_bt = vector_element_basic_type(this); + KRegister ktmp = k2; + __ evpcmp(elem_bt, ktmp, k0, $mask$$XMMRegister, ExternalAddress(vector_all_bits_set()), Assembler::eq, vlen_enc, $scratch$$Register); + __ evpblend(elem_bt, $dst$$XMMRegister, ktmp, $src1$$XMMRegister, $src2$$XMMRegister, true, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +// --------------------------------- ABS -------------------------------------- +// a = |a| +instruct vabsB_reg(vec dst, vec src) %{ + match(Set dst (AbsVB src)); + format %{ "vabsb $dst,$src\t# $dst = |$src| abs packedB" %} + ins_encode %{ + uint vlen = vector_length(this); if (vlen <= 16) { __ pabsb($dst$$XMMRegister, $src$$XMMRegister); } else { @@ -5228,8 +7183,11 @@ instruct vabsL_reg(vec dst, vec src) %{ format %{ "evpabsq $dst,$src\t# $dst = |$src| abs packedL" %} ins_encode %{ assert(UseAVX > 2, "required"); - int vector_len = vector_length_encoding(this); - __ evpabsq($dst$$XMMRegister, $src$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + if (!VM_Version::supports_avx512vl()) { + vlen_enc = Assembler::AVX_512bit; + } + __ evpabsq($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -5237,7 +7195,7 @@ instruct vabsL_reg(vec dst, vec src) %{ // --------------------------------- ABSNEG -------------------------------------- instruct vabsnegF(vec dst, vec src, rRegI scratch) %{ - predicate(n->as_Vector()->length() != 4); // handled by 1-operand instruction vabsneg4F + predicate(vector_length(n) != 4); // handled by 1-operand instruction vabsneg4F match(Set dst (AbsVF src)); match(Set dst (NegVF src)); effect(TEMP scratch); @@ -5258,7 +7216,7 @@ instruct vabsnegF(vec dst, vec src, rRegI scratch) %{ %} instruct vabsneg4F(vec dst, rRegI scratch) %{ - predicate(n->as_Vector()->length() == 4); + predicate(vector_length(n) == 4); match(Set dst (AbsVF dst)); match(Set dst (NegVF dst)); effect(TEMP scratch); @@ -5290,6 +7248,504 @@ instruct vabsnegD(vec dst, vec src, rRegI scratch) %{ ins_pipe( pipe_slow ); %} +//------------------------------------- VectorTest -------------------------------------------- + +#ifdef _LP64 +instruct vptest_alltrue(rRegI dst, legVec src1, legVec src2, rFlagsReg cr) %{ + predicate(static_cast(n)->get_predicate() == BoolTest::overflow); + match(Set dst (VectorTest src1 src2 )); + effect(KILL cr); + format %{ "vector_test $dst,$src1, $src2\t! using $cr as TEMP" %} + ins_encode %{ + int vlen = vector_length_in_bytes(this, $src1); + int vlen_enc = vector_length_encoding(vlen); + if (vlen <= 32) { + if (UseAVX == 0) { + assert(vlen <= 16, "required"); + __ ptest($src1$$XMMRegister, $src2$$XMMRegister); + } else { + __ vptest($src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); + } + } else { + KRegister ktmp = k2; // Use a hardcoded temp due to no k register allocation. + __ evpcmpeqb(ktmp, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); + __ kortestql(ktmp, ktmp); + } + __ setb(Assembler::carrySet, $dst$$Register); + __ movzbl($dst$$Register, $dst$$Register); + %} + ins_pipe( pipe_slow ); +%} + +instruct vptest_anytrue(rRegI dst, legVec src1, legVec src2, rFlagsReg cr) %{ + predicate(static_cast(n)->get_predicate() == BoolTest::ne); + match(Set dst (VectorTest src1 src2 )); + effect(KILL cr); + format %{ "vector_test_any_true $dst,$src1,$src2\t! using $cr as TEMP" %} + ins_encode %{ + int vlen = vector_length_in_bytes(this, $src1); + int vlen_enc = vector_length_encoding(vlen); + if (vlen <= 32) { + if (UseAVX == 0) { + assert(vlen <= 16, "required"); + __ ptest($src1$$XMMRegister, $src2$$XMMRegister); + } else { + __ vptest($src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); + } + } else { + KRegister ktmp = k2; // Use a hardcoded temp due to no k register allocation. + __ evpcmpeqb(ktmp, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); + __ ktestql(ktmp, ktmp); + } + __ setb(Assembler::notZero, $dst$$Register); + __ movzbl($dst$$Register, $dst$$Register); + %} + ins_pipe( pipe_slow ); +%} +#endif + +//------------------------------------- LoadMask -------------------------------------------- + +instruct loadMask(vec dst, vec src) %{ + match(Set dst (VectorLoadMask src)); + effect(TEMP dst); + format %{ "vector_loadmask_byte $dst,$src\n\t" %} + ins_encode %{ + int vlen_in_bytes = vector_length_in_bytes(this); + BasicType elem_bt = vector_element_basic_type(this); + + __ load_vector_mask($dst$$XMMRegister, $src$$XMMRegister, vlen_in_bytes, elem_bt); + %} + ins_pipe( pipe_slow ); +%} + +//------------------------------------- StoreMask -------------------------------------------- + +instruct storeMask1B(vec dst, vec src, immI_1 size) %{ + predicate(vector_length(n) < 64 || VM_Version::supports_avx512vlbw()); + match(Set dst (VectorStoreMask src size)); + format %{ "vector_store_mask $dst,$src\t!" %} + ins_encode %{ + assert(UseSSE >= 3, "required"); + if (vector_length_in_bytes(this) <= 16) { + __ pabsb($dst$$XMMRegister, $src$$XMMRegister); + } else { + assert(UseAVX >= 2, "required"); + int src_vlen_enc = vector_length_encoding(this, $src); + __ vpabsb($dst$$XMMRegister, $src$$XMMRegister, src_vlen_enc); + } + %} + ins_pipe( pipe_slow ); +%} + +instruct storeMask2B(vec dst, vec src, immI_2 size) %{ + predicate(vector_length(n) <= 8); + match(Set dst (VectorStoreMask src size)); + format %{ "vector_store_mask $dst,$src\n\t" %} + ins_encode %{ + assert(UseSSE >= 3, "required"); + __ pabsw($dst$$XMMRegister, $src$$XMMRegister); + __ packsswb($dst$$XMMRegister, $dst$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct vstoreMask2B(vec dst, vec src, immI_2 size) %{ + predicate(vector_length(n) == 16 && !VM_Version::supports_avx512bw()); + match(Set dst (VectorStoreMask src size)); + effect(TEMP dst); + format %{ "vector_store_mask $dst,$src\t!" %} + ins_encode %{ + int vlen_enc = Assembler::AVX_128bit; + __ vextracti128($dst$$XMMRegister, $src$$XMMRegister, 0x1); + __ vpacksswb($dst$$XMMRegister, $src$$XMMRegister, $dst$$XMMRegister,vlen_enc); + __ vpabsb($dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct vstoreMask2B_evex(vec dst, vec src, immI_2 size) %{ + predicate(VM_Version::supports_avx512bw()); + match(Set dst (VectorStoreMask src size)); + format %{ "vector_store_mask $dst,$src\t!" %} + ins_encode %{ + int src_vlen_enc = vector_length_encoding(this, $src); + int dst_vlen_enc = vector_length_encoding(this); + __ evpmovwb($dst$$XMMRegister, $src$$XMMRegister, src_vlen_enc); + __ vpabsb($dst$$XMMRegister, $dst$$XMMRegister, dst_vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct storeMask4B(vec dst, vec src, immI_4 size) %{ + predicate (vector_length(n) <= 4 && UseAVX <= 2); + match(Set dst (VectorStoreMask src size)); + format %{ "vector_store_mask $dst,$src\t!" %} + ins_encode %{ + assert(UseSSE >= 3, "required"); + __ pabsd($dst$$XMMRegister, $src$$XMMRegister); + __ packssdw($dst$$XMMRegister, $dst$$XMMRegister); + __ packsswb($dst$$XMMRegister, $dst$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct vstoreMask4B(vec dst, vec src, immI_4 size) %{ + predicate(vector_length(n) == 8 && UseAVX <= 2); + match(Set dst (VectorStoreMask src size)); + format %{ "vector_store_mask $dst,$src\t!" %} + effect(TEMP dst); + ins_encode %{ + int vlen_enc = Assembler::AVX_128bit; + __ vextracti128($dst$$XMMRegister, $src$$XMMRegister, 0x1); + __ vpackssdw($dst$$XMMRegister, $src$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpacksswb($dst$$XMMRegister, $dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpabsb($dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct vstoreMask4B_evex(vec dst, vec src, immI_4 size) %{ + predicate(UseAVX > 2); + match(Set dst (VectorStoreMask src size)); + format %{ "vector_store_mask $dst,$src\t!" %} + ins_encode %{ + int src_vlen_enc = vector_length_encoding(this, $src); + int dst_vlen_enc = vector_length_encoding(this); + if (!VM_Version::supports_avx512vl()) { + src_vlen_enc = Assembler::AVX_512bit; + } + __ evpmovdb($dst$$XMMRegister, $src$$XMMRegister, src_vlen_enc); + __ vpabsb($dst$$XMMRegister, $dst$$XMMRegister, dst_vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct storeMask8B(vec dst, vec src, immI_8 size) %{ + predicate(vector_length(n) == 2 && UseAVX <= 2); + match(Set dst (VectorStoreMask src size)); + format %{ "vector_store_mask $dst,$src\t!" %} + ins_encode %{ + assert(UseSSE >= 3, "required"); + __ pshufd($dst$$XMMRegister, $src$$XMMRegister, 0x8); + __ packssdw($dst$$XMMRegister, $dst$$XMMRegister); + __ packsswb($dst$$XMMRegister, $dst$$XMMRegister); + __ pabsb($dst$$XMMRegister, $dst$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct storeMask8B_avx(vec dst, vec src, immI_8 size, legVec vtmp) %{ + predicate(vector_length(n) == 4 && UseAVX <= 2); + match(Set dst (VectorStoreMask src size)); + format %{ "vector_store_mask $dst,$src\t! using $vtmp as TEMP" %} + effect(TEMP dst, TEMP vtmp); + ins_encode %{ + int vlen_enc = Assembler::AVX_128bit; + __ vpshufps($dst$$XMMRegister, $src$$XMMRegister, $src$$XMMRegister, 0x88, Assembler::AVX_256bit); + __ vextracti128($vtmp$$XMMRegister, $dst$$XMMRegister, 0x1); + __ vblendps($dst$$XMMRegister, $dst$$XMMRegister, $vtmp$$XMMRegister, 0xC, vlen_enc); + __ vpackssdw($dst$$XMMRegister, $dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpacksswb($dst$$XMMRegister, $dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + __ vpabsb($dst$$XMMRegister, $dst$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct vstoreMask8B_evex(vec dst, vec src, immI_8 size) %{ + predicate(UseAVX > 2); + match(Set dst (VectorStoreMask src size)); + format %{ "vector_store_mask $dst,$src\t!" %} + ins_encode %{ + int src_vlen_enc = vector_length_encoding(this, $src); + int dst_vlen_enc = vector_length_encoding(this); + if (!VM_Version::supports_avx512vl()) { + src_vlen_enc = Assembler::AVX_512bit; + } + __ evpmovqb($dst$$XMMRegister, $src$$XMMRegister, src_vlen_enc); + __ vpabsb($dst$$XMMRegister, $dst$$XMMRegister, dst_vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +//-------------------------------- Load Iota Indices ---------------------------------- + +instruct loadIotaIndices(vec dst, immI_0 src, rRegP scratch) %{ + predicate(vector_element_basic_type(n) == T_BYTE); + match(Set dst (VectorLoadConst src)); + effect(TEMP scratch); + format %{ "vector_load_iota $dst CONSTANT_MEMORY\t! load iota indices" %} + ins_encode %{ + int vlen_in_bytes = vector_length_in_bytes(this); + __ load_iota_indices($dst$$XMMRegister, $scratch$$Register, vlen_in_bytes); + %} + ins_pipe( pipe_slow ); +%} + +//-------------------------------- Rearrange ---------------------------------- + +// LoadShuffle/Rearrange for Byte + +instruct loadShuffleB(vec dst) %{ + predicate(vector_element_basic_type(n) == T_BYTE); + match(Set dst (VectorLoadShuffle dst)); + format %{ "vector_load_shuffle $dst, $dst" %} + ins_encode %{ + // empty + %} + ins_pipe( pipe_slow ); +%} + +instruct rearrangeB(vec dst, vec shuffle) %{ + predicate(vector_element_basic_type(n) == T_BYTE && + vector_length(n) < 32); + match(Set dst (VectorRearrange dst shuffle)); + format %{ "vector_rearrange $dst, $shuffle, $dst" %} + ins_encode %{ + assert(UseSSE >= 4, "required"); + __ pshufb($dst$$XMMRegister, $shuffle$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct rearrangeB_avx(vec dst, vec src, vec shuffle) %{ + predicate(vector_element_basic_type(n) == T_BYTE && + vector_length(n) == 32 && !VM_Version::supports_avx512_vbmi()); + match(Set dst (VectorRearrange src shuffle)); + format %{ "vector_rearrange $dst, $shuffle, $src" %} + ins_encode %{ + __ vpshufb($dst$$XMMRegister, $shuffle$$XMMRegister, $src$$XMMRegister, Assembler::AVX_256bit); + %} + ins_pipe( pipe_slow ); +%} + +instruct rearrangeB_evex(vec dst, vec src, vec shuffle) %{ + predicate(vector_element_basic_type(n) == T_BYTE && + vector_length(n) >= 32 && VM_Version::supports_avx512_vbmi()); + match(Set dst (VectorRearrange src shuffle)); + format %{ "vector_rearrange $dst, $shuffle, $src" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this); + __ vpermb($dst$$XMMRegister, $shuffle$$XMMRegister, $src$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +// LoadShuffle/Rearrange for Short + +instruct loadShuffleS(vec dst, vec src, vec vtmp, rRegP scratch) %{ + predicate(vector_element_basic_type(n) == T_SHORT && + vector_length(n) <= 8 && !VM_Version::supports_avx512bw()); // NB! aligned with rearrangeS + match(Set dst (VectorLoadShuffle src)); + effect(TEMP dst, TEMP vtmp, TEMP scratch); + format %{ "vector_load_shuffle $dst, $src\t! using $vtmp and $scratch as TEMP" %} + ins_encode %{ + // Create a byte shuffle mask from short shuffle mask + // only byte shuffle instruction available on these platforms + + // Multiply each shuffle by two to get byte index + __ pmovzxbw($vtmp$$XMMRegister, $src$$XMMRegister); + __ psllw($vtmp$$XMMRegister, 1); + + // Duplicate to create 2 copies of byte index + __ movdqu($dst$$XMMRegister, $vtmp$$XMMRegister); + __ psllw($dst$$XMMRegister, 8); + __ por($dst$$XMMRegister, $vtmp$$XMMRegister); + + // Add one to get alternate byte index + __ movdqu($vtmp$$XMMRegister, ExternalAddress(vector_short_shufflemask()), $scratch$$Register); + __ paddb($dst$$XMMRegister, $vtmp$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct rearrangeS(vec dst, vec shuffle) %{ + predicate(vector_element_basic_type(n) == T_SHORT && + vector_length(n) <= 8 && !VM_Version::supports_avx512bw()); + match(Set dst (VectorRearrange dst shuffle)); + format %{ "vector_rearrange $dst, $shuffle, $dst" %} + ins_encode %{ + assert(UseSSE >= 4, "required"); + __ pshufb($dst$$XMMRegister, $shuffle$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct loadShuffleS_evex(vec dst, vec src) %{ + predicate(vector_element_basic_type(n) == T_SHORT && + VM_Version::supports_avx512bw()); + match(Set dst (VectorLoadShuffle src)); + format %{ "vector_load_shuffle $dst, $src" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this); + if (!VM_Version::supports_avx512vl()) { + vlen_enc = Assembler::AVX_512bit; + } + __ vpmovzxbw($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct rearrangeS_evex(vec dst, vec src, vec shuffle) %{ + predicate(vector_element_basic_type(n) == T_SHORT && + VM_Version::supports_avx512bw()); + match(Set dst (VectorRearrange src shuffle)); + format %{ "vector_rearrange $dst, $shuffle, $src" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this); + if (!VM_Version::supports_avx512vl()) { + vlen_enc = Assembler::AVX_512bit; + } + __ vpermw($dst$$XMMRegister, $shuffle$$XMMRegister, $src$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +// LoadShuffle/Rearrange for Integer and Float + +instruct loadShuffleI(vec dst, vec src, vec vtmp, rRegP scratch) %{ + predicate((vector_element_basic_type(n) == T_INT || vector_element_basic_type(n) == T_FLOAT) && + vector_length(n) == 4 && UseAVX < 2); + match(Set dst (VectorLoadShuffle src)); + effect(TEMP dst, TEMP vtmp, TEMP scratch); + format %{ "vector_load_shuffle $dst, $src\t! using $vtmp and $scratch as TEMP" %} + ins_encode %{ + assert(UseSSE >= 4, "required"); + + // Create a byte shuffle mask from int shuffle mask + // only byte shuffle instruction available on these platforms + + // Duplicate and multiply each shuffle by 4 + __ pmovzxbd($vtmp$$XMMRegister, $src$$XMMRegister); + __ pshuflw($vtmp$$XMMRegister, $vtmp$$XMMRegister, 0xA0); + __ pshufhw($vtmp$$XMMRegister, $vtmp$$XMMRegister, 0xA0); + __ psllw($vtmp$$XMMRegister, 2); + + // Duplicate again to create 4 copies of byte index + __ movdqu($dst$$XMMRegister, $vtmp$$XMMRegister); + __ psllw($dst$$XMMRegister, 8); + __ por($vtmp$$XMMRegister, $dst$$XMMRegister); + + // Add 3,2,1,0 to get alternate byte index + __ movdqu($dst$$XMMRegister, ExternalAddress(vector_int_shufflemask()), $scratch$$Register); + __ paddb($dst$$XMMRegister, $vtmp$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct rearrangeI(vec dst, vec shuffle) %{ + predicate((vector_element_basic_type(n) == T_INT || vector_element_basic_type(n) == T_FLOAT) && + vector_length(n) == 4 && UseAVX < 2); + match(Set dst (VectorRearrange dst shuffle)); + format %{ "vector_rearrange $dst, $shuffle, $dst" %} + ins_encode %{ + assert(UseSSE >= 4, "required"); + __ pshufb($dst$$XMMRegister, $shuffle$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct loadShuffleI_avx(vec dst, vec src) %{ + predicate((vector_element_basic_type(n) == T_INT || vector_element_basic_type(n) == T_FLOAT) && + UseAVX >= 2); + match(Set dst (VectorLoadShuffle src)); + format %{ "vector_load_shuffle $dst, $src" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this); + __ vpmovzxbd($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct rearrangeI_avx(vec dst, vec src, vec shuffle) %{ + predicate((vector_element_basic_type(n) == T_INT || vector_element_basic_type(n) == T_FLOAT) && + UseAVX >= 2); + match(Set dst (VectorRearrange src shuffle)); + format %{ "vector_rearrange $dst, $shuffle, $src" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this); + if (vlen_enc == Assembler::AVX_128bit) { + vlen_enc = Assembler::AVX_256bit; + } + __ vpermd($dst$$XMMRegister, $shuffle$$XMMRegister, $src$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +// LoadShuffle/Rearrange for Long and Double + +instruct loadShuffleL(vec dst, vec src, vec vtmp, rRegP scratch) %{ + predicate(is_double_word_type(vector_element_basic_type(n)) && // T_LONG, T_DOUBLE + vector_length(n) < 8 && !VM_Version::supports_avx512vl()); + match(Set dst (VectorLoadShuffle src)); + effect(TEMP dst, TEMP vtmp, TEMP scratch); + format %{ "vector_load_shuffle $dst, $src\t! using $vtmp and $scratch as TEMP" %} + ins_encode %{ + assert(UseAVX >= 2, "required"); + + int vlen_enc = vector_length_encoding(this); + // Create a double word shuffle mask from long shuffle mask + // only double word shuffle instruction available on these platforms + + // Multiply each shuffle by two to get double word index + __ vpmovzxbq($vtmp$$XMMRegister, $src$$XMMRegister, vlen_enc); + __ vpsllq($vtmp$$XMMRegister, $vtmp$$XMMRegister, 1, vlen_enc); + + // Duplicate each double word shuffle + __ vpsllq($dst$$XMMRegister, $vtmp$$XMMRegister, 32, vlen_enc); + __ vpor($dst$$XMMRegister, $dst$$XMMRegister, $vtmp$$XMMRegister, vlen_enc); + + // Add one to get alternate double word index + __ vpaddd($dst$$XMMRegister, $dst$$XMMRegister, ExternalAddress(vector_long_shufflemask()), vlen_enc, $scratch$$Register); + %} + ins_pipe( pipe_slow ); +%} + +instruct rearrangeL(vec dst, vec src, vec shuffle) %{ + predicate(is_double_word_type(vector_element_basic_type(n)) && // T_LONG, T_DOUBLE + vector_length(n) < 8 && !VM_Version::supports_avx512vl()); + match(Set dst (VectorRearrange src shuffle)); + format %{ "vector_rearrange $dst, $shuffle, $src" %} + ins_encode %{ + assert(UseAVX >= 2, "required"); + + int vlen_enc = vector_length_encoding(this); + __ vpermd($dst$$XMMRegister, $shuffle$$XMMRegister, $src$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct loadShuffleL_evex(vec dst, vec src) %{ + predicate(is_double_word_type(vector_element_basic_type(n)) && // T_LONG, T_DOUBLE + (vector_length(n) == 8 || VM_Version::supports_avx512vl())); + match(Set dst (VectorLoadShuffle src)); + format %{ "vector_load_shuffle $dst, $src" %} + ins_encode %{ + assert(UseAVX > 2, "required"); + + int vlen_enc = vector_length_encoding(this); + __ vpmovzxbq($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct rearrangeL_evex(vec dst, vec src, vec shuffle) %{ + predicate(is_double_word_type(vector_element_basic_type(n)) && // T_LONG, T_DOUBLE + (vector_length(n) == 8 || VM_Version::supports_avx512vl())); + match(Set dst (VectorRearrange src shuffle)); + format %{ "vector_rearrange $dst, $shuffle, $src" %} + ins_encode %{ + assert(UseAVX > 2, "required"); + + int vlen_enc = vector_length_encoding(this); + if (vlen_enc == Assembler::AVX_128bit) { + vlen_enc = Assembler::AVX_256bit; + } + __ vpermq($dst$$XMMRegister, $shuffle$$XMMRegister, $src$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + // --------------------------------- FMA -------------------------------------- // a * b + c @@ -5299,8 +7755,8 @@ instruct vfmaF_reg(vec a, vec b, vec c) %{ ins_cost(150); ins_encode %{ assert(UseFMA, "not enabled"); - int vector_len = vector_length_encoding(this); - __ vfmaf($c$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $c$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vfmaf($c$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $c$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -5311,8 +7767,8 @@ instruct vfmaF_mem(vec a, memory b, vec c) %{ ins_cost(150); ins_encode %{ assert(UseFMA, "not enabled"); - int vector_len = vector_length_encoding(this); - __ vfmaf($c$$XMMRegister, $a$$XMMRegister, $b$$Address, $c$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vfmaf($c$$XMMRegister, $a$$XMMRegister, $b$$Address, $c$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -5323,8 +7779,8 @@ instruct vfmaD_reg(vec a, vec b, vec c) %{ ins_cost(150); ins_encode %{ assert(UseFMA, "not enabled"); - int vector_len = vector_length_encoding(this); - __ vfmad($c$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $c$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vfmad($c$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $c$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -5335,8 +7791,8 @@ instruct vfmaD_mem(vec a, memory b, vec c) %{ ins_cost(150); ins_encode %{ assert(UseFMA, "not enabled"); - int vector_len = vector_length_encoding(this); - __ vfmad($c$$XMMRegister, $a$$XMMRegister, $b$$Address, $c$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vfmad($c$$XMMRegister, $a$$XMMRegister, $b$$Address, $c$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -5346,7 +7802,7 @@ instruct vfmaD_mem(vec a, memory b, vec c) %{ instruct vmuladdS2I_reg_sse(vec dst, vec src1) %{ predicate(UseAVX == 0); match(Set dst (MulAddVS2VI dst src1)); - format %{ "pmaddwd $dst,$dst,$src1\t! muladd packedStoI" %} + format %{ "pmaddwd $dst,$src1\t! muladd packedStoI" %} ins_encode %{ __ pmaddwd($dst$$XMMRegister, $src1$$XMMRegister); %} @@ -5358,8 +7814,8 @@ instruct vmuladdS2I_reg_avx(vec dst, vec src1, vec src2) %{ match(Set dst (MulAddVS2VI src1 src2)); format %{ "vpmaddwd $dst,$src1,$src2\t! muladd packedStoI" %} ins_encode %{ - int vector_len = vector_length_encoding(this); - __ vpmaddwd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpmaddwd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -5372,8 +7828,8 @@ instruct vmuladdaddS2I_reg(vec dst, vec src1, vec src2) %{ format %{ "evpdpwssd $dst,$src1,$src2\t! muladdadd packedStoI" %} ins_encode %{ assert(UseAVX > 2, "required"); - int vector_len = vector_length_encoding(this); - __ evpdpwssd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ evpdpwssd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); ins_cost(10); @@ -5387,8 +7843,8 @@ instruct vpopcountI(vec dst, vec src) %{ ins_encode %{ assert(UsePopCountInstruction, "not enabled"); - int vector_len = vector_length_encoding(this); - __ vpopcntd($dst$$XMMRegister, $src$$XMMRegister, vector_len); + int vlen_enc = vector_length_encoding(this); + __ vpopcntd($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} diff --git a/src/hotspot/cpu/x86/x86_32.ad b/src/hotspot/cpu/x86/x86_32.ad index e572f3ca895..130cdbed3e1 100644 --- a/src/hotspot/cpu/x86/x86_32.ad +++ b/src/hotspot/cpu/x86/x86_32.ad @@ -131,9 +131,7 @@ alloc_class chunk0( ECX, EBX, EBP, EDI, EAX, EDX, ESI, ESP, // Several register classes are automatically defined based upon information in // this architecture description. // 1) reg_class inline_cache_reg ( /* as def'd in frame section */ ) -// 2) reg_class compiler_method_reg ( /* as def'd in frame section */ ) -// 2) reg_class interpreter_method_reg ( /* as def'd in frame section */ ) -// 3) reg_class stack_slots( /* one chunk of stack-based "registers" */ ) +// 2) reg_class stack_slots( /* one chunk of stack-based "registers" */ ) // // Class for no registers (empty set). reg_class no_reg(); @@ -150,7 +148,6 @@ reg_class_dynamic any_reg(any_reg_no_ebp, any_reg_with_ebp, %{ PreserveFramePoin // Class for general registers reg_class int_reg_with_ebp(EAX, EDX, EBP, EDI, ESI, ECX, EBX); // Class for general registers (excluding EBP). -// This register class can be used for implicit null checks on win95. // It is also safe for use by tailjumps (we don't want to allocate in ebp). // Used also if the PreserveFramePointer flag is true. reg_class int_reg_no_ebp(EAX, EDX, EDI, ESI, ECX, EBX); @@ -656,8 +653,9 @@ void MachEpilogNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { } st->print_cr("POPL EBP"); st->print("\t"); if (do_polling() && C->is_method_compilation()) { - st->print("TEST PollPage,EAX\t! Poll Safepoint"); - st->cr(); st->print("\t"); + st->print("CMPL rsp, poll_offset[thread] \n\t" + "JA #safepoint_stub\t" + "# Safepoint: poll for GC"); } } #endif @@ -700,12 +698,16 @@ void MachEpilogNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { } if (do_polling() && C->is_method_compilation()) { - Register pollReg = as_Register(EBX_enc); + Register thread = as_Register(EBX_enc); MacroAssembler masm(&cbuf); - masm.get_thread(pollReg); - masm.movl(pollReg, Address(pollReg, in_bytes(Thread::polling_page_offset()))); - masm.relocate(relocInfo::poll_return_type); - masm.testl(rax, Address(pollReg, 0)); + __ get_thread(thread); + Label dummy_label; + Label* code_stub = &dummy_label; + if (!C->output()->in_scratch_emit_size()) { + code_stub = &C->output()->safepoint_poll_table()->add_safepoint(__ offset()); + } + __ relocate(relocInfo::poll_return_type); + __ safepoint_poll(*code_stub, thread, true /* at_return */, true /* in_nmethod */); } } @@ -1446,57 +1448,6 @@ const bool Matcher::rematerialize_float_constants = true; // Java calling convention forces doubles to be aligned. const bool Matcher::misaligned_doubles_ok = true; - -void Matcher::pd_implicit_null_fixup(MachNode *node, uint idx) { - // Get the memory operand from the node - uint numopnds = node->num_opnds(); // Virtual call for number of operands - uint skipped = node->oper_input_base(); // Sum of leaves skipped so far - assert( idx >= skipped, "idx too low in pd_implicit_null_fixup" ); - uint opcnt = 1; // First operand - uint num_edges = node->_opnds[1]->num_edges(); // leaves for first operand - while( idx >= skipped+num_edges ) { - skipped += num_edges; - opcnt++; // Bump operand count - assert( opcnt < numopnds, "Accessing non-existent operand" ); - num_edges = node->_opnds[opcnt]->num_edges(); // leaves for next operand - } - - MachOper *memory = node->_opnds[opcnt]; - MachOper *new_memory = NULL; - switch (memory->opcode()) { - case DIRECT: - case INDOFFSET32X: - // No transformation necessary. - return; - case INDIRECT: - new_memory = new indirect_win95_safeOper( ); - break; - case INDOFFSET8: - new_memory = new indOffset8_win95_safeOper(memory->disp(NULL, NULL, 0)); - break; - case INDOFFSET32: - new_memory = new indOffset32_win95_safeOper(memory->disp(NULL, NULL, 0)); - break; - case INDINDEXOFFSET: - new_memory = new indIndexOffset_win95_safeOper(memory->disp(NULL, NULL, 0)); - break; - case INDINDEXSCALE: - new_memory = new indIndexScale_win95_safeOper(memory->scale()); - break; - case INDINDEXSCALEOFFSET: - new_memory = new indIndexScaleOffset_win95_safeOper(memory->scale(), memory->disp(NULL, NULL, 0)); - break; - case LOAD_LONG_INDIRECT: - case LOAD_LONG_INDOFFSET32: - // Does not use EBP as address register, use { EDX, EBX, EDI, ESI} - return; - default: - assert(false, "unexpected memory operand in pd_implicit_null_fixup()"); - return; - } - node->_opnds[opcnt] = new_memory; -} - // Advertise here if the CPU requires explicit rounding operations to implement strictfp mode. const bool Matcher::strict_fp_requires_explicit_rounding = true; @@ -3190,7 +3141,6 @@ frame %{ // These three registers define part of the calling convention // between compiled code and the interpreter. inline_cache_reg(EAX); // Inline Cache Register - interpreter_method_reg(EBX); // Method Register when calling interpreter // Optional: name the operand used by cisc-spilling to access [stack_pointer + offset] cisc_spilling_operand_name(indOffset32); @@ -3315,7 +3265,7 @@ operand immI() %{ %} // Constant for test vs zero -operand immI0() %{ +operand immI_0() %{ predicate(n->get_int() == 0); match(ConI); @@ -3325,7 +3275,7 @@ operand immI0() %{ %} // Constant for increment -operand immI1() %{ +operand immI_1() %{ predicate(n->get_int() == 1); match(ConI); @@ -3419,8 +3369,8 @@ operand immI_32_63() %{ interface(CONST_INTER); %} -operand immI_1() %{ - predicate( n->get_int() == 1 ); +operand immI_2() %{ + predicate( n->get_int() == 2 ); match(ConI); op_cost(0); @@ -3428,8 +3378,8 @@ operand immI_1() %{ interface(CONST_INTER); %} -operand immI_2() %{ - predicate( n->get_int() == 2 ); +operand immI_3() %{ + predicate( n->get_int() == 3 ); match(ConI); op_cost(0); @@ -3437,8 +3387,19 @@ operand immI_2() %{ interface(CONST_INTER); %} -operand immI_3() %{ - predicate( n->get_int() == 3 ); +operand immI_4() +%{ + predicate(n->get_int() == 4); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immI_8() +%{ + predicate(n->get_int() == 8); match(ConI); op_cost(0); @@ -3815,6 +3776,18 @@ operand eRegP() %{ interface(REG_INTER); %} +operand rRegP() %{ + constraint(ALLOC_IN_RC(int_reg)); + match(RegP); + match(eAXRegP); + match(eBXRegP); + match(eCXRegP); + match(eDIRegP); + + format %{ %} + interface(REG_INTER); +%} + // On windows95, EBP is not safe to use for implicit null tests. operand eRegP_no_EBP() %{ constraint(ALLOC_IN_RC(int_reg_no_ebp)); @@ -3947,6 +3920,15 @@ operand eADXRegL_low_only() %{ interface(REG_INTER); %} +// Flags register, used as output of compare instructions +operand rFlagsReg() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + + format %{ "EFLAGS" %} + interface(REG_INTER); +%} + // Flags register, used as output of compare instructions operand eFlagsReg() %{ constraint(ALLOC_IN_RC(int_flags)); @@ -4077,6 +4059,14 @@ operand regF() %{ interface(REG_INTER); %} +operand legRegF() %{ + predicate( UseSSE>=1 ); + constraint(ALLOC_IN_RC(float_reg_legacy)); + match(RegF); + format %{ %} + interface(REG_INTER); +%} + // Float register operands operand vlRegF() %{ constraint(ALLOC_IN_RC(float_reg_vl)); @@ -4096,6 +4086,14 @@ operand regD() %{ %} // Double register operands +operand legRegD() %{ + predicate( UseSSE>=2 ); + constraint(ALLOC_IN_RC(double_reg_legacy)); + match(RegD); + format %{ %} + interface(REG_INTER); +%} + operand vlRegD() %{ constraint(ALLOC_IN_RC(double_reg_vl)); match(RegD); @@ -4356,98 +4354,6 @@ operand stackSlotL(sRegL reg) %{ %} %} -//----------Memory Operands - Win95 Implicit Null Variants---------------- -// Indirect Memory Operand -operand indirect_win95_safe(eRegP_no_EBP reg) -%{ - constraint(ALLOC_IN_RC(int_reg)); - match(reg); - - op_cost(100); - format %{ "[$reg]" %} - interface(MEMORY_INTER) %{ - base($reg); - index(0x4); - scale(0x0); - disp(0x0); - %} -%} - -// Indirect Memory Plus Short Offset Operand -operand indOffset8_win95_safe(eRegP_no_EBP reg, immI8 off) -%{ - match(AddP reg off); - - op_cost(100); - format %{ "[$reg + $off]" %} - interface(MEMORY_INTER) %{ - base($reg); - index(0x4); - scale(0x0); - disp($off); - %} -%} - -// Indirect Memory Plus Long Offset Operand -operand indOffset32_win95_safe(eRegP_no_EBP reg, immI off) -%{ - match(AddP reg off); - - op_cost(100); - format %{ "[$reg + $off]" %} - interface(MEMORY_INTER) %{ - base($reg); - index(0x4); - scale(0x0); - disp($off); - %} -%} - -// Indirect Memory Plus Index Register Plus Offset Operand -operand indIndexOffset_win95_safe(eRegP_no_EBP reg, rRegI ireg, immI off) -%{ - match(AddP (AddP reg ireg) off); - - op_cost(100); - format %{"[$reg + $off + $ireg]" %} - interface(MEMORY_INTER) %{ - base($reg); - index($ireg); - scale(0x0); - disp($off); - %} -%} - -// Indirect Memory Times Scale Plus Index Register -operand indIndexScale_win95_safe(eRegP_no_EBP reg, rRegI ireg, immI2 scale) -%{ - match(AddP reg (LShiftI ireg scale)); - - op_cost(100); - format %{"[$reg + $ireg << $scale]" %} - interface(MEMORY_INTER) %{ - base($reg); - index($ireg); - scale($scale); - disp(0x0); - %} -%} - -// Indirect Memory Times Scale Plus Index Register Plus Offset Operand -operand indIndexScaleOffset_win95_safe(eRegP_no_EBP reg, immI off, rRegI ireg, immI2 scale) -%{ - match(AddP (AddP reg (LShiftI ireg scale)) off); - - op_cost(100); - format %{"[$reg + $off + $ireg << $scale]" %} - interface(MEMORY_INTER) %{ - base($reg); - index($ireg); - scale($scale); - disp($off); - %} -%} - //----------Conditional Branch Operands---------------------------------------- // Comparison Op - This is the operation of the comparison, and is limited to // the following set of codes: @@ -5846,6 +5752,46 @@ instruct loadKlass(eRegP dst, memory mem) %{ ins_pipe( ialu_reg_mem ); %} +// Load Float +instruct MoveF2LEG(legRegF dst, regF src) %{ + match(Set dst src); + format %{ "movss $dst,$src\t# if src != dst load float (4 bytes)" %} + ins_encode %{ + __ movflt($dst$$XMMRegister, $src$$XMMRegister); + %} + ins_pipe( fpu_reg_reg ); +%} + +// Load Float +instruct MoveLEG2F(regF dst, legRegF src) %{ + match(Set dst src); + format %{ "movss $dst,$src\t# if src != dst load float (4 bytes)" %} + ins_encode %{ + __ movflt($dst$$XMMRegister, $src$$XMMRegister); + %} + ins_pipe( fpu_reg_reg ); +%} + +// Load Double +instruct MoveD2LEG(legRegD dst, regD src) %{ + match(Set dst src); + format %{ "movsd $dst,$src\t# if src != dst load double (8 bytes)" %} + ins_encode %{ + __ movdbl($dst$$XMMRegister, $src$$XMMRegister); + %} + ins_pipe( fpu_reg_reg ); +%} + +// Load Double +instruct MoveLEG2D(regD dst, legRegD src) %{ + match(Set dst src); + format %{ "movsd $dst,$src\t# if src != dst load double (8 bytes)" %} + ins_encode %{ + __ movdbl($dst$$XMMRegister, $src$$XMMRegister); + %} + ins_pipe( fpu_reg_reg ); +%} + // Load Double instruct loadDPR(regDPR dst, memory mem) %{ predicate(UseSSE<=1); @@ -5971,7 +5917,7 @@ instruct loadConI(rRegI dst, immI src) %{ %} // Load Constant zero -instruct loadConI0(rRegI dst, immI0 src, eFlagsReg cr) %{ +instruct loadConI0(rRegI dst, immI_0 src, eFlagsReg cr) %{ match(Set dst src); effect(KILL cr); @@ -7083,7 +7029,7 @@ instruct addI_eReg_imm(rRegI dst, immI src, eFlagsReg cr) %{ ins_pipe( ialu_reg ); %} -instruct incI_eReg(rRegI dst, immI1 src, eFlagsReg cr) %{ +instruct incI_eReg(rRegI dst, immI_1 src, eFlagsReg cr) %{ predicate(UseIncDec); match(Set dst (AddI dst src)); effect(KILL cr); @@ -7183,7 +7129,7 @@ instruct addI_mem_imm(memory dst, immI src, eFlagsReg cr) %{ ins_pipe( ialu_mem_imm ); %} -instruct incI_mem(memory dst, immI1 src, eFlagsReg cr) %{ +instruct incI_mem(memory dst, immI_1 src, eFlagsReg cr) %{ match(Set dst (StoreI dst (AddI (LoadI dst) src))); effect(KILL cr); @@ -7552,7 +7498,7 @@ instruct subI_mem_eReg(memory dst, rRegI src, eFlagsReg cr) %{ %} // Subtract from a pointer -instruct subP_eReg(eRegP dst, rRegI src, immI0 zero, eFlagsReg cr) %{ +instruct subP_eReg(eRegP dst, rRegI src, immI_0 zero, eFlagsReg cr) %{ match(Set dst (AddP dst (SubI zero src))); effect(KILL cr); @@ -7563,7 +7509,7 @@ instruct subP_eReg(eRegP dst, rRegI src, immI0 zero, eFlagsReg cr) %{ ins_pipe( ialu_reg_reg ); %} -instruct negI_eReg(rRegI dst, immI0 zero, eFlagsReg cr) %{ +instruct negI_eReg(rRegI dst, immI_0 zero, eFlagsReg cr) %{ match(Set dst (SubI zero dst)); effect(KILL cr); @@ -8017,7 +7963,7 @@ instruct modL_eReg_imm32( eADXRegL dst, immL32 imm, rRegI tmp, rRegI tmp2, eFlag // Integer Shift Instructions // Shift Left by one -instruct shlI_eReg_1(rRegI dst, immI1 shift, eFlagsReg cr) %{ +instruct shlI_eReg_1(rRegI dst, immI_1 shift, eFlagsReg cr) %{ match(Set dst (LShiftI dst shift)); effect(KILL cr); @@ -8053,7 +7999,7 @@ instruct salI_eReg_CL(rRegI dst, eCXRegI shift, eFlagsReg cr) %{ %} // Arithmetic shift right by one -instruct sarI_eReg_1(rRegI dst, immI1 shift, eFlagsReg cr) %{ +instruct sarI_eReg_1(rRegI dst, immI_1 shift, eFlagsReg cr) %{ match(Set dst (RShiftI dst shift)); effect(KILL cr); @@ -8065,7 +8011,7 @@ instruct sarI_eReg_1(rRegI dst, immI1 shift, eFlagsReg cr) %{ %} // Arithmetic shift right by one -instruct sarI_mem_1(memory dst, immI1 shift, eFlagsReg cr) %{ +instruct sarI_mem_1(memory dst, immI_1 shift, eFlagsReg cr) %{ match(Set dst (StoreI dst (RShiftI (LoadI dst) shift))); effect(KILL cr); format %{ "SAR $dst,$shift" %} @@ -8110,7 +8056,7 @@ instruct sarI_eReg_CL(rRegI dst, eCXRegI shift, eFlagsReg cr) %{ %} // Logical shift right by one -instruct shrI_eReg_1(rRegI dst, immI1 shift, eFlagsReg cr) %{ +instruct shrI_eReg_1(rRegI dst, immI_1 shift, eFlagsReg cr) %{ match(Set dst (URShiftI dst shift)); effect(KILL cr); @@ -8266,7 +8212,7 @@ instruct andnI_rReg_rReg_mem(rRegI dst, rRegI src1, memory src2, immI_M1 minus_1 ins_pipe(ialu_reg_mem); %} -instruct blsiI_rReg_rReg(rRegI dst, rRegI src, immI0 imm_zero, eFlagsReg cr) %{ +instruct blsiI_rReg_rReg(rRegI dst, rRegI src, immI_0 imm_zero, eFlagsReg cr) %{ match(Set dst (AndI (SubI imm_zero src) src)); predicate(UseBMI1Instructions); effect(KILL cr); @@ -8279,7 +8225,7 @@ instruct blsiI_rReg_rReg(rRegI dst, rRegI src, immI0 imm_zero, eFlagsReg cr) %{ ins_pipe(ialu_reg); %} -instruct blsiI_rReg_mem(rRegI dst, memory src, immI0 imm_zero, eFlagsReg cr) %{ +instruct blsiI_rReg_mem(rRegI dst, memory src, immI_0 imm_zero, eFlagsReg cr) %{ match(Set dst (AndI (SubI imm_zero (LoadI src) ) (LoadI src) )); predicate(UseBMI1Instructions); effect(KILL cr); @@ -8431,7 +8377,7 @@ instruct orI_mem_imm(memory dst, immI src, eFlagsReg cr) %{ // ROL/ROR // ROL expand -instruct rolI_eReg_imm1(rRegI dst, immI1 shift, eFlagsReg cr) %{ +instruct rolI_eReg_imm1(rRegI dst, immI_1 shift, eFlagsReg cr) %{ effect(USE_DEF dst, USE shift, KILL cr); format %{ "ROL $dst, $shift" %} @@ -8460,7 +8406,7 @@ instruct rolI_eReg_CL(ncxRegI dst, eCXRegI shift, eFlagsReg cr) %{ // end of ROL expand // ROL 32bit by one once -instruct rolI_eReg_i1(rRegI dst, immI1 lshift, immI_M1 rshift, eFlagsReg cr) %{ +instruct rolI_eReg_i1(rRegI dst, immI_1 lshift, immI_M1 rshift, eFlagsReg cr) %{ match(Set dst ( OrI (LShiftI dst lshift) (URShiftI dst rshift))); expand %{ @@ -8479,7 +8425,7 @@ instruct rolI_eReg_i8(rRegI dst, immI8 lshift, immI8 rshift, eFlagsReg cr) %{ %} // ROL 32bit var by var once -instruct rolI_eReg_Var_C0(ncxRegI dst, eCXRegI shift, immI0 zero, eFlagsReg cr) %{ +instruct rolI_eReg_Var_C0(ncxRegI dst, eCXRegI shift, immI_0 zero, eFlagsReg cr) %{ match(Set dst ( OrI (LShiftI dst shift) (URShiftI dst (SubI zero shift)))); expand %{ @@ -8497,7 +8443,7 @@ instruct rolI_eReg_Var_C32(ncxRegI dst, eCXRegI shift, immI_32 c32, eFlagsReg cr %} // ROR expand -instruct rorI_eReg_imm1(rRegI dst, immI1 shift, eFlagsReg cr) %{ +instruct rorI_eReg_imm1(rRegI dst, immI_1 shift, eFlagsReg cr) %{ effect(USE_DEF dst, USE shift, KILL cr); format %{ "ROR $dst, $shift" %} @@ -8526,7 +8472,7 @@ instruct rorI_eReg_CL(ncxRegI dst, eCXRegI shift, eFlagsReg cr)%{ // end of ROR expand // ROR right once -instruct rorI_eReg_i1(rRegI dst, immI1 rshift, immI_M1 lshift, eFlagsReg cr) %{ +instruct rorI_eReg_i1(rRegI dst, immI_1 rshift, immI_M1 lshift, eFlagsReg cr) %{ match(Set dst ( OrI (URShiftI dst rshift) (LShiftI dst lshift))); expand %{ @@ -8545,7 +8491,7 @@ instruct rorI_eReg_i8(rRegI dst, immI8 rshift, immI8 lshift, eFlagsReg cr) %{ %} // ROR 32bit var by var once -instruct rorI_eReg_Var_C0(ncxRegI dst, eCXRegI shift, immI0 zero, eFlagsReg cr) %{ +instruct rorI_eReg_Var_C0(ncxRegI dst, eCXRegI shift, immI_0 zero, eFlagsReg cr) %{ match(Set dst ( OrI (URShiftI dst shift) (LShiftI dst (SubI zero shift)))); expand %{ @@ -8713,7 +8659,7 @@ instruct cmpLTMask(eCXRegI dst, ncxRegI p, ncxRegI q, eFlagsReg cr) %{ ins_pipe(pipe_slow); %} -instruct cmpLTMask0(rRegI dst, immI0 zero, eFlagsReg cr) %{ +instruct cmpLTMask0(rRegI dst, immI_0 zero, eFlagsReg cr) %{ match(Set dst (CmpLTMask dst zero)); effect(DEF dst, KILL cr); ins_cost(100); @@ -8827,7 +8773,7 @@ instruct overflowSubI_rReg_imm(eFlagsReg cr, rRegI op1, immI op2) ins_pipe(ialu_reg_reg); %} -instruct overflowNegI_rReg(eFlagsReg cr, immI0 zero, eAXRegI op2) +instruct overflowNegI_rReg(eFlagsReg cr, immI_0 zero, eAXRegI op2) %{ match(Set cr (OverflowSubI zero op2)); effect(DEF cr, USE_KILL op2); @@ -11824,12 +11770,12 @@ instruct string_indexofUL(eDIRegP str1, eDXRegI cnt1, eSIRegP str2, eAXRegI cnt2 ins_pipe( pipe_slow ); %} -instruct string_indexofU_char(eDIRegP str1, eDXRegI cnt1, eAXRegI ch, +instruct string_indexof_char(eDIRegP str1, eDXRegI cnt1, eAXRegI ch, eBXRegI result, regD vec1, regD vec2, regD vec3, eCXRegI tmp, eFlagsReg cr) %{ - predicate(UseSSE42Intrinsics); + predicate(UseSSE42Intrinsics && (((StrIndexOfCharNode*)n)->encoding() == StrIntrinsicNode::U)); match(Set result (StrIndexOfChar (Binary str1 cnt1) ch)); effect(TEMP vec1, TEMP vec2, TEMP vec3, USE_KILL str1, USE_KILL cnt1, USE_KILL ch, TEMP tmp, KILL cr); - format %{ "String IndexOf char[] $str1,$cnt1,$ch -> $result // KILL all" %} + format %{ "StringUTF16 IndexOf char[] $str1,$cnt1,$ch -> $result // KILL all" %} ins_encode %{ __ string_indexof_char($str1$$Register, $cnt1$$Register, $ch$$Register, $result$$Register, $vec1$$XMMRegister, $vec2$$XMMRegister, $vec3$$XMMRegister, $tmp$$Register); @@ -11837,6 +11783,20 @@ instruct string_indexofU_char(eDIRegP str1, eDXRegI cnt1, eAXRegI ch, ins_pipe( pipe_slow ); %} +instruct stringL_indexof_char(eDIRegP str1, eDXRegI cnt1, eAXRegI ch, + eBXRegI result, regD vec1, regD vec2, regD vec3, eCXRegI tmp, eFlagsReg cr) %{ + predicate(UseSSE42Intrinsics && (((StrIndexOfCharNode*)n)->encoding() == StrIntrinsicNode::L)); + match(Set result (StrIndexOfChar (Binary str1 cnt1) ch)); + effect(TEMP vec1, TEMP vec2, TEMP vec3, USE_KILL str1, USE_KILL cnt1, USE_KILL ch, TEMP tmp, KILL cr); + format %{ "StringLatin1 IndexOf char[] $str1,$cnt1,$ch -> $result // KILL all" %} + ins_encode %{ + __ stringL_indexof_char($str1$$Register, $cnt1$$Register, $ch$$Register, $result$$Register, + $vec1$$XMMRegister, $vec2$$XMMRegister, $vec3$$XMMRegister, $tmp$$Register); + %} + ins_pipe( pipe_slow ); +%} + + // fast array equals instruct array_equalsB(eDIRegP ary1, eSIRegP ary2, eAXRegI result, regD tmp1, regD tmp2, eCXRegI tmp3, eBXRegI tmp4, eFlagsReg cr) @@ -11965,7 +11925,7 @@ instruct compI_eReg_mem(eFlagsReg cr, rRegI op1, memory op2) %{ ins_pipe( ialu_cr_reg_mem ); %} -instruct testI_reg( eFlagsReg cr, rRegI src, immI0 zero ) %{ +instruct testI_reg( eFlagsReg cr, rRegI src, immI_0 zero ) %{ match(Set cr (CmpI src zero)); effect( DEF cr, USE src ); @@ -11975,7 +11935,7 @@ instruct testI_reg( eFlagsReg cr, rRegI src, immI0 zero ) %{ ins_pipe( ialu_cr_reg_imm ); %} -instruct testI_reg_imm( eFlagsReg cr, rRegI src, immI con, immI0 zero ) %{ +instruct testI_reg_imm( eFlagsReg cr, rRegI src, immI con, immI_0 zero ) %{ match(Set cr (CmpI (AndI src con) zero)); format %{ "TEST $src,$con" %} @@ -11984,7 +11944,7 @@ instruct testI_reg_imm( eFlagsReg cr, rRegI src, immI con, immI0 zero ) %{ ins_pipe( ialu_cr_reg_imm ); %} -instruct testI_reg_mem( eFlagsReg cr, rRegI src, memory mem, immI0 zero ) %{ +instruct testI_reg_mem( eFlagsReg cr, rRegI src, memory mem, immI_0 zero ) %{ match(Set cr (CmpI (AndI src mem) zero)); format %{ "TEST $src,$mem" %} @@ -12034,7 +11994,7 @@ instruct compU_eReg_mem(eFlagsRegU cr, rRegI op1, memory op2) %{ // ins_encode( OpcP, RegMem( op1, op2) ); //%} -instruct testU_reg( eFlagsRegU cr, rRegI src, immI0 zero ) %{ +instruct testU_reg( eFlagsRegU cr, rRegI src, immI_0 zero ) %{ match(Set cr (CmpU src zero)); format %{ "TESTu $src,$src" %} @@ -12111,7 +12071,7 @@ instruct testP_reg( eFlagsReg cr, eRegP src, immP0 zero ) %{ // Cisc-spilled version of testP_reg // This will generate a signed flags result. This should be ok // since any compare to a zero should be eq/neq. -instruct testP_Reg_mem( eFlagsReg cr, memory op, immI0 zero ) %{ +instruct testP_Reg_mem( eFlagsReg cr, memory op, immI_0 zero ) %{ match(Set cr (CmpP (LoadP op) zero)); format %{ "TEST $op,0xFFFFFFFF" %} @@ -13142,6 +13102,28 @@ instruct cmovLL_mem_LEGT(cmpOp_commute cmp, flagsReg_long_LEGT flags, eRegL dst, ins_pipe( pipe_cmov_reg_long ); %} +instruct cmovLL_reg_LEGT_U(cmpOpU_commute cmp, flagsReg_ulong_LEGT flags, eRegL dst, eRegL src) %{ + match(Set dst (CMoveL (Binary cmp flags) (Binary dst src))); + predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::le || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::gt )); + ins_cost(400); + format %{ "CMOV$cmp $dst.lo,$src.lo\n\t" + "CMOV$cmp $dst.hi,$src.hi" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cmp), RegReg_Lo2( dst, src ), enc_cmov(cmp), RegReg_Hi2( dst, src ) ); + ins_pipe( pipe_cmov_reg_long ); +%} + +instruct cmovLL_mem_LEGT_U(cmpOpU_commute cmp, flagsReg_ulong_LEGT flags, eRegL dst, load_long_memory src) %{ + match(Set dst (CMoveL (Binary cmp flags) (Binary dst (LoadL src)))); + predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::le || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::gt )); + ins_cost(500); + format %{ "CMOV$cmp $dst.lo,$src.lo\n\t" + "CMOV$cmp $dst.hi,$src.hi+4" %} + opcode(0x0F,0x40); + ins_encode( enc_cmov(cmp), RegMem(dst, src), enc_cmov(cmp), RegMem_Hi(dst, src) ); + ins_pipe( pipe_cmov_reg_long ); +%} + // Compare 2 longs and CMOVE ints. instruct cmovII_reg_LEGT(cmpOp_commute cmp, flagsReg_long_LEGT flags, rRegI dst, rRegI src) %{ predicate(VM_Version::supports_cmov() && ( _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::le || _kids[0]->_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::gt )); @@ -13482,7 +13464,7 @@ instruct tlsLoadP(eRegP dst, eFlagsReg cr) %{ // match(Set dst (CopyI src)); // %} // -// instruct incI_eReg(rRegI dst, immI1 src, eFlagsReg cr) %{ +// instruct incI_eReg(rRegI dst, immI_1 src, eFlagsReg cr) %{ // match(Set dst (AddI dst src)); // effect(KILL cr); // %} diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index fe1b44741a8..16cf230d602 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -161,9 +161,7 @@ alloc_class chunk0(R10, R10_H, // Several register classes are automatically defined based upon information in // this architecture description. // 1) reg_class inline_cache_reg ( /* as def'd in frame section */ ) -// 2) reg_class compiler_method_reg ( /* as def'd in frame section */ ) -// 2) reg_class interpreter_method_reg ( /* as def'd in frame section */ ) -// 3) reg_class stack_slots( /* one chunk of stack-based "registers" */ ) +// 2) reg_class stack_slots( /* one chunk of stack-based "registers" */ ) // // Empty register class. @@ -932,8 +930,8 @@ void MachEpilogNode::format(PhaseRegAlloc* ra_, outputStream* st) const st->print_cr("popq rbp"); if (do_polling() && C->is_method_compilation()) { st->print("\t"); - st->print_cr("movq rscratch1, poll_offset[r15_thread] #polling_page_address\n\t" - "testl rax, [rscratch1]\t" + st->print_cr("cmpq rsp, poll_offset[r15_thread] \n\t" + "ja #safepoint_stub\t" "# Safepoint: poll for GC"); } } @@ -980,9 +978,13 @@ void MachEpilogNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const if (do_polling() && C->is_method_compilation()) { MacroAssembler _masm(&cbuf); - __ movq(rscratch1, Address(r15_thread, Thread::polling_page_offset())); + Label dummy_label; + Label* code_stub = &dummy_label; + if (!C->output()->in_scratch_emit_size()) { + code_stub = &C->output()->safepoint_poll_table()->add_safepoint(__ offset()); + } __ relocate(relocInfo::poll_return_type); - __ testl(rax, Address(rscratch1, 0)); + __ safepoint_poll(*code_stub, r15_thread, true /* at_return */, true /* in_nmethod */); } } @@ -1657,9 +1659,6 @@ const bool Matcher::rematerialize_float_constants = true; // XXX // C code as the Java calling convention forces doubles to be aligned. const bool Matcher::misaligned_doubles_ok = true; -// No-op on amd64 -void Matcher::pd_implicit_null_fixup(MachNode *node, uint idx) {} - // Advertise here if the CPU requires explicit rounding operations to implement strictfp mode. const bool Matcher::strict_fp_requires_explicit_rounding = false; @@ -2738,8 +2737,6 @@ frame // These three registers define part of the calling convention // between compiled code and the interpreter. inline_cache_reg(RAX); // Inline Cache Register - interpreter_method_reg(RBX); // Method Register when - // calling interpreter // Optional: name the operand used by cisc-spilling to access // [stack_pointer + offset] @@ -2867,7 +2864,7 @@ operand immI() %} // Constant for test vs zero -operand immI0() +operand immI_0() %{ predicate(n->get_int() == 0); match(ConI); @@ -2878,7 +2875,7 @@ operand immI0() %} // Constant for increment -operand immI1() +operand immI_1() %{ predicate(n->get_int() == 1); match(ConI); @@ -2899,6 +2896,36 @@ operand immI_M1() interface(CONST_INTER); %} +operand immI_2() +%{ + predicate(n->get_int() == 2); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immI_4() +%{ + predicate(n->get_int() == 4); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immI_8() +%{ + predicate(n->get_int() == 8); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + // Valid scale values for addressing modes operand immI2() %{ @@ -2909,6 +2936,16 @@ operand immI2() interface(CONST_INTER); %} +operand immU7() +%{ + predicate((0 <= n->get_int()) && (n->get_int() <= 0x7F)); + match(ConI); + + op_cost(5); + format %{ %} + interface(CONST_INTER); +%} + operand immI8() %{ predicate((-0x80 <= n->get_int()) && (n->get_int() < 0x80)); @@ -5203,19 +5240,19 @@ instruct maxF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, match(Set dst (MaxF a b)); effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); format %{ - "blendvps $btmp,$b,$a,$b \n\t" - "blendvps $atmp,$a,$b,$b \n\t" + "vblendvps $btmp,$b,$a,$b \n\t" + "vblendvps $atmp,$a,$b,$b \n\t" "vmaxss $tmp,$atmp,$btmp \n\t" - "cmpps.unordered $btmp,$atmp,$atmp \n\t" - "blendvps $dst,$tmp,$atmp,$btmp \n\t" + "vcmpps.unordered $btmp,$atmp,$atmp \n\t" + "vblendvps $dst,$tmp,$atmp,$btmp \n\t" %} ins_encode %{ int vector_len = Assembler::AVX_128bit; - __ blendvps($btmp$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, vector_len); - __ blendvps($atmp$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $b$$XMMRegister, vector_len); + __ vblendvps($btmp$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, vector_len); + __ vblendvps($atmp$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $b$$XMMRegister, vector_len); __ vmaxss($tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister); - __ cmpps($btmp$$XMMRegister, $atmp$$XMMRegister, $atmp$$XMMRegister, Assembler::_false, vector_len); - __ blendvps($dst$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, vector_len); + __ vcmpps($btmp$$XMMRegister, $atmp$$XMMRegister, $atmp$$XMMRegister, Assembler::_false, vector_len); + __ vblendvps($dst$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, vector_len); %} ins_pipe( pipe_slow ); %} @@ -5239,19 +5276,19 @@ instruct maxD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, match(Set dst (MaxD a b)); effect(USE a, USE b, TEMP atmp, TEMP btmp, TEMP tmp); format %{ - "blendvpd $btmp,$b,$a,$b \n\t" - "blendvpd $atmp,$a,$b,$b \n\t" + "vblendvpd $btmp,$b,$a,$b \n\t" + "vblendvpd $atmp,$a,$b,$b \n\t" "vmaxsd $tmp,$atmp,$btmp \n\t" - "cmppd.unordered $btmp,$atmp,$atmp \n\t" - "blendvpd $dst,$tmp,$atmp,$btmp \n\t" + "vcmppd.unordered $btmp,$atmp,$atmp \n\t" + "vblendvpd $dst,$tmp,$atmp,$btmp \n\t" %} ins_encode %{ int vector_len = Assembler::AVX_128bit; - __ blendvpd($btmp$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, vector_len); - __ blendvpd($atmp$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $b$$XMMRegister, vector_len); + __ vblendvpd($btmp$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, vector_len); + __ vblendvpd($atmp$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $b$$XMMRegister, vector_len); __ vmaxsd($tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister); - __ cmppd($btmp$$XMMRegister, $atmp$$XMMRegister, $atmp$$XMMRegister, Assembler::_false, vector_len); - __ blendvpd($dst$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, vector_len); + __ vcmppd($btmp$$XMMRegister, $atmp$$XMMRegister, $atmp$$XMMRegister, Assembler::_false, vector_len); + __ vblendvpd($dst$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, vector_len); %} ins_pipe( pipe_slow ); %} @@ -5275,19 +5312,19 @@ instruct minF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, match(Set dst (MinF a b)); effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); format %{ - "blendvps $atmp,$a,$b,$a \n\t" - "blendvps $btmp,$b,$a,$a \n\t" + "vblendvps $atmp,$a,$b,$a \n\t" + "vblendvps $btmp,$b,$a,$a \n\t" "vminss $tmp,$atmp,$btmp \n\t" - "cmpps.unordered $btmp,$atmp,$atmp \n\t" - "blendvps $dst,$tmp,$atmp,$btmp \n\t" + "vcmpps.unordered $btmp,$atmp,$atmp \n\t" + "vblendvps $dst,$tmp,$atmp,$btmp \n\t" %} ins_encode %{ int vector_len = Assembler::AVX_128bit; - __ blendvps($atmp$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, vector_len); - __ blendvps($btmp$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, $a$$XMMRegister, vector_len); + __ vblendvps($atmp$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, vector_len); + __ vblendvps($btmp$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, $a$$XMMRegister, vector_len); __ vminss($tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister); - __ cmpps($btmp$$XMMRegister, $atmp$$XMMRegister, $atmp$$XMMRegister, Assembler::_false, vector_len); - __ blendvps($dst$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, vector_len); + __ vcmpps($btmp$$XMMRegister, $atmp$$XMMRegister, $atmp$$XMMRegister, Assembler::_false, vector_len); + __ vblendvps($dst$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, vector_len); %} ins_pipe( pipe_slow ); %} @@ -5311,19 +5348,19 @@ instruct minD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, match(Set dst (MinD a b)); effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); format %{ - "blendvpd $atmp,$a,$b,$a \n\t" - "blendvpd $btmp,$b,$a,$a \n\t" + "vblendvpd $atmp,$a,$b,$a \n\t" + "vblendvpd $btmp,$b,$a,$a \n\t" "vminsd $tmp,$atmp,$btmp \n\t" - "cmppd.unordered $btmp,$atmp,$atmp \n\t" - "blendvpd $dst,$tmp,$atmp,$btmp \n\t" + "vcmppd.unordered $btmp,$atmp,$atmp \n\t" + "vblendvpd $dst,$tmp,$atmp,$btmp \n\t" %} ins_encode %{ int vector_len = Assembler::AVX_128bit; - __ blendvpd($atmp$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, vector_len); - __ blendvpd($btmp$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, $a$$XMMRegister, vector_len); + __ vblendvpd($atmp$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, vector_len); + __ vblendvpd($btmp$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, $a$$XMMRegister, vector_len); __ vminsd($tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister); - __ cmppd($btmp$$XMMRegister, $atmp$$XMMRegister, $atmp$$XMMRegister, Assembler::_false, vector_len); - __ blendvpd($dst$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, vector_len); + __ vcmppd($btmp$$XMMRegister, $atmp$$XMMRegister, $atmp$$XMMRegister, Assembler::_false, vector_len); + __ vblendvpd($dst$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, vector_len); %} ins_pipe( pipe_slow ); %} @@ -5547,7 +5584,7 @@ instruct loadConI(rRegI dst, immI src) ins_pipe(ialu_reg_fat); // XXX %} -instruct loadConI0(rRegI dst, immI0 src, rFlagsReg cr) +instruct loadConI0(rRegI dst, immI_0 src, rFlagsReg cr) %{ match(Set dst src); effect(KILL cr); @@ -5983,7 +6020,7 @@ instruct storeImmNKlass(memory mem, immNKlass src) %} // Store Integer Immediate -instruct storeImmI0(memory mem, immI0 zero) +instruct storeImmI0(memory mem, immI_0 zero) %{ predicate(UseCompressedOops && (CompressedOops::base() == NULL)); match(Set mem (StoreI mem zero)); @@ -6033,7 +6070,7 @@ instruct storeImmL(memory mem, immL32 src) %} // Store Short/Char Immediate -instruct storeImmC0(memory mem, immI0 zero) +instruct storeImmC0(memory mem, immI_0 zero) %{ predicate(UseCompressedOops && (CompressedOops::base() == NULL)); match(Set mem (StoreC mem zero)); @@ -6059,7 +6096,7 @@ instruct storeImmI16(memory mem, immI16 src) %} // Store Byte Immediate -instruct storeImmB0(memory mem, immI0 zero) +instruct storeImmB0(memory mem, immI_0 zero) %{ predicate(UseCompressedOops && (CompressedOops::base() == NULL)); match(Set mem (StoreB mem zero)); @@ -6084,7 +6121,7 @@ instruct storeImmB(memory mem, immI8 src) %} // Store CMS card-mark Immediate -instruct storeImmCM0_reg(memory mem, immI0 zero) +instruct storeImmCM0_reg(memory mem, immI_0 zero) %{ predicate(UseCompressedOops && (CompressedOops::base() == NULL)); match(Set mem (StoreCM mem zero)); @@ -6097,7 +6134,7 @@ instruct storeImmCM0_reg(memory mem, immI0 zero) ins_pipe(ialu_mem_reg); %} -instruct storeImmCM0(memory mem, immI0 src) +instruct storeImmCM0(memory mem, immI_0 src) %{ match(Set mem (StoreCM mem src)); @@ -7182,7 +7219,7 @@ instruct addI_mem_imm(memory dst, immI src, rFlagsReg cr) ins_pipe(ialu_mem_imm); %} -instruct incI_rReg(rRegI dst, immI1 src, rFlagsReg cr) +instruct incI_rReg(rRegI dst, immI_1 src, rFlagsReg cr) %{ predicate(UseIncDec); match(Set dst (AddI dst src)); @@ -7194,7 +7231,7 @@ instruct incI_rReg(rRegI dst, immI1 src, rFlagsReg cr) ins_pipe(ialu_reg); %} -instruct incI_mem(memory dst, immI1 src, rFlagsReg cr) +instruct incI_mem(memory dst, immI_1 src, rFlagsReg cr) %{ predicate(UseIncDec); match(Set dst (StoreI dst (AddI (LoadI dst) src))); @@ -8077,7 +8114,7 @@ instruct subL_mem_imm(memory dst, immL32 src, rFlagsReg cr) // Subtract from a pointer // XXX hmpf??? -instruct subP_rReg(rRegP dst, rRegI src, immI0 zero, rFlagsReg cr) +instruct subP_rReg(rRegP dst, rRegI src, immI_0 zero, rFlagsReg cr) %{ match(Set dst (AddP dst (SubI zero src))); effect(KILL cr); @@ -8088,7 +8125,7 @@ instruct subP_rReg(rRegP dst, rRegI src, immI0 zero, rFlagsReg cr) ins_pipe(ialu_reg_reg); %} -instruct negI_rReg(rRegI dst, immI0 zero, rFlagsReg cr) +instruct negI_rReg(rRegI dst, immI_0 zero, rFlagsReg cr) %{ match(Set dst (SubI zero dst)); effect(KILL cr); @@ -8099,7 +8136,19 @@ instruct negI_rReg(rRegI dst, immI0 zero, rFlagsReg cr) ins_pipe(ialu_reg); %} -instruct negI_mem(memory dst, immI0 zero, rFlagsReg cr) +instruct negI_rReg_2(rRegI dst, rFlagsReg cr) +%{ + match(Set dst (NegI dst)); + effect(KILL cr); + + format %{ "negl $dst\t# int" %} + ins_encode %{ + __ negl($dst$$Register); + %} + ins_pipe(ialu_reg); +%} + +instruct negI_mem(memory dst, immI_0 zero, rFlagsReg cr) %{ match(Set dst (StoreI dst (SubI zero (LoadI dst)))); effect(KILL cr); @@ -8121,6 +8170,18 @@ instruct negL_rReg(rRegL dst, immL0 zero, rFlagsReg cr) ins_pipe(ialu_reg); %} +instruct negL_rReg_2(rRegL dst, rFlagsReg cr) +%{ + match(Set dst (NegL dst)); + effect(KILL cr); + + format %{ "negq $dst\t# int" %} + ins_encode %{ + __ negq($dst$$Register); + %} + ins_pipe(ialu_reg); +%} + instruct negL_mem(memory dst, immL0 zero, rFlagsReg cr) %{ match(Set dst (StoreL dst (SubL zero (LoadL dst)))); @@ -8446,7 +8507,7 @@ instruct modL_rReg(rdx_RegL rdx, rax_RegL rax, no_rax_rdx_RegL div, // Integer Shift Instructions // Shift Left by one -instruct salI_rReg_1(rRegI dst, immI1 shift, rFlagsReg cr) +instruct salI_rReg_1(rRegI dst, immI_1 shift, rFlagsReg cr) %{ match(Set dst (LShiftI dst shift)); effect(KILL cr); @@ -8458,7 +8519,7 @@ instruct salI_rReg_1(rRegI dst, immI1 shift, rFlagsReg cr) %} // Shift Left by one -instruct salI_mem_1(memory dst, immI1 shift, rFlagsReg cr) +instruct salI_mem_1(memory dst, immI_1 shift, rFlagsReg cr) %{ match(Set dst (StoreI dst (LShiftI (LoadI dst) shift))); effect(KILL cr); @@ -8518,7 +8579,7 @@ instruct salI_mem_CL(memory dst, rcx_RegI shift, rFlagsReg cr) %} // Arithmetic shift right by one -instruct sarI_rReg_1(rRegI dst, immI1 shift, rFlagsReg cr) +instruct sarI_rReg_1(rRegI dst, immI_1 shift, rFlagsReg cr) %{ match(Set dst (RShiftI dst shift)); effect(KILL cr); @@ -8530,7 +8591,7 @@ instruct sarI_rReg_1(rRegI dst, immI1 shift, rFlagsReg cr) %} // Arithmetic shift right by one -instruct sarI_mem_1(memory dst, immI1 shift, rFlagsReg cr) +instruct sarI_mem_1(memory dst, immI_1 shift, rFlagsReg cr) %{ match(Set dst (StoreI dst (RShiftI (LoadI dst) shift))); effect(KILL cr); @@ -8590,7 +8651,7 @@ instruct sarI_mem_CL(memory dst, rcx_RegI shift, rFlagsReg cr) %} // Logical shift right by one -instruct shrI_rReg_1(rRegI dst, immI1 shift, rFlagsReg cr) +instruct shrI_rReg_1(rRegI dst, immI_1 shift, rFlagsReg cr) %{ match(Set dst (URShiftI dst shift)); effect(KILL cr); @@ -8602,7 +8663,7 @@ instruct shrI_rReg_1(rRegI dst, immI1 shift, rFlagsReg cr) %} // Logical shift right by one -instruct shrI_mem_1(memory dst, immI1 shift, rFlagsReg cr) +instruct shrI_mem_1(memory dst, immI_1 shift, rFlagsReg cr) %{ match(Set dst (StoreI dst (URShiftI (LoadI dst) shift))); effect(KILL cr); @@ -8663,7 +8724,7 @@ instruct shrI_mem_CL(memory dst, rcx_RegI shift, rFlagsReg cr) // Long Shift Instructions // Shift Left by one -instruct salL_rReg_1(rRegL dst, immI1 shift, rFlagsReg cr) +instruct salL_rReg_1(rRegL dst, immI_1 shift, rFlagsReg cr) %{ match(Set dst (LShiftL dst shift)); effect(KILL cr); @@ -8675,7 +8736,7 @@ instruct salL_rReg_1(rRegL dst, immI1 shift, rFlagsReg cr) %} // Shift Left by one -instruct salL_mem_1(memory dst, immI1 shift, rFlagsReg cr) +instruct salL_mem_1(memory dst, immI_1 shift, rFlagsReg cr) %{ match(Set dst (StoreL dst (LShiftL (LoadL dst) shift))); effect(KILL cr); @@ -8736,7 +8797,7 @@ instruct salL_mem_CL(memory dst, rcx_RegI shift, rFlagsReg cr) %} // Arithmetic shift right by one -instruct sarL_rReg_1(rRegL dst, immI1 shift, rFlagsReg cr) +instruct sarL_rReg_1(rRegL dst, immI_1 shift, rFlagsReg cr) %{ match(Set dst (RShiftL dst shift)); effect(KILL cr); @@ -8748,7 +8809,7 @@ instruct sarL_rReg_1(rRegL dst, immI1 shift, rFlagsReg cr) %} // Arithmetic shift right by one -instruct sarL_mem_1(memory dst, immI1 shift, rFlagsReg cr) +instruct sarL_mem_1(memory dst, immI_1 shift, rFlagsReg cr) %{ match(Set dst (StoreL dst (RShiftL (LoadL dst) shift))); effect(KILL cr); @@ -8809,7 +8870,7 @@ instruct sarL_mem_CL(memory dst, rcx_RegI shift, rFlagsReg cr) %} // Logical shift right by one -instruct shrL_rReg_1(rRegL dst, immI1 shift, rFlagsReg cr) +instruct shrL_rReg_1(rRegL dst, immI_1 shift, rFlagsReg cr) %{ match(Set dst (URShiftL dst shift)); effect(KILL cr); @@ -8821,7 +8882,7 @@ instruct shrL_rReg_1(rRegL dst, immI1 shift, rFlagsReg cr) %} // Logical shift right by one -instruct shrL_mem_1(memory dst, immI1 shift, rFlagsReg cr) +instruct shrL_mem_1(memory dst, immI_1 shift, rFlagsReg cr) %{ match(Set dst (StoreL dst (URShiftL (LoadL dst) shift))); effect(KILL cr); @@ -9193,7 +9254,7 @@ instruct andnI_rReg_rReg_rReg(rRegI dst, rRegI src1, rRegI src2, immI_M1 minus_1 ins_pipe(ialu_reg); %} -instruct blsiI_rReg_rReg(rRegI dst, rRegI src, immI0 imm_zero, rFlagsReg cr) %{ +instruct blsiI_rReg_rReg(rRegI dst, rRegI src, immI_0 imm_zero, rFlagsReg cr) %{ match(Set dst (AndI (SubI imm_zero src) src)); predicate(UseBMI1Instructions); effect(KILL cr); @@ -9206,7 +9267,7 @@ instruct blsiI_rReg_rReg(rRegI dst, rRegI src, immI0 imm_zero, rFlagsReg cr) %{ ins_pipe(ialu_reg); %} -instruct blsiI_rReg_mem(rRegI dst, memory src, immI0 imm_zero, rFlagsReg cr) %{ +instruct blsiI_rReg_mem(rRegI dst, memory src, immI_0 imm_zero, rFlagsReg cr) %{ match(Set dst (AndI (SubI imm_zero (LoadI src) ) (LoadI src) )); predicate(UseBMI1Instructions); effect(KILL cr); @@ -9889,7 +9950,7 @@ instruct cmpLTMask(rRegI dst, rRegI p, rRegI q, rFlagsReg cr) ins_pipe(pipe_slow); %} -instruct cmpLTMask0(rRegI dst, immI0 zero, rFlagsReg cr) +instruct cmpLTMask0(rRegI dst, immI_0 zero, rFlagsReg cr) %{ match(Set dst (CmpLTMask dst zero)); effect(KILL cr); @@ -11003,13 +11064,13 @@ instruct string_indexofUL(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, rax_RegI ins_pipe( pipe_slow ); %} -instruct string_indexofU_char(rdi_RegP str1, rdx_RegI cnt1, rax_RegI ch, +instruct string_indexof_char(rdi_RegP str1, rdx_RegI cnt1, rax_RegI ch, rbx_RegI result, legRegD tmp_vec1, legRegD tmp_vec2, legRegD tmp_vec3, rcx_RegI tmp, rFlagsReg cr) %{ - predicate(UseSSE42Intrinsics); + predicate(UseSSE42Intrinsics && (((StrIndexOfCharNode*)n)->encoding() == StrIntrinsicNode::U)); match(Set result (StrIndexOfChar (Binary str1 cnt1) ch)); effect(TEMP tmp_vec1, TEMP tmp_vec2, TEMP tmp_vec3, USE_KILL str1, USE_KILL cnt1, USE_KILL ch, TEMP tmp, KILL cr); - format %{ "String IndexOf char[] $str1,$cnt1,$ch -> $result // KILL all" %} + format %{ "StringUTF16 IndexOf char[] $str1,$cnt1,$ch -> $result // KILL all" %} ins_encode %{ __ string_indexof_char($str1$$Register, $cnt1$$Register, $ch$$Register, $result$$Register, $tmp_vec1$$XMMRegister, $tmp_vec2$$XMMRegister, $tmp_vec3$$XMMRegister, $tmp$$Register); @@ -11017,6 +11078,20 @@ instruct string_indexofU_char(rdi_RegP str1, rdx_RegI cnt1, rax_RegI ch, ins_pipe( pipe_slow ); %} +instruct stringL_indexof_char(rdi_RegP str1, rdx_RegI cnt1, rax_RegI ch, + rbx_RegI result, legRegD tmp_vec1, legRegD tmp_vec2, legRegD tmp_vec3, rcx_RegI tmp, rFlagsReg cr) +%{ + predicate(UseSSE42Intrinsics && (((StrIndexOfCharNode*)n)->encoding() == StrIntrinsicNode::L)); + match(Set result (StrIndexOfChar (Binary str1 cnt1) ch)); + effect(TEMP tmp_vec1, TEMP tmp_vec2, TEMP tmp_vec3, USE_KILL str1, USE_KILL cnt1, USE_KILL ch, TEMP tmp, KILL cr); + format %{ "StringLatin1 IndexOf char[] $str1,$cnt1,$ch -> $result // KILL all" %} + ins_encode %{ + __ stringL_indexof_char($str1$$Register, $cnt1$$Register, $ch$$Register, $result$$Register, + $tmp_vec1$$XMMRegister, $tmp_vec2$$XMMRegister, $tmp_vec3$$XMMRegister, $tmp$$Register); + %} + ins_pipe( pipe_slow ); +%} + // fast string equals instruct string_equals(rdi_RegP str1, rsi_RegP str2, rcx_RegI cnt, rax_RegI result, legRegD tmp1, legRegD tmp2, rbx_RegI tmp3, rFlagsReg cr) @@ -11222,7 +11297,7 @@ instruct overflowSubL_rReg_imm(rFlagsReg cr, rRegL op1, immL32 op2) ins_pipe(ialu_reg_reg); %} -instruct overflowNegI_rReg(rFlagsReg cr, immI0 zero, rax_RegI op2) +instruct overflowNegI_rReg(rFlagsReg cr, immI_0 zero, rax_RegI op2) %{ match(Set cr (OverflowSubI zero op2)); effect(DEF cr, USE_KILL op2); @@ -11331,7 +11406,7 @@ instruct compI_rReg_mem(rFlagsReg cr, rRegI op1, memory op2) ins_pipe(ialu_cr_reg_mem); %} -instruct testI_reg(rFlagsReg cr, rRegI src, immI0 zero) +instruct testI_reg(rFlagsReg cr, rRegI src, immI_0 zero) %{ match(Set cr (CmpI src zero)); @@ -11341,7 +11416,7 @@ instruct testI_reg(rFlagsReg cr, rRegI src, immI0 zero) ins_pipe(ialu_cr_reg_imm); %} -instruct testI_reg_imm(rFlagsReg cr, rRegI src, immI con, immI0 zero) +instruct testI_reg_imm(rFlagsReg cr, rRegI src, immI con, immI_0 zero) %{ match(Set cr (CmpI (AndI src con) zero)); @@ -11351,7 +11426,7 @@ instruct testI_reg_imm(rFlagsReg cr, rRegI src, immI con, immI0 zero) ins_pipe(ialu_cr_reg_imm); %} -instruct testI_reg_mem(rFlagsReg cr, rRegI src, memory mem, immI0 zero) +instruct testI_reg_mem(rFlagsReg cr, rRegI src, memory mem, immI_0 zero) %{ match(Set cr (CmpI (AndI src (LoadI mem)) zero)); @@ -11405,7 +11480,7 @@ instruct compU_rReg_mem(rFlagsRegU cr, rRegI op1, memory op2) // // ins_encode( OpcP, reg_mem( op1, op2) ); // //%} -instruct testU_reg(rFlagsRegU cr, rRegI src, immI0 zero) +instruct testU_reg(rFlagsRegU cr, rRegI src, immI_0 zero) %{ match(Set cr (CmpU src zero)); @@ -11743,7 +11818,7 @@ instruct compB_mem_imm(rFlagsReg cr, memory mem, immI8 imm) ins_pipe(ialu_cr_reg_mem); %} -instruct testUB_mem_imm(rFlagsReg cr, memory mem, immU8 imm, immI0 zero) +instruct testUB_mem_imm(rFlagsReg cr, memory mem, immU7 imm, immI_0 zero) %{ match(Set cr (CmpI (AndI (LoadUB mem) imm) zero)); @@ -11753,7 +11828,7 @@ instruct testUB_mem_imm(rFlagsReg cr, memory mem, immU8 imm, immI0 zero) ins_pipe(ialu_cr_reg_mem); %} -instruct testB_mem_imm(rFlagsReg cr, memory mem, immI8 imm, immI0 zero) +instruct testB_mem_imm(rFlagsReg cr, memory mem, immI8 imm, immI_0 zero) %{ match(Set cr (CmpI (AndI (LoadB mem) imm) zero)); @@ -12476,7 +12551,7 @@ instruct tlsLoadP(r15_RegP dst) %{ // match(Set dst (CopyI src)); // %} // -// instruct incI_rReg(rRegI dst, immI1 src, rFlagsReg cr) +// instruct incI_rReg(rRegI dst, immI_1 src, rFlagsReg cr) // %{ // match(Set dst (AddI dst src)); // effect(KILL cr); diff --git a/src/hotspot/cpu/zero/assembler_zero.cpp b/src/hotspot/cpu/zero/assembler_zero.cpp index b23048bb82b..706e020123a 100644 --- a/src/hotspot/cpu/zero/assembler_zero.cpp +++ b/src/hotspot/cpu/zero/assembler_zero.cpp @@ -66,12 +66,6 @@ void MacroAssembler::advance(int bytes) { code_section()->set_end(code_section()->end() + bytes); } -RegisterOrConstant MacroAssembler::delayed_value_impl( - intptr_t* delayed_value_addr, Register tmpl, int offset) { - ShouldNotCallThis(); - return RegisterOrConstant(); -} - void MacroAssembler::store_oop(jobject obj) { code_section()->relocate(pc(), oop_Relocation::spec_for_immediate()); emit_address((address) obj); diff --git a/src/hotspot/cpu/zero/assembler_zero.hpp b/src/hotspot/cpu/zero/assembler_zero.hpp index 1edf2c7df49..ae4c58e8ac7 100644 --- a/src/hotspot/cpu/zero/assembler_zero.hpp +++ b/src/hotspot/cpu/zero/assembler_zero.hpp @@ -54,9 +54,6 @@ class MacroAssembler : public Assembler { void bang_stack_with_offset(int offset); bool needs_explicit_null_check(intptr_t offset); bool uses_implicit_null_check(void* address); - RegisterOrConstant delayed_value_impl(intptr_t* delayed_value_addr, - Register tmp, int offset); - public: void advance(int bytes); void store_oop(jobject obj); void store_Metadata(Metadata* obj); diff --git a/src/hotspot/cpu/zero/bytecodeInterpreter_zero.cpp b/src/hotspot/cpu/zero/bytecodeInterpreter_zero.cpp index d20fe3e4f7f..f165de3a086 100644 --- a/src/hotspot/cpu/zero/bytecodeInterpreter_zero.cpp +++ b/src/hotspot/cpu/zero/bytecodeInterpreter_zero.cpp @@ -47,7 +47,6 @@ const char *BytecodeInterpreter::name_of_field_at_address(address addr) { DO(_constants); DO(_method); DO(_mirror); - DO(_mdx); DO(_stack); DO(_msg); DO(_result); @@ -84,7 +83,6 @@ void BytecodeInterpreter::layout_interpreterState(interpreterState istate, istate->set_msg(BytecodeInterpreter::method_resume); istate->set_bcp_advance(0); istate->set_oop_temp(NULL); - istate->set_mdx(NULL); if (caller->is_interpreted_frame()) { interpreterState prev = caller->get_interpreterState(); prev->set_callee(method); diff --git a/src/hotspot/cpu/zero/bytecodeInterpreter_zero.hpp b/src/hotspot/cpu/zero/bytecodeInterpreter_zero.hpp index 9e3c6e209a4..07166fbef76 100644 --- a/src/hotspot/cpu/zero/bytecodeInterpreter_zero.hpp +++ b/src/hotspot/cpu/zero/bytecodeInterpreter_zero.hpp @@ -153,22 +153,4 @@ #define SET_LOCALS_LONG_FROM_ADDR(addr, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->l = \ ((VMJavaVal64*)(addr))->l) -// VMSlots implementation - -#define VMSLOTS_SLOT(offset) ((intptr_t*)&vmslots[(offset)]) -#define VMSLOTS_ADDR(offset) ((address)vmslots[(offset)]) -#define VMSLOTS_INT(offset) (*((jint*)&vmslots[(offset)])) -#define VMSLOTS_FLOAT(offset) (*((jfloat*)&vmslots[(offset)])) -#define VMSLOTS_OBJECT(offset) ((oop)vmslots[(offset)]) -#define VMSLOTS_DOUBLE(offset) (((VMJavaVal64*)&vmslots[(offset) - 1])->d) -#define VMSLOTS_LONG(offset) (((VMJavaVal64*)&vmslots[(offset) - 1])->l) - -#define SET_VMSLOTS_SLOT(value, offset) (*(intptr_t*)&vmslots[(offset)] = *(intptr_t *)(value)) -#define SET_VMSLOTS_ADDR(value, offset) (*((address *)&vmslots[(offset)]) = (value)) -#define SET_VMSLOTS_INT(value, offset) (*((jint *)&vmslots[(offset)]) = (value)) -#define SET_VMSLOTS_FLOAT(value, offset) (*((jfloat *)&vmslots[(offset)]) = (value)) -#define SET_VMSLOTS_OBJECT(value, offset) (*((oop *)&vmslots[(offset)]) = (value)) -#define SET_VMSLOTS_DOUBLE(value, offset) (((VMJavaVal64*)&vmslots[(offset) - 1])->d = (value)) -#define SET_VMSLOTS_LONG(value, offset) (((VMJavaVal64*)&vmslots[(offset) - 1])->l = (value)) - #endif // CPU_ZERO_BYTECODEINTERPRETER_ZERO_HPP diff --git a/src/hotspot/cpu/zero/frame_zero.inline.hpp b/src/hotspot/cpu/zero/frame_zero.inline.hpp index 11472461d64..396e189a5db 100644 --- a/src/hotspot/cpu/zero/frame_zero.inline.hpp +++ b/src/hotspot/cpu/zero/frame_zero.inline.hpp @@ -107,7 +107,8 @@ inline oop* frame::interpreter_frame_mirror_addr() const { } inline intptr_t* frame::interpreter_frame_mdp_addr() const { - return (intptr_t*) &(get_interpreterState()->_mdx); + fatal("Should not call this: Zero never profiles"); + return NULL; // silence compiler warnings } inline intptr_t* frame::interpreter_frame_tos_address() const { diff --git a/src/hotspot/cpu/zero/interp_masm_zero.hpp b/src/hotspot/cpu/zero/interp_masm_zero.hpp index 5d8aa9a3bdc..a109b12dec0 100644 --- a/src/hotspot/cpu/zero/interp_masm_zero.hpp +++ b/src/hotspot/cpu/zero/interp_masm_zero.hpp @@ -35,14 +35,6 @@ class InterpreterMacroAssembler : public MacroAssembler { public: InterpreterMacroAssembler(CodeBuffer* code) : MacroAssembler(code) {} - - public: - RegisterOrConstant delayed_value_impl(intptr_t* delayed_value_addr, - Register tmp, - int offset) { - ShouldNotCallThis(); - return RegisterOrConstant(); - } }; #endif // CPU_ZERO_INTERP_MASM_ZERO_HPP diff --git a/src/hotspot/cpu/zero/stack_zero.inline.hpp b/src/hotspot/cpu/zero/stack_zero.inline.hpp index 0c85af5dfde..f363de57180 100644 --- a/src/hotspot/cpu/zero/stack_zero.inline.hpp +++ b/src/hotspot/cpu/zero/stack_zero.inline.hpp @@ -47,7 +47,7 @@ inline void ZeroStack::overflow_check(int required_words, TRAPS) { // to use under normal circumstances. Note that the returned // value can be negative. inline int ZeroStack::abi_stack_available(Thread *thread) const { - guarantee(Thread::current() == thread, "should run in the same thread"); + assert(Thread::current() == thread, "should run in the same thread"); int stack_used = thread->stack_base() - (address) &stack_used + (StackOverflow::stack_guard_zone_size() + StackOverflow::stack_shadow_zone_size()); int stack_free = thread->stack_size() - stack_used; diff --git a/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp b/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp index 7c4c1f73908..a865ef37a2f 100644 --- a/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp +++ b/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp @@ -70,10 +70,11 @@ void ZeroInterpreter::initialize_code() { // Allow c++ interpreter to do one initialization now that switches are set, etc. BytecodeInterpreter start_msg(BytecodeInterpreter::initialize); - if (JvmtiExport::can_post_interpreter_events()) - BytecodeInterpreter::runWithChecks(&start_msg); - else - BytecodeInterpreter::run(&start_msg); + if (JvmtiExport::can_post_interpreter_events()) { + BytecodeInterpreter::run(&start_msg); + } else { + BytecodeInterpreter::run(&start_msg); + } } void ZeroInterpreter::invoke_method(Method* method, address entry_point, TRAPS) { @@ -169,10 +170,11 @@ void ZeroInterpreter::main_loop(int recurse, TRAPS) { thread->set_last_Java_frame(); // Call the interpreter - if (JvmtiExport::can_post_interpreter_events()) - BytecodeInterpreter::runWithChecks(istate); - else - BytecodeInterpreter::run(istate); + if (JvmtiExport::can_post_interpreter_events()) { + BytecodeInterpreter::run(istate); + } else { + BytecodeInterpreter::run(istate); + } fixup_after_potential_safepoint(); // Clear the frame anchor @@ -596,6 +598,9 @@ int ZeroInterpreter::accessor_entry(Method* method, intptr_t UNUSED, TRAPS) { break; } if (entry->is_volatile()) { + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + OrderAccess::fence(); + } switch (entry->flag_state()) { case ctos: SET_LOCALS_INT(object->char_field_acquire(entry->f2_as_index()), 0); @@ -694,56 +699,6 @@ int ZeroInterpreter::empty_entry(Method* method, intptr_t UNUSED, TRAPS) { return 0; } -// The new slots will be inserted before slot insert_before. -// Slots < insert_before will have the same slot number after the insert. -// Slots >= insert_before will become old_slot + num_slots. -void ZeroInterpreter::insert_vmslots(int insert_before, int num_slots, TRAPS) { - JavaThread *thread = THREAD->as_Java_thread(); - ZeroStack *stack = thread->zero_stack(); - - // Allocate the space - stack->overflow_check(num_slots, CHECK); - stack->alloc(num_slots * wordSize); - intptr_t *vmslots = stack->sp(); - - // Shuffle everything up - for (int i = 0; i < insert_before; i++) - SET_VMSLOTS_SLOT(VMSLOTS_SLOT(i + num_slots), i); -} - -void ZeroInterpreter::remove_vmslots(int first_slot, int num_slots, TRAPS) { - JavaThread *thread = THREAD->as_Java_thread(); - ZeroStack *stack = thread->zero_stack(); - intptr_t *vmslots = stack->sp(); - - // Move everything down - for (int i = first_slot - 1; i >= 0; i--) - SET_VMSLOTS_SLOT(VMSLOTS_SLOT(i), i + num_slots); - - // Deallocate the space - stack->set_sp(stack->sp() + num_slots); -} - -BasicType ZeroInterpreter::result_type_of_handle(oop method_handle) { - oop method_type = java_lang_invoke_MethodHandle::type(method_handle); - oop return_type = java_lang_invoke_MethodType::rtype(method_type); - return java_lang_Class::as_BasicType(return_type, (Klass* *) NULL); -} - -intptr_t* ZeroInterpreter::calculate_unwind_sp(ZeroStack* stack, - oop method_handle) { - oop method_type = java_lang_invoke_MethodHandle::type(method_handle); - int argument_slots = java_lang_invoke_MethodType::ptype_slot_count(method_type); - - return stack->sp() + argument_slots; -} - -JRT_ENTRY(void, ZeroInterpreter::throw_exception(JavaThread* thread, - Symbol* name, - char* message)) - THROW_MSG(name, message); -JRT_END - InterpreterFrame *InterpreterFrame::build(Method* const method, TRAPS) { JavaThread *thread = THREAD->as_Java_thread(); ZeroStack *stack = thread->zero_stack(); @@ -796,7 +751,6 @@ InterpreterFrame *InterpreterFrame::build(Method* const method, TRAPS) { istate->set_constants(method->constants()->cache()); istate->set_msg(BytecodeInterpreter::method_entry); istate->set_oop_temp(NULL); - istate->set_mdx(NULL); istate->set_callee(NULL); istate->set_monitor_base((BasicObjectLock *) stack->sp()); diff --git a/src/hotspot/cpu/zero/zeroInterpreter_zero.hpp b/src/hotspot/cpu/zero/zeroInterpreter_zero.hpp index e534ff2fa29..2e6239c7f02 100644 --- a/src/hotspot/cpu/zero/zeroInterpreter_zero.hpp +++ b/src/hotspot/cpu/zero/zeroInterpreter_zero.hpp @@ -41,12 +41,4 @@ // Main loop of normal_entry static void main_loop(int recurse, TRAPS); - private: - // Helpers for method_handle_entry - static void insert_vmslots(int insert_before, int num_slots, TRAPS); - static void remove_vmslots(int first_slot, int num_slots, TRAPS); - static BasicType result_type_of_handle(oop method_handle); - static intptr_t* calculate_unwind_sp(ZeroStack* stack, oop method_handle); - static void throw_exception(JavaThread* thread, Symbol* name,char *msg=NULL); - #endif // CPU_ZERO_CPPINTERPRETER_ZERO_HPP diff --git a/src/hotspot/os/aix/globals_aix.hpp b/src/hotspot/os/aix/globals_aix.hpp index b6b67a4f3bb..1203b09c883 100644 --- a/src/hotspot/os/aix/globals_aix.hpp +++ b/src/hotspot/os/aix/globals_aix.hpp @@ -88,7 +88,6 @@ // Use Use64KPages or Use16MPages instead. define_pd_global(bool, UseLargePages, false); define_pd_global(bool, UseLargePagesIndividualAllocation, false); -define_pd_global(bool, UseOSErrorReporting, false); define_pd_global(bool, UseThreadPriorities, true) ; #endif // OS_AIX_GLOBALS_AIX_HPP diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 552c345563a..71cc3056a55 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -2236,7 +2236,7 @@ bool os::can_execute_large_page_memory() { return false; } -char* os::pd_attempt_reserve_memory_at(char* requested_addr, size_t bytes, int file_desc) { +char* os::pd_attempt_map_memory_to_file_at(char* requested_addr, size_t bytes, int file_desc) { assert(file_desc >= 0, "file_desc is not valid"); char* result = NULL; diff --git a/src/hotspot/os/aix/safepointMechanism_aix.cpp b/src/hotspot/os/aix/safepointMechanism_aix.cpp index cda4301239f..44b01a51afe 100644 --- a/src/hotspot/os/aix/safepointMechanism_aix.cpp +++ b/src/hotspot/os/aix/safepointMechanism_aix.cpp @@ -37,6 +37,10 @@ void SafepointMechanism::pd_initialize() { return; } + // Poll bit values + _poll_word_armed_value = poll_bit(); + _poll_word_disarmed_value = ~_poll_word_armed_value; + // Allocate one protected page char* map_address = (char*)MAP_FAILED; const size_t page_size = os::vm_page_size(); @@ -104,8 +108,8 @@ void SafepointMechanism::pd_initialize() { if (!os::guard_memory((char*)_polling_page, page_size)) { fatal("Could not protect polling page"); } - intptr_t bad_page_val = reinterpret_cast(map_address), - good_page_val = bad_page_val + os::vm_page_size(); - _poll_armed_value = reinterpret_cast(bad_page_val + poll_bit()); - _poll_disarmed_value = reinterpret_cast(good_page_val); + uintptr_t bad_page_val = reinterpret_cast(map_address), + good_page_val = bad_page_val + os::vm_page_size(); + _poll_page_armed_value = bad_page_val + poll_bit(); + _poll_page_disarmed_value = good_page_val; } diff --git a/src/hotspot/os/bsd/globals_bsd.hpp b/src/hotspot/os/bsd/globals_bsd.hpp index 6c8939a6dc0..b36173655df 100644 --- a/src/hotspot/os/bsd/globals_bsd.hpp +++ b/src/hotspot/os/bsd/globals_bsd.hpp @@ -44,7 +44,6 @@ // define_pd_global(bool, UseLargePages, false); define_pd_global(bool, UseLargePagesIndividualAllocation, false); -define_pd_global(bool, UseOSErrorReporting, false); define_pd_global(bool, UseThreadPriorities, true) ; #endif // OS_BSD_GLOBALS_BSD_HPP diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 68196755732..d95cb6807d2 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -1905,7 +1905,7 @@ bool os::can_execute_large_page_memory() { return false; } -char* os::pd_attempt_reserve_memory_at(char* requested_addr, size_t bytes, int file_desc) { +char* os::pd_attempt_map_memory_to_file_at(char* requested_addr, size_t bytes, int file_desc) { assert(file_desc >= 0, "file_desc is not valid"); char* result = pd_attempt_reserve_memory_at(requested_addr, bytes); if (result != NULL) { diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index fada2a732bf..fb653c762bc 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -294,14 +294,15 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, // Skip cgroup2 fs lines on hybrid or unified hierarchy. continue; } - any_cgroup_mounts_found = true; while ((token = strsep(&cptr, ",")) != NULL) { if (strcmp(token, "memory") == 0) { + any_cgroup_mounts_found = true; assert(cg_infos[MEMORY_IDX]._mount_path == NULL, "stomping of _mount_path"); cg_infos[MEMORY_IDX]._mount_path = os::strdup(tmpmount); cg_infos[MEMORY_IDX]._root_mount_path = os::strdup(tmproot); cg_infos[MEMORY_IDX]._data_complete = true; } else if (strcmp(token, "cpuset") == 0) { + any_cgroup_mounts_found = true; if (cg_infos[CPUSET_IDX]._mount_path != NULL) { // On some systems duplicate cpuset controllers get mounted in addition to // the main cgroup controllers most likely under /sys/fs/cgroup. In that @@ -321,11 +322,13 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, cg_infos[CPUSET_IDX]._root_mount_path = os::strdup(tmproot); cg_infos[CPUSET_IDX]._data_complete = true; } else if (strcmp(token, "cpu") == 0) { + any_cgroup_mounts_found = true; assert(cg_infos[CPU_IDX]._mount_path == NULL, "stomping of _mount_path"); cg_infos[CPU_IDX]._mount_path = os::strdup(tmpmount); cg_infos[CPU_IDX]._root_mount_path = os::strdup(tmproot); cg_infos[CPU_IDX]._data_complete = true; } else if (strcmp(token, "cpuacct") == 0) { + any_cgroup_mounts_found = true; assert(cg_infos[CPUACCT_IDX]._mount_path == NULL, "stomping of _mount_path"); cg_infos[CPUACCT_IDX]._mount_path = os::strdup(tmpmount); cg_infos[CPUACCT_IDX]._root_mount_path = os::strdup(tmproot); @@ -339,7 +342,7 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, // Neither cgroup2 nor cgroup filesystems mounted via /proc/self/mountinfo // No point in continuing. if (!any_cgroup_mounts_found) { - log_trace(os, container)("No cgroup controllers mounted."); + log_trace(os, container)("No relevant cgroup controllers mounted."); cleanup(cg_infos); *flags = INVALID_CGROUPS_NO_MOUNT; return false; diff --git a/src/hotspot/os/linux/globals_linux.hpp b/src/hotspot/os/linux/globals_linux.hpp index f2a889285af..a37fd28f0e4 100644 --- a/src/hotspot/os/linux/globals_linux.hpp +++ b/src/hotspot/os/linux/globals_linux.hpp @@ -79,7 +79,10 @@ "be dumped into the corefile.") \ \ product(bool, UseCpuAllocPath, false, DIAGNOSTIC, \ - "Use CPU_ALLOC code path in os::active_processor_count ") + "Use CPU_ALLOC code path in os::active_processor_count ") \ + \ + product(bool, DumpPerfMapAtExit, false, DIAGNOSTIC, \ + "Write map file for Linux perf tool at exit") // end of RUNTIME_OS_FLAGS @@ -89,7 +92,6 @@ // define_pd_global(bool, UseLargePages, false); define_pd_global(bool, UseLargePagesIndividualAllocation, false); -define_pd_global(bool, UseOSErrorReporting, false); define_pd_global(bool, UseThreadPriorities, true) ; #endif // OS_LINUX_GLOBALS_LINUX_HPP diff --git a/src/hotspot/os/linux/osContainer_linux.hpp b/src/hotspot/os/linux/osContainer_linux.hpp index 8775288d450..21801b7dc4b 100644 --- a/src/hotspot/os/linux/osContainer_linux.hpp +++ b/src/hotspot/os/linux/osContainer_linux.hpp @@ -41,8 +41,6 @@ class OSContainer: AllStatic { static bool _is_containerized; static int _active_processor_count; - static jlong read_memory_limit_in_bytes(); - public: static void init(); static inline bool is_containerized(); diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 85560ccbbb0..e7e332c16b5 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -104,7 +104,6 @@ # include # include # include -# include # include # include # include @@ -137,6 +136,17 @@ // for timer info max values which include all bits #define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF) +#ifdef MUSL_LIBC +// dlvsym is not a part of POSIX +// and musl libc doesn't implement it. +static void *dlvsym(void *handle, + const char *symbol, + const char *version) { + // load the latest version of symbol + return dlsym(handle, symbol); +} +#endif + enum CoredumpFilterBit { FILE_BACKED_PVT_BIT = 1 << 2, FILE_BACKED_SHARED_BIT = 1 << 3, @@ -156,7 +166,7 @@ int (*os::Linux::_pthread_setname_np)(pthread_t, const char*) = NULL; pthread_t os::Linux::_main_thread; int os::Linux::_page_size = -1; bool os::Linux::_supports_fast_thread_cpu_time = false; -const char * os::Linux::_glibc_version = NULL; +const char * os::Linux::_libc_version = NULL; const char * os::Linux::_libpthread_version = NULL; size_t os::Linux::_default_large_page_size = 0; @@ -510,17 +520,24 @@ void os::Linux::libpthread_init() { #error "glibc too old (< 2.3.2)" #endif +#ifdef MUSL_LIBC + // confstr() from musl libc returns EINVAL for + // _CS_GNU_LIBC_VERSION and _CS_GNU_LIBPTHREAD_VERSION + os::Linux::set_libc_version("musl - unknown"); + os::Linux::set_libpthread_version("musl - unknown"); +#else size_t n = confstr(_CS_GNU_LIBC_VERSION, NULL, 0); assert(n > 0, "cannot retrieve glibc version"); char *str = (char *)malloc(n, mtInternal); confstr(_CS_GNU_LIBC_VERSION, str, n); - os::Linux::set_glibc_version(str); + os::Linux::set_libc_version(str); n = confstr(_CS_GNU_LIBPTHREAD_VERSION, NULL, 0); assert(n > 0, "cannot retrieve pthread version"); str = (char *)malloc(n, mtInternal); confstr(_CS_GNU_LIBPTHREAD_VERSION, str, n); os::Linux::set_libpthread_version(str); +#endif } ///////////////////////////////////////////////////////////////////////////// @@ -1935,7 +1952,7 @@ void * os::Linux::dll_load_in_vmthread(const char *filename, char *ebuf, StackOverflow* overflow_state = jt->stack_overflow_state(); if (!overflow_state->stack_guard_zone_unused() && // Stack not yet fully initialized overflow_state->stack_guards_enabled()) { // No pending stack overflow exceptions - if (!os::guard_memory((char *)jt->stack_end(), overflow_state->stack_guard_zone_size())) { + if (!os::guard_memory((char *)jt->stack_end(), StackOverflow::stack_guard_zone_size())) { warning("Attempt to reguard stack yellow zone failed."); } } @@ -2211,7 +2228,7 @@ void os::get_summary_os_info(char* buf, size_t buflen) { void os::Linux::print_libversion_info(outputStream* st) { // libc, pthread st->print("libc: "); - st->print("%s ", os::Linux::glibc_version()); + st->print("%s ", os::Linux::libc_version()); st->print("%s ", os::Linux::libpthread_version()); st->cr(); } @@ -3070,6 +3087,8 @@ bool os::Linux::libnuma_init() { if (handle != NULL) { set_numa_node_to_cpus(CAST_TO_FN_PTR(numa_node_to_cpus_func_t, libnuma_dlsym(handle, "numa_node_to_cpus"))); + set_numa_node_to_cpus_v2(CAST_TO_FN_PTR(numa_node_to_cpus_v2_func_t, + libnuma_v2_dlsym(handle, "numa_node_to_cpus"))); set_numa_max_node(CAST_TO_FN_PTR(numa_max_node_func_t, libnuma_dlsym(handle, "numa_max_node"))); set_numa_num_configured_nodes(CAST_TO_FN_PTR(numa_num_configured_nodes_func_t, @@ -3198,7 +3217,17 @@ void os::Linux::rebuild_cpu_to_node_map() { if (cpu_map[j] != 0) { for (size_t k = 0; k < BitsPerCLong; k++) { if (cpu_map[j] & (1UL << k)) { - cpu_to_node()->at_put(j * BitsPerCLong + k, closest_node); + int cpu_index = j * BitsPerCLong + k; + +#ifndef PRODUCT + if (UseDebuggerErgo1 && cpu_index >= (int)cpu_num) { + // Some debuggers limit the processor count without + // intercepting the NUMA APIs. Just fake the values. + cpu_index = 0; + } +#endif + + cpu_to_node()->at_put(cpu_index, closest_node); } } } @@ -3208,6 +3237,26 @@ void os::Linux::rebuild_cpu_to_node_map() { FREE_C_HEAP_ARRAY(unsigned long, cpu_map); } +int os::Linux::numa_node_to_cpus(int node, unsigned long *buffer, int bufferlen) { + // use the latest version of numa_node_to_cpus if available + if (_numa_node_to_cpus_v2 != NULL) { + + // libnuma bitmask struct + struct bitmask { + unsigned long size; /* number of bits in the map */ + unsigned long *maskp; + }; + + struct bitmask mask; + mask.maskp = (unsigned long *)buffer; + mask.size = bufferlen * 8; + return _numa_node_to_cpus_v2(node, &mask); + } else if (_numa_node_to_cpus != NULL) { + return _numa_node_to_cpus(node, buffer, bufferlen); + } + return -1; +} + int os::Linux::get_node_by_cpu(int cpu_id) { if (cpu_to_node() != NULL && cpu_id >= 0 && cpu_id < cpu_to_node()->length()) { return cpu_to_node()->at(cpu_id); @@ -3219,6 +3268,7 @@ GrowableArray* os::Linux::_cpu_to_node; GrowableArray* os::Linux::_nindex_to_node; os::Linux::sched_getcpu_func_t os::Linux::_sched_getcpu; os::Linux::numa_node_to_cpus_func_t os::Linux::_numa_node_to_cpus; +os::Linux::numa_node_to_cpus_v2_func_t os::Linux::_numa_node_to_cpus_v2; os::Linux::numa_max_node_func_t os::Linux::_numa_max_node; os::Linux::numa_num_configured_nodes_func_t os::Linux::_numa_num_configured_nodes; os::Linux::numa_available_func_t os::Linux::_numa_available; @@ -4164,7 +4214,7 @@ bool os::can_execute_large_page_memory() { return UseTransparentHugePages || UseHugeTLBFS; } -char* os::pd_attempt_reserve_memory_at(char* requested_addr, size_t bytes, int file_desc) { +char* os::pd_attempt_map_memory_to_file_at(char* requested_addr, size_t bytes, int file_desc) { assert(file_desc >= 0, "file_desc is not valid"); char* result = pd_attempt_reserve_memory_at(requested_addr, bytes); if (result != NULL) { @@ -4321,6 +4371,40 @@ jlong os::Linux::fast_thread_cpu_time(clockid_t clockid) { extern void report_error(char* file_name, int line_no, char* title, char* format, ...); +// Some linux distributions (notably: Alpine Linux) include the +// grsecurity in the kernel. Of particular interest from a JVM perspective +// is PaX (https://pax.grsecurity.net/), which adds some security features +// related to page attributes. Specifically, the MPROTECT PaX functionality +// (https://pax.grsecurity.net/docs/mprotect.txt) prevents dynamic +// code generation by disallowing a (previously) writable page to be +// marked as executable. This is, of course, exactly what HotSpot does +// for both JIT compiled method, as well as for stubs, adapters, etc. +// +// Instead of crashing "lazily" when trying to make a page executable, +// this code probes for the presence of PaX and reports the failure +// eagerly. +static void check_pax(void) { + // Zero doesn't generate code dynamically, so no need to perform the PaX check +#ifndef ZERO + size_t size = os::Linux::page_size(); + + void* p = ::mmap(NULL, size, PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (p == MAP_FAILED) { + log_debug(os)("os_linux.cpp: check_pax: mmap failed (%s)" , os::strerror(errno)); + vm_exit_out_of_memory(size, OOM_MMAP_ERROR, "failed to allocate memory for PaX check."); + } + + int res = ::mprotect(p, size, PROT_WRITE|PROT_EXEC); + if (res == -1) { + log_debug(os)("os_linux.cpp: check_pax: mprotect failed (%s)" , os::strerror(errno)); + vm_exit_during_initialization( + "Failed to mark memory page as executable - check if grsecurity/PaX is enabled"); + } + + ::munmap(p, size); +#endif +} + // this is called _before_ most of the global arguments have been parsed void os::init(void) { char dummy; // used to get a guess on initial stack address @@ -4354,6 +4438,8 @@ void os::init(void) { Linux::_pthread_setname_np = (int(*)(pthread_t, const char*))dlsym(RTLD_DEFAULT, "pthread_setname_np"); + check_pax(); + os::Posix::init(); initial_time_count = javaTimeNanos(); @@ -4493,7 +4579,7 @@ jint os::init_2(void) { Linux::libpthread_init(); Linux::sched_getcpu_init(); log_info(os)("HotSpot is running with %s, %s", - Linux::glibc_version(), Linux::libpthread_version()); + Linux::libc_version(), Linux::libpthread_version()); if (UseNUMA || UseNUMAInterleaving) { Linux::numa_init(); @@ -4549,6 +4635,12 @@ jint os::init_2(void) { set_coredump_filter(FILE_BACKED_SHARED_BIT); } + if (DumpPerfMapAtExit && FLAG_IS_DEFAULT(UseCodeCacheFlushing)) { + // Disable code cache flushing to ensure the map file written at + // exit contains all nmethods generated during execution. + FLAG_SET_DEFAULT(UseCodeCacheFlushing, false); + } + return JNI_OK; } @@ -4686,7 +4778,16 @@ int os::active_processor_count() { uint os::processor_id() { const int id = Linux::sched_getcpu(); - assert(id >= 0 && id < _processor_count, "Invalid processor id"); + +#ifndef PRODUCT + if (UseDebuggerErgo1 && id >= _processor_count) { + // Some debuggers limit the processor count without limiting + // the returned processor ids. Fake the processor id. + return 0; + } +#endif + + assert(id >= 0 && id < _processor_count, "Invalid processor id [%d]", id); return (uint)id; } diff --git a/src/hotspot/os/linux/os_linux.hpp b/src/hotspot/os/linux/os_linux.hpp index 45d3bbc9d74..26e40d2bdfb 100644 --- a/src/hotspot/os/linux/os_linux.hpp +++ b/src/hotspot/os/linux/os_linux.hpp @@ -42,7 +42,7 @@ class Linux { static address _initial_thread_stack_bottom; static uintptr_t _initial_thread_stack_size; - static const char *_glibc_version; + static const char *_libc_version; static const char *_libpthread_version; static bool _supports_fast_thread_cpu_time; @@ -69,7 +69,7 @@ class Linux { static int commit_memory_impl(char* addr, size_t bytes, size_t alignment_hint, bool exec); - static void set_glibc_version(const char *s) { _glibc_version = s; } + static void set_libc_version(const char *s) { _libc_version = s; } static void set_libpthread_version(const char *s) { _libpthread_version = s; } static void rebuild_cpu_to_node_map(); @@ -139,10 +139,8 @@ class Linux { static intptr_t* ucontext_get_sp(const ucontext_t* uc); static intptr_t* ucontext_get_fp(const ucontext_t* uc); - static bool get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr); - // GNU libc and libpthread version strings - static const char *glibc_version() { return _glibc_version; } + static const char *libc_version() { return _libc_version; } static const char *libpthread_version() { return _libpthread_version; } static void libpthread_init(); @@ -183,6 +181,7 @@ class Linux { typedef int (*sched_getcpu_func_t)(void); typedef int (*numa_node_to_cpus_func_t)(int node, unsigned long *buffer, int bufferlen); + typedef int (*numa_node_to_cpus_v2_func_t)(int node, void *mask); typedef int (*numa_max_node_func_t)(void); typedef int (*numa_num_configured_nodes_func_t)(void); typedef int (*numa_available_func_t)(void); @@ -199,6 +198,7 @@ class Linux { static sched_getcpu_func_t _sched_getcpu; static numa_node_to_cpus_func_t _numa_node_to_cpus; + static numa_node_to_cpus_v2_func_t _numa_node_to_cpus_v2; static numa_max_node_func_t _numa_max_node; static numa_num_configured_nodes_func_t _numa_num_configured_nodes; static numa_available_func_t _numa_available; @@ -220,6 +220,7 @@ class Linux { static void set_sched_getcpu(sched_getcpu_func_t func) { _sched_getcpu = func; } static void set_numa_node_to_cpus(numa_node_to_cpus_func_t func) { _numa_node_to_cpus = func; } + static void set_numa_node_to_cpus_v2(numa_node_to_cpus_v2_func_t func) { _numa_node_to_cpus_v2 = func; } static void set_numa_max_node(numa_max_node_func_t func) { _numa_max_node = func; } static void set_numa_num_configured_nodes(numa_num_configured_nodes_func_t func) { _numa_num_configured_nodes = func; } static void set_numa_available(numa_available_func_t func) { _numa_available = func; } @@ -249,9 +250,7 @@ class Linux { public: static int sched_getcpu() { return _sched_getcpu != NULL ? _sched_getcpu() : -1; } - static int numa_node_to_cpus(int node, unsigned long *buffer, int bufferlen) { - return _numa_node_to_cpus != NULL ? _numa_node_to_cpus(node, buffer, bufferlen) : -1; - } + static int numa_node_to_cpus(int node, unsigned long *buffer, int bufferlen); static int numa_max_node() { return _numa_max_node != NULL ? _numa_max_node() : -1; } static int numa_num_configured_nodes() { return _numa_num_configured_nodes != NULL ? _numa_num_configured_nodes() : -1; diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index b819f4c202f..7298fb21638 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -30,6 +30,7 @@ #include "utilities/globalDefinitions.hpp" #include "runtime/frame.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" +#include "runtime/sharedRuntime.hpp" #include "services/memTracker.hpp" #include "runtime/atomic.hpp" #include "runtime/java.hpp" @@ -296,37 +297,18 @@ char* os::replace_existing_mapping_with_file_mapping(char* base, size_t size, in return map_memory_to_file(base, size, fd); } -// Multiple threads can race in this code, and can remap over each other with MAP_FIXED, -// so on posix, unmap the section at the start and at the end of the chunk that we mapped -// rather than unmapping and remapping the whole chunk to get requested alignment. -char* os::reserve_memory_aligned(size_t size, size_t alignment, int file_desc) { +static size_t calculate_aligned_extra_size(size_t size, size_t alignment) { assert((alignment & (os::vm_allocation_granularity() - 1)) == 0, "Alignment must be a multiple of allocation granularity (page size)"); assert((size & (alignment -1)) == 0, "size must be 'alignment' aligned"); size_t extra_size = size + alignment; assert(extra_size >= size, "overflow, size is too large to allow alignment"); + return extra_size; +} - char* extra_base; - if (file_desc != -1) { - // For file mapping, we do not call os:reserve_memory_with_fd since: - // - we later chop away parts of the mapping using os::release_memory and that could fail if the - // original mmap call had been tied to an fd. - // - The memory API os::reserve_memory uses is an implementation detail. It may (and usually is) - // mmap but it also may System V shared memory which cannot be uncommitted as a whole, so - // chopping off and unmapping excess bits back and front (see below) would not work. - extra_base = reserve_mmapped_memory(extra_size, NULL); - if (extra_base != NULL) { - MemTracker::record_virtual_memory_reserve((address)extra_base, extra_size, CALLER_PC); - } - } else { - extra_base = os::reserve_memory(extra_size); - } - - if (extra_base == NULL) { - return NULL; - } - +// After a bigger chunk was mapped, unmaps start and end parts to get the requested alignment. +static char* chop_extra_memory(size_t size, size_t alignment, char* extra_base, size_t extra_size) { // Do manual alignment char* aligned_base = align_up(extra_base, alignment); @@ -348,13 +330,39 @@ char* os::reserve_memory_aligned(size_t size, size_t alignment, int file_desc) { os::release_memory(extra_base + begin_offset + size, end_offset); } - if (file_desc != -1) { - // After we have an aligned address, we can replace anonymous mapping with file mapping - if (replace_existing_mapping_with_file_mapping(aligned_base, size, file_desc) == NULL) { - vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory")); - } - MemTracker::record_virtual_memory_commit((address)aligned_base, size, CALLER_PC); + return aligned_base; +} + +// Multiple threads can race in this code, and can remap over each other with MAP_FIXED, +// so on posix, unmap the section at the start and at the end of the chunk that we mapped +// rather than unmapping and remapping the whole chunk to get requested alignment. +char* os::reserve_memory_aligned(size_t size, size_t alignment) { + size_t extra_size = calculate_aligned_extra_size(size, alignment); + char* extra_base = os::reserve_memory(extra_size); + if (extra_base == NULL) { + return NULL; } + return chop_extra_memory(size, alignment, extra_base, extra_size); +} + +char* os::map_memory_to_file_aligned(size_t size, size_t alignment, int file_desc) { + size_t extra_size = calculate_aligned_extra_size(size, alignment); + // For file mapping, we do not call os:map_memory_to_file(size,fd) since: + // - we later chop away parts of the mapping using os::release_memory and that could fail if the + // original mmap call had been tied to an fd. + // - The memory API os::reserve_memory uses is an implementation detail. It may (and usually is) + // mmap but it also may System V shared memory which cannot be uncommitted as a whole, so + // chopping off and unmapping excess bits back and front (see below) would not work. + char* extra_base = reserve_mmapped_memory(extra_size, NULL); + if (extra_base == NULL) { + return NULL; + } + char* aligned_base = chop_extra_memory(size, alignment, extra_base, extra_size); + // After we have an aligned address, we can replace anonymous mapping with file mapping + if (replace_existing_mapping_with_file_mapping(aligned_base, size, file_desc) == NULL) { + vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory")); + } + MemTracker::record_virtual_memory_commit((address)aligned_base, size, CALLER_PC); return aligned_base; } @@ -907,6 +915,123 @@ size_t os::Posix::get_initial_stack_size(ThreadType thr_type, size_t req_stack_s return stack_size; } +#ifndef ZERO +#ifndef ARM +static bool get_frame_at_stack_banging_point(JavaThread* thread, address pc, const void* ucVoid, frame* fr) { + if (Interpreter::contains(pc)) { + // interpreter performs stack banging after the fixed frame header has + // been generated while the compilers perform it before. To maintain + // semantic consistency between interpreted and compiled frames, the + // method returns the Java sender of the current frame. + *fr = os::fetch_frame_from_context(ucVoid); + if (!fr->is_first_java_frame()) { + // get_frame_at_stack_banging_point() is only called when we + // have well defined stacks so java_sender() calls do not need + // to assert safe_for_sender() first. + *fr = fr->java_sender(); + } + } else { + // more complex code with compiled code + assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above"); + CodeBlob* cb = CodeCache::find_blob(pc); + if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) { + // Not sure where the pc points to, fallback to default + // stack overflow handling + return false; + } else { + // in compiled code, the stack banging is performed just after the return pc + // has been pushed on the stack + *fr = os::fetch_compiled_frame_from_context(ucVoid); + if (!fr->is_java_frame()) { + assert(!fr->is_first_frame(), "Safety check"); + // See java_sender() comment above. + *fr = fr->java_sender(); + } + } + } + assert(fr->is_java_frame(), "Safety check"); + return true; +} +#endif // ARM + +// This return true if the signal handler should just continue, ie. return after calling this +bool os::Posix::handle_stack_overflow(JavaThread* thread, address addr, address pc, + const void* ucVoid, address* stub) { + // stack overflow + StackOverflow* overflow_state = thread->stack_overflow_state(); + if (overflow_state->in_stack_yellow_reserved_zone(addr)) { + if (thread->thread_state() == _thread_in_Java) { +#ifndef ARM + // arm32 doesn't have this + if (overflow_state->in_stack_reserved_zone(addr)) { + frame fr; + if (get_frame_at_stack_banging_point(thread, pc, ucVoid, &fr)) { + assert(fr.is_java_frame(), "Must be a Java frame"); + frame activation = + SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr); + if (activation.sp() != NULL) { + overflow_state->disable_stack_reserved_zone(); + if (activation.is_interpreted_frame()) { + overflow_state->set_reserved_stack_activation((address)(activation.fp() + // Some platforms use frame pointers for interpreter frames, others use initial sp. +#if !defined(PPC64) && !defined(S390) + + frame::interpreter_frame_initial_sp_offset +#endif + )); + } else { + overflow_state->set_reserved_stack_activation((address)activation.unextended_sp()); + } + return true; // just continue + } + } + } +#endif // ARM + // Throw a stack overflow exception. Guard pages will be reenabled + // while unwinding the stack. + overflow_state->disable_stack_yellow_reserved_zone(); + *stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW); + } else { + // Thread was in the vm or native code. Return and try to finish. + overflow_state->disable_stack_yellow_reserved_zone(); + return true; // just continue + } + } else if (overflow_state->in_stack_red_zone(addr)) { + // Fatal red zone violation. Disable the guard pages and fall through + // to handle_unexpected_exception way down below. + overflow_state->disable_stack_red_zone(); + tty->print_raw_cr("An irrecoverable stack overflow has occurred."); + + // This is a likely cause, but hard to verify. Let's just print + // it as a hint. + tty->print_raw_cr("Please check if any of your loaded .so files has " + "enabled executable stack (see man page execstack(8))"); + + } else { +#if !defined(AIX) && !defined(__APPLE__) + // bsd and aix don't have this + + // Accessing stack address below sp may cause SEGV if current + // thread has MAP_GROWSDOWN stack. This should only happen when + // current thread was created by user code with MAP_GROWSDOWN flag + // and then attached to VM. See notes in os_linux.cpp. + if (thread->osthread()->expanding_stack() == 0) { + thread->osthread()->set_expanding_stack(); + if (os::Linux::manually_expand_stack(thread, addr)) { + thread->osthread()->clear_expanding_stack(); + return true; // just continue + } + thread->osthread()->clear_expanding_stack(); + } else { + fatal("recursive segv. expanding stack."); + } +#else + tty->print_raw_cr("SIGSEGV happened inside stack but outside yellow and red zone."); +#endif // AIX or BSD + } + return false; +} +#endif // ZERO + bool os::Posix::is_root(uid_t uid){ return ROOT_UID == uid; } diff --git a/src/hotspot/os/posix/os_posix.hpp b/src/hotspot/os/posix/os_posix.hpp index 514932f57d3..7a6e7e60479 100644 --- a/src/hotspot/os/posix/os_posix.hpp +++ b/src/hotspot/os/posix/os_posix.hpp @@ -106,6 +106,10 @@ class Posix { #endif static void to_RTC_abstime(timespec* abstime, int64_t millis); + + static bool handle_stack_overflow(JavaThread* thread, address addr, address pc, + const void* ucVoid, + address* stub); }; /* diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp index 4eea0664566..94251da145d 100644 --- a/src/hotspot/os/posix/signals_posix.cpp +++ b/src/hotspot/os/posix/signals_posix.cpp @@ -26,6 +26,7 @@ #include "jvm.h" #include "logging/log.hpp" +#include "runtime/atomic.hpp" #include "runtime/globals.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/os.hpp" @@ -71,10 +72,6 @@ extern "C" { static sigset_t check_signal_done; static bool check_signals = true; -// This boolean allows users to forward their own non-matching signals -// to JVM_handle_bsd_signal/JVM_handle_linux_signal, harmlessly. -static bool signal_handlers_are_installed = false; - debug_only(static bool signal_sets_initialized = false); static sigset_t unblocked_sigs, vm_sigs, preinstalled_sigs; struct sigaction sigact[NSIG]; @@ -261,6 +258,8 @@ static const struct { { -1, NULL } }; +static const char* get_signal_name(int sig, char* out, size_t outlen); + //////////////////////////////////////////////////////////////////////////////// // sun.misc.Signal support @@ -313,6 +312,8 @@ static int check_pending_signals() { } } while (threadIsSuspended); } + ShouldNotReachHere(); + return 0; // Satisfy compiler } int os::signal_wait() { @@ -408,15 +409,76 @@ bool PosixSignals::chained_handler(int sig, siginfo_t* siginfo, void* context) { return chained; } +///// Synchronous (non-deferrable) error signals (ILL, SEGV, FPE, BUS, TRAP): + +// These signals are special because they cannot be deferred and, if they +// happen while delivery is blocked for the receiving thread, will cause UB +// (in practice typically resulting in sudden process deaths or hangs, see +// JDK-8252533). So we must take care never to block them when we cannot be +// absolutely sure they won't happen. In practice, this is always. +// +// Relevant Posix quote: +// "The behavior of a process is undefined after it ignores a SIGFPE, SIGILL, +// SIGSEGV, or SIGBUS signal that was not generated by kill(), sigqueue(), or +// raise()." +// +// We also include SIGTRAP in that list of never-to-block-signals. While not +// mentioned by the Posix documentation, in our (SAPs) experience blocking it +// causes similar problems. Beside, during normal operation - outside of error +// handling - SIGTRAP may be used for implicit NULL checking, so it makes sense +// to never block it. +// +// We deal with those signals in two ways: +// - we just never explicitly block them, which includes not accidentally blocking +// them via sa_mask when establishing signal handlers. +// - as an additional safety measure, at the entrance of a signal handler, we +// unblock them explicitly. + +static void add_error_signals_to_set(sigset_t* set) { + sigaddset(set, SIGILL); + sigaddset(set, SIGBUS); + sigaddset(set, SIGFPE); + sigaddset(set, SIGSEGV); + sigaddset(set, SIGTRAP); +} + +static void remove_error_signals_from_set(sigset_t* set) { + sigdelset(set, SIGILL); + sigdelset(set, SIGBUS); + sigdelset(set, SIGFPE); + sigdelset(set, SIGSEGV); + sigdelset(set, SIGTRAP); +} + +// Unblock all signals whose delivery cannot be deferred and which, if they happen +// while delivery is blocked, would cause crashes or hangs (JDK-8252533). +void PosixSignals::unblock_error_signals() { + sigset_t set; + sigemptyset(&set); + add_error_signals_to_set(&set); + ::pthread_sigmask(SIG_UNBLOCK, &set, NULL); +} + +class ErrnoPreserver: public StackObj { + const int _saved; +public: + ErrnoPreserver() : _saved(errno) {} + ~ErrnoPreserver() { errno = _saved; } +}; + //////////////////////////////////////////////////////////////////////////////// -// signal handling (except suspend/resume) +// JVM_handle_(linux|aix|bsd)_signal() -// This routine may be used by user applications as a "hook" to catch signals. +// This routine is the shared part of the central hotspot signal handler. It can +// also be called by a user application, if a user application prefers to do +// signal handling itself - in that case it needs to pass signals the VM +// internally uses on to the VM first. +// // The user-defined signal handler must pass unrecognized signals to this // routine, and if it returns true (non-zero), then the signal handler must // return immediately. If the flag "abort_if_unrecognized" is true, then this -// routine will never retun false (zero), but instead will execute a VM panic -// routine kill the process. +// routine will never return false (zero), but instead will execute a VM panic +// routine to kill the process. // // If this routine returns false, it is OK to call it again. This allows // the user-defined signal handler to perform checks either before or after @@ -438,72 +500,116 @@ bool PosixSignals::chained_handler(int sig, siginfo_t* siginfo, void* context) { // #if defined(BSD) -extern "C" JNIEXPORT int JVM_handle_bsd_signal(int signo, siginfo_t* siginfo, - void* ucontext, - int abort_if_unrecognized); +#define JVM_HANDLE_XXX_SIGNAL JVM_handle_bsd_signal #elif defined(AIX) -extern "C" JNIEXPORT int JVM_handle_aix_signal(int signo, siginfo_t* siginfo, - void* ucontext, - int abort_if_unrecognized); +#define JVM_HANDLE_XXX_SIGNAL JVM_handle_aix_signal +#elif defined(LINUX) +#define JVM_HANDLE_XXX_SIGNAL JVM_handle_linux_signal #else -extern "C" JNIEXPORT int JVM_handle_linux_signal(int signo, siginfo_t* siginfo, - void* ucontext, - int abort_if_unrecognized); +#error who are you? #endif -#if defined(AIX) +extern "C" JNIEXPORT +int JVM_HANDLE_XXX_SIGNAL(int sig, siginfo_t* info, + void* ucVoid, int abort_if_unrecognized) +{ + assert(info != NULL && ucVoid != NULL, "sanity"); -// Set thread signal mask (for some reason on AIX sigthreadmask() seems -// to be the thing to call; documentation is not terribly clear about whether -// pthread_sigmask also works, and if it does, whether it does the same. -bool set_thread_signal_mask(int how, const sigset_t* set, sigset_t* oset) { - const int rc = ::pthread_sigmask(how, set, oset); - // return value semantics differ slightly for error case: - // pthread_sigmask returns error number, sigthreadmask -1 and sets global errno - // (so, pthread_sigmask is more theadsafe for error handling) - // But success is always 0. - return rc == 0 ? true : false; -} + // Note: it's not uncommon that JNI code uses signal/sigset to install, + // then restore certain signal handler (e.g. to temporarily block SIGPIPE, + // or have a SIGILL handler when detecting CPU type). When that happens, + // this handler might be invoked with junk info/ucVoid. To avoid unnecessary + // crash when libjsig is not preloaded, try handle signals that do not require + // siginfo/ucontext first. -// Function to unblock all signals which are, according -// to POSIX, typical program error signals. If they happen while being blocked, -// they typically will bring down the process immediately. -bool unblock_program_error_signals() { - sigset_t set; - sigemptyset(&set); - sigaddset(&set, SIGILL); - sigaddset(&set, SIGBUS); - sigaddset(&set, SIGFPE); - sigaddset(&set, SIGSEGV); - return set_thread_signal_mask(SIG_UNBLOCK, &set, NULL); -} + // Preserve errno value over signal handler. + // (note: RAII ok here, even with JFR thread crash protection, see below). + ErrnoPreserver ep; -#endif + // Unblock all synchronous error signals (see JDK-8252533) + PosixSignals::unblock_error_signals(); -// Renamed from 'signalHandler' to avoid collision with other shared libs. -static void javaSignalHandler(int sig, siginfo_t* info, void* uc) { - assert(info != NULL && uc != NULL, "it must be old kernel"); + ucontext_t* const uc = (ucontext_t*) ucVoid; + Thread* const t = Thread::current_or_null_safe(); -// TODO: reconcile the differences between Linux/BSD vs AIX here! -#if defined(AIX) - // Never leave program error signals blocked; - // on all our platforms they would bring down the process immediately when - // getting raised while being blocked. - unblock_program_error_signals(); + // Handle JFR thread crash protection. + // Note: this may cause us to longjmp away. Do not use any code before this + // point which really needs any form of epilogue code running, eg RAII objects. + os::ThreadCrashProtection::check_crash_protection(sig, t); + + bool signal_was_handled = false; + + // Handle assertion poison page accesses. +#ifdef CAN_SHOW_REGISTERS_ON_ASSERT + if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) { + signal_was_handled = handle_assert_poison_fault(ucVoid, info->si_addr); + } #endif - int orig_errno = errno; // Preserve errno value over signal handler. -#if defined(BSD) - JVM_handle_bsd_signal(sig, info, uc, true); -#elif defined(AIX) - JVM_handle_aix_signal(sig, info, uc, true); + // Ignore SIGPIPE and SIGXFSZ (4229104, 6499219). + if (sig == SIGPIPE || sig == SIGXFSZ) { + PosixSignals::chained_handler(sig, info, ucVoid); + signal_was_handled = true; // unconditionally. + } + + // Call platform dependent signal handler. + if (!signal_was_handled) { + JavaThread* const jt = (t != NULL && t->is_Java_thread()) ? (JavaThread*) t : NULL; + signal_was_handled = PosixSignals::pd_hotspot_signal_handler(sig, info, uc, jt); + } + + // From here on, if the signal had not been handled, it is a fatal error. + + // Give the chained signal handler - should it exist - a shot. + if (!signal_was_handled) { + signal_was_handled = PosixSignals::chained_handler(sig, info, ucVoid); + } + + // Invoke fatal error handling. + if (!signal_was_handled && abort_if_unrecognized) { + // Extract pc from context for the error handler to display. + address pc = NULL; + if (uc != NULL) { + // prepare fault pc address for error reporting. + if (S390_ONLY(sig == SIGILL || sig == SIGFPE) NOT_S390(false)) { + pc = (address)info->si_addr; + } else { + pc = PosixSignals::ucontext_get_pc(uc); + } + } +#if defined(ZERO) && !defined(PRODUCT) + char buf[64]; + VMError::report_and_die(t, sig, pc, info, ucVoid, + "\n#" + "\n# /--------------------\\" + "\n# | %-7s |" + "\n# \\---\\ /--------------/" + "\n# /" + "\n# [-] |\\_/| " + "\n# (+)=C |o o|__ " + "\n# | | =-*-=__\\ " + "\n# OOO c_c_(___)", + get_signal_name(sig, buf, sizeof(buf))); #else - JVM_handle_linux_signal(sig, info, uc, true); + VMError::report_and_die(t, sig, pc, info, ucVoid); #endif - errno = orig_errno; + // VMError should not return. + ShouldNotReachHere(); + } + return signal_was_handled; +} + +// Entry point for the hotspot signal handler. +static void javaSignalHandler(int sig, siginfo_t* info, void* ucVoid) { + // Do not add any code here! + // Only add code to either JVM_HANDLE_XXX_SIGNAL or PosixSignals::pd_hotspot_signal_handler. + (void)JVM_HANDLE_XXX_SIGNAL(sig, info, ucVoid, true); } static void UserHandler(int sig, void *siginfo, void *context) { + + PosixSignals::unblock_error_signals(); + // Ctrl-C is pressed during error reporting, likely because the error // handler fails to abort. Let VM die immediately. if (sig == SIGINT && VMError::is_error_reported()) { @@ -702,23 +808,7 @@ void* os::signal(int signal_number, void* handler) { struct sigaction sigAct, oldSigAct; sigfillset(&(sigAct.sa_mask)); - -#if defined(AIX) - // Do not block out synchronous signals in the signal handler. - // Blocking synchronous signals only makes sense if you can really - // be sure that those signals won't happen during signal handling, - // when the blocking applies. Normal signal handlers are lean and - // do not cause signals. But our signal handlers tend to be "risky" - // - secondary SIGSEGV, SIGILL, SIGBUS' may and do happen. - // On AIX, PASE there was a case where a SIGSEGV happened, followed - // by a SIGILL, which was blocked due to the signal mask. The process - // just hung forever. Better to crash from a secondary signal than to hang. - sigdelset(&(sigAct.sa_mask), SIGSEGV); - sigdelset(&(sigAct.sa_mask), SIGBUS); - sigdelset(&(sigAct.sa_mask), SIGILL); - sigdelset(&(sigAct.sa_mask), SIGFPE); - sigdelset(&(sigAct.sa_mask), SIGTRAP); -#endif + remove_error_signals_from_set(&(sigAct.sa_mask)); sigAct.sa_flags = SA_RESTART|SA_SIGINFO; sigAct.sa_handler = CAST_TO_FN_PTR(sa_handler_t, handler); @@ -763,9 +853,7 @@ void os::run_periodic_checks() { do_signal_check(SIGBUS); do_signal_check(SIGPIPE); do_signal_check(SIGXFSZ); -#if defined(PPC64) - do_signal_check(SIGTRAP); -#endif + PPC64_ONLY(do_signal_check(SIGTRAP);) // ReduceSignalUsage allows the user to override these handlers // see comments at the very top and jvm_md.h @@ -932,7 +1020,6 @@ static bool is_valid_signal(int sig) { #endif } -// Returned string is a constant. For unknown signals "UNKNOWN" is returned. static const char* get_signal_name(int sig, char* out, size_t outlen) { const char* ret = NULL; @@ -1072,7 +1159,7 @@ int os::get_signal_number(const char* signal_name) { return -1; } -void set_signal_handler(int sig, bool set_installed) { +void set_signal_handler(int sig) { // Check for overwrite. struct sigaction oldAct; sigaction(sig, (struct sigaction*)NULL, &oldAct); @@ -1083,7 +1170,7 @@ void set_signal_handler(int sig, bool set_installed) { if (oldhand != CAST_FROM_FN_PTR(void*, SIG_DFL) && oldhand != CAST_FROM_FN_PTR(void*, SIG_IGN) && oldhand != CAST_FROM_FN_PTR(void*, (sa_sigaction_t)javaSignalHandler)) { - if (AllowUserSignalHandlers || !set_installed) { + if (AllowUserSignalHandlers) { // Do not overwrite; user takes responsibility to forward to us. return; } else if (UseSignalChaining) { @@ -1099,13 +1186,9 @@ void set_signal_handler(int sig, bool set_installed) { struct sigaction sigAct; sigfillset(&(sigAct.sa_mask)); - sigAct.sa_handler = SIG_DFL; - if (!set_installed) { - sigAct.sa_flags = SA_SIGINFO|SA_RESTART; - } else { - sigAct.sa_sigaction = javaSignalHandler; - sigAct.sa_flags = SA_SIGINFO|SA_RESTART; - } + remove_error_signals_from_set(&(sigAct.sa_mask)); + sigAct.sa_sigaction = javaSignalHandler; + sigAct.sa_flags = SA_SIGINFO|SA_RESTART; #if defined(__APPLE__) // Needed for main thread as XNU (Mac OS X kernel) will only deliver SIGSEGV // (which starts as SIGBUS) on main thread with faulting address inside "stack+guard pages" @@ -1132,87 +1215,75 @@ void set_signal_handler(int sig, bool set_installed) { assert(oldhand2 == oldhand, "no concurrent signal handler installation"); } -// install signal handlers for signals that HotSpot needs to -// handle in order to support Java-level exception handling. - -bool PosixSignals::are_signal_handlers_installed() { - return signal_handlers_are_installed; -} - // install signal handlers for signals that HotSpot needs to // handle in order to support Java-level exception handling. void PosixSignals::install_signal_handlers() { - if (!signal_handlers_are_installed) { - signal_handlers_are_installed = true; - - // signal-chaining - typedef void (*signal_setting_t)(); - signal_setting_t begin_signal_setting = NULL; - signal_setting_t end_signal_setting = NULL; - begin_signal_setting = CAST_TO_FN_PTR(signal_setting_t, - dlsym(RTLD_DEFAULT, "JVM_begin_signal_setting")); - if (begin_signal_setting != NULL) { - end_signal_setting = CAST_TO_FN_PTR(signal_setting_t, - dlsym(RTLD_DEFAULT, "JVM_end_signal_setting")); - get_signal_action = CAST_TO_FN_PTR(get_signal_t, - dlsym(RTLD_DEFAULT, "JVM_get_signal_action")); - libjsig_is_loaded = true; - assert(UseSignalChaining, "should enable signal-chaining"); - } - if (libjsig_is_loaded) { - // Tell libjsig jvm is setting signal handlers - (*begin_signal_setting)(); - } - set_signal_handler(SIGSEGV, true); - set_signal_handler(SIGPIPE, true); - set_signal_handler(SIGBUS, true); - set_signal_handler(SIGILL, true); - set_signal_handler(SIGFPE, true); -#if defined(PPC64) || defined(AIX) - set_signal_handler(SIGTRAP, true); -#endif - set_signal_handler(SIGXFSZ, true); + // signal-chaining + typedef void (*signal_setting_t)(); + signal_setting_t begin_signal_setting = NULL; + signal_setting_t end_signal_setting = NULL; + begin_signal_setting = CAST_TO_FN_PTR(signal_setting_t, + dlsym(RTLD_DEFAULT, "JVM_begin_signal_setting")); + if (begin_signal_setting != NULL) { + end_signal_setting = CAST_TO_FN_PTR(signal_setting_t, + dlsym(RTLD_DEFAULT, "JVM_end_signal_setting")); + get_signal_action = CAST_TO_FN_PTR(get_signal_t, + dlsym(RTLD_DEFAULT, "JVM_get_signal_action")); + libjsig_is_loaded = true; + assert(UseSignalChaining, "should enable signal-chaining"); + } + if (libjsig_is_loaded) { + // Tell libjsig jvm is setting signal handlers + (*begin_signal_setting)(); + } + + set_signal_handler(SIGSEGV); + set_signal_handler(SIGPIPE); + set_signal_handler(SIGBUS); + set_signal_handler(SIGILL); + set_signal_handler(SIGFPE); + PPC64_ONLY(set_signal_handler(SIGTRAP);) + set_signal_handler(SIGXFSZ); #if defined(__APPLE__) - // In Mac OS X 10.4, CrashReporter will write a crash log for all 'fatal' signals, including - // signals caught and handled by the JVM. To work around this, we reset the mach task - // signal handler that's placed on our process by CrashReporter. This disables - // CrashReporter-based reporting. - // - // This work-around is not necessary for 10.5+, as CrashReporter no longer intercedes - // on caught fatal signals. - // - // Additionally, gdb installs both standard BSD signal handlers, and mach exception - // handlers. By replacing the existing task exception handler, we disable gdb's mach - // exception handling, while leaving the standard BSD signal handlers functional. - kern_return_t kr; - kr = task_set_exception_ports(mach_task_self(), - EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC, - MACH_PORT_NULL, - EXCEPTION_STATE_IDENTITY, - MACHINE_THREAD_STATE); - - assert(kr == KERN_SUCCESS, "could not set mach task signal handler"); + // In Mac OS X 10.4, CrashReporter will write a crash log for all 'fatal' signals, including + // signals caught and handled by the JVM. To work around this, we reset the mach task + // signal handler that's placed on our process by CrashReporter. This disables + // CrashReporter-based reporting. + // + // This work-around is not necessary for 10.5+, as CrashReporter no longer intercedes + // on caught fatal signals. + // + // Additionally, gdb installs both standard BSD signal handlers, and mach exception + // handlers. By replacing the existing task exception handler, we disable gdb's mach + // exception handling, while leaving the standard BSD signal handlers functional. + kern_return_t kr; + kr = task_set_exception_ports(mach_task_self(), + EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC, + MACH_PORT_NULL, + EXCEPTION_STATE_IDENTITY, + MACHINE_THREAD_STATE); + + assert(kr == KERN_SUCCESS, "could not set mach task signal handler"); #endif + if (libjsig_is_loaded) { + // Tell libjsig jvm finishes setting signal handlers + (*end_signal_setting)(); + } + + // We don't activate signal checker if libjsig is in place, we trust ourselves + // and if UserSignalHandler is installed all bets are off. + // Log that signal checking is off only if -verbose:jni is specified. + if (CheckJNICalls) { if (libjsig_is_loaded) { - // Tell libjsig jvm finishes setting signal handlers - (*end_signal_setting)(); + log_debug(jni, resolve)("Info: libjsig is activated, all active signal checking is disabled"); + check_signals = false; } - - // We don't activate signal checker if libjsig is in place, we trust ourselves - // and if UserSignalHandler is installed all bets are off. - // Log that signal checking is off only if -verbose:jni is specified. - if (CheckJNICalls) { - if (libjsig_is_loaded) { - log_debug(jni, resolve)("Info: libjsig is activated, all active signal checking is disabled"); - check_signals = false; - } - if (AllowUserSignalHandlers) { - log_debug(jni, resolve)("Info: AllowUserSignalHandlers is activated, all active signal checking is disabled"); - check_signals = false; - } + if (AllowUserSignalHandlers) { + log_debug(jni, resolve)("Info: AllowUserSignalHandlers is activated, all active signal checking is disabled"); + check_signals = false; } } } @@ -1303,10 +1374,6 @@ bool PosixSignals::is_sig_ignored(int sig) { } } -int PosixSignals::unblock_thread_signal_mask(const sigset_t *set) { - return pthread_sigmask(SIG_UNBLOCK, set, NULL); -} - address PosixSignals::ucontext_get_pc(const ucontext_t* ctx) { #if defined(AIX) return os::Aix::ucontext_get_pc(ctx); @@ -1354,9 +1421,7 @@ void PosixSignals::signal_sets_init() { sigaddset(&unblocked_sigs, SIGSEGV); sigaddset(&unblocked_sigs, SIGBUS); sigaddset(&unblocked_sigs, SIGFPE); - #if defined(PPC64) || defined(AIX) - sigaddset(&unblocked_sigs, SIGTRAP); - #endif + PPC64_ONLY(sigaddset(&unblocked_sigs, SIGTRAP);) sigaddset(&unblocked_sigs, SR_signum); if (!ReduceSignalUsage) { @@ -1470,10 +1535,13 @@ static void suspend_save_context(OSThread *osthread, siginfo_t* siginfo, ucontex // Currently only ever called on the VMThread and JavaThreads (PC sampling) // static void SR_handler(int sig, siginfo_t* siginfo, ucontext_t* context) { + // Save and restore errno to avoid confusing native code with EINTR // after sigsuspend. int old_errno = errno; + PosixSignals::unblock_error_signals(); + Thread* thread = Thread::current_or_null_safe(); assert(thread != NULL, "Missing current thread in SR_handler"); @@ -1567,6 +1635,7 @@ int PosixSignals::SR_initialize() { // SR_signum is blocked by default. pthread_sigmask(SIG_BLOCK, NULL, &act.sa_mask); + remove_error_signals_from_set(&(act.sa_mask)); if (sigaction(SR_signum, &act, 0) == -1) { return -1; diff --git a/src/hotspot/os/posix/signals_posix.hpp b/src/hotspot/os/posix/signals_posix.hpp index a2f7d955e14..7f0f1b55884 100644 --- a/src/hotspot/os/posix/signals_posix.hpp +++ b/src/hotspot/os/posix/signals_posix.hpp @@ -38,14 +38,16 @@ class PosixSignals : public AllStatic { public: - static bool are_signal_handlers_installed(); + // The platform dependent parts of the central hotspot signal handler. + // Returns true if the signal had been recognized and handled, false if not. If true, caller should + // return from signal handling. + static bool pd_hotspot_signal_handler(int sig, siginfo_t* info, ucontext_t* uc, JavaThread* thread); + static void install_signal_handlers(); static bool is_sig_ignored(int sig); static void signal_sets_init(); - // unblocks the signal masks for current thread - static int unblock_thread_signal_mask(const sigset_t *set); static void hotspot_sigmask(Thread* thread); static void print_signal_handler(outputStream* st, int sig, char* buf, size_t buflen); @@ -64,6 +66,11 @@ class PosixSignals : public AllStatic { // sun.misc.Signal support static void jdk_misc_signal_init(); + + // Unblock all signals whose delivery cannot be deferred and which, if they happen + // while delivery is blocked, would cause crashes or hangs (see JDK-8252533). + static void unblock_error_signals(); + }; #endif // OS_POSIX_SIGNALS_POSIX_HPP diff --git a/src/hotspot/os/posix/vmError_posix.cpp b/src/hotspot/os/posix/vmError_posix.cpp index 9c83d263e71..bde46e28741 100644 --- a/src/hotspot/os/posix/vmError_posix.cpp +++ b/src/hotspot/os/posix/vmError_posix.cpp @@ -101,15 +101,8 @@ address VMError::get_resetted_sighandler(int sig) { } static void crash_handler(int sig, siginfo_t* info, void* ucVoid) { - // unmask current signal - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - // also unmask other synchronous signals - for (int i = 0; i < NUM_SIGNALS; i++) { - sigaddset(&newset, SIGNALS[i]); - } - PosixSignals::unblock_thread_signal_mask(&newset); + + PosixSignals::unblock_error_signals(); // support safefetch faults in error handling ucontext_t* const uc = (ucontext_t*) ucVoid; @@ -139,16 +132,10 @@ static void crash_handler(int sig, siginfo_t* info, void* ucVoid) { } void VMError::reset_signal_handlers() { - // install signal handlers for all synchronous program error signals - sigset_t newset; - sigemptyset(&newset); - for (int i = 0; i < NUM_SIGNALS; i++) { save_signal(i, SIGNALS[i]); os::signal(SIGNALS[i], CAST_FROM_FN_PTR(void *, crash_handler)); - sigaddset(&newset, SIGNALS[i]); } - PosixSignals::unblock_thread_signal_mask(&newset); } // Write a hint to the stream in case siginfo relates to a segv/bus error diff --git a/src/hotspot/os/windows/globals_windows.hpp b/src/hotspot/os/windows/globals_windows.hpp index a712e102a76..61157041f88 100644 --- a/src/hotspot/os/windows/globals_windows.hpp +++ b/src/hotspot/os/windows/globals_windows.hpp @@ -28,24 +28,25 @@ // // Declare Windows specific flags. They are not available on other platforms. // -#define RUNTIME_OS_FLAGS(develop, \ - develop_pd, \ - product, \ - product_pd, \ - notproduct, \ - range, \ - constraint) +#define RUNTIME_OS_FLAGS(develop, \ + develop_pd, \ + product, \ + product_pd, \ + notproduct, \ + range, \ + constraint) \ + \ +product(bool, UseOSErrorReporting, false, \ + "Let VM fatal error propagate to the OS (ie. WER on Windows)") // end of RUNTIME_OS_FLAGS - // // Defines Windows-specific default values. The flags are available on all // platforms, but they may have different default values on other platforms. // define_pd_global(bool, UseLargePages, false); define_pd_global(bool, UseLargePagesIndividualAllocation, true); -define_pd_global(bool, UseOSErrorReporting, false); // for now. define_pd_global(bool, UseThreadPriorities, true) ; #endif // OS_WINDOWS_GLOBALS_WINDOWS_HPP diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 0416605e309..3b829cddac7 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -2144,6 +2144,8 @@ static int check_pending_signals() { } } while (threadIsSuspended); } + ShouldNotReachHere(); + return 0; // Satisfy compiler } int os::signal_wait() { @@ -2354,7 +2356,7 @@ static inline void report_error(Thread* t, DWORD exception_code, address addr, void* siginfo, void* context) { VMError::report_and_die(t, exception_code, addr, siginfo, context); - // If UseOsErrorReporting, this will return here and save the error file + // If UseOSErrorReporting, this will return here and save the error file // somewhere where we can find it in the minidump. } @@ -3137,7 +3139,7 @@ void os::split_reserved_memory(char *base, size_t size, size_t split) { // Multiple threads can race in this code but it's not possible to unmap small sections of // virtual space to get requested alignment, like posix-like os's. // Windows prevents multiple thread from remapping over each other so this loop is thread-safe. -char* os::reserve_memory_aligned(size_t size, size_t alignment, int file_desc) { +static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int file_desc) { assert((alignment & (os::vm_allocation_granularity() - 1)) == 0, "Alignment must be a multiple of allocation granularity (page size)"); assert((size & (alignment -1)) == 0, "size must be 'alignment' aligned"); @@ -3148,7 +3150,9 @@ char* os::reserve_memory_aligned(size_t size, size_t alignment, int file_desc) { char* aligned_base = NULL; do { - char* extra_base = os::reserve_memory_with_fd(extra_size, file_desc); + char* extra_base = file_desc != -1 ? + os::map_memory_to_file(extra_size, file_desc) : + os::reserve_memory(extra_size); if (extra_base == NULL) { return NULL; } @@ -3161,13 +3165,23 @@ char* os::reserve_memory_aligned(size_t size, size_t alignment, int file_desc) { os::release_memory(extra_base, extra_size); } - aligned_base = os::attempt_reserve_memory_at(aligned_base, size, file_desc); + aligned_base = file_desc != -1 ? + os::attempt_map_memory_to_file_at(aligned_base, size, file_desc) : + os::attempt_reserve_memory_at(aligned_base, size); } while (aligned_base == NULL); return aligned_base; } +char* os::reserve_memory_aligned(size_t size, size_t alignment) { + return map_or_reserve_memory_aligned(size, alignment, -1 /* file_desc */); +} + +char* os::map_memory_to_file_aligned(size_t size, size_t alignment, int fd) { + return map_or_reserve_memory_aligned(size, alignment, fd); +} + char* os::pd_reserve_memory(size_t bytes) { return pd_attempt_reserve_memory_at(NULL /* addr */, bytes); } @@ -3205,7 +3219,7 @@ char* os::pd_attempt_reserve_memory_at(char* addr, size_t bytes) { return res; } -char* os::pd_attempt_reserve_memory_at(char* requested_addr, size_t bytes, int file_desc) { +char* os::pd_attempt_map_memory_to_file_at(char* requested_addr, size_t bytes, int file_desc) { assert(file_desc >= 0, "file_desc is not valid"); return map_memory_to_file(requested_addr, bytes, file_desc); } diff --git a/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp b/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp index 94e515932f2..f214eee454a 100644 --- a/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp +++ b/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp @@ -142,40 +142,11 @@ frame os::fetch_frame_from_context(const void* ucVoid) { return fr; } -bool os::Aix::get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr) { - address pc = (address) os::Aix::ucontext_get_pc(uc); - if (Interpreter::contains(pc)) { - // Interpreter performs stack banging after the fixed frame header has - // been generated while the compilers perform it before. To maintain - // semantic consistency between interpreted and compiled frames, the - // method returns the Java sender of the current frame. - *fr = os::fetch_frame_from_context(uc); - if (!fr->is_first_java_frame()) { - assert(fr->safe_for_sender(thread), "Safety check"); - *fr = fr->java_sender(); - } - } else { - // More complex code with compiled code. - assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above"); - CodeBlob* cb = CodeCache::find_blob(pc); - if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) { - // Not sure where the pc points to, fallback to default - // stack overflow handling. In compiled code, we bang before - // the frame is complete. - return false; - } else { - intptr_t* sp = os::Aix::ucontext_get_sp(uc); - address lr = ucontext_get_lr(uc); - *fr = frame(sp, lr); - if (!fr->is_java_frame()) { - assert(fr->safe_for_sender(thread), "Safety check"); - assert(!fr->is_first_frame(), "Safety check"); - *fr = fr->java_sender(); - } - } - } - assert(fr->is_java_frame(), "Safety check"); - return true; +frame os::fetch_compiled_frame_from_context(const void* ucVoid) { + const ucontext_t* uc = (const ucontext_t*)ucVoid; + intptr_t* sp = os::Aix::ucontext_get_sp(uc); + address lr = ucontext_get_lr(uc); + return frame(sp, lr); } frame os::get_sender_for_C_frame(frame* fr) { @@ -197,45 +168,8 @@ frame os::current_frame() { return os::get_sender_for_C_frame(&tmp); } -// Utility functions - -extern "C" JNIEXPORT int -JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrecognized) { - - ucontext_t* uc = (ucontext_t*) ucVoid; - - Thread* t = Thread::current_or_null_safe(); - - SignalHandlerMark shm(t); - - // Note: it's not uncommon that JNI code uses signal/sigset to install - // then restore certain signal handler (e.g. to temporarily block SIGPIPE, - // or have a SIGILL handler when detecting CPU type). When that happens, - // JVM_handle_aix_signal() might be invoked with junk info/ucVoid. To - // avoid unnecessary crash when libjsig is not preloaded, try handle signals - // that do not require siginfo/ucontext first. - - if (sig == SIGPIPE) { - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return 1; - } else { - // Ignoring SIGPIPE - see bugs 4229104 - return 1; - } - } - - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL) { - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } - else if(t->is_VM_thread()) { - vmthread = (VMThread *)t; - } - } - } +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { // Decide if this trap can be handled by a stub. address stub = NULL; @@ -257,8 +191,8 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec } } - if (info == NULL || uc == NULL || thread == NULL && vmthread == NULL) { - goto run_chained_handler; + if (info == NULL || uc == NULL) { + return false; // Fatal error } // If we are a java thread... @@ -267,56 +201,13 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec // Handle ALL stack overflow variations here if (sig == SIGSEGV && thread->is_in_full_stack(addr)) { // stack overflow - StackOverflow* overflow_state = thread->stack_overflow_state(); - - // - // If we are in a yellow zone and we are inside java, we disable the yellow zone and - // throw a stack overflow exception. - // If we are in native code or VM C code, we report-and-die. The original coding tried - // to continue with yellow zone disabled, but that doesn't buy us much and prevents - // hs_err_pid files. - if (overflow_state->in_stack_yellow_reserved_zone(addr)) { - if (thread->thread_state() == _thread_in_Java) { - if (overflow_state->in_stack_reserved_zone(addr)) { - frame fr; - if (os::Aix::get_frame_at_stack_banging_point(thread, uc, &fr)) { - assert(fr.is_java_frame(), "Must be a Javac frame"); - frame activation = - SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr); - if (activation.sp() != NULL) { - overflow_state->disable_stack_reserved_zone(); - if (activation.is_interpreted_frame()) { - overflow_state->set_reserved_stack_activation((address)activation.fp()); - } else { - overflow_state->set_reserved_stack_activation((address)activation.unextended_sp()); - } - return 1; - } - } - } - // Throw a stack overflow exception. - // Guard pages will be reenabled while unwinding the stack. - overflow_state->disable_stack_yellow_reserved_zone(); - stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW); - goto run_stub; - } else { - // Thread was in the vm or native code. Return and try to finish. - overflow_state->disable_stack_yellow_reserved_zone(); - return 1; - } - } else if (overflow_state->in_stack_red_zone(addr)) { - // Fatal red zone violation. Disable the guard pages and fall through - // to handle_unexpected_exception way down below. - overflow_state->disable_stack_red_zone(); - tty->print_raw_cr("An irrecoverable stack overflow has occurred."); - goto report_and_die; + if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) { + return true; // continue + } else if (stub != NULL) { + goto run_stub; } else { - // This means a segv happened inside our stack, but not in - // the guarded zone. I'd like to know when this happens, - tty->print_raw_cr("SIGSEGV happened inside stack but outside yellow and red zone."); - goto report_and_die; + return false; // Fatal error } - } // end handle SIGSEGV inside stack boundaries if (thread->thread_state() == _thread_in_Java) { @@ -355,17 +246,6 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec // happens rarely. In heap based and disjoint base compressd oop modes also loads // are used for null checks. - // A VM-related SIGILL may only occur if we are not in the zero page. - // On AIX, we get a SIGILL if we jump to 0x0 or to somewhere else - // in the zero page, because it is filled with 0x0. We ignore - // explicit SIGILLs in the zero page. - if (sig == SIGILL && (pc < (address) 0x200)) { - if (TraceTraps) { - tty->print_raw_cr("SIGILL happened inside zero page."); - } - goto report_and_die; - } - int stop_type = -1; // Handle signal from NativeJump::patch_verified_entry(). if (sig == SIGILL && nativeInstruction_at(pc)->is_sigill_zombie_not_entrant()) { @@ -458,10 +338,7 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec tty->print_cr("trap: %s: %s (SIGTRAP, stop type %d)", msg, detail_msg, stop_type); } - va_list detail_args; - VMError::report_and_die(INTERNAL_ERROR, msg, detail_msg, detail_args, thread, - pc, info, ucVoid, NULL, 0, 0); - va_end(detail_args); + return false; // Fatal error } else if (sig == SIGBUS) { @@ -477,7 +354,7 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec } next_pc = SharedRuntime::handle_unsafe_access(thread, next_pc); os::Aix::ucontext_set_pc(uc, next_pc); - return 1; + return true; } } } @@ -502,7 +379,7 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec } next_pc = SharedRuntime::handle_unsafe_access(thread, next_pc); os::Aix::ucontext_set_pc(uc, next_pc); - return 1; + return true; } } @@ -524,32 +401,10 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec // Save all thread context in case we need to restore it. if (thread != NULL) thread->set_saved_exception_pc(pc); os::Aix::ucontext_set_pc(uc, stub); - return 1; + return true; } -run_chained_handler: - - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return 1; - } - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return 0; - } - -report_and_die: - - // Use sigthreadmask instead of sigprocmask on AIX and unmask current signal. - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - sigthreadmask(SIG_UNBLOCK, &newset, NULL); - - VMError::report_and_die(t, sig, pc, info, ucVoid); - - ShouldNotReachHere(); - return 0; + return false; // Fatal error } void os::Aix::init_thread_fpu_state(void) { diff --git a/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp b/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp index c6c72b9c92d..0b1a2424d2b 100644 --- a/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp +++ b/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp @@ -339,41 +339,12 @@ frame os::fetch_frame_from_context(const void* ucVoid) { return frame(sp, fp, epc); } -bool os::Bsd::get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr) { - address pc = (address) os::Bsd::ucontext_get_pc(uc); - if (Interpreter::contains(pc)) { - // interpreter performs stack banging after the fixed frame header has - // been generated while the compilers perform it before. To maintain - // semantic consistency between interpreted and compiled frames, the - // method returns the Java sender of the current frame. - *fr = os::fetch_frame_from_context(uc); - if (!fr->is_first_java_frame()) { - // get_frame_at_stack_banging_point() is only called when we - // have well defined stacks so java_sender() calls do not need - // to assert safe_for_sender() first. - *fr = fr->java_sender(); - } - } else { - // more complex code with compiled code - assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above"); - CodeBlob* cb = CodeCache::find_blob(pc); - if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) { - // Not sure where the pc points to, fallback to default - // stack overflow handling - return false; - } else { - *fr = os::fetch_frame_from_context(uc); - // in compiled code, the stack banging is performed just after the return pc - // has been pushed on the stack - *fr = frame(fr->sp() + 1, fr->fp(), (address)*(fr->sp())); - if (!fr->is_java_frame()) { - // See java_sender() comment above. - *fr = fr->java_sender(); - } - } - } - assert(fr->is_java_frame(), "Safety check"); - return true; +frame os::fetch_compiled_frame_from_context(const void* ucVoid) { + const ucontext_t* uc = (const ucontext_t*)ucVoid; + frame fr = os::fetch_frame_from_context(uc); + // in compiled code, the stack banging is performed just after the return pc + // has been pushed on the stack + return frame(fr.sp() + 1, fr.fp(), (address)*(fr.sp())); } // By default, gcc always save frame pointer (%ebp/%rbp) on stack. It may get @@ -414,57 +385,14 @@ frame os::current_frame() { } } -// Utility functions - // From IA32 System Programming Guide enum { trap_page_fault = 0xE }; -extern "C" JNIEXPORT int -JVM_handle_bsd_signal(int sig, - siginfo_t* info, - void* ucVoid, - int abort_if_unrecognized) { - ucontext_t* uc = (ucontext_t*) ucVoid; +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { - Thread* t = Thread::current_or_null_safe(); - - // Must do this before SignalHandlerMark, if crash protection installed we will longjmp away - // (no destructors can be run) - os::ThreadCrashProtection::check_crash_protection(sig, t); - - SignalHandlerMark shm(t); - - // Note: it's not uncommon that JNI code uses signal/sigset to install - // then restore certain signal handler (e.g. to temporarily block SIGPIPE, - // or have a SIGILL handler when detecting CPU type). When that happens, - // JVM_handle_bsd_signal() might be invoked with junk info/ucVoid. To - // avoid unnecessary crash when libjsig is not preloaded, try handle signals - // that do not require siginfo/ucontext first. - - if (sig == SIGPIPE || sig == SIGXFSZ) { - // allow chained handler to go first - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } else { - // Ignoring SIGPIPE/SIGXFSZ - see bugs 4229104 or 6499219 - return true; - } - } - - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL ){ - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } - else if(t->is_VM_thread()){ - vmthread = (VMThread *)t; - } - } - } /* NOTE: does not seem to work on bsd. if (info == NULL || info->si_code <= 0 || info->si_code == SI_NOINFO) { @@ -485,7 +413,7 @@ JVM_handle_bsd_signal(int sig, if (StubRoutines::is_safefetch_fault(pc)) { os::Bsd::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); - return 1; + return true; } // Handle ALL stack overflow variations here @@ -495,40 +423,8 @@ JVM_handle_bsd_signal(int sig, // check if fault address is within thread stack if (thread->is_in_full_stack(addr)) { // stack overflow - StackOverflow* overflow_state = thread->stack_overflow_state(); - if (overflow_state->in_stack_yellow_reserved_zone(addr)) { - if (thread->thread_state() == _thread_in_Java) { - if (overflow_state->in_stack_reserved_zone(addr)) { - frame fr; - if (os::Bsd::get_frame_at_stack_banging_point(thread, uc, &fr)) { - assert(fr.is_java_frame(), "Must be a Java frame"); - frame activation = SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr); - if (activation.sp() != NULL) { - overflow_state->disable_stack_reserved_zone(); - if (activation.is_interpreted_frame()) { - overflow_state->set_reserved_stack_activation((address)( - activation.fp() + frame::interpreter_frame_initial_sp_offset)); - } else { - overflow_state->set_reserved_stack_activation((address)activation.unextended_sp()); - } - return 1; - } - } - } - // Throw a stack overflow exception. Guard pages will be reenabled - // while unwinding the stack. - overflow_state->disable_stack_yellow_reserved_zone(); - stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW); - } else { - // Thread was in the vm or native code. Return and try to finish. - overflow_state->disable_stack_yellow_reserved_zone(); - return 1; - } - } else if (overflow_state->in_stack_red_zone(addr)) { - // Fatal red zone violation. Disable the guard pages and fall through - // to handle_unexpected_exception way down below. - overflow_state->disable_stack_red_zone(); - tty->print_raw_cr("An irrecoverable stack overflow has occurred."); + if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) { + return true; // continue } } } @@ -611,7 +507,7 @@ JVM_handle_bsd_signal(int sig, int op = pc[0]; if (op == 0xDB) { // FIST - // TODO: The encoding of D2I in i486.ad can cause an exception + // TODO: The encoding of D2I in x86_32.ad can cause an exception // prior to the fist instruction if there was an invalid operation // pending. We want to dismiss that exception. From the win_32 // side it also seems that if it really was the fist causing @@ -740,29 +636,6 @@ JVM_handle_bsd_signal(int sig, return true; } - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } - - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return false; - } - - if (pc == NULL && uc != NULL) { - pc = os::Bsd::ucontext_get_pc(uc); - } - - // unmask current signal - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - sigprocmask(SIG_UNBLOCK, &newset, NULL); - - VMError::report_and_die(t, sig, pc, info, ucVoid); - - ShouldNotReachHere(); return false; } diff --git a/src/hotspot/os_cpu/bsd_zero/atomic_bsd_zero.hpp b/src/hotspot/os_cpu/bsd_zero/atomic_bsd_zero.hpp index 1c6f2155ef8..b9ee395bc30 100644 --- a/src/hotspot/os_cpu/bsd_zero/atomic_bsd_zero.hpp +++ b/src/hotspot/os_cpu/bsd_zero/atomic_bsd_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * Copyright 2007, 2008, 2011, 2015, Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -184,7 +184,9 @@ inline D Atomic::PlatformAdd<4>::add_and_fetch(D volatile* dest, I add_value, #ifdef M68K return add_using_helper(m68k_add_and_fetch, dest, add_value); #else - return __sync_add_and_fetch(dest, add_value); + D res = __atomic_add_fetch(dest, add_value, __ATOMIC_RELEASE); + FULL_MEM_BARRIER; + return res; #endif // M68K #endif // ARM } @@ -196,7 +198,9 @@ inline D Atomic::PlatformAdd<8>::add_and_fetch(D volatile* dest, I add_value, STATIC_ASSERT(8 == sizeof(I)); STATIC_ASSERT(8 == sizeof(D)); - return __sync_add_and_fetch(dest, add_value); + D res = __atomic_add_fetch(dest, add_value, __ATOMIC_RELEASE); + FULL_MEM_BARRIER; + return res; } template<> @@ -255,7 +259,12 @@ inline T Atomic::PlatformCmpxchg<4>::operator()(T volatile* dest, #ifdef M68K return cmpxchg_using_helper(m68k_compare_and_swap, dest, compare_value, exchange_value); #else - return __sync_val_compare_and_swap(dest, compare_value, exchange_value); + T value = compare_value; + FULL_MEM_BARRIER; + __atomic_compare_exchange(dest, &value, &exchange_value, /*weak*/false, + __ATOMIC_RELAXED, __ATOMIC_RELAXED); + FULL_MEM_BARRIER; + return value; #endif // M68K #endif // ARM } @@ -267,7 +276,13 @@ inline T Atomic::PlatformCmpxchg<8>::operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { STATIC_ASSERT(8 == sizeof(T)); - return __sync_val_compare_and_swap(dest, compare_value, exchange_value); + + T value = compare_value; + FULL_MEM_BARRIER; + __atomic_compare_exchange(dest, &value, &exchange_value, /*weak*/false, + __ATOMIC_RELAXED, __ATOMIC_RELAXED); + FULL_MEM_BARRIER; + return value; } template<> diff --git a/src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp b/src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp index 0e0dfeca71c..1ab2001f5ab 100644 --- a/src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp +++ b/src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp @@ -115,18 +115,10 @@ frame os::fetch_frame_from_context(const void* ucVoid) { return frame(); } -extern "C" JNIEXPORT int -JVM_handle_bsd_signal(int sig, - siginfo_t* info, - void* ucVoid, - int abort_if_unrecognized) { - ucontext_t* uc = (ucontext_t*) ucVoid; +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { - Thread* t = Thread::current_or_null_safe(); - - SignalHandlerMark shm(t); - - // handle SafeFetch faults + // handle SafeFetch faults the zero way if (sig == SIGSEGV || sig == SIGBUS) { sigjmp_buf* const pjb = get_jmp_buf_for_continuation(); if (pjb) { @@ -134,37 +126,6 @@ JVM_handle_bsd_signal(int sig, } } - // Note: it's not uncommon that JNI code uses signal/sigset to - // install then restore certain signal handler (e.g. to temporarily - // block SIGPIPE, or have a SIGILL handler when detecting CPU - // type). When that happens, JVM_handle_bsd_signal() might be - // invoked with junk info/ucVoid. To avoid unnecessary crash when - // libjsig is not preloaded, try handle signals that do not require - // siginfo/ucontext first. - - if (sig == SIGPIPE || sig == SIGXFSZ) { - // allow chained handler to go first - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } else { - // Ignoring SIGPIPE/SIGXFSZ - see bugs 4229104 or 6499219 - return true; - } - } - - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL ){ - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } - else if(t->is_VM_thread()){ - vmthread = (VMThread *)t; - } - } - } - if (info != NULL && thread != NULL) { // Handle ALL stack overflow variations here if (sig == SIGSEGV || sig == SIGBUS) { @@ -172,13 +133,14 @@ JVM_handle_bsd_signal(int sig, // check if fault address is within thread stack if (thread->is_in_full_stack(addr)) { + StackOverflow* overflow_state = thread->stack_overflow_state(); // stack overflow - if (thread->in_stack_yellow_reserved_zone(addr)) { - thread->disable_stack_yellow_reserved_zone(); + if (overflow_state->in_stack_yellow_reserved_zone(addr)) { + overflow_state->disable_stack_yellow_reserved_zone(); ShouldNotCallThis(); } - else if (thread->in_stack_red_zone(addr)) { - thread->disable_stack_red_zone(); + else if (overflow_state->in_stack_red_zone(addr)) { + overflow_state->disable_stack_red_zone(); ShouldNotCallThis(); } } @@ -203,36 +165,6 @@ JVM_handle_bsd_signal(int sig, }*/ } - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } - - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return false; - } - -#ifndef PRODUCT - if (sig == SIGSEGV) { - fatal("\n#" - "\n# /--------------------\\" - "\n# | segmentation fault |" - "\n# \\---\\ /--------------/" - "\n# /" - "\n# [-] |\\_/| " - "\n# (+)=C |o o|__ " - "\n# | | =-*-=__\\ " - "\n# OOO c_c_(___)"); - } -#endif // !PRODUCT - - const char *fmt = - "caught unhandled signal " INT32_FORMAT " at address " PTR_FORMAT; - char buf[128]; - - sprintf(buf, fmt, sig, info->si_addr); - fatal(buf); return false; } @@ -436,6 +368,7 @@ extern "C" { long long unsigned int oldval, long long unsigned int newval) { ShouldNotCallThis(); + return 0; // silence compiler warnings } }; #endif // !_LP64 diff --git a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp index d4bca5cd374..9ba2179a80c 100644 --- a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp +++ b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp @@ -75,7 +75,6 @@ # include # include # include -# include #define REG_FP 29 #define REG_LR 30 @@ -134,44 +133,16 @@ frame os::fetch_frame_from_context(const void* ucVoid) { return frame(sp, fp, epc); } -bool os::Linux::get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr) { - address pc = (address) os::Linux::ucontext_get_pc(uc); - if (Interpreter::contains(pc)) { - // interpreter performs stack banging after the fixed frame header has - // been generated while the compilers perform it before. To maintain - // semantic consistency between interpreted and compiled frames, the - // method returns the Java sender of the current frame. - *fr = os::fetch_frame_from_context(uc); - if (!fr->is_first_java_frame()) { - assert(fr->safe_for_sender(thread), "Safety check"); - *fr = fr->java_sender(); - } - } else { - // more complex code with compiled code - assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above"); - CodeBlob* cb = CodeCache::find_blob(pc); - if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) { - // Not sure where the pc points to, fallback to default - // stack overflow handling - return false; - } else { - // In compiled code, the stack banging is performed before LR - // has been saved in the frame. LR is live, and SP and FP - // belong to the caller. - intptr_t* fp = os::Linux::ucontext_get_fp(uc); - intptr_t* sp = os::Linux::ucontext_get_sp(uc); - address pc = (address)(uc->uc_mcontext.regs[REG_LR] +frame os::fetch_compiled_frame_from_context(const void* ucVoid) { + const ucontext_t* uc = (const ucontext_t*)ucVoid; + // In compiled code, the stack banging is performed before LR + // has been saved in the frame. LR is live, and SP and FP + // belong to the caller. + intptr_t* fp = os::Linux::ucontext_get_fp(uc); + intptr_t* sp = os::Linux::ucontext_get_sp(uc); + address pc = (address)(uc->uc_mcontext.regs[REG_LR] - NativeInstruction::instruction_size); - *fr = frame(sp, fp, pc); - if (!fr->is_java_frame()) { - assert(fr->safe_for_sender(thread), "Safety check"); - assert(!fr->is_first_frame(), "Safety check"); - *fr = fr->java_sender(); - } - } - } - assert(fr->is_java_frame(), "Safety check"); - return true; + return frame(sp, fp, pc); } // By default, gcc always saves frame pointer rfp on this stack. This @@ -193,58 +164,9 @@ NOINLINE frame os::current_frame() { } } -extern "C" JNIEXPORT int -JVM_handle_linux_signal(int sig, - siginfo_t* info, - void* ucVoid, - int abort_if_unrecognized) { - ucontext_t* uc = (ucontext_t*) ucVoid; - - Thread* t = Thread::current_or_null_safe(); - - // Must do this before SignalHandlerMark, if crash protection installed we will longjmp away - // (no destructors can be run) - os::ThreadCrashProtection::check_crash_protection(sig, t); - - SignalHandlerMark shm(t); - - // Note: it's not uncommon that JNI code uses signal/sigset to install - // then restore certain signal handler (e.g. to temporarily block SIGPIPE, - // or have a SIGILL handler when detecting CPU type). When that happens, - // JVM_handle_linux_signal() might be invoked with junk info/ucVoid. To - // avoid unnecessary crash when libjsig is not preloaded, try handle signals - // that do not require siginfo/ucontext first. - - if (sig == SIGPIPE || sig == SIGXFSZ) { - // allow chained handler to go first - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } else { - // Ignoring SIGPIPE/SIGXFSZ - see bugs 4229104 or 6499219 - return true; - } - } - -#ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) { - if (handle_assert_poison_fault(ucVoid, info->si_addr)) { - return 1; - } - } -#endif +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL ){ - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } - else if(t->is_VM_thread()){ - vmthread = (VMThread *)t; - } - } - } /* NOTE: does not seem to work on linux. if (info == NULL || info->si_code <= 0 || info->si_code == SI_NOINFO) { @@ -265,7 +187,7 @@ JVM_handle_linux_signal(int sig, if (StubRoutines::is_safefetch_fault(pc)) { os::Linux::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); - return 1; + return true; } address addr = (address) info->si_addr; @@ -279,62 +201,8 @@ JVM_handle_linux_signal(int sig, if (sig == SIGSEGV) { // check if fault address is within thread stack if (thread->is_in_full_stack(addr)) { - StackOverflow* overflow_state = thread->stack_overflow_state(); - // stack overflow - if (overflow_state->in_stack_yellow_reserved_zone(addr)) { - if (thread->thread_state() == _thread_in_Java) { - if (overflow_state->in_stack_reserved_zone(addr)) { - frame fr; - if (os::Linux::get_frame_at_stack_banging_point(thread, uc, &fr)) { - assert(fr.is_java_frame(), "Must be a Java frame"); - frame activation = - SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr); - if (activation.sp() != NULL) { - overflow_state->disable_stack_reserved_zone(); - if (activation.is_interpreted_frame()) { - overflow_state->set_reserved_stack_activation((address)( - activation.fp() + frame::interpreter_frame_initial_sp_offset)); - } else { - overflow_state->set_reserved_stack_activation((address)activation.unextended_sp()); - } - return 1; - } - } - } - // Throw a stack overflow exception. Guard pages will be reenabled - // while unwinding the stack. - overflow_state->disable_stack_yellow_reserved_zone(); - stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW); - } else { - // Thread was in the vm or native code. Return and try to finish. - overflow_state->disable_stack_yellow_reserved_zone(); - return 1; - } - } else if (overflow_state->in_stack_red_zone(addr)) { - // Fatal red zone violation. Disable the guard pages and fall through - // to handle_unexpected_exception way down below. - overflow_state->disable_stack_red_zone(); - tty->print_raw_cr("An irrecoverable stack overflow has occurred."); - - // This is a likely cause, but hard to verify. Let's just print - // it as a hint. - tty->print_raw_cr("Please check if any of your loaded .so files has " - "enabled executable stack (see man page execstack(8))"); - } else { - // Accessing stack address below sp may cause SEGV if current - // thread has MAP_GROWSDOWN stack. This should only happen when - // current thread was created by user code with MAP_GROWSDOWN flag - // and then attached to VM. See notes in os_linux.cpp. - if (thread->osthread()->expanding_stack() == 0) { - thread->osthread()->set_expanding_stack(); - if (os::Linux::manually_expand_stack(thread, addr)) { - thread->osthread()->clear_expanding_stack(); - return 1; - } - thread->osthread()->clear_expanding_stack(); - } else { - fatal("recursive segv. expanding stack."); - } + if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) { + return true; // continue } } } @@ -377,10 +245,7 @@ JVM_handle_linux_signal(int sig, tty->print_cr("trap: %s: (SIGILL)", msg); } - va_list detail_args; - VMError::report_and_die(INTERNAL_ERROR, msg, detail_msg, detail_args, thread, - pc, info, ucVoid, NULL, 0, 0); - va_end(detail_args); + return false; // Fatal error } else @@ -426,30 +291,8 @@ JVM_handle_linux_signal(int sig, return true; } - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } - - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return false; - } - - if (pc == NULL && uc != NULL) { - pc = os::Linux::ucontext_get_pc(uc); - } - - // unmask current signal - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - sigprocmask(SIG_UNBLOCK, &newset, NULL); - - VMError::report_and_die(t, sig, pc, info, ucVoid); + return false; // Mute compiler - ShouldNotReachHere(); - return true; // Mute compiler } void os::Linux::init_thread_fpu_state(void) { diff --git a/src/hotspot/os_cpu/linux_aarch64/thread_linux_aarch64.hpp b/src/hotspot/os_cpu/linux_aarch64/thread_linux_aarch64.hpp index 2a6c3a74689..5a1f273c548 100644 --- a/src/hotspot/os_cpu/linux_aarch64/thread_linux_aarch64.hpp +++ b/src/hotspot/os_cpu/linux_aarch64/thread_linux_aarch64.hpp @@ -27,15 +27,6 @@ #define OS_CPU_LINUX_AARCH64_THREAD_LINUX_AARCH64_HPP private: -#ifdef ASSERT - // spill stack holds N callee-save registers at each Java call and - // grows downwards towards limit - // we need limit to check we have space for a spill and base so we - // can identify all live spill frames at GC (eventually) - address _spill_stack; - address _spill_stack_base; - address _spill_stack_limit; -#endif // ASSERT void pd_initialize() { _anchor.clear(); diff --git a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp index d5fb3f4680b..199a096d7f9 100644 --- a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp +++ b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp @@ -59,6 +59,10 @@ #define HWCAP_DCPOP (1<<16) #endif +#ifndef HWCAP_SHA3 +#define HWCAP_SHA3 (1 << 17) +#endif + #ifndef HWCAP_SHA512 #define HWCAP_SHA512 (1 << 21) #endif @@ -82,7 +86,7 @@ int VM_Version::get_current_sve_vector_length() { return prctl(PR_SVE_GET_VL); } -int VM_Version::set_and_get_current_sve_vector_lenght(int length) { +int VM_Version::set_and_get_current_sve_vector_length(int length) { assert(_features & CPU_SVE, "should not call this"); int new_length = prctl(PR_SVE_SET_VL, length); return new_length; @@ -103,6 +107,7 @@ void VM_Version::get_os_cpu_info() { static_assert(CPU_CRC32 == HWCAP_CRC32); static_assert(CPU_LSE == HWCAP_ATOMICS); static_assert(CPU_DCPOP == HWCAP_DCPOP); + static_assert(CPU_SHA3 == HWCAP_SHA3); static_assert(CPU_SHA512 == HWCAP_SHA512); static_assert(CPU_SVE == HWCAP_SVE); _features = auxv & ( @@ -116,6 +121,7 @@ void VM_Version::get_os_cpu_info() { HWCAP_CRC32 | HWCAP_ATOMICS | HWCAP_DCPOP | + HWCAP_SHA3 | HWCAP_SHA512 | HWCAP_SVE); @@ -136,7 +142,6 @@ void VM_Version::get_os_cpu_info() { _zva_length = 4 << (dczid_el0 & 0xf); } - int cpu_lines = 0; if (FILE *f = fopen("/proc/cpuinfo", "r")) { // need a large buffer as the flags line may include lots of text char buf[1024], *p; @@ -145,7 +150,6 @@ void VM_Version::get_os_cpu_info() { long v = strtol(p+1, NULL, 0); if (strncmp(buf, "CPU implementer", sizeof "CPU implementer" - 1) == 0) { _cpu = v; - cpu_lines++; } else if (strncmp(buf, "CPU variant", sizeof "CPU variant" - 1) == 0) { _variant = v; } else if (strncmp(buf, "CPU part", sizeof "CPU part" - 1) == 0) { @@ -162,5 +166,4 @@ void VM_Version::get_os_cpu_info() { } fclose(f); } - guarantee(cpu_lines == os::processor_count(), "core count should be consistent"); } diff --git a/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp b/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp index ab8481c749b..41733be6ca3 100644 --- a/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp +++ b/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp @@ -241,19 +241,9 @@ address check_vfp3_32_fault_instr = NULL; address check_simd_fault_instr = NULL; address check_mp_ext_fault_instr = NULL; -// Utility functions -extern "C" int JVM_handle_linux_signal(int sig, siginfo_t* info, - void* ucVoid, int abort_if_unrecognized) { - ucontext_t* uc = (ucontext_t*) ucVoid; - - Thread* t = Thread::current_or_null_safe(); - - // Must do this before SignalHandlerMark, if crash protection installed we will longjmp away - // (no destructors can be run) - os::ThreadCrashProtection::check_crash_protection(sig, t); - - SignalHandlerMark shm(t); +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { if (sig == SIGILL && ((info->si_addr == (caddr_t)check_simd_fault_instr) @@ -267,44 +257,6 @@ extern "C" int JVM_handle_linux_signal(int sig, siginfo_t* info, return true; } - // Note: it's not uncommon that JNI code uses signal/sigset to install - // then restore certain signal handler (e.g. to temporarily block SIGPIPE, - // or have a SIGILL handler when detecting CPU type). When that happens, - // JVM_handle_linux_signal() might be invoked with junk info/ucVoid. To - // avoid unnecessary crash when libjsig is not preloaded, try handle signals - // that do not require siginfo/ucontext first. - - if (sig == SIGPIPE || sig == SIGXFSZ) { - // allow chained handler to go first - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } else { - // Ignoring SIGPIPE/SIGXFSZ - see bugs 4229104 or 6499219 - return true; - } - } - -#ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) { - if (handle_assert_poison_fault(ucVoid, info->si_addr)) { - return 1; - } - } -#endif - - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL ){ - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } - else if(t->is_VM_thread()){ - vmthread = (VMThread *)t; - } - } - } - address stub = NULL; address pc = NULL; bool unsafe_access = false; @@ -318,7 +270,7 @@ extern "C" int JVM_handle_linux_signal(int sig, siginfo_t* info, if (StubRoutines::is_safefetch_fault(pc)) { os::Linux::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); - return 1; + return true; } // check if fault address is within thread stack if (thread->is_in_full_stack(addr)) { @@ -332,7 +284,7 @@ extern "C" int JVM_handle_linux_signal(int sig, siginfo_t* info, stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW); } else { // Thread was in the vm or native code. Return and try to finish. - return 1; + return true; } } else if (overflow_state->in_stack_red_zone(addr)) { // Fatal red zone violation. Disable the guard pages and fall through @@ -348,7 +300,7 @@ extern "C" int JVM_handle_linux_signal(int sig, siginfo_t* info, thread->osthread()->set_expanding_stack(); if (os::Linux::manually_expand_stack(thread, addr)) { thread->osthread()->clear_expanding_stack(); - return 1; + return true; } thread->osthread()->clear_expanding_stack(); } else { @@ -441,30 +393,8 @@ extern "C" int JVM_handle_linux_signal(int sig, siginfo_t* info, return true; } - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } - - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return false; - } - - if (pc == NULL && uc != NULL) { - pc = os::Linux::ucontext_get_pc(uc); - } - - // unmask current signal - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - sigprocmask(SIG_UNBLOCK, &newset, NULL); - - VMError::report_and_die(t, sig, pc, info, ucVoid); - - ShouldNotReachHere(); return false; + } void os::Linux::init_thread_fpu_state(void) { diff --git a/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp b/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp index c820886ba80..ac4d1b72eb3 100644 --- a/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp +++ b/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp @@ -162,40 +162,11 @@ frame os::fetch_frame_from_context(const void* ucVoid) { return frame(sp, epc); } -bool os::Linux::get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr) { - address pc = (address) os::Linux::ucontext_get_pc(uc); - if (Interpreter::contains(pc)) { - // Interpreter performs stack banging after the fixed frame header has - // been generated while the compilers perform it before. To maintain - // semantic consistency between interpreted and compiled frames, the - // method returns the Java sender of the current frame. - *fr = os::fetch_frame_from_context(uc); - if (!fr->is_first_java_frame()) { - assert(fr->safe_for_sender(thread), "Safety check"); - *fr = fr->java_sender(); - } - } else { - // More complex code with compiled code. - assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above"); - CodeBlob* cb = CodeCache::find_blob(pc); - if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) { - // Not sure where the pc points to, fallback to default - // stack overflow handling. In compiled code, we bang before - // the frame is complete. - return false; - } else { - intptr_t* sp = os::Linux::ucontext_get_sp(uc); - address lr = ucontext_get_lr(uc); - *fr = frame(sp, lr); - if (!fr->is_java_frame()) { - assert(fr->safe_for_sender(thread), "Safety check"); - assert(!fr->is_first_frame(), "Safety check"); - *fr = fr->java_sender(); - } - } - } - assert(fr->is_java_frame(), "Safety check"); - return true; +frame os::fetch_compiled_frame_from_context(const void* ucVoid) { + const ucontext_t* uc = (const ucontext_t*)ucVoid; + intptr_t* sp = os::Linux::ucontext_get_sp(uc); + address lr = ucontext_get_lr(uc); + return frame(sp, lr); } frame os::get_sender_for_C_frame(frame* fr) { @@ -217,34 +188,8 @@ frame os::current_frame() { return os::get_sender_for_C_frame(&tmp); } -// Utility functions - -extern "C" JNIEXPORT int -JVM_handle_linux_signal(int sig, - siginfo_t* info, - void* ucVoid, - int abort_if_unrecognized) { - ucontext_t* uc = (ucontext_t*) ucVoid; - - Thread* t = Thread::current_or_null_safe(); - - SignalHandlerMark shm(t); - - // Note: it's not uncommon that JNI code uses signal/sigset to install - // then restore certain signal handler (e.g. to temporarily block SIGPIPE, - // or have a SIGILL handler when detecting CPU type). When that happens, - // JVM_handle_linux_signal() might be invoked with junk info/ucVoid. To - // avoid unnecessary crash when libjsig is not preloaded, try handle signals - // that do not require siginfo/ucontext first. - - if (sig == SIGPIPE) { - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } else { - // Ignoring SIGPIPE - see bugs 4229104 - return true; - } - } +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { // Make the signal handler transaction-aware by checking the existence of a // second (transactional) context with MSR TS bits active. If the signal is @@ -268,26 +213,6 @@ JVM_handle_linux_signal(int sig, } } -#ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) { - if (handle_assert_poison_fault(ucVoid, info->si_addr)) { - return 1; - } - } -#endif - - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL) { - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } else if(t->is_VM_thread()) { - vmthread = (VMThread *)t; - } - } - } - // Moved SafeFetch32 handling outside thread!=NULL conditional block to make // it work if no associated JavaThread object exists. if (uc) { @@ -327,60 +252,8 @@ JVM_handle_linux_signal(int sig, // Check if fault address is within thread stack. if (thread->is_in_full_stack(addr)) { // stack overflow - StackOverflow* overflow_state = thread->stack_overflow_state(); - if (overflow_state->in_stack_yellow_reserved_zone(addr)) { - if (thread->thread_state() == _thread_in_Java) { - if (overflow_state->in_stack_reserved_zone(addr)) { - frame fr; - if (os::Linux::get_frame_at_stack_banging_point(thread, uc, &fr)) { - assert(fr.is_java_frame(), "Must be a Javac frame"); - frame activation = - SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr); - if (activation.sp() != NULL) { - overflow_state->disable_stack_reserved_zone(); - if (activation.is_interpreted_frame()) { - overflow_state->set_reserved_stack_activation((address)activation.fp()); - } else { - overflow_state->set_reserved_stack_activation((address)activation.unextended_sp()); - } - return 1; - } - } - } - // Throw a stack overflow exception. - // Guard pages will be reenabled while unwinding the stack. - overflow_state->disable_stack_yellow_reserved_zone(); - stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW); - } else { - // Thread was in the vm or native code. Return and try to finish. - overflow_state->disable_stack_yellow_reserved_zone(); - return 1; - } - } else if (overflow_state->in_stack_red_zone(addr)) { - // Fatal red zone violation. Disable the guard pages and fall through - // to handle_unexpected_exception way down below. - overflow_state->disable_stack_red_zone(); - tty->print_raw_cr("An irrecoverable stack overflow has occurred."); - - // This is a likely cause, but hard to verify. Let's just print - // it as a hint. - tty->print_raw_cr("Please check if any of your loaded .so files has " - "enabled executable stack (see man page execstack(8))"); - } else { - // Accessing stack address below sp may cause SEGV if current - // thread has MAP_GROWSDOWN stack. This should only happen when - // current thread was created by user code with MAP_GROWSDOWN flag - // and then attached to VM. See notes in os_linux.cpp. - if (thread->osthread()->expanding_stack() == 0) { - thread->osthread()->set_expanding_stack(); - if (os::Linux::manually_expand_stack(thread, addr)) { - thread->osthread()->clear_expanding_stack(); - return 1; - } - thread->osthread()->clear_expanding_stack(); - } else { - fatal("recursive segv. expanding stack."); - } + if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) { + return true; // continue } } } @@ -389,17 +262,6 @@ JVM_handle_linux_signal(int sig, // Java thread running in Java code => find exception handler if any // a fault inside compiled code, the interpreter, or a stub - // A VM-related SIGILL may only occur if we are not in the zero page. - // On AIX, we get a SIGILL if we jump to 0x0 or to somewhere else - // in the zero page, because it is filled with 0x0. We ignore - // explicit SIGILLs in the zero page. - if (sig == SIGILL && (pc < (address) 0x200)) { - if (TraceTraps) { - tty->print_raw_cr("SIGILL happened inside zero page."); - } - goto report_and_die; - } - CodeBlob *cb = NULL; int stop_type = -1; // Handle signal from NativeJump::patch_verified_entry(). @@ -487,10 +349,7 @@ JVM_handle_linux_signal(int sig, tty->print_cr("trap: %s: %s (SIGTRAP, stop type %d)", msg, detail_msg, stop_type); } - va_list detail_args; - VMError::report_and_die(INTERNAL_ERROR, msg, detail_msg, detail_args, thread, - pc, info, ucVoid, NULL, 0, 0); - va_end(detail_args); + return false; // Fatal error } else if (sig == SIGBUS) { @@ -548,31 +407,8 @@ JVM_handle_linux_signal(int sig, return true; } - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } - - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return false; - } - - if (pc == NULL && uc != NULL) { - pc = os::Linux::ucontext_get_pc(uc); - } - -report_and_die: - // unmask current signal - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - sigprocmask(SIG_UNBLOCK, &newset, NULL); - - VMError::report_and_die(t, sig, pc, info, ucVoid); - - ShouldNotReachHere(); return false; + } void os::Linux::init_thread_fpu_state(void) { diff --git a/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp b/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp index 97473c2b99f..d3d73053089 100644 --- a/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp +++ b/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp @@ -145,40 +145,11 @@ frame os::fetch_frame_from_context(const void* ucVoid) { return frame(sp, epc); } -bool os::Linux::get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr) { - address pc = (address) os::Linux::ucontext_get_pc(uc); - if (Interpreter::contains(pc)) { - // Interpreter performs stack banging after the fixed frame header has - // been generated while the compilers perform it before. To maintain - // semantic consistency between interpreted and compiled frames, the - // method returns the Java sender of the current frame. - *fr = os::fetch_frame_from_context(uc); - if (!fr->is_first_java_frame()) { - assert(fr->safe_for_sender(thread), "Safety check"); - *fr = fr->java_sender(); - } - } else { - // More complex code with compiled code. - assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above"); - CodeBlob* cb = CodeCache::find_blob(pc); - if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) { - // Not sure where the pc points to, fallback to default - // stack overflow handling. In compiled code, we bang before - // the frame is complete. - return false; - } else { - intptr_t* sp = os::Linux::ucontext_get_sp(uc); - address lr = ucontext_get_lr(uc); - *fr = frame(sp, lr); - if (!fr->is_java_frame()) { - assert(fr->safe_for_sender(thread), "Safety check"); - assert(!fr->is_first_frame(), "Safety check"); - *fr = fr->java_sender(); - } - } - } - assert(fr->is_java_frame(), "Safety check"); - return true; +frame os::fetch_compiled_frame_from_context(const void* ucVoid) { + const ucontext_t* uc = (const ucontext_t*)ucVoid; + intptr_t* sp = os::Linux::ucontext_get_sp(uc); + address lr = ucontext_get_lr(uc); + return frame(sp, lr); } frame os::get_sender_for_C_frame(frame* fr) { @@ -233,60 +204,8 @@ frame os::current_frame() { } } -// Utility functions - -extern "C" JNIEXPORT int -JVM_handle_linux_signal(int sig, - siginfo_t* info, - void* ucVoid, - int abort_if_unrecognized) { - ucontext_t* uc = (ucontext_t*) ucVoid; - - Thread* t = Thread::current_or_null_safe(); - - // Must do this before SignalHandlerMark, if crash protection installed we will longjmp away - // (no destructors can be run). - os::ThreadCrashProtection::check_crash_protection(sig, t); - - SignalHandlerMark shm(t); - - // Note: it's not uncommon that JNI code uses signal/sigset to install - // then restore certain signal handler (e.g. to temporarily block SIGPIPE, - // or have a SIGILL handler when detecting CPU type). When that happens, - // JVM_handle_linux_signal() might be invoked with junk info/ucVoid. To - // avoid unnecessary crash when libjsig is not preloaded, try handle signals - // that do not require siginfo/ucontext first. - - if (sig == SIGPIPE) { - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } else { - if (PrintMiscellaneous && (WizardMode || Verbose)) { - warning("Ignoring SIGPIPE - see bug 4229104"); - } - return true; - } - } - -#ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) { - if (handle_assert_poison_fault(ucVoid, info->si_addr)) { - return 1; - } - } -#endif - - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL) { - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } else if(t->is_VM_thread()) { - vmthread = (VMThread *)t; - } - } - } +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { // Moved SafeFetch32 handling outside thread!=NULL conditional block to make // it work if no associated JavaThread object exists. @@ -323,60 +242,8 @@ JVM_handle_linux_signal(int sig, // Check if fault address is within thread stack. if (thread->is_in_full_stack(addr)) { // stack overflow - StackOverflow* overflow_state = thread->stack_overflow_state(); - if (overflow_state->in_stack_yellow_reserved_zone(addr)) { - if (thread->thread_state() == _thread_in_Java) { - if (overflow_state->in_stack_reserved_zone(addr)) { - frame fr; - if (os::Linux::get_frame_at_stack_banging_point(thread, uc, &fr)) { - assert(fr.is_java_frame(), "Must be a Javac frame"); - frame activation = - SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr); - if (activation.sp() != NULL) { - overflow_state->disable_stack_reserved_zone(); - if (activation.is_interpreted_frame()) { - overflow_state->set_reserved_stack_activation((address)activation.fp()); - } else { - overflow_state->set_reserved_stack_activation((address)activation.unextended_sp()); - } - return 1; - } - } - } - // Throw a stack overflow exception. - // Guard pages will be reenabled while unwinding the stack. - overflow_state->disable_stack_yellow_reserved_zone(); - stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW); - } else { - // Thread was in the vm or native code. Return and try to finish. - overflow_state->disable_stack_yellow_reserved_zone(); - return 1; - } - } else if (overflow_state->in_stack_red_zone(addr)) { - // Fatal red zone violation. Disable the guard pages and fall through - // to handle_unexpected_exception way down below. - overflow_state->disable_stack_red_zone(); - tty->print_raw_cr("An irrecoverable stack overflow has occurred."); - - // This is a likely cause, but hard to verify. Let's just print - // it as a hint. - tty->print_raw_cr("Please check if any of your loaded .so files has " - "enabled executable stack (see man page execstack(8))"); - } else { - // Accessing stack address below sp may cause SEGV if current - // thread has MAP_GROWSDOWN stack. This should only happen when - // current thread was created by user code with MAP_GROWSDOWN flag - // and then attached to VM. See notes in os_linux.cpp. - if (thread->osthread()->expanding_stack() == 0) { - thread->osthread()->set_expanding_stack(); - if (os::Linux::manually_expand_stack(thread, addr)) { - thread->osthread()->clear_expanding_stack(); - return 1; - } - thread->osthread()->clear_expanding_stack(); - } else { - fatal("recursive segv. expanding stack."); - } + if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) { + return true; // continue } } } @@ -500,38 +367,8 @@ JVM_handle_linux_signal(int sig, return true; } - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } - - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return false; - } - - if (pc == NULL && uc != NULL) { - pc = os::Linux::ucontext_get_pc(uc); - } - - // unmask current signal - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - sigprocmask(SIG_UNBLOCK, &newset, NULL); - - // Hand down correct pc for SIGILL, SIGFPE. pc from context - // usually points to the instruction after the failing instruction. - // Note: this should be combined with the trap_pc handling above, - // because it handles the same issue. - if (sig == SIGILL || sig == SIGFPE) { - pc = (address)info->si_addr; - } - - VMError::report_and_die(t, sig, pc, info, ucVoid); - - ShouldNotReachHere(); return false; + } void os::Linux::init_thread_fpu_state(void) { diff --git a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp index aacbcce08c3..6c977fc96f1 100644 --- a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp +++ b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp @@ -148,43 +148,11 @@ frame os::fetch_frame_from_context(const void* ucVoid) { return frame(sp, fp, epc); } -bool os::Linux::get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr) { - address pc = (address) os::Linux::ucontext_get_pc(uc); - if (Interpreter::contains(pc)) { - // interpreter performs stack banging after the fixed frame header has - // been generated while the compilers perform it before. To maintain - // semantic consistency between interpreted and compiled frames, the - // method returns the Java sender of the current frame. - *fr = os::fetch_frame_from_context(uc); - if (!fr->is_first_java_frame()) { - // get_frame_at_stack_banging_point() is only called when we - // have well defined stacks so java_sender() calls do not need - // to assert safe_for_sender() first. - *fr = fr->java_sender(); - } - } else { - // more complex code with compiled code - assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above"); - CodeBlob* cb = CodeCache::find_blob(pc); - if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) { - // Not sure where the pc points to, fallback to default - // stack overflow handling - return false; - } else { - // in compiled code, the stack banging is performed just after the return pc - // has been pushed on the stack - intptr_t* fp = os::Linux::ucontext_get_fp(uc); - intptr_t* sp = os::Linux::ucontext_get_sp(uc); - *fr = frame(sp + 1, fp, (address)*sp); - if (!fr->is_java_frame()) { - assert(!fr->is_first_frame(), "Safety check"); - // See java_sender() comment above. - *fr = fr->java_sender(); - } - } - } - assert(fr->is_java_frame(), "Safety check"); - return true; +frame os::fetch_compiled_frame_from_context(const void* ucVoid) { + const ucontext_t* uc = (const ucontext_t*)ucVoid; + intptr_t* fp = os::Linux::ucontext_get_fp(uc); + intptr_t* sp = os::Linux::ucontext_get_sp(uc); + return frame(sp + 1, fp, (address)*sp); } // By default, gcc always save frame pointer (%ebp/%rbp) on stack. It may get @@ -232,59 +200,10 @@ enum { trap_page_fault = 0xE }; -extern "C" JNIEXPORT int -JVM_handle_linux_signal(int sig, - siginfo_t* info, - void* ucVoid, - int abort_if_unrecognized) { - ucontext_t* uc = (ucontext_t*) ucVoid; - - Thread* t = Thread::current_or_null_safe(); - - // Must do this before SignalHandlerMark, if crash protection installed we will longjmp away - // (no destructors can be run) - os::ThreadCrashProtection::check_crash_protection(sig, t); - - SignalHandlerMark shm(t); - - // Note: it's not uncommon that JNI code uses signal/sigset to install - // then restore certain signal handler (e.g. to temporarily block SIGPIPE, - // or have a SIGILL handler when detecting CPU type). When that happens, - // JVM_handle_linux_signal() might be invoked with junk info/ucVoid. To - // avoid unnecessary crash when libjsig is not preloaded, try handle signals - // that do not require siginfo/ucontext first. - - if (sig == SIGPIPE || sig == SIGXFSZ) { - // allow chained handler to go first - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } else { - // Ignoring SIGPIPE/SIGXFSZ - see bugs 4229104 or 6499219 - return true; - } - } - -#ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) { - if (handle_assert_poison_fault(ucVoid, info->si_addr)) { - return 1; - } - } -#endif +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL ){ - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } - else if(t->is_VM_thread()){ - vmthread = (VMThread *)t; - } - } - } -/* + /* NOTE: does not seem to work on linux. if (info == NULL || info->si_code <= 0 || info->si_code == SI_NOINFO) { // can't decode this kind of signal @@ -304,7 +223,7 @@ JVM_handle_linux_signal(int sig, if (StubRoutines::is_safefetch_fault(pc)) { os::Linux::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); - return 1; + return true; } #ifndef AMD64 @@ -324,61 +243,8 @@ JVM_handle_linux_signal(int sig, // check if fault address is within thread stack if (thread->is_in_full_stack(addr)) { // stack overflow - StackOverflow* overflow_state = thread->stack_overflow_state(); - if (overflow_state->in_stack_yellow_reserved_zone(addr)) { - if (thread->thread_state() == _thread_in_Java) { - if (overflow_state->in_stack_reserved_zone(addr)) { - frame fr; - if (os::Linux::get_frame_at_stack_banging_point(thread, uc, &fr)) { - assert(fr.is_java_frame(), "Must be a Java frame"); - frame activation = - SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr); - if (activation.sp() != NULL) { - overflow_state->disable_stack_reserved_zone(); - if (activation.is_interpreted_frame()) { - overflow_state->set_reserved_stack_activation((address)( - activation.fp() + frame::interpreter_frame_initial_sp_offset)); - } else { - overflow_state->set_reserved_stack_activation((address)activation.unextended_sp()); - } - return 1; - } - } - } - // Throw a stack overflow exception. Guard pages will be reenabled - // while unwinding the stack. - overflow_state->disable_stack_yellow_reserved_zone(); - stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW); - } else { - // Thread was in the vm or native code. Return and try to finish. - overflow_state->disable_stack_yellow_reserved_zone(); - return 1; - } - } else if (overflow_state->in_stack_red_zone(addr)) { - // Fatal red zone violation. Disable the guard pages and fall through - // to handle_unexpected_exception way down below. - overflow_state->disable_stack_red_zone(); - tty->print_raw_cr("An irrecoverable stack overflow has occurred."); - - // This is a likely cause, but hard to verify. Let's just print - // it as a hint. - tty->print_raw_cr("Please check if any of your loaded .so files has " - "enabled executable stack (see man page execstack(8))"); - } else { - // Accessing stack address below sp may cause SEGV if current - // thread has MAP_GROWSDOWN stack. This should only happen when - // current thread was created by user code with MAP_GROWSDOWN flag - // and then attached to VM. See notes in os_linux.cpp. - if (thread->osthread()->expanding_stack() == 0) { - thread->osthread()->set_expanding_stack(); - if (os::Linux::manually_expand_stack(thread, addr)) { - thread->osthread()->clear_expanding_stack(); - return 1; - } - thread->osthread()->clear_expanding_stack(); - } else { - fatal("recursive segv. expanding stack."); - } + if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) { + return true; // continue } } } @@ -426,7 +292,7 @@ JVM_handle_linux_signal(int sig, int op = pc[0]; if (op == 0xDB) { // FIST - // TODO: The encoding of D2I in i486.ad can cause an exception + // TODO: The encoding of D2I in x86_32.ad can cause an exception // prior to the fist instruction if there was an invalid operation // pending. We want to dismiss that exception. From the win_32 // side it also seems that if it really was the fist causing @@ -555,30 +421,7 @@ JVM_handle_linux_signal(int sig, return true; } - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } - - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return false; - } - - if (pc == NULL && uc != NULL) { - pc = os::Linux::ucontext_get_pc(uc); - } - - // unmask current signal - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - sigprocmask(SIG_UNBLOCK, &newset, NULL); - - VMError::report_and_die(t, sig, pc, info, ucVoid); - - ShouldNotReachHere(); - return true; // Mute compiler + return false; } void os::Linux::init_thread_fpu_state(void) { @@ -838,8 +681,8 @@ void os::workaround_expand_exec_shield_cs_limit() { if (os::is_primordial_thread()) { address limit = Linux::initial_thread_stack_bottom(); if (! DisablePrimordialThreadGuardPages) { - limit += JavaThread::stack_red_zone_size() + - JavaThread::stack_yellow_zone_size(); + limit += StackOverflow::stack_red_zone_size() + + StackOverflow::stack_yellow_zone_size(); } os::Linux::expand_stack_to(limit); } @@ -860,7 +703,7 @@ void os::workaround_expand_exec_shield_cs_limit() { * we don't have much control or understanding of the address space, just let it slide. */ char* hint = (char*)(Linux::initial_thread_stack_bottom() - - (JavaThread::stack_guard_zone_size() + page_size)); + (StackOverflow::stack_guard_zone_size() + page_size)); char* codebuf = os::attempt_reserve_memory_at(hint, page_size); if (codebuf == NULL) { diff --git a/src/hotspot/os_cpu/linux_zero/atomic_linux_zero.hpp b/src/hotspot/os_cpu/linux_zero/atomic_linux_zero.hpp index fb6e350cf05..e373b90bd9b 100644 --- a/src/hotspot/os_cpu/linux_zero/atomic_linux_zero.hpp +++ b/src/hotspot/os_cpu/linux_zero/atomic_linux_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * Copyright 2007, 2008, 2011, 2015, Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -49,7 +49,9 @@ inline D Atomic::PlatformAdd<4>::add_and_fetch(D volatile* dest, I add_value, STATIC_ASSERT(4 == sizeof(I)); STATIC_ASSERT(4 == sizeof(D)); - return __sync_add_and_fetch(dest, add_value); + D res = __atomic_add_fetch(dest, add_value, __ATOMIC_RELEASE); + FULL_MEM_BARRIER; + return res; } template<> @@ -58,7 +60,10 @@ inline D Atomic::PlatformAdd<8>::add_and_fetch(D volatile* dest, I add_value, atomic_memory_order order) const { STATIC_ASSERT(8 == sizeof(I)); STATIC_ASSERT(8 == sizeof(D)); - return __sync_add_and_fetch(dest, add_value); + + D res = __atomic_add_fetch(dest, add_value, __ATOMIC_RELEASE); + FULL_MEM_BARRIER; + return res; } template<> @@ -103,7 +108,13 @@ inline T Atomic::PlatformCmpxchg<4>::operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { STATIC_ASSERT(4 == sizeof(T)); - return __sync_val_compare_and_swap(dest, compare_value, exchange_value); + + T value = compare_value; + FULL_MEM_BARRIER; + __atomic_compare_exchange(dest, &value, &exchange_value, /*weak*/false, + __ATOMIC_RELAXED, __ATOMIC_RELAXED); + FULL_MEM_BARRIER; + return value; } template<> @@ -113,7 +124,13 @@ inline T Atomic::PlatformCmpxchg<8>::operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { STATIC_ASSERT(8 == sizeof(T)); - return __sync_val_compare_and_swap(dest, compare_value, exchange_value); + + FULL_MEM_BARRIER; + T value = compare_value; + __atomic_compare_exchange(dest, &value, &exchange_value, /*weak*/false, + __ATOMIC_RELAXED, __ATOMIC_RELAXED); + FULL_MEM_BARRIER; + return value; } template<> diff --git a/src/hotspot/os_cpu/linux_zero/os_linux_zero.cpp b/src/hotspot/os_cpu/linux_zero/os_linux_zero.cpp index de09ba311b5..8ff5ac61c5c 100644 --- a/src/hotspot/os_cpu/linux_zero/os_linux_zero.cpp +++ b/src/hotspot/os_cpu/linux_zero/os_linux_zero.cpp @@ -111,16 +111,8 @@ frame os::fetch_frame_from_context(const void* ucVoid) { return frame(NULL, NULL); // silence compile warnings } -extern "C" JNIEXPORT int -JVM_handle_linux_signal(int sig, - siginfo_t* info, - void* ucVoid, - int abort_if_unrecognized) { - ucontext_t* uc = (ucontext_t*) ucVoid; - - Thread* t = Thread::current_or_null_safe(); - - SignalHandlerMark shm(t); +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { // handle SafeFetch faults if (sig == SIGSEGV || sig == SIGBUS) { @@ -130,37 +122,6 @@ JVM_handle_linux_signal(int sig, } } - // Note: it's not uncommon that JNI code uses signal/sigset to - // install then restore certain signal handler (e.g. to temporarily - // block SIGPIPE, or have a SIGILL handler when detecting CPU - // type). When that happens, JVM_handle_linux_signal() might be - // invoked with junk info/ucVoid. To avoid unnecessary crash when - // libjsig is not preloaded, try handle signals that do not require - // siginfo/ucontext first. - - if (sig == SIGPIPE || sig == SIGXFSZ) { - // allow chained handler to go first - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } else { - // Ignoring SIGPIPE/SIGXFSZ - see bugs 4229104 or 6499219 - return true; - } - } - - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL ){ - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } - else if(t->is_VM_thread()){ - vmthread = (VMThread *)t; - } - } - } - if (info != NULL && thread != NULL) { // Handle ALL stack overflow variations here if (sig == SIGSEGV) { @@ -218,40 +179,8 @@ JVM_handle_linux_signal(int sig, }*/ } - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } - - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return false; - } - -#ifndef PRODUCT - if (sig == SIGSEGV) { - fatal("\n#" - "\n# /--------------------\\" - "\n# | segmentation fault |" - "\n# \\---\\ /--------------/" - "\n# /" - "\n# [-] |\\_/| " - "\n# (+)=C |o o|__ " - "\n# | | =-*-=__\\ " - "\n# OOO c_c_(___)"); - } -#endif // !PRODUCT - - char buf[64]; - - sprintf(buf, "caught unhandled signal %d", sig); + return false; // Fatal error -// Silence -Wformat-security warning for fatal() -PRAGMA_DIAG_PUSH -PRAGMA_FORMAT_NONLITERAL_IGNORED - fatal(buf); -PRAGMA_DIAG_POP - return true; // silence compiler warnings } void os::Linux::init_thread_fpu_state(void) { @@ -481,7 +410,7 @@ extern "C" { long long unsigned int oldval, long long unsigned int newval) { ShouldNotCallThis(); - return 0; // silence compiler compiler warnings + return 0; // silence compiler warnings } }; #endif // !_LP64 diff --git a/src/hotspot/os_cpu/windows_aarch64/thread_windows_aarch64.hpp b/src/hotspot/os_cpu/windows_aarch64/thread_windows_aarch64.hpp index 2b004fd75af..bcf43c8b088 100644 --- a/src/hotspot/os_cpu/windows_aarch64/thread_windows_aarch64.hpp +++ b/src/hotspot/os_cpu/windows_aarch64/thread_windows_aarch64.hpp @@ -27,16 +27,6 @@ private: -#ifdef ASSERT - // spill stack holds N callee-save registers at each Java call and - // grows downwards towards limit - // we need limit to check we have space for a spill and base so we - // can identify all live spill frames at GC (eventually) - address _spill_stack; - address _spill_stack_base; - address _spill_stack_limit; -#endif // ASSERT - void pd_initialize() { _anchor.clear(); } diff --git a/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp b/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp index 80270aa62e1..825faddde01 100644 --- a/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp +++ b/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp @@ -32,7 +32,7 @@ int VM_Version::get_current_sve_vector_length() { return 0; } -int VM_Version::set_and_get_current_sve_vector_lenght(int length) { +int VM_Version::set_and_get_current_sve_vector_length(int length) { assert(_features & CPU_SVE, "should not call this"); ShouldNotReachHere(); return 0; diff --git a/src/hotspot/share/adlc/adlparse.cpp b/src/hotspot/share/adlc/adlparse.cpp index 198ee4eb586..7d62f861686 100644 --- a/src/hotspot/share/adlc/adlparse.cpp +++ b/src/hotspot/share/adlc/adlparse.cpp @@ -1006,7 +1006,8 @@ void ADLParser::frame_parse(void) { skipws(); } if (strcmp(token,"interpreter_method_reg")==0) { - interpreter_method_parse(frame, false); + parse_err(WARN, "Using obsolete Token, interpreter_method_reg"); + skipws(); } if (strcmp(token,"cisc_spilling_operand_name")==0) { cisc_spilling_operand_name_parse(frame, false); @@ -1134,11 +1135,6 @@ void ADLParser::inline_cache_parse(FrameForm *frame, bool native) { frame->_inline_cache_reg = parse_one_arg("inline cache reg entry"); } -//------------------------------interpreter_method_parse------------------ -void ADLParser::interpreter_method_parse(FrameForm *frame, bool native) { - frame->_interpreter_method_reg = parse_one_arg("method reg entry"); -} - //------------------------------cisc_spilling_operand_parse--------------------- void ADLParser::cisc_spilling_operand_name_parse(FrameForm *frame, bool native) { frame->_cisc_spilling_operand_name = parse_one_arg("cisc spilling operand name"); diff --git a/src/hotspot/share/adlc/forms.cpp b/src/hotspot/share/adlc/forms.cpp index ef72faa4a57..204f693cca3 100644 --- a/src/hotspot/share/adlc/forms.cpp +++ b/src/hotspot/share/adlc/forms.cpp @@ -268,6 +268,7 @@ Form::DataType Form::is_load_from_memory(const char *opType) const { if( strcmp(opType,"LoadRange")==0 ) return Form::idealI; if( strcmp(opType,"LoadS")==0 ) return Form::idealS; if( strcmp(opType,"LoadVector")==0 ) return Form::idealV; + if( strcmp(opType,"LoadVectorGather")==0 ) return Form::idealV; assert( strcmp(opType,"Load") != 0, "Must type Loads" ); return Form::none; } @@ -284,6 +285,7 @@ Form::DataType Form::is_store_to_memory(const char *opType) const { if( strcmp(opType,"StoreN")==0) return Form::idealN; if( strcmp(opType,"StoreNKlass")==0) return Form::idealNKlass; if( strcmp(opType,"StoreVector")==0 ) return Form::idealV; + if( strcmp(opType,"StoreVectorScatter")==0 ) return Form::idealV; assert( strcmp(opType,"Store") != 0, "Must type Stores" ); return Form::none; } diff --git a/src/hotspot/share/adlc/formsopt.hpp b/src/hotspot/share/adlc/formsopt.hpp index 0ee97160e3a..400c2690a9a 100644 --- a/src/hotspot/share/adlc/formsopt.hpp +++ b/src/hotspot/share/adlc/formsopt.hpp @@ -336,7 +336,6 @@ class FrameForm : public Form { // Public Data char *_sync_stack_slots; char *_inline_cache_reg; - char *_interpreter_method_reg; char *_interpreter_frame_pointer_reg; char *_cisc_spilling_operand_name; char *_frame_pointer; diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp index 4ac726bdf6d..42d3cc5d10d 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -3484,7 +3484,7 @@ int MatchNode::needs_ideal_memory_edge(FormDict &globals) const { "StoreB","StoreC","Store" ,"StoreFP", "LoadI", "LoadL", "LoadP" ,"LoadN", "LoadD" ,"LoadF" , "LoadB" , "LoadUB", "LoadUS" ,"LoadS" ,"Load" , - "StoreVector", "LoadVector", + "StoreVector", "LoadVector", "LoadVectorGather", "StoreVectorScatter", "LoadRange", "LoadKlass", "LoadNKlass", "LoadL_unaligned", "LoadD_unaligned", "LoadPLocked", "StorePConditional", "StoreIConditional", "StoreLConditional", @@ -3801,6 +3801,7 @@ void MatchNode::count_commutative_op(int& count) { "MaxV", "MinV", "MulI","MulL","MulF","MulD", "MulVB","MulVS","MulVI","MulVL","MulVF","MulVD", + "MinV","MaxV", "OrI","OrL", "OrV", "XorI","XorL", @@ -4151,8 +4152,9 @@ bool MatchRule::is_vector() const { "MulVB","MulVS","MulVI","MulVL","MulVF","MulVD", "CMoveVD", "CMoveVF", "DivVF","DivVD", + "MinV","MaxV", "AbsVB","AbsVS","AbsVI","AbsVL","AbsVF","AbsVD", - "NegVF","NegVD", + "NegVF","NegVD","NegVI", "SqrtVD","SqrtVF", "AndV" ,"XorV" ,"OrV", "MaxV", "MinV", @@ -4169,6 +4171,12 @@ bool MatchRule::is_vector() const { "URShiftVB","URShiftVS","URShiftVI","URShiftVL", "ReplicateB","ReplicateS","ReplicateI","ReplicateL","ReplicateF","ReplicateD", "RoundDoubleModeV","RotateLeftV" , "RotateRightV", "LoadVector","StoreVector", + "LoadVectorGather", "StoreVectorScatter", + "VectorTest", "VectorLoadMask", "VectorStoreMask", "VectorBlend", "VectorInsert", + "VectorRearrange","VectorLoadShuffle", "VectorLoadConst", + "VectorCastB2X", "VectorCastS2X", "VectorCastI2X", + "VectorCastL2X", "VectorCastF2X", "VectorCastD2X", + "VectorMaskWrapper", "VectorMaskCmp", "VectorReinterpret", "FmaVD", "FmaVF","PopCountVI", // Next are not supported currently. "PackB","PackS","PackI","PackL","PackF","PackD","Pack2L","Pack2D", diff --git a/src/hotspot/share/adlc/output_c.cpp b/src/hotspot/share/adlc/output_c.cpp index 674748dd1aa..c6083533b58 100644 --- a/src/hotspot/share/adlc/output_c.cpp +++ b/src/hotspot/share/adlc/output_c.cpp @@ -1150,10 +1150,9 @@ static void check_peepconstraints(FILE *fp, FormDict &globals, PeepMatch *pmatch // // Check for equivalence // - // fprintf(fp, "phase->eqv( "); - // fprintf(fp, "inst%d->in(%d+%d) /* %s */, inst%d->in(%d+%d) /* %s */", - // left_index, left_op_base, left_op_index, left_op, - // right_index, right_op_base, right_op_index, right_op ); + // fprintf(fp, "(inst%d->_opnds[%d]->reg(ra_,inst%d%s) /* %d.%s */ == /* %d.%s */ inst%d->_opnds[%d]->reg(ra_,inst%d%s)", + // left_index, left_op_index, left_index, left_reg_index, left_index, left_op + // right_index, right_op, right_index, right_op_index, right_index, right_reg_index); // fprintf(fp, ")"); // switch( left_interface_type ) { @@ -3001,7 +3000,7 @@ void ArchDesc::define_oper_interface(FILE *fp, OperandForm &oper, FormDict &glob // Provide a non-NULL return for disp_as_type() that will allow adr_type() // to correctly compute the access type for alias analysis. // - // See BugId 4796752, operand indOffset32X in i486.ad + // See BugId 4796752, operand indOffset32X in x86_32.ad int idx = rep_var_to_constant_index(disp, oper, globals); fprintf(fp," virtual const TypePtr *disp_as_type() const { return _c%d; }\n", idx); } @@ -4192,14 +4191,7 @@ void ArchDesc::buildFrameMethods(FILE *fp_cpp) { fprintf(fp_cpp,"int Matcher::inline_cache_reg_encode() {"); fprintf(fp_cpp," return _regEncode[inline_cache_reg()]; }\n\n"); - // Interpreter's Method Register, mask definition, and encoding - fprintf(fp_cpp,"OptoReg::Name Matcher::interpreter_method_reg() {"); - fprintf(fp_cpp," return OptoReg::Name(%s_num); }\n\n", - _frame->_interpreter_method_reg); - fprintf(fp_cpp,"int Matcher::interpreter_method_reg_encode() {"); - fprintf(fp_cpp," return _regEncode[interpreter_method_reg()]; }\n\n"); - - // Interpreter's Frame Pointer Register, mask definition, and encoding + // Interpreter's Frame Pointer Register fprintf(fp_cpp,"OptoReg::Name Matcher::interpreter_frame_pointer_reg() {"); if (_frame->_interpreter_frame_pointer_reg == NULL) fprintf(fp_cpp," return OptoReg::Bad; }\n\n"); diff --git a/src/hotspot/share/asm/assembler.cpp b/src/hotspot/share/asm/assembler.cpp index 3a0b3ce0b3b..66e3052bdeb 100644 --- a/src/hotspot/share/asm/assembler.cpp +++ b/src/hotspot/share/asm/assembler.cpp @@ -199,19 +199,6 @@ void Label::patch_instructions(MacroAssembler* masm) { continue; } -#ifdef ASSERT - // Cross-section branches only work if the - // intermediate section boundaries are frozen. - if (target_sect != branch_sect) { - for (int n = MIN2(target_sect, branch_sect), - nlimit = (target_sect + branch_sect) - n; - n < nlimit; n++) { - CodeSection* cs = cb->code_section(n); - assert(cs->is_frozen(), "cross-section branch needs stable offsets"); - } - } -#endif //ASSERT - // Push the target offset into the branch instruction. masm->pd_patch_instruction(branch, target, file, line); } diff --git a/src/hotspot/share/asm/assembler.hpp b/src/hotspot/share/asm/assembler.hpp index a4c5d7d4554..8f76130522e 100644 --- a/src/hotspot/share/asm/assembler.hpp +++ b/src/hotspot/share/asm/assembler.hpp @@ -431,22 +431,6 @@ class AbstractAssembler : public ResourceObj { return ptr; } - // Bootstrapping aid to cope with delayed determination of constants. - // Returns a static address which will eventually contain the constant. - // The value zero (NULL) stands instead of a constant which is still uncomputed. - // Thus, the eventual value of the constant must not be zero. - // This is fine, since this is designed for embedding object field - // offsets in code which must be generated before the object class is loaded. - // Field offsets are never zero, since an object's header (mark word) - // is located at offset zero. - RegisterOrConstant delayed_value(int(*value_fn)(), Register tmp, int offset = 0); - RegisterOrConstant delayed_value(address(*value_fn)(), Register tmp, int offset = 0); - virtual RegisterOrConstant delayed_value_impl(intptr_t* delayed_value_addr, Register tmp, int offset) = 0; - // Last overloading is platform-dependent; look in assembler_.cpp. - static intptr_t* delayed_value_addr(int(*constant_fn)()); - static intptr_t* delayed_value_addr(address(*constant_fn)()); - static void update_delayed_values(); - // Bang stack to trigger StackOverflowError at a safe location // implementation delegates to machine-specific bang_stack_with_offset void generate_stack_overflow_check( int frame_size_in_bytes ); diff --git a/src/hotspot/share/asm/codeBuffer.cpp b/src/hotspot/share/asm/codeBuffer.cpp index 558dee23ecc..db2178f9977 100644 --- a/src/hotspot/share/asm/codeBuffer.cpp +++ b/src/hotspot/share/asm/codeBuffer.cpp @@ -140,7 +140,7 @@ CodeBuffer::~CodeBuffer() { // Claim is that stack allocation ensures resources are cleaned up. // This is resource clean up, let's hope that all were properly copied out. - free_strings(); + NOT_PRODUCT(free_strings();) #ifdef ASSERT // Save allocation type to execute assert in ~ResourceObj() @@ -176,28 +176,6 @@ void CodeBuffer::initialize_section_size(CodeSection* cs, csize_t size) { if (_insts.has_locs()) cs->initialize_locs(1); } -void CodeBuffer::freeze_section(CodeSection* cs) { - CodeSection* next_cs = (cs == consts())? NULL: code_section(cs->index()+1); - csize_t frozen_size = cs->size(); - if (next_cs != NULL) { - frozen_size = next_cs->align_at_start(frozen_size); - } - address old_limit = cs->limit(); - address new_limit = cs->start() + frozen_size; - relocInfo* old_locs_limit = cs->locs_limit(); - relocInfo* new_locs_limit = cs->locs_end(); - // Patch the limits. - cs->_limit = new_limit; - cs->_locs_limit = new_locs_limit; - cs->_frozen = true; - if (next_cs != NULL && !next_cs->is_allocated() && !next_cs->is_frozen()) { - // Give remaining buffer space to the following section. - next_cs->initialize(new_limit, old_limit - new_limit); - next_cs->initialize_shared_locs(new_locs_limit, - old_locs_limit - new_locs_limit); - } -} - void CodeBuffer::set_blob(BufferBlob* blob) { _blob = blob; if (blob != NULL) { @@ -257,23 +235,19 @@ int CodeBuffer::locator(address addr) const { return -1; } -address CodeBuffer::locator_address(int locator) const { - if (locator < 0) return NULL; - address start = code_section(locator_sect(locator))->start(); - return start + locator_pos(locator); -} bool CodeBuffer::is_backward_branch(Label& L) { return L.is_bound() && insts_end() <= locator_address(L.loc()); } +#ifndef PRODUCT address CodeBuffer::decode_begin() { address begin = _insts.start(); if (_decode_begin != NULL && _decode_begin > begin) begin = _decode_begin; return begin; } - +#endif // !PRODUCT GrowableArray* CodeBuffer::create_patch_overflow() { if (_overflow_arena == NULL) { @@ -505,18 +479,6 @@ void CodeBuffer::compute_final_layout(CodeBuffer* dest) const { } else { guarantee(padding == 0, "In first iteration no padding should be needed."); } - #ifdef ASSERT - if (prev_cs != NULL && prev_cs->is_frozen() && n < (SECT_LIMIT - 1)) { - // Make sure the ends still match up. - // This is important because a branch in a frozen section - // might target code in a following section, via a Label, - // and without a relocation record. See Label::patch_instructions. - address dest_start = buf+buf_offset; - csize_t start2start = cs->start() - prev_cs->start(); - csize_t dest_start2start = dest_start - prev_dest_cs->start(); - assert(start2start == dest_start2start, "cannot stretch frozen sect"); - } - #endif //ASSERT prev_dest_cs = dest_cs; prev_cs = cs; } @@ -752,7 +714,7 @@ void CodeBuffer::copy_code_to(CodeBlob* dest_blob) { relocate_code_to(&dest); // transfer strings and comments from buffer to blob - dest_blob->set_strings(_code_strings); + NOT_PRODUCT(dest_blob->set_strings(_code_strings);) // Done moving code bytes; were they the right size? assert((int)align_up(dest.total_content_size(), oopSize) == dest_blob->content_size(), "sanity"); @@ -895,9 +857,6 @@ void CodeBuffer::expand(CodeSection* which_cs, csize_t amount) { // Resizing must be allowed { if (blob() == NULL) return; // caller must check for blob == NULL - for (int n = 0; n < (int)SECT_LIMIT; n++) { - guarantee(!code_section(n)->is_frozen(), "resizing not allowed when frozen"); - } } // Figure new capacity for each section. @@ -957,12 +916,11 @@ void CodeBuffer::expand(CodeSection* which_cs, csize_t amount) { debug_only(Copy::fill_to_bytes(bxp->_total_start, bxp->_total_size, badCodeHeapFreeVal)); - _decode_begin = NULL; // sanity - // Make certain that the new sections are all snugly inside the new blob. verify_section_allocation(); #ifndef PRODUCT + _decode_begin = NULL; // sanity if (PrintNMethods && (WizardMode || Verbose)) { tty->print("expanded CodeBuffer:"); this->print(); @@ -1032,10 +990,6 @@ void CodeBuffer::log_section_sizes(const char* name) { #ifndef PRODUCT -void CodeSection::decode() { - Disassembler::decode(start(), end()); -} - void CodeBuffer::block_comment(intptr_t offset, const char * comment) { if (_collect_comments) { _code_strings.add_comment(offset, comment); @@ -1054,8 +1008,12 @@ class CodeString: public CHeapObj { CodeString* _prev; intptr_t _offset; + static long allocated_code_strings; + ~CodeString() { assert(_next == NULL && _prev == NULL, "wrong interface for freeing list"); + allocated_code_strings--; + log_trace(codestrings)("Freeing CodeString [%s] (%p)", _string, (void*)_string); os::free((void*)_string); } @@ -1064,12 +1022,14 @@ class CodeString: public CHeapObj { public: CodeString(const char * string, intptr_t offset = -1) : _next(NULL), _prev(NULL), _offset(offset) { + allocated_code_strings++; _string = os::strdup(string, mtCode); + log_trace(codestrings)("Created CodeString [%s] (%p)", _string, (void*)_string); } const char * string() const { return _string; } intptr_t offset() const { assert(_offset >= 0, "offset for non comment?"); return _offset; } - CodeString* next() const { return _next; } + CodeString* next() const { return _next; } void set_next(CodeString* next) { _next = next; @@ -1094,6 +1054,10 @@ class CodeString: public CHeapObj { } }; +// For tracing statistics. Will use raw increment/decrement, so it might not be +// exact +long CodeString::allocated_code_strings = 0; + CodeString* CodeStrings::find(intptr_t offset) const { CodeString* a = _strings->first_comment(); while (a != NULL && a->offset() != offset) { @@ -1116,7 +1080,7 @@ void CodeStrings::add_comment(intptr_t offset, const char * comment) { CodeString* c = new CodeString(comment, offset); CodeString* inspos = (_strings == NULL) ? NULL : find_last(offset); - if (inspos) { + if (inspos != NULL) { // insert after already existing comments with same offset c->set_next(inspos->next()); inspos->set_next(c); @@ -1130,21 +1094,10 @@ void CodeStrings::add_comment(intptr_t offset, const char * comment) { } } -void CodeStrings::assign(CodeStrings& other) { - other.check_valid(); - assert(is_null(), "Cannot assign onto non-empty CodeStrings"); - _strings = other._strings; - _strings_last = other._strings_last; -#ifdef ASSERT - _defunct = false; -#endif - other.set_null_and_invalidate(); -} - // Deep copy of CodeStrings for consistent memory management. -// Only used for actual disassembly so this is cheaper than reference counting -// for the "normal" fastdebug case. void CodeStrings::copy(CodeStrings& other) { + log_debug(codestrings)("Copying %d Codestring(s)", other.count()); + other.check_valid(); check_valid(); assert(is_null(), "Cannot copy onto non-empty CodeStrings"); @@ -1152,7 +1105,11 @@ void CodeStrings::copy(CodeStrings& other) { CodeString** ps = &_strings; CodeString* prev = NULL; while (n != NULL) { - *ps = new CodeString(n->string(),n->offset()); + if (n->is_comment()) { + *ps = new CodeString(n->string(), n->offset()); + } else { + *ps = new CodeString(n->string()); + } (*ps)->_prev = prev; prev = *ps; ps = &((*ps)->_next); @@ -1162,13 +1119,6 @@ void CodeStrings::copy(CodeStrings& other) { const char* CodeStrings::_prefix = " ;; "; // default: can be changed via set_prefix -// Check if any block comments are pending for the given offset. -bool CodeStrings::has_block_comment(intptr_t offset) const { - if (_strings == NULL) return false; - CodeString* c = find(offset); - return c != NULL; -} - void CodeStrings::print_block_comment(outputStream* stream, intptr_t offset) const { check_valid(); if (_strings != NULL) { @@ -1184,8 +1134,19 @@ void CodeStrings::print_block_comment(outputStream* stream, intptr_t offset) con } } -// Also sets isNull() +int CodeStrings::count() const { + int i = 0; + CodeString* s = _strings; + while (s != NULL) { + i++; + s = s->_next; + } + return i; +} + +// Also sets is_null() void CodeStrings::free() { + log_debug(codestrings)("Freeing %d out of approx. %ld CodeString(s), ", count(), CodeString::allocated_code_strings); CodeString* n = _strings; while (n) { // unlink the node from the list saving a pointer to the next @@ -1215,15 +1176,14 @@ const char* CodeStrings::add_string(const char * string) { void CodeBuffer::decode() { ttyLocker ttyl; - Disassembler::decode(decode_begin(), insts_end(), tty); + Disassembler::decode(decode_begin(), insts_end(), tty NOT_PRODUCT(COMMA &strings())); _decode_begin = insts_end(); } void CodeSection::print(const char* name) { csize_t locs_size = locs_end() - locs_start(); - tty->print_cr(" %7s.code = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d)%s", - name, p2i(start()), p2i(end()), p2i(limit()), size(), capacity(), - is_frozen()? " [frozen]": ""); + tty->print_cr(" %7s.code = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d)", + name, p2i(start()), p2i(end()), p2i(limit()), size(), capacity()); tty->print_cr(" %7s.locs = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d) point=%d", name, p2i(locs_start()), p2i(locs_end()), p2i(locs_limit()), locs_size, locs_capacity(), locs_point_off()); if (PrintRelocations) { @@ -1246,10 +1206,4 @@ void CodeBuffer::print() { } } -// Directly disassemble code buffer. -void CodeBuffer::decode(address start, address end) { - ttyLocker ttyl; - Disassembler::decode(this, start, end, tty); -} - #endif // PRODUCT diff --git a/src/hotspot/share/asm/codeBuffer.hpp b/src/hotspot/share/asm/codeBuffer.hpp index 6363ea41193..255342cbbd0 100644 --- a/src/hotspot/share/asm/codeBuffer.hpp +++ b/src/hotspot/share/asm/codeBuffer.hpp @@ -92,7 +92,6 @@ class CodeSection { relocInfo* _locs_limit; // first byte after relocation information buf address _locs_point; // last relocated position (grows upward) bool _locs_own; // did I allocate the locs myself? - bool _frozen; // no more expansion of this section bool _scratch_emit; // Buffer is used for scratch emit, don't relocate. char _index; // my section number (SECT_INST, etc.) CodeBuffer* _outer; // enclosing CodeBuffer @@ -109,7 +108,6 @@ class CodeSection { _locs_limit = NULL; _locs_point = NULL; _locs_own = false; - _frozen = false; _scratch_emit = false; debug_only(_index = (char)-1); debug_only(_outer = (CodeBuffer*)badAddress); @@ -161,12 +159,10 @@ class CodeSection { address locs_point() const { return _locs_point; } csize_t locs_point_off() const{ return (csize_t)(_locs_point - _start); } csize_t locs_capacity() const { return (csize_t)(_locs_limit - _locs_start); } - csize_t locs_remaining()const { return (csize_t)(_locs_limit - _locs_end); } int index() const { return _index; } bool is_allocated() const { return _start != NULL; } bool is_empty() const { return _start == _end; } - bool is_frozen() const { return _frozen; } bool has_locs() const { return _locs_end != NULL; } // Mark scratch buffer. @@ -184,8 +180,6 @@ class CodeSection { void set_end(address pc) { assert(allocates2(pc), "not in CodeBuffer memory: " INTPTR_FORMAT " <= " INTPTR_FORMAT " <= " INTPTR_FORMAT, p2i(_start), p2i(pc), p2i(_limit)); _end = pc; } void set_mark(address pc) { assert(contains2(pc), "not in codeBuffer"); _mark = pc; } - void set_mark_off(int offset) { assert(contains2(offset+_start),"not in codeBuffer"); - _mark = offset + _start; } void set_mark() { _mark = _end; } void clear_mark() { _mark = NULL; } @@ -259,10 +253,6 @@ class CodeSection { csize_t align_at_start(csize_t off) const { return (csize_t) align_up(off, alignment()); } - // Mark a section frozen. Assign its remaining space to - // the following section. It will never expand after this point. - inline void freeze(); // { _outer->freeze_section(this); } - // Ensure there's enough space left in the current section. // Return true if there was an expansion. bool maybe_expand_to_ensure_remaining(csize_t amount); @@ -284,20 +274,18 @@ class CodeStrings { bool _defunct; // Zero bit pattern is "valid", see memset call in decode_env::decode_env #endif static const char* _prefix; // defaults to " ;; " -#endif CodeString* find(intptr_t offset) const; CodeString* find_last(intptr_t offset) const; void set_null_and_invalidate() { -#ifndef PRODUCT _strings = NULL; _strings_last = NULL; #ifdef ASSERT _defunct = true; -#endif #endif } +#endif public: CodeStrings() { @@ -310,6 +298,7 @@ class CodeStrings { #endif } +#ifndef PRODUCT bool is_null() { #ifdef ASSERT return _strings == NULL; @@ -318,30 +307,25 @@ class CodeStrings { #endif } - const char* add_string(const char * string) PRODUCT_RETURN_(return NULL;); + const char* add_string(const char * string); - void add_comment(intptr_t offset, const char * comment) PRODUCT_RETURN; - bool has_block_comment(intptr_t offset) const; - void print_block_comment(outputStream* stream, intptr_t offset) const PRODUCT_RETURN; - // MOVE strings from other to this; invalidate other. - void assign(CodeStrings& other) PRODUCT_RETURN; + void add_comment(intptr_t offset, const char * comment); + void print_block_comment(outputStream* stream, intptr_t offset) const; + int count() const; // COPY strings from other to this; leave other valid. - void copy(CodeStrings& other) PRODUCT_RETURN; + void copy(CodeStrings& other); // FREE strings; invalidate this. - void free() PRODUCT_RETURN; + void free(); // Guarantee that _strings are used at most once; assign and free invalidate a buffer. inline void check_valid() const { -#ifdef ASSERT assert(!_defunct, "Use of invalid CodeStrings"); -#endif } static void set_prefix(const char *prefix) { -#ifndef PRODUCT _prefix = prefix; -#endif } +#endif // !PRODUCT }; // A CodeBuffer describes a memory space into which assembly @@ -410,8 +394,7 @@ class CodeBuffer: public StackObj { csize_t _total_size; // size in bytes of combined memory buffer OopRecorder* _oop_recorder; - CodeStrings _code_strings; - bool _collect_comments; // Indicate if we need to collect block comments at all. + OopRecorder _default_oop_recorder; // override with initialize_oop_recorder Arena* _overflow_arena; @@ -421,8 +404,12 @@ class CodeBuffer: public StackObj { bool _immutable_PIC; #endif - address _decode_begin; // start address for decode +#ifndef PRODUCT + CodeStrings _code_strings; + bool _collect_comments; // Indicate if we need to collect block comments at all. + address _decode_begin; // start address for decode address decode_begin(); +#endif void initialize_misc(const char * name) { // all pointers other than code_start/end and those inside the sections @@ -431,14 +418,15 @@ class CodeBuffer: public StackObj { _before_expand = NULL; _blob = NULL; _oop_recorder = NULL; - _decode_begin = NULL; _overflow_arena = NULL; - _code_strings = CodeStrings(); _last_insn = NULL; #if INCLUDE_AOT _immutable_PIC = false; #endif +#ifndef PRODUCT + _decode_begin = NULL; + _code_strings = CodeStrings(); // Collect block comments, but restrict collection to cases where a disassembly is output. _collect_comments = ( PrintAssembly || PrintStubCode @@ -447,6 +435,7 @@ class CodeBuffer: public StackObj { || PrintSignatureHandlers || UnlockDiagnosticVMOptions ); +#endif } void initialize(address code_start, csize_t code_size) { @@ -464,8 +453,6 @@ class CodeBuffer: public StackObj { void initialize_section_size(CodeSection* cs, csize_t size); - void freeze_section(CodeSection* cs); - // helper for CodeBuffer::expand() void take_over_code_from(CodeBuffer* cs); @@ -557,7 +544,11 @@ class CodeBuffer: public StackObj { static int locator_sect(int locator) { return locator & sect_mask; } static int locator(int pos, int sect) { return (pos << sect_bits) | sect; } int locator(address addr) const; - address locator_address(int locator) const; + address locator_address(int locator) const { + if (locator < 0) return NULL; + address start = code_section(locator_sect(locator))->start(); + return start + locator_pos(locator); + } // Heuristic for pre-packing the taken/not-taken bit of a predicted branch. bool is_backward_branch(Label& L); @@ -574,10 +565,8 @@ class CodeBuffer: public StackObj { address insts_begin() const { return _insts.start(); } address insts_end() const { return _insts.end(); } void set_insts_end(address end) { _insts.set_end(end); } - address insts_limit() const { return _insts.limit(); } address insts_mark() const { return _insts.mark(); } void set_insts_mark() { _insts.set_mark(); } - void clear_insts_mark() { _insts.clear_mark(); } // is there anything in the buffer other than the current section? bool is_pure() const { return insts_size() == total_content_size(); } @@ -635,35 +624,21 @@ class CodeBuffer: public StackObj { // Override default oop recorder. void initialize_oop_recorder(OopRecorder* r); - OopRecorder* oop_recorder() const { return _oop_recorder; } - CodeStrings& strings() { return _code_strings; } + OopRecorder* oop_recorder() const { return _oop_recorder; } address last_insn() const { return _last_insn; } void set_last_insn(address a) { _last_insn = a; } void clear_last_insn() { set_last_insn(NULL); } +#ifndef PRODUCT + CodeStrings& strings() { return _code_strings; } + void free_strings() { if (!_code_strings.is_null()) { _code_strings.free(); // sets _strings Null as a side-effect. } } - - // Directly disassemble code buffer. - // Print the comment associated with offset on stream, if there is one. - virtual void print_block_comment(outputStream* stream, address block_begin) { -#ifndef PRODUCT - intptr_t offset = (intptr_t)(block_begin - _total_start); // I assume total_start is not correct for all code sections. - _code_strings.print_block_comment(stream, offset); -#endif - } - bool has_block_comment(address block_begin) { -#ifndef PRODUCT - intptr_t offset = (intptr_t)(block_begin - _total_start); // I assume total_start is not correct for all code sections. - return _code_strings.has_block_comment(offset); -#else - return false; #endif - } // Code generation void relocate(address at, RelocationHolder const& rspec, int format = 0) { @@ -688,9 +663,6 @@ class CodeBuffer: public StackObj { } } - // Transform an address from the code in this code buffer to a specified code buffer - address transform_address(const CodeBuffer &cb, address addr) const; - void block_comment(intptr_t offset, const char * comment) PRODUCT_RETURN; const char* code_string(const char* str) PRODUCT_RETURN_(return NULL;); @@ -719,11 +691,6 @@ class CodeBuffer: public StackObj { }; - -inline void CodeSection::freeze() { - _outer->freeze_section(this); -} - inline bool CodeSection::maybe_expand_to_ensure_remaining(csize_t amount) { if (remaining() < amount) { _outer->expand(this, amount); return true; } return false; diff --git a/src/hotspot/share/c1/c1_CFGPrinter.cpp b/src/hotspot/share/c1/c1_CFGPrinter.cpp index 21244d92a18..9cd617d5428 100644 --- a/src/hotspot/share/c1/c1_CFGPrinter.cpp +++ b/src/hotspot/share/c1/c1_CFGPrinter.cpp @@ -325,7 +325,7 @@ void CFGPrinterOutput::print_intervals(IntervalList* intervals, const char* name for (int i = 0; i < intervals->length(); i++) { if (intervals->at(i) != NULL) { - intervals->at(i)->print_on(output()); + intervals->at(i)->print_on(output(), true); } } diff --git a/src/hotspot/share/c1/c1_CodeStubs.hpp b/src/hotspot/share/c1/c1_CodeStubs.hpp index cd45e39f0b6..961f33ca69e 100644 --- a/src/hotspot/share/c1/c1_CodeStubs.hpp +++ b/src/hotspot/share/c1/c1_CodeStubs.hpp @@ -89,6 +89,28 @@ class CodeStubList: public GrowableArray { } }; +class C1SafepointPollStub: public CodeStub { + private: + uintptr_t _safepoint_offset; + + public: + C1SafepointPollStub() : + _safepoint_offset(0) { + } + + uintptr_t safepoint_offset() { return _safepoint_offset; } + void set_safepoint_offset(uintptr_t safepoint_offset) { _safepoint_offset = safepoint_offset; } + + virtual void emit_code(LIR_Assembler* e); + virtual void visit(LIR_OpVisitState* visitor) { + // don't pass in the code emit info since it's processed in the fast path + visitor->do_slow_case(); + } +#ifndef PRODUCT + virtual void print_name(outputStream* out) const { out->print("C1SafepointPollStub"); } +#endif // PRODUCT +}; + class CounterOverflowStub: public CodeStub { private: CodeEmitInfo* _info; diff --git a/src/hotspot/share/c1/c1_IR.hpp b/src/hotspot/share/c1/c1_IR.hpp index ab63b32c14b..10501291e88 100644 --- a/src/hotspot/share/c1/c1_IR.hpp +++ b/src/hotspot/share/c1/c1_IR.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -244,7 +244,11 @@ class IRScopeDebugInfo: public CompilationResourceObj { bool reexecute = topmost ? should_reexecute() : false; bool return_oop = false; // This flag will be ignored since it used only for C2 with escape analysis. bool rethrow_exception = false; - recorder->describe_scope(pc_offset, methodHandle(), scope()->method(), bci(), reexecute, rethrow_exception, is_method_handle_invoke, return_oop, locvals, expvals, monvals); + bool has_ea_local_in_scope = false; + bool arg_escape = false; + recorder->describe_scope(pc_offset, methodHandle(), scope()->method(), bci(), + reexecute, rethrow_exception, is_method_handle_invoke, return_oop, + has_ea_local_in_scope, arg_escape, locvals, expvals, monvals); } }; diff --git a/src/hotspot/share/c1/c1_LIR.cpp b/src/hotspot/share/c1/c1_LIR.cpp index e0e2b2fb6e4..1d6d33a2be1 100644 --- a/src/hotspot/share/c1/c1_LIR.cpp +++ b/src/hotspot/share/c1/c1_LIR.cpp @@ -23,11 +23,13 @@ */ #include "precompiled.hpp" +#include "c1/c1_CodeStubs.hpp" #include "c1/c1_InstructionPrinter.hpp" #include "c1/c1_LIR.hpp" #include "c1/c1_LIRAssembler.hpp" #include "c1/c1_ValueStack.hpp" #include "ci/ciInstance.hpp" +#include "runtime/safepointMechanism.inline.hpp" #include "runtime/sharedRuntime.hpp" Register LIR_OprDesc::as_register() const { @@ -447,7 +449,6 @@ void LIR_OpVisitState::visit(LIR_Op* op) { case lir_fld: // input always valid, result and info always invalid case lir_push: // input always valid, result and info always invalid case lir_pop: // input always valid, result and info always invalid - case lir_return: // input always valid, result and info always invalid case lir_leal: // input and result always valid, info always invalid case lir_monaddr: // input and result always valid, info always invalid case lir_null_check: // input and info always valid, result always invalid @@ -463,6 +464,19 @@ void LIR_OpVisitState::visit(LIR_Op* op) { break; } + case lir_return: + { + assert(op->as_OpReturn() != NULL, "must be"); + LIR_OpReturn* op_ret = (LIR_OpReturn*)op; + + if (op_ret->_info) do_info(op_ret->_info); + if (op_ret->_opr->is_valid()) do_input(op_ret->_opr); + if (op_ret->_result->is_valid()) do_output(op_ret->_result); + if (op_ret->stub() != NULL) do_stub(op_ret->stub()); + + break; + } + case lir_safepoint: { assert(op->as_Op1() != NULL, "must be"); @@ -948,6 +962,15 @@ bool LIR_OpVisitState::no_operands(LIR_Op* op) { } #endif +// LIR_OpReturn +LIR_OpReturn::LIR_OpReturn(LIR_Opr opr) : + LIR_Op1(lir_return, opr, (CodeEmitInfo*)NULL /* info */), + _stub(NULL) { + if (VM_Version::supports_stack_watermark_barrier()) { + _stub = new C1SafepointPollStub(); + } +} + //--------------------------------------------------- diff --git a/src/hotspot/share/c1/c1_LIR.hpp b/src/hotspot/share/c1/c1_LIR.hpp index 787a22fff11..dcd2d50b327 100644 --- a/src/hotspot/share/c1/c1_LIR.hpp +++ b/src/hotspot/share/c1/c1_LIR.hpp @@ -36,6 +36,7 @@ class LIR_Assembler; class CodeEmitInfo; class CodeStub; class CodeStubList; +class C1SafepointPollStub; class ArrayCopyStub; class LIR_Op; class ciType; @@ -856,6 +857,7 @@ class LIR_Op1; class LIR_OpBranch; class LIR_OpConvert; class LIR_OpAllocObj; +class LIR_OpReturn; class LIR_OpRoundFP; class LIR_Op2; class LIR_OpDelay; @@ -1116,6 +1118,7 @@ class LIR_Op: public CompilationResourceObj { virtual LIR_OpAllocObj* as_OpAllocObj() { return NULL; } virtual LIR_OpRoundFP* as_OpRoundFP() { return NULL; } virtual LIR_OpBranch* as_OpBranch() { return NULL; } + virtual LIR_OpReturn* as_OpReturn() { return NULL; } virtual LIR_OpRTCall* as_OpRTCall() { return NULL; } virtual LIR_OpConvert* as_OpConvert() { return NULL; } virtual LIR_Op0* as_Op0() { return NULL; } @@ -1439,6 +1442,18 @@ class LIR_OpBranch: public LIR_Op { virtual void print_instr(outputStream* out) const PRODUCT_RETURN; }; +class LIR_OpReturn: public LIR_Op1 { + friend class LIR_OpVisitState; + + private: + C1SafepointPollStub* _stub; + + public: + LIR_OpReturn(LIR_Opr opr); + + C1SafepointPollStub* stub() const { return _stub; } + virtual LIR_OpReturn* as_OpReturn() { return this; } +}; class ConversionStub; @@ -2094,9 +2109,8 @@ class LIR_List: public CompilationResourceObj { void metadata2reg (Metadata* o, LIR_Opr reg) { assert(reg->type() == T_METADATA, "bad reg"); append(new LIR_Op1(lir_move, LIR_OprFact::metadataConst(o), reg)); } void klass2reg_patch(Metadata* o, LIR_Opr reg, CodeEmitInfo* info); - void return_op(LIR_Opr result) { append(new LIR_Op1(lir_return, result)); } - void safepoint(LIR_Opr tmp, CodeEmitInfo* info) { append(new LIR_Op1(lir_safepoint, tmp, info)); } + void return_op(LIR_Opr result) { append(new LIR_OpReturn(result)); } void convert(Bytecodes::Code code, LIR_Opr left, LIR_Opr dst, ConversionStub* stub = NULL/*, bool is_32bit = false*/) { append(new LIR_OpConvert(code, left, dst, stub)); } diff --git a/src/hotspot/share/c1/c1_LIRAssembler.cpp b/src/hotspot/share/c1/c1_LIRAssembler.cpp index 370f2dad241..c625ac063a6 100644 --- a/src/hotspot/share/c1/c1_LIRAssembler.cpp +++ b/src/hotspot/share/c1/c1_LIRAssembler.cpp @@ -521,9 +521,15 @@ void LIR_Assembler::emit_op1(LIR_Op1* op) { break; } - case lir_return: - return_op(op->in_opr()); + case lir_return: { + assert(op->as_OpReturn() != NULL, "sanity"); + LIR_OpReturn *ret_op = (LIR_OpReturn*)op; + return_op(ret_op->in_opr(), ret_op->stub()); + if (ret_op->stub() != NULL) { + append_code_stub(ret_op->stub()); + } break; + } case lir_safepoint: if (compilation()->debug_info_recorder()->last_pc_offset() == code_offset()) { diff --git a/src/hotspot/share/c1/c1_LIRAssembler.hpp b/src/hotspot/share/c1/c1_LIRAssembler.hpp index ab4f82ddcd3..a40b084a7bf 100644 --- a/src/hotspot/share/c1/c1_LIRAssembler.hpp +++ b/src/hotspot/share/c1/c1_LIRAssembler.hpp @@ -157,7 +157,7 @@ class LIR_Assembler: public CompilationResourceObj { // particular sparc uses this for delay slot filling. void peephole(LIR_List* list); - void return_op(LIR_Opr result); + void return_op(LIR_Opr result, C1SafepointPollStub* code_stub); // returns offset of poll instruction int safepoint_poll(LIR_Opr result, CodeEmitInfo* info); diff --git a/src/hotspot/share/c1/c1_LinearScan.cpp b/src/hotspot/share/c1/c1_LinearScan.cpp index f5713821a81..782fa7ada03 100644 --- a/src/hotspot/share/c1/c1_LinearScan.cpp +++ b/src/hotspot/share/c1/c1_LinearScan.cpp @@ -3212,6 +3212,12 @@ void LinearScan::print_reg_num(outputStream* out, int reg_num) { return; } + LIR_Opr opr = get_operand(reg_num); + assert(opr->is_valid(), "unknown register"); + opr->print(out); +} + +LIR_Opr LinearScan::get_operand(int reg_num) { LIR_Opr opr = LIR_OprFact::illegal(); #ifdef X86 @@ -3231,9 +3237,9 @@ void LinearScan::print_reg_num(outputStream* out, int reg_num) { opr = LIR_OprFact::single_xmm(reg_num - pd_first_xmm_reg); #endif } else { - assert(false, "unknown register"); + // reg_num == -1 or a virtual register, return the illegal operand } - opr->print(out); + return opr; } Interval* LinearScan::find_interval_at(int reg_num) const { @@ -4598,7 +4604,7 @@ bool Interval::intersects_any_children_of(Interval* interval) const { #ifndef PRODUCT -void Interval::print_on(outputStream* out) const { +void Interval::print_on(outputStream* out, bool is_cfg_printer) const { const char* SpillState2Name[] = { "no definition", "no spill store", "one spill store", "store at definition", "start in memory", "no optimization" }; const char* UseKind2Name[] = { "N", "L", "S", "M" }; @@ -4608,18 +4614,29 @@ void Interval::print_on(outputStream* out) const { } else { type_name = type2name(type()); } - out->print("%d %s ", reg_num(), type_name); - if (reg_num() < LIR_OprDesc::vreg_base) { - LinearScan::print_reg_num(out, assigned_reg()); - } else if (assigned_reg() != -1 && (LinearScan::num_physical_regs(type()) == 1 || assigned_regHi() != -1)) { - LinearScan::calc_operand_for_interval(this)->print(out); + + if (is_cfg_printer) { + // Special version for compatibility with C1 Visualizer. + LIR_Opr opr = LinearScan::get_operand(reg_num()); + if (opr->is_valid()) { + out->print("\""); + opr->print(out); + out->print("\" "); + } } else { - // Virtual register that has no assigned register yet. - out->print("[ANY]"); + // Improved output for normal debugging. + if (reg_num() < LIR_OprDesc::vreg_base) { + LinearScan::print_reg_num(out, assigned_reg()); + } else if (assigned_reg() != -1 && (LinearScan::num_physical_regs(type()) == 1 || assigned_regHi() != -1)) { + LinearScan::calc_operand_for_interval(this)->print(out); + } else { + // Virtual register that has no assigned register yet. + out->print("[ANY]"); + } + out->print(" "); } - - out->print(" %d %d ", split_parent()->reg_num(), (register_hint(false) != NULL ? register_hint(false)->reg_num() : -1)); + out->print("%d %d ", split_parent()->reg_num(), (register_hint(false) != NULL ? register_hint(false)->reg_num() : -1)); // print ranges Range* cur = _first; @@ -5436,7 +5453,7 @@ bool LinearScanWalker::alloc_free_reg(Interval* cur) { hint_reg = register_hint->assigned_reg(); hint_regHi = register_hint->assigned_regHi(); - if (allocator()->is_precolored_cpu_interval(register_hint)) { + if (_num_phys_regs == 2 && allocator()->is_precolored_cpu_interval(register_hint)) { assert(hint_reg != any_reg && hint_regHi == any_reg, "must be for fixed intervals"); hint_regHi = hint_reg + 1; // connect e.g. eax-edx } @@ -6410,7 +6427,7 @@ void ControlFlowOptimizer::delete_jumps_to_return(BlockList* code) { if (pred_last_branch->block() == block && pred_last_branch->cond() == lir_cond_always && pred_last_branch->info() == NULL) { // replace the jump to a return with a direct return // Note: currently the edge between the blocks is not deleted - pred_instructions->at_put(pred_instructions->length() - 1, new LIR_Op1(lir_return, return_opr)); + pred_instructions->at_put(pred_instructions->length() - 1, new LIR_OpReturn(return_opr)); #ifdef ASSERT return_converted.set_bit(pred->block_id()); #endif diff --git a/src/hotspot/share/c1/c1_LinearScan.hpp b/src/hotspot/share/c1/c1_LinearScan.hpp index 8d86a0d7836..0249453d9c1 100644 --- a/src/hotspot/share/c1/c1_LinearScan.hpp +++ b/src/hotspot/share/c1/c1_LinearScan.hpp @@ -369,6 +369,7 @@ class LinearScan : public CompilationResourceObj { void print_lir(int level, const char* label, bool hir_valid = true); static void print_reg_num(int reg_num) { print_reg_num(tty, reg_num); } static void print_reg_num(outputStream* out, int reg_num); + static LIR_Opr get_operand(int reg_num); #endif #ifdef ASSERT @@ -633,7 +634,11 @@ class Interval : public CompilationResourceObj { // printing #ifndef PRODUCT void print() const { print_on(tty); } - void print_on(outputStream* out) const; + void print_on(outputStream* out) const { + print_on(out, false); + } + // Special version for compatibility with C1 Visualizer. + void print_on(outputStream* out, bool is_cfg_printer) const; // Used for debugging void print_parent() const; diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp index 9c433f648ec..6c776670cac 100644 --- a/src/hotspot/share/c1/c1_Runtime1.cpp +++ b/src/hotspot/share/c1/c1_Runtime1.cpp @@ -63,6 +63,7 @@ #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaCalls.hpp" #include "runtime/sharedRuntime.hpp" +#include "runtime/stackWatermarkSet.hpp" #include "runtime/threadCritical.hpp" #include "runtime/vframe.inline.hpp" #include "runtime/vframeArray.hpp" @@ -505,6 +506,17 @@ JRT_ENTRY_NO_ASYNC(static address, exception_handler_for_pc_helper(JavaThread* t thread->set_is_method_handle_return(false); Handle exception(thread, ex); + + // This function is called when we are about to throw an exception. Therefore, + // we have to poll the stack watermark barrier to make sure that not yet safe + // stack frames are made safe before returning into them. + if (thread->last_frame().cb() == Runtime1::blob_for(Runtime1::handle_exception_from_callee_id)) { + // The Runtime1::handle_exception_from_callee_id handler is invoked after the + // frame has been unwound. It instead builds its own stub frame, to call the + // runtime. But the throwing frame has already been unwound here. + StackWatermarkSet::after_unwind(thread); + } + nm = CodeCache::find_nmethod(pc); assert(nm != NULL, "this is not an nmethod"); // Adjust the pc as needed/ diff --git a/src/hotspot/share/ci/bcEscapeAnalyzer.hpp b/src/hotspot/share/ci/bcEscapeAnalyzer.hpp index 8b84a24c449..db77b9c580e 100644 --- a/src/hotspot/share/ci/bcEscapeAnalyzer.hpp +++ b/src/hotspot/share/ci/bcEscapeAnalyzer.hpp @@ -88,8 +88,6 @@ class BCEscapeAnalyzer : public ResourceObj { void set_modified(ArgumentMap vars, int offs, int size); bool is_recursive_call(ciMethod* callee); - void add_dependence(ciKlass *klass, ciMethod *meth); - void propagate_dependencies(ciMethod *meth); void invoke(StateInfo &state, Bytecodes::Code code, ciMethod* target, ciKlass* holder); void iterate_one_block(ciBlock *blk, StateInfo &state, GrowableArray &successors); diff --git a/src/hotspot/share/ci/ciEnv.cpp b/src/hotspot/share/ci/ciEnv.cpp index 43a66cc7a08..f4d550460d6 100644 --- a/src/hotspot/share/ci/ciEnv.cpp +++ b/src/hotspot/share/ci/ciEnv.cpp @@ -59,6 +59,7 @@ #include "oops/objArrayOop.inline.hpp" #include "oops/oop.inline.hpp" #include "prims/jvmtiExport.hpp" +#include "prims/methodHandles.hpp" #include "runtime/handles.inline.hpp" #include "runtime/init.hpp" #include "runtime/reflection.hpp" @@ -241,6 +242,7 @@ bool ciEnv::cache_jvmti_state() { _jvmti_can_post_on_exceptions = JvmtiExport::can_post_on_exceptions(); _jvmti_can_pop_frame = JvmtiExport::can_pop_frame(); _jvmti_can_get_owned_monitor_info = JvmtiExport::can_get_owned_monitor_info(); + _jvmti_can_walk_any_space = JvmtiExport::can_walk_any_space(); return _task != NULL && _task->method()->is_old(); } @@ -270,6 +272,10 @@ bool ciEnv::jvmti_state_changed() const { JvmtiExport::can_get_owned_monitor_info()) { return true; } + if (!_jvmti_can_walk_any_space && + JvmtiExport::can_walk_any_space()) { + return true; + } return false; } @@ -755,34 +761,29 @@ Method* ciEnv::lookup_method(ciInstanceKlass* accessor, Symbol* sig, Bytecodes::Code bc, constantTag tag) { - // Accessibility checks are performed in ciEnv::get_method_by_index_impl. - assert(check_klass_accessibility(accessor, holder->get_Klass()), "holder not accessible"); - InstanceKlass* accessor_klass = accessor->get_instanceKlass(); Klass* holder_klass = holder->get_Klass(); - Method* dest_method; - LinkInfo link_info(holder_klass, name, sig, accessor_klass, LinkInfo::AccessCheck::required, LinkInfo::LoaderConstraintCheck::required, tag); + + // Accessibility checks are performed in ciEnv::get_method_by_index_impl. + assert(check_klass_accessibility(accessor, holder_klass), "holder not accessible"); + + LinkInfo link_info(holder_klass, name, sig, accessor_klass, + LinkInfo::AccessCheck::required, + LinkInfo::LoaderConstraintCheck::required, + tag); switch (bc) { - case Bytecodes::_invokestatic: - dest_method = - LinkResolver::resolve_static_call_or_null(link_info); - break; - case Bytecodes::_invokespecial: - dest_method = - LinkResolver::resolve_special_call_or_null(link_info); - break; - case Bytecodes::_invokeinterface: - dest_method = - LinkResolver::linktime_resolve_interface_method_or_null(link_info); - break; - case Bytecodes::_invokevirtual: - dest_method = - LinkResolver::linktime_resolve_virtual_method_or_null(link_info); - break; - default: ShouldNotReachHere(); + case Bytecodes::_invokestatic: + return LinkResolver::resolve_static_call_or_null(link_info); + case Bytecodes::_invokespecial: + return LinkResolver::resolve_special_call_or_null(link_info); + case Bytecodes::_invokeinterface: + return LinkResolver::linktime_resolve_interface_method_or_null(link_info); + case Bytecodes::_invokevirtual: + return LinkResolver::linktime_resolve_virtual_method_or_null(link_info); + default: + fatal("Unhandled bytecode: %s", Bytecodes::name(bc)); + return NULL; // silence compiler warnings } - - return dest_method; } diff --git a/src/hotspot/share/ci/ciEnv.hpp b/src/hotspot/share/ci/ciEnv.hpp index 65949bde48a..da7b65a1325 100644 --- a/src/hotspot/share/ci/ciEnv.hpp +++ b/src/hotspot/share/ci/ciEnv.hpp @@ -27,6 +27,7 @@ #include "ci/ciClassList.hpp" #include "ci/ciObjectFactory.hpp" +#include "classfile/systemDictionary.hpp" #include "code/debugInfoRec.hpp" #include "code/dependencies.hpp" #include "code/exceptionHandlerTable.hpp" @@ -73,6 +74,7 @@ class ciEnv : StackObj { bool _jvmti_can_post_on_exceptions; bool _jvmti_can_pop_frame; bool _jvmti_can_get_owned_monitor_info; // includes can_get_owned_monitor_stack_depth_info + bool _jvmti_can_walk_any_space; // Cache DTrace flags bool _dtrace_extended_probes; @@ -348,6 +350,7 @@ class ciEnv : StackObj { bool jvmti_can_hotswap_or_post_breakpoint() const { return _jvmti_can_hotswap_or_post_breakpoint; } bool jvmti_can_post_on_exceptions() const { return _jvmti_can_post_on_exceptions; } bool jvmti_can_get_owned_monitor_info() const { return _jvmti_can_get_owned_monitor_info; } + bool jvmti_can_walk_any_space() const { return _jvmti_can_walk_any_space; } // Cache DTrace flags void cache_dtrace_flags(); diff --git a/src/hotspot/share/ci/ciField.cpp b/src/hotspot/share/ci/ciField.cpp index df4dfc9ae63..f5abad6bc10 100644 --- a/src/hotspot/share/ci/ciField.cpp +++ b/src/hotspot/share/ci/ciField.cpp @@ -222,6 +222,7 @@ static bool trust_final_non_static_fields(ciInstanceKlass* holder) { // Even if general trusting is disabled, trust system-built closures in these packages. if (holder->is_in_package("java/lang/invoke") || holder->is_in_package("sun/invoke") || holder->is_in_package("jdk/internal/foreign") || holder->is_in_package("jdk/incubator/foreign") || + holder->is_in_package("jdk/internal/vm/vector") || holder->is_in_package("jdk/incubator/vector") || holder->is_in_package("java/lang")) return true; // Trust hidden classes and VM unsafe anonymous classes. They are created via diff --git a/src/hotspot/share/ci/ciMemberName.cpp b/src/hotspot/share/ci/ciMemberName.cpp index 978ba2b975c..4306e6e649e 100644 --- a/src/hotspot/share/ci/ciMemberName.cpp +++ b/src/hotspot/share/ci/ciMemberName.cpp @@ -34,11 +34,6 @@ // Return: MN.vmtarget ciMethod* ciMemberName::get_vmtarget() const { VM_ENTRY_MARK; - // FIXME: Share code with ciMethodHandle::get_vmtarget - Metadata* vmtarget = java_lang_invoke_MemberName::vmtarget(get_oop()); - if (vmtarget->is_method()) - return CURRENT_ENV->get_method((Method*) vmtarget); - // FIXME: What if the vmtarget is a Klass? - assert(false, ""); - return NULL; + Method* vmtarget = java_lang_invoke_MemberName::vmtarget(get_oop()); + return CURRENT_ENV->get_method(vmtarget); } diff --git a/src/hotspot/share/ci/ciMethod.cpp b/src/hotspot/share/ci/ciMethod.cpp index 3fc9a0f6ebd..de6aace25a9 100644 --- a/src/hotspot/share/ci/ciMethod.cpp +++ b/src/hotspot/share/ci/ciMethod.cpp @@ -44,6 +44,7 @@ #include "oops/generateOopMap.hpp" #include "oops/method.inline.hpp" #include "oops/oop.inline.hpp" +#include "prims/methodHandles.hpp" #include "prims/nativeLookup.hpp" #include "runtime/deoptimization.hpp" #include "runtime/handles.inline.hpp" @@ -1352,6 +1353,11 @@ bool ciMethod::is_unboxing_method() const { return false; } +bool ciMethod::is_vector_method() const { + return (holder() == ciEnv::current()->vector_VectorSupport_klass()) && + (intrinsic_id() != vmIntrinsics::_none); +} + BCEscapeAnalyzer *ciMethod::get_bcea() { #ifdef COMPILER2 if (_bcea == NULL) { diff --git a/src/hotspot/share/ci/ciMethod.hpp b/src/hotspot/share/ci/ciMethod.hpp index 371f974a9ae..37eb0da8db3 100644 --- a/src/hotspot/share/ci/ciMethod.hpp +++ b/src/hotspot/share/ci/ciMethod.hpp @@ -30,7 +30,7 @@ #include "ci/ciObject.hpp" #include "ci/ciSignature.hpp" #include "compiler/methodLiveness.hpp" -#include "prims/methodHandles.hpp" +#include "runtime/handles.hpp" #include "utilities/bitMap.hpp" class ciMethodBlocks; @@ -356,6 +356,7 @@ class ciMethod : public ciMetadata { bool has_reserved_stack_access() const { return _has_reserved_stack_access; } bool is_boxing_method() const; bool is_unboxing_method() const; + bool is_vector_method() const; bool is_object_initializer() const; bool can_be_statically_bound(ciInstanceKlass* context) const; diff --git a/src/hotspot/share/ci/ciMethodData.cpp b/src/hotspot/share/ci/ciMethodData.cpp index fae10b58eb2..76dde0b9f74 100644 --- a/src/hotspot/share/ci/ciMethodData.cpp +++ b/src/hotspot/share/ci/ciMethodData.cpp @@ -335,7 +335,10 @@ ciProfileData* ciMethodData::data_at(int data_index) { return NULL; } DataLayout* data_layout = data_layout_at(data_index); + return data_from(data_layout); +} +ciProfileData* ciMethodData::data_from(DataLayout* data_layout) { switch (data_layout->tag()) { case DataLayout::no_tag: default: @@ -376,6 +379,16 @@ ciProfileData* ciMethodData::next_data(ciProfileData* current) { return next; } +DataLayout* ciMethodData::next_data_layout(DataLayout* current) { + int current_index = dp_to_di((address)current); + int next_index = current_index + current->size_in_bytes(); + if (out_of_bounds(next_index)) { + return NULL; + } + DataLayout* next = data_layout_at(next_index); + return next; +} + ciProfileData* ciMethodData::bci_to_extra_data(int bci, ciMethod* m, bool& two_free_slots) { DataLayout* dp = extra_data_base(); DataLayout* end = args_data_limit(); @@ -413,12 +426,12 @@ ciProfileData* ciMethodData::bci_to_extra_data(int bci, ciMethod* m, bool& two_f ciProfileData* ciMethodData::bci_to_data(int bci, ciMethod* m) { // If m is not NULL we look for a SpeculativeTrapData entry if (m == NULL) { - ciProfileData* data = data_before(bci); - for ( ; is_valid(data); data = next_data(data)) { - if (data->bci() == bci) { - set_hint_di(dp_to_di(data->dp())); - return data; - } else if (data->bci() > bci) { + DataLayout* data_layout = data_layout_before(bci); + for ( ; is_valid(data_layout); data_layout = next_data_layout(data_layout)) { + if (data_layout->bci() == bci) { + set_hint_di(dp_to_di((address)data_layout)); + return data_from(data_layout); + } else if (data_layout->bci() > bci) { break; } } diff --git a/src/hotspot/share/ci/ciMethodData.hpp b/src/hotspot/share/ci/ciMethodData.hpp index 09f692fd7a6..2e78e5f3bad 100644 --- a/src/hotspot/share/ci/ciMethodData.hpp +++ b/src/hotspot/share/ci/ciMethodData.hpp @@ -379,7 +379,7 @@ class ciMethodData : public ciMetadata { // Data entries intptr_t* _data; - // Cached hint for data_before() + // Cached hint for data_layout_before() int _hint_di; // Is data attached? And is it mature? @@ -445,17 +445,17 @@ class ciMethodData : public ciMetadata { assert(!out_of_bounds(di), "hint_di out of bounds"); _hint_di = di; } - ciProfileData* data_before(int bci) { + + DataLayout* data_layout_before(int bci) { // avoid SEGV on this edge case if (data_size() == 0) return NULL; - int hint = hint_di(); - if (data_layout_at(hint)->bci() <= bci) - return data_at(hint); - return first_data(); + DataLayout* layout = data_layout_at(hint_di()); + if (layout->bci() <= bci) + return layout; + return data_layout_at(first_di()); } - // What is the index of the first data entry? int first_di() { return 0; } @@ -469,6 +469,7 @@ class ciMethodData : public ciMetadata { template void dump_replay_data_call_type_helper(outputStream* out, int round, int& count, T* call_type_data); template void dump_replay_data_receiver_type_helper(outputStream* out, int round, int& count, T* call_type_data); void dump_replay_data_extra_data_helper(outputStream* out, int round, int& count); + ciProfileData* data_from(DataLayout* data_layout); public: bool is_method_data() const { return true; } @@ -519,7 +520,9 @@ class ciMethodData : public ciMetadata { // Walk through the data in order. ciProfileData* first_data() { return data_at(first_di()); } ciProfileData* next_data(ciProfileData* current); + DataLayout* next_data_layout(DataLayout* current); bool is_valid(ciProfileData* current) { return current != NULL; } + bool is_valid(DataLayout* current) { return current != NULL; } DataLayout* extra_data_base() const { return data_layout_at(data_size()); } DataLayout* args_data_limit() const { return data_layout_at(data_size() + extra_data_size() - diff --git a/src/hotspot/share/ci/ciMethodHandle.cpp b/src/hotspot/share/ci/ciMethodHandle.cpp index be6b76bd49a..49de75353a6 100644 --- a/src/hotspot/share/ci/ciMethodHandle.cpp +++ b/src/hotspot/share/ci/ciMethodHandle.cpp @@ -36,11 +36,6 @@ ciMethod* ciMethodHandle::get_vmtarget() const { VM_ENTRY_MARK; oop form_oop = java_lang_invoke_MethodHandle::form(get_oop()); oop vmentry_oop = java_lang_invoke_LambdaForm::vmentry(form_oop); - // FIXME: Share code with ciMemberName::get_vmtarget - Metadata* vmtarget = java_lang_invoke_MemberName::vmtarget(vmentry_oop); - if (vmtarget->is_method()) - return CURRENT_ENV->get_method((Method*) vmtarget); - // FIXME: What if the vmtarget is a Klass? - assert(false, ""); - return NULL; + Method* vmtarget = java_lang_invoke_MemberName::vmtarget(vmentry_oop); + return CURRENT_ENV->get_method(vmtarget); } diff --git a/src/hotspot/share/ci/ciObjectFactory.cpp b/src/hotspot/share/ci/ciObjectFactory.cpp index f74f5e627a0..fb227a300f3 100644 --- a/src/hotspot/share/ci/ciObjectFactory.cpp +++ b/src/hotspot/share/ci/ciObjectFactory.cpp @@ -67,7 +67,7 @@ // sort of balanced binary tree. GrowableArray* ciObjectFactory::_shared_ci_metadata = NULL; -ciSymbol* ciObjectFactory::_shared_ci_symbols[vmSymbols::SID_LIMIT]; +ciSymbol* ciObjectFactory::_shared_ci_symbols[vmSymbols::number_of_symbols()]; int ciObjectFactory::_shared_ident_limit = 0; volatile bool ciObjectFactory::_initialized = false; @@ -126,18 +126,19 @@ void ciObjectFactory::init_shared_objects() { { // Create the shared symbols, but not in _shared_ci_metadata. - int i; - for (i = vmSymbols::FIRST_SID; i < vmSymbols::SID_LIMIT; i++) { - Symbol* vmsym = vmSymbols::symbol_at((vmSymbols::SID) i); - assert(vmSymbols::find_sid(vmsym) == i, "1-1 mapping"); - ciSymbol* sym = new (_arena) ciSymbol(vmsym, (vmSymbols::SID) i); + for (vmSymbolsIterator it = vmSymbolsRange.begin(); it != vmSymbolsRange.end(); ++it) { + vmSymbolID index = *it; + Symbol* vmsym = vmSymbols::symbol_at(index); + assert(vmSymbols::find_sid(vmsym) == index, "1-1 mapping"); + ciSymbol* sym = new (_arena) ciSymbol(vmsym, index); init_ident_of(sym); - _shared_ci_symbols[i] = sym; + _shared_ci_symbols[vmSymbols::as_int(index)] = sym; } #ifdef ASSERT - for (i = vmSymbols::FIRST_SID; i < vmSymbols::SID_LIMIT; i++) { - Symbol* vmsym = vmSymbols::symbol_at((vmSymbols::SID) i); - ciSymbol* sym = vm_symbol_at((vmSymbols::SID) i); + for (vmSymbolsIterator it = vmSymbolsRange.begin(); it != vmSymbolsRange.end(); ++it) { + vmSymbolID index = *it; + Symbol* vmsym = vmSymbols::symbol_at(index); + ciSymbol* sym = vm_symbol_at(index); assert(sym->get_symbol() == vmsym, "oop must match"); } assert(ciSymbol::void_class_signature()->get_symbol() == vmSymbols::void_class_signature(), "spot check"); @@ -208,14 +209,14 @@ void ciObjectFactory::init_shared_objects() { ciSymbol* ciObjectFactory::get_symbol(Symbol* key) { - vmSymbols::SID sid = vmSymbols::find_sid(key); - if (sid != vmSymbols::NO_SID) { + vmSymbolID sid = vmSymbols::find_sid(key); + if (sid != vmSymbolID::NO_SID) { // do not pollute the main cache with it return vm_symbol_at(sid); } - assert(vmSymbols::find_sid(key) == vmSymbols::NO_SID, ""); - ciSymbol* s = new (arena()) ciSymbol(key, vmSymbols::NO_SID); + assert(vmSymbols::find_sid(key) == vmSymbolID::NO_SID, ""); + ciSymbol* s = new (arena()) ciSymbol(key, vmSymbolID::NO_SID); _symbols->push(s); return s; } @@ -678,8 +679,8 @@ void ciObjectFactory::insert_non_perm(ciObjectFactory::NonPermObject* &where, oo // ------------------------------------------------------------------ // ciObjectFactory::vm_symbol_at // Get the ciSymbol corresponding to some index in vmSymbols. -ciSymbol* ciObjectFactory::vm_symbol_at(int index) { - assert(index >= vmSymbols::FIRST_SID && index < vmSymbols::SID_LIMIT, "oob"); +ciSymbol* ciObjectFactory::vm_symbol_at(vmSymbolID sid) { + int index = vmSymbols::as_int(sid); return _shared_ci_symbols[index]; } diff --git a/src/hotspot/share/ci/ciObjectFactory.hpp b/src/hotspot/share/ci/ciObjectFactory.hpp index f78ae05a905..ca5a9613eea 100644 --- a/src/hotspot/share/ci/ciObjectFactory.hpp +++ b/src/hotspot/share/ci/ciObjectFactory.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ #include "ci/ciClassList.hpp" #include "ci/ciObject.hpp" #include "utilities/growableArray.hpp" +#include "utilities/vmEnums.hpp" // ciObjectFactory // @@ -104,7 +105,7 @@ class ciObjectFactory : public ResourceObj { ciSymbol* get_symbol(Symbol* key); // Get the ciSymbol corresponding to one of the vmSymbols. - static ciSymbol* vm_symbol_at(int index); + static ciSymbol* vm_symbol_at(vmSymbolID index); // Get the ciMethod representing an unloaded/unfound method. ciMethod* get_unloaded_method(ciInstanceKlass* holder, diff --git a/src/hotspot/share/ci/ciReplay.cpp b/src/hotspot/share/ci/ciReplay.cpp index b9fec53b08b..552c6346741 100644 --- a/src/hotspot/share/ci/ciReplay.cpp +++ b/src/hotspot/share/ci/ciReplay.cpp @@ -494,7 +494,7 @@ class CompileReplay : public StackObj { return true; } - // compile inline ... + // compile inline ( )* void* process_inline(ciMethod* imethod, Method* m, int entry_bci, int comp_level, TRAPS) { _imethod = m; _iklass = imethod->holder(); @@ -524,7 +524,7 @@ class CompileReplay : public StackObj { return NULL; } - // compile inline ... + // compile inline ( )* void process_compile(TRAPS) { Method* method = parse_method(CHECK); if (had_error()) return; @@ -606,8 +606,6 @@ class CompileReplay : public StackObj { } // ciMethod - // - // void process_ciMethod(TRAPS) { Method* method = parse_method(CHECK); if (had_error()) return; @@ -619,7 +617,7 @@ class CompileReplay : public StackObj { rec->_instructions_size = parse_int("instructions_size"); } - // ciMethodData orig # # ... data # # ... oops # ... methods + // ciMethodData orig * data * oops ( )* methods ( )* void process_ciMethodData(TRAPS) { Method* method = parse_method(CHECK); if (had_error()) return; @@ -694,7 +692,7 @@ class CompileReplay : public StackObj { Klass* k = parse_klass(CHECK); } - // ciInstanceKlass tag # # # ... + // ciInstanceKlass tag* // // Load the klass 'name' and link or initialize it. Verify that the // constant pool is the same length as 'length' and make sure the @@ -789,10 +787,12 @@ class CompileReplay : public StackObj { } } + // staticfield + // // Initialize a class and fill in the value for a static field. // This is useful when the compile was dependent on the value of // static fields but it's impossible to properly rerun the static - // initiailizer. + // initializer. void process_staticfield(TRAPS) { InstanceKlass* k = (InstanceKlass *)parse_klass(CHECK); @@ -906,6 +906,7 @@ class CompileReplay : public StackObj { } #if INCLUDE_JVMTI + // JvmtiExport void process_JvmtiExport(TRAPS) { const char* field = parse_string(); bool value = parse_int("JvmtiExport flag") != 0; diff --git a/src/hotspot/share/ci/ciReplay.hpp b/src/hotspot/share/ci/ciReplay.hpp index c224ba556d7..1bac57debf0 100644 --- a/src/hotspot/share/ci/ciReplay.hpp +++ b/src/hotspot/share/ci/ciReplay.hpp @@ -116,7 +116,6 @@ class ciReplay { static void initialize(ciMethod* method); static bool is_loaded(Method* method); - static bool is_loaded(Klass* klass); static bool should_not_inline(ciMethod* method); static bool should_inline(void* data, ciMethod* method, int bci, int inline_depth); diff --git a/src/hotspot/share/ci/ciSignature.hpp b/src/hotspot/share/ci/ciSignature.hpp index 79341bab7d2..0a150d88e61 100644 --- a/src/hotspot/share/ci/ciSignature.hpp +++ b/src/hotspot/share/ci/ciSignature.hpp @@ -50,8 +50,6 @@ class ciSignature : public ResourceObj { ciSignature(ciKlass* accessing_klass, const constantPoolHandle& cpool, ciSymbol* signature); ciSignature(ciKlass* accessing_klass, ciSymbol* signature, ciMethodType* method_type); - void get_all_klasses(); - Symbol* get_symbol() const { return _symbol->get_symbol(); } public: diff --git a/src/hotspot/share/ci/ciSymbol.cpp b/src/hotspot/share/ci/ciSymbol.cpp index 24de284c53e..32ab241bd3a 100644 --- a/src/hotspot/share/ci/ciSymbol.cpp +++ b/src/hotspot/share/ci/ciSymbol.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,12 +27,13 @@ #include "ci/ciUtilities.inline.hpp" #include "classfile/symbolTable.hpp" #include "memory/oopFactory.hpp" +#include "prims/methodHandles.hpp" // ------------------------------------------------------------------ // ciSymbol::ciSymbol // // Preallocated symbol variant. Used with symbols from vmSymbols. -ciSymbol::ciSymbol(Symbol* s, vmSymbols::SID sid) +ciSymbol::ciSymbol(Symbol* s, vmSymbolID sid) : _symbol(s), _sid(sid) { assert(_symbol != NULL, "adding null symbol"); @@ -42,7 +43,7 @@ ciSymbol::ciSymbol(Symbol* s, vmSymbols::SID sid) // Normal case for non-famous symbols. ciSymbol::ciSymbol(Symbol* s) - : _symbol(s), _sid(vmSymbols::NO_SID) + : _symbol(s), _sid(vmSymbolID::NO_SID) { assert(_symbol != NULL, "adding null symbol"); _symbol->increment_refcount(); // increment ref count diff --git a/src/hotspot/share/ci/ciSymbol.hpp b/src/hotspot/share/ci/ciSymbol.hpp index fb05a6239f4..30facfc9c09 100644 --- a/src/hotspot/share/ci/ciSymbol.hpp +++ b/src/hotspot/share/ci/ciSymbol.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ #include "ci/ciObjectFactory.hpp" #include "classfile/vmSymbols.hpp" #include "oops/symbol.hpp" +#include "utilities/vmEnums.hpp" // ciSymbol // @@ -48,11 +49,11 @@ class ciSymbol : public ciBaseObject { friend class ciObjArrayKlass; private: - const vmSymbols::SID _sid; + const vmSymbolID _sid; DEBUG_ONLY( bool sid_ok() { return vmSymbols::find_sid(get_symbol()) == _sid; } ) ciSymbol(Symbol* s); // normal case, for symbols not mentioned in vmSymbols - ciSymbol(Symbol* s, vmSymbols::SID sid); // for use with vmSymbols + ciSymbol(Symbol* s, vmSymbolID sid); // for use with vmSymbols Symbol* get_symbol() const { return _symbol; } @@ -68,7 +69,7 @@ class ciSymbol : public ciBaseObject { public: // The enumeration ID from vmSymbols, or vmSymbols::NO_SID if none. - vmSymbols::SID sid() const { return _sid; } + vmSymbolID sid() const { return _sid; } // The text of the symbol as a null-terminated utf8 string. const char* as_utf8(); @@ -98,7 +99,7 @@ class ciSymbol : public ciBaseObject { static ciSymbol* make(const char* s); #define CI_SYMBOL_DECLARE(name, ignore_def) \ - static ciSymbol* name() { return ciObjectFactory::vm_symbol_at(vmSymbols::VM_SYMBOL_ENUM_NAME(name)); } + static ciSymbol* name() { return ciObjectFactory::vm_symbol_at(VM_SYMBOL_ENUM_NAME(name)); } VM_SYMBOLS_DO(CI_SYMBOL_DECLARE, CI_SYMBOL_DECLARE) #undef CI_SYMBOL_DECLARE diff --git a/src/hotspot/share/classfile/altHashing.cpp b/src/hotspot/share/classfile/altHashing.cpp index bce06ad5813..a01e6e29fb7 100644 --- a/src/hotspot/share/classfile/altHashing.cpp +++ b/src/hotspot/share/classfile/altHashing.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,13 +22,30 @@ * */ +/* + * halfsiphash code adapted from reference implementation + * (https://github.com/veorq/SipHash/blob/master/halfsiphash.c) + * which is distributed with the following copyright: + * + * SipHash reference C implementation + * + * Copyright (c) 2016 Jean-Philippe Aumasson + * + * To the extent possible under law, the author(s) have dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide. This software is distributed without any warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication along + * with this software. If not, see + * . + */ + #include "precompiled.hpp" #include "classfile/altHashing.hpp" -#include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" #include "oops/markWord.hpp" #include "oops/oop.inline.hpp" -#include "runtime/thread.hpp" +#include "runtime/os.hpp" // Get the hash code of the classes mirror if it exists, otherwise just // return a random number, which is one of the possible hash code used for @@ -40,169 +57,182 @@ static intptr_t object_hash(Klass* k) { } // Seed value used for each alternative hash calculated. -juint AltHashing::compute_seed() { - jlong nanos = os::javaTimeNanos(); - jlong now = os::javaTimeMillis(); - jint SEED_MATERIAL[8] = { - (jint) object_hash(SystemDictionary::String_klass()), - (jint) object_hash(SystemDictionary::System_klass()), - (jint) os::random(), // current thread isn't a java thread - (jint) (((julong)nanos) >> 32), - (jint) nanos, - (jint) (((julong)now) >> 32), - (jint) now, - (jint) (os::javaTimeNanos() >> 2) +uint64_t AltHashing::compute_seed() { + uint64_t nanos = os::javaTimeNanos(); + uint64_t now = os::javaTimeMillis(); + uint32_t SEED_MATERIAL[8] = { + (uint32_t) object_hash(SystemDictionary::String_klass()), + (uint32_t) object_hash(SystemDictionary::System_klass()), + (uint32_t) os::random(), // current thread isn't a java thread + (uint32_t) (((uint64_t)nanos) >> 32), + (uint32_t) nanos, + (uint32_t) (((uint64_t)now) >> 32), + (uint32_t) now, + (uint32_t) (os::javaTimeNanos() >> 2) }; - return murmur3_32(SEED_MATERIAL, 8); + return halfsiphash_64(SEED_MATERIAL, 8); +} + +// utility function copied from java/lang/Integer +static uint32_t Integer_rotateLeft(uint32_t i, int distance) { + return (i << distance) | (i >> (32 - distance)); } +static void halfsiphash_rounds(uint32_t v[4], int rounds) { + while (rounds-- > 0) { + v[0] += v[1]; + v[1] = Integer_rotateLeft(v[1], 5); + v[1] ^= v[0]; + v[0] = Integer_rotateLeft(v[0], 16); + v[2] += v[3]; + v[3] = Integer_rotateLeft(v[3], 8); + v[3] ^= v[2]; + v[0] += v[3]; + v[3] = Integer_rotateLeft(v[3], 7); + v[3] ^= v[0]; + v[2] += v[1]; + v[1] = Integer_rotateLeft(v[1], 13); + v[1] ^= v[2]; + v[2] = Integer_rotateLeft(v[2], 16); + } +} + +static void halfsiphash_adddata(uint32_t v[4], uint32_t newdata, int rounds) { + v[3] ^= newdata; + halfsiphash_rounds(v, rounds); + v[0] ^= newdata; +} + +static void halfsiphash_init32(uint32_t v[4], uint64_t seed) { + v[0] = seed & 0xffffffff; + v[1] = seed >> 32; + v[2] = 0x6c796765 ^ v[0]; + v[3] = 0x74656462 ^ v[1]; +} + +static void halfsiphash_init64(uint32_t v[4], uint64_t seed) { + halfsiphash_init32(v, seed); + v[1] ^= 0xee; +} + +uint32_t halfsiphash_finish32(uint32_t v[4], int rounds) { + v[2] ^= 0xff; + halfsiphash_rounds(v, rounds); + return (v[1] ^ v[3]); +} + +static uint64_t halfsiphash_finish64(uint32_t v[4], int rounds) { + uint64_t rv; + v[2] ^= 0xee; + halfsiphash_rounds(v, rounds); + rv = v[1] ^ v[3]; + v[1] ^= 0xdd; + halfsiphash_rounds(v, rounds); + rv |= (uint64_t)(v[1] ^ v[3]) << 32; + return rv; +} -// Murmur3 hashing for Symbol -juint AltHashing::murmur3_32(juint seed, const jbyte* data, int len) { - juint h1 = seed; +// HalfSipHash-2-4 (32-bit output) for Symbols +uint32_t AltHashing::halfsiphash_32(uint64_t seed, const uint8_t* data, int len) { + uint32_t v[4]; + uint32_t newdata; + int off = 0; int count = len; - int offset = 0; + + halfsiphash_init32(v, seed); // body while (count >= 4) { - juint k1 = (data[offset] & 0x0FF) - | (data[offset + 1] & 0x0FF) << 8 - | (data[offset + 2] & 0x0FF) << 16 - | data[offset + 3] << 24; - count -= 4; - offset += 4; + // Avoid sign extension with 0x0ff + newdata = (data[off] & 0x0FF) + | (data[off + 1] & 0x0FF) << 8 + | (data[off + 2] & 0x0FF) << 16 + | data[off + 3] << 24; - k1 *= 0xcc9e2d51; - k1 = Integer_rotateLeft(k1, 15); - k1 *= 0x1b873593; + count -= 4; + off += 4; - h1 ^= k1; - h1 = Integer_rotateLeft(h1, 13); - h1 = h1 * 5 + 0xe6546b64; + halfsiphash_adddata(v, newdata, 2); } // tail + newdata = ((uint32_t)len) << 24; // (Byte.SIZE / Byte.SIZE); if (count > 0) { - juint k1 = 0; - switch (count) { case 3: - k1 ^= (data[offset + 2] & 0xff) << 16; + newdata |= (data[off + 2] & 0x0ff) << 16; // fall through case 2: - k1 ^= (data[offset + 1] & 0xff) << 8; + newdata |= (data[off + 1] & 0x0ff) << 8; // fall through case 1: - k1 ^= (data[offset] & 0xff); + newdata |= (data[off] & 0x0ff); // fall through - default: - k1 *= 0xcc9e2d51; - k1 = Integer_rotateLeft(k1, 15); - k1 *= 0x1b873593; - h1 ^= k1; } } - // finalization - h1 ^= len; + halfsiphash_adddata(v, newdata, 2); - // finalization mix force all bits of a hash block to avalanche - h1 ^= h1 >> 16; - h1 *= 0x85ebca6b; - h1 ^= h1 >> 13; - h1 *= 0xc2b2ae35; - h1 ^= h1 >> 16; - - return h1; + // finalization + return halfsiphash_finish32(v, 4); } -// Murmur3 hashing for Strings -juint AltHashing::murmur3_32(juint seed, const jchar* data, int len) { - juint h1 = seed; - +// HalfSipHash-2-4 (32-bit output) for Strings +uint32_t AltHashing::halfsiphash_32(uint64_t seed, const uint16_t* data, int len) { + uint32_t v[4]; + uint32_t newdata; int off = 0; int count = len; + halfsiphash_init32(v, seed); + // body while (count >= 2) { - jchar d1 = data[off++] & 0xFFFF; - jchar d2 = data[off++]; - juint k1 = (d1 | d2 << 16); + uint16_t d1 = data[off++] & 0x0FFFF; + uint16_t d2 = data[off++]; + newdata = (d1 | d2 << 16); count -= 2; - k1 *= 0xcc9e2d51; - k1 = Integer_rotateLeft(k1, 15); - k1 *= 0x1b873593; - - h1 ^= k1; - h1 = Integer_rotateLeft(h1, 13); - h1 = h1 * 5 + 0xe6546b64; + halfsiphash_adddata(v, newdata, 2); } // tail - + newdata = ((uint32_t)len * 2) << 24; // (Character.SIZE / Byte.SIZE); if (count > 0) { - juint k1 = (juint)data[off]; - - k1 *= 0xcc9e2d51; - k1 = Integer_rotateLeft(k1, 15); - k1 *= 0x1b873593; - h1 ^= k1; + newdata |= (uint32_t)data[off]; } + halfsiphash_adddata(v, newdata, 2); // finalization - h1 ^= len * 2; // (Character.SIZE / Byte.SIZE); - - // finalization mix force all bits of a hash block to avalanche - h1 ^= h1 >> 16; - h1 *= 0x85ebca6b; - h1 ^= h1 >> 13; - h1 *= 0xc2b2ae35; - h1 ^= h1 >> 16; - - return h1; + return halfsiphash_finish32(v, 4); } -// Hash used for the seed. -juint AltHashing::murmur3_32(juint seed, const jint* data, int len) { - juint h1 = seed; +// HalfSipHash-2-4 (64-bit output) for integers (used to create seed) +uint64_t AltHashing::halfsiphash_64(uint64_t seed, const uint32_t* data, int len) { + uint32_t v[4]; int off = 0; int end = len; + halfsiphash_init64(v, seed); + // body while (off < end) { - juint k1 = (juint)data[off++]; - - k1 *= 0xcc9e2d51; - k1 = Integer_rotateLeft(k1, 15); - k1 *= 0x1b873593; - - h1 ^= k1; - h1 = Integer_rotateLeft(h1, 13); - h1 = h1 * 5 + 0xe6546b64; + halfsiphash_adddata(v, (uint32_t)data[off++], 2); } // tail (always empty, as body is always 32-bit chunks) // finalization - - h1 ^= len * 4; // (Integer.SIZE / Byte.SIZE); - - // finalization mix force all bits of a hash block to avalanche - h1 ^= h1 >> 16; - h1 *= 0x85ebca6b; - h1 ^= h1 >> 13; - h1 *= 0xc2b2ae35; - h1 ^= h1 >> 16; - - return h1; + halfsiphash_adddata(v, ((uint32_t)len * 4) << 24, 2); // (Integer.SIZE / Byte.SIZE); + return halfsiphash_finish64(v, 4); } -juint AltHashing::murmur3_32(const jint* data, int len) { - return murmur3_32(0, data, len); +// HalfSipHash-2-4 (64-bit output) for integers (used to create seed) +uint64_t AltHashing::halfsiphash_64(const uint32_t* data, int len) { + return halfsiphash_64((uint64_t)0, data, len); } diff --git a/src/hotspot/share/classfile/altHashing.hpp b/src/hotspot/share/classfile/altHashing.hpp index 8af990d26ee..e1726ae5152 100644 --- a/src/hotspot/share/classfile/altHashing.hpp +++ b/src/hotspot/share/classfile/altHashing.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,29 +26,25 @@ #define SHARE_CLASSFILE_ALTHASHING_HPP #include "jni.h" -#include "classfile/symbolTable.hpp" +#include "memory/allocation.hpp" /** - * Hashing utilities. - * - * Implementation of Murmur3 hashing. - * This code was translated from src/share/classes/sun/misc/Hashing.java - * code in the JDK. + * Implementation of alternate more secure hashing. */ class AltHashing : AllStatic { friend class AltHashingTest; - // utility function copied from java/lang/Integer - static juint Integer_rotateLeft(juint i, int distance) { - return (i << distance) | (i >> (32 - distance)); - } - static juint murmur3_32(const jint* data, int len); - static juint murmur3_32(juint seed, const jint* data, int len); + // For the seed computation + static uint64_t halfsiphash_64(const uint32_t* data, int len); + static uint64_t halfsiphash_64(uint64_t seed, const uint32_t* data, int len); public: - static juint compute_seed(); - static juint murmur3_32(juint seed, const jbyte* data, int len); - static juint murmur3_32(juint seed, const jchar* data, int len); + static uint64_t compute_seed(); + + // For Symbols + static uint32_t halfsiphash_32(uint64_t seed, const uint8_t* data, int len); + // For Strings + static uint32_t halfsiphash_32(uint64_t seed, const uint16_t* data, int len); }; #endif // SHARE_CLASSFILE_ALTHASHING_HPP diff --git a/src/hotspot/share/classfile/classFileError.cpp b/src/hotspot/share/classfile/classFileError.cpp index f44588efbc4..b3610f8b838 100644 --- a/src/hotspot/share/classfile/classFileError.cpp +++ b/src/hotspot/share/classfile/classFileError.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -78,6 +78,25 @@ void ClassFileParser::classfile_parse_error(const char* msg, msg, name, signature, _class_name->as_C_string()); } +void ClassFileParser::classfile_icce_error(const char* msg, + const Klass* k, + TRAPS) const { + assert(_class_name != NULL, "invariant"); + ResourceMark rm(THREAD); + Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbols::java_lang_IncompatibleClassChangeError(), + msg, _class_name->as_klass_external_name(), k->external_name()); +} + +void ClassFileParser::classfile_ucve_error(const char* msg, + const Symbol* class_name, + u2 major, + u2 minor, + TRAPS) const { + ResourceMark rm(THREAD); + Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbols::java_lang_UnsupportedClassVersionError(), + msg, class_name->as_C_string(), major, minor); +} + PRAGMA_DIAG_POP void StackMapStream::stackmap_format_error(const char* msg, TRAPS) { diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index e6ab199417b..52767594516 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -212,7 +212,8 @@ void ClassFileParser::parse_constant_pool_entries(const ClassFileStream* const s if (_major_version < Verifier::INVOKEDYNAMIC_MAJOR_VERSION) { classfile_parse_error( "Class file version does not support constant tag %u in class file %s", - tag, CHECK); + tag, THREAD); + return; } if (tag == JVM_CONSTANT_MethodHandle) { cfs->guarantee_more(4, CHECK); // ref_kind, method_index, tag/access_flags @@ -234,7 +235,8 @@ void ClassFileParser::parse_constant_pool_entries(const ClassFileStream* const s if (_major_version < Verifier::DYNAMICCONSTANT_MAJOR_VERSION) { classfile_parse_error( "Class file version does not support constant tag %u in class file %s", - tag, CHECK); + tag, THREAD); + return; } cfs->guarantee_more(5, CHECK); // bsm_index, nt, tag/access_flags const u2 bootstrap_specifier_index = cfs->get_u2_fast(); @@ -249,7 +251,8 @@ void ClassFileParser::parse_constant_pool_entries(const ClassFileStream* const s if (_major_version < Verifier::INVOKEDYNAMIC_MAJOR_VERSION) { classfile_parse_error( "Class file version does not support constant tag %u in class file %s", - tag, CHECK); + tag, THREAD); + return; } cfs->guarantee_more(5, CHECK); // bsm_index, nt, tag/access_flags const u2 bootstrap_specifier_index = cfs->get_u2_fast(); @@ -368,8 +371,8 @@ void ClassFileParser::parse_constant_pool_entries(const ClassFileStream* const s default: { classfile_parse_error("Unknown constant tag %u in class file %s", tag, - CHECK); - break; + THREAD); + return; } } // end of switch(tag) } // end of for @@ -562,7 +565,8 @@ void ClassFileParser::parse_constant_pool(const ClassFileStream* const stream, default: { classfile_parse_error( "Bad method handle kind at constant pool index %u in class file %s", - index, CHECK); + index, THREAD); + return; } } // switch(refkind) // Keep the ref_index unchanged. It will be indirected at link-time. @@ -740,7 +744,8 @@ void ClassFileParser::parse_constant_pool(const ClassFileStream* const stream, name != vmSymbols::object_initializer_name()) { classfile_parse_error( "Bad method name at constant pool index %u in class file %s", - name_ref_index, CHECK); + name_ref_index, THREAD); + return; } } break; @@ -762,13 +767,15 @@ void ClassFileParser::parse_constant_pool(const ClassFileStream* const stream, if (name != vmSymbols::object_initializer_name()) { classfile_parse_error( "Bad constructor name at constant pool index %u in class file %s", - name_ref_index, CHECK); + name_ref_index, THREAD); + return; } } else { if (name == vmSymbols::object_initializer_name()) { classfile_parse_error( "Bad method name at constant pool index %u in class file %s", - name_ref_index, CHECK); + name_ref_index, THREAD); + return; } } break; @@ -1007,7 +1014,7 @@ void ClassFileParser::parse_interfaces(const ClassFileStream* const stream, } if (dup) { classfile_parse_error("Duplicate interface name \"%s\" in class file %s", - name->as_C_string(), CHECK); + name->as_C_string(), THREAD); } } } @@ -1063,7 +1070,7 @@ void ClassFileParser::verify_constantvalue(const ConstantPool* const cp, default: { classfile_parse_error("Unable to set initial value %u in class file %s", constantvalue_index, - CHECK); + THREAD); } } } @@ -1350,7 +1357,8 @@ void ClassFileParser::parse_field_attributes(const ClassFileStream* const cfs, if (is_static && attribute_name == vmSymbols::tag_constant_value()) { // ignore if non-static if (constantvalue_index != 0) { - classfile_parse_error("Duplicate ConstantValue attribute in class file %s", CHECK); + classfile_parse_error("Duplicate ConstantValue attribute in class file %s", THREAD); + return; } check_property( attribute_length == 2, @@ -1365,31 +1373,36 @@ void ClassFileParser::parse_field_attributes(const ClassFileStream* const cfs, if (attribute_length != 0) { classfile_parse_error( "Invalid Synthetic field attribute length %u in class file %s", - attribute_length, CHECK); + attribute_length, THREAD); + return; } is_synthetic = true; } else if (attribute_name == vmSymbols::tag_deprecated()) { // 4276120 if (attribute_length != 0) { classfile_parse_error( "Invalid Deprecated field attribute length %u in class file %s", - attribute_length, CHECK); + attribute_length, THREAD); + return; } } else if (_major_version >= JAVA_1_5_VERSION) { if (attribute_name == vmSymbols::tag_signature()) { if (generic_signature_index != 0) { classfile_parse_error( - "Multiple Signature attributes for field in class file %s", CHECK); + "Multiple Signature attributes for field in class file %s", THREAD); + return; } if (attribute_length != 2) { classfile_parse_error( "Wrong size %u for field's Signature attribute in class file %s", - attribute_length, CHECK); + attribute_length, THREAD); + return; } generic_signature_index = parse_generic_signature_attribute(cfs, CHECK); } else if (attribute_name == vmSymbols::tag_runtime_visible_annotations()) { if (runtime_visible_annotations != NULL) { classfile_parse_error( - "Multiple RuntimeVisibleAnnotations attributes for field in class file %s", CHECK); + "Multiple RuntimeVisibleAnnotations attributes for field in class file %s", THREAD); + return; } runtime_visible_annotations_length = attribute_length; runtime_visible_annotations = cfs->current(); @@ -1406,7 +1419,8 @@ void ClassFileParser::parse_field_attributes(const ClassFileStream* const cfs, } else if (attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { if (runtime_invisible_annotations_exists) { classfile_parse_error( - "Multiple RuntimeInvisibleAnnotations attributes for field in class file %s", CHECK); + "Multiple RuntimeInvisibleAnnotations attributes for field in class file %s", THREAD); + return; } runtime_invisible_annotations_exists = true; if (PreserveAllAnnotations) { @@ -1418,7 +1432,8 @@ void ClassFileParser::parse_field_attributes(const ClassFileStream* const cfs, } else if (attribute_name == vmSymbols::tag_runtime_visible_type_annotations()) { if (runtime_visible_type_annotations != NULL) { classfile_parse_error( - "Multiple RuntimeVisibleTypeAnnotations attributes for field in class file %s", CHECK); + "Multiple RuntimeVisibleTypeAnnotations attributes for field in class file %s", THREAD); + return; } runtime_visible_type_annotations_length = attribute_length; runtime_visible_type_annotations = cfs->current(); @@ -1427,7 +1442,8 @@ void ClassFileParser::parse_field_attributes(const ClassFileStream* const cfs, } else if (attribute_name == vmSymbols::tag_runtime_invisible_type_annotations()) { if (runtime_invisible_type_annotations_exists) { classfile_parse_error( - "Multiple RuntimeInvisibleTypeAnnotations attributes for field in class file %s", CHECK); + "Multiple RuntimeInvisibleTypeAnnotations attributes for field in class file %s", THREAD); + return; } else { runtime_invisible_type_annotations_exists = true; } @@ -1541,14 +1557,13 @@ class ClassFileParser::FieldAllocationCount : public ResourceObj { } } - FieldAllocationType update(bool is_static, BasicType type) { + void update(bool is_static, BasicType type) { FieldAllocationType atype = basic_type_to_atype(is_static, type); if (atype != BAD_ALLOCATION_TYPE) { // Make sure there is no overflow with injected fields. assert(count[atype] < 0xFFFF, "More than 65535 fields"); count[atype]++; } - return atype; } }; @@ -1688,9 +1703,8 @@ void ClassFileParser::parse_fields(const ClassFileStream* const cfs, constantvalue_index); const BasicType type = cp->basic_type_for_signature_at(signature_index); - // Remember how many oops we encountered and compute allocation type - const FieldAllocationType atype = fac->update(is_static, type); - field->set_allocation_type(atype); + // Update FieldAllocationCount for this kind of field + fac->update(is_static, type); // After field is initialized with type, we can augment it with aux info if (parsed_annotations.has_any_annotations()) { @@ -1726,16 +1740,15 @@ void ClassFileParser::parse_fields(const ClassFileStream* const cfs, // Injected field FieldInfo* const field = FieldInfo::from_field_array(fa, index); - field->initialize(JVM_ACC_FIELD_INTERNAL, - injected[n].name_index, - injected[n].signature_index, + field->initialize((u2)JVM_ACC_FIELD_INTERNAL, + (u2)(injected[n].name_index), + (u2)(injected[n].signature_index), 0); const BasicType type = Signature::basic_type(injected[n].signature()); - // Remember how many oops we encountered and compute allocation type - const FieldAllocationType atype = fac->update(false, type); - field->set_allocation_type(atype); + // Update FieldAllocationCount for this kind of field + fac->update(false, type); index++; } } @@ -1785,7 +1798,7 @@ void ClassFileParser::parse_fields(const ClassFileStream* const cfs, } if (dup) { classfile_parse_error("Duplicate field name \"%s\" with signature \"%s\" in class file %s", - name->as_C_string(), sig->as_klass_external_name(), CHECK); + name->as_C_string(), sig->as_klass_external_name(), THREAD); } } } @@ -1953,12 +1966,14 @@ const ClassFileParser::unsafe_u2* ClassFileParser::parse_localvariable_table(con if (start_pc >= code_length) { classfile_parse_error( "Invalid start_pc %u in %s in class file %s", - start_pc, tbl_name, CHECK_NULL); + start_pc, tbl_name, THREAD); + return NULL; } if (end_pc > code_length) { classfile_parse_error( "Invalid length %u in %s in class file %s", - length, tbl_name, CHECK_NULL); + length, tbl_name, THREAD); + return NULL; } const int cp_size = cp->length(); guarantee_property(valid_symbol_at(name_index), @@ -2066,53 +2081,53 @@ AnnotationCollector::ID AnnotationCollector::annotation_index(const ClassLoaderData* loader_data, const Symbol* name, const bool can_access_vm_annotations) { - const vmSymbols::SID sid = vmSymbols::find_sid(name); + const vmSymbolID sid = vmSymbols::find_sid(name); // Privileged code can use all annotations. Other code silently drops some. const bool privileged = loader_data->is_boot_class_loader_data() || loader_data->is_platform_class_loader_data() || can_access_vm_annotations; switch (sid) { - case vmSymbols::VM_SYMBOL_ENUM_NAME(reflect_CallerSensitive_signature): { + case VM_SYMBOL_ENUM_NAME(reflect_CallerSensitive_signature): { if (_location != _in_method) break; // only allow for methods if (!privileged) break; // only allow in privileged code return _method_CallerSensitive; } - case vmSymbols::VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_ForceInline_signature): { + case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_ForceInline_signature): { if (_location != _in_method) break; // only allow for methods if (!privileged) break; // only allow in privileged code return _method_ForceInline; } - case vmSymbols::VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_DontInline_signature): { + case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_DontInline_signature): { if (_location != _in_method) break; // only allow for methods if (!privileged) break; // only allow in privileged code return _method_DontInline; } - case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_InjectedProfile_signature): { + case VM_SYMBOL_ENUM_NAME(java_lang_invoke_InjectedProfile_signature): { if (_location != _in_method) break; // only allow for methods if (!privileged) break; // only allow in privileged code return _method_InjectedProfile; } - case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_LambdaForm_Compiled_signature): { + case VM_SYMBOL_ENUM_NAME(java_lang_invoke_LambdaForm_Compiled_signature): { if (_location != _in_method) break; // only allow for methods if (!privileged) break; // only allow in privileged code return _method_LambdaForm_Compiled; } - case vmSymbols::VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_Hidden_signature): { + case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_Hidden_signature): { if (_location != _in_method) break; // only allow for methods if (!privileged) break; // only allow in privileged code return _method_Hidden; } - case vmSymbols::VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_IntrinsicCandidate_signature): { + case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_IntrinsicCandidate_signature): { if (_location != _in_method) break; // only allow for methods if (!privileged) break; // only allow in privileged code return _method_IntrinsicCandidate; } - case vmSymbols::VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_Stable_signature): { + case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_Stable_signature): { if (_location != _in_field) break; // only allow for fields if (!privileged) break; // only allow in privileged code return _field_Stable; } - case vmSymbols::VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_Contended_signature): { + case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_Contended_signature): { if (_location != _in_field && _location != _in_class) { break; // only allow for fields and classes } @@ -2121,7 +2136,7 @@ AnnotationCollector::annotation_index(const ClassLoaderData* loader_data, } return _jdk_internal_vm_annotation_Contended; } - case vmSymbols::VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_ReservedStackAccess_signature): { + case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_ReservedStackAccess_signature): { if (_location != _in_method) break; // only allow for methods if (RestrictReservedStack && !privileged) break; // honor privileges return _jdk_internal_vm_annotation_ReservedStackAccess; @@ -2218,7 +2233,8 @@ void ClassFileParser::copy_localvariable_table(const ConstMethod* cm, classfile_parse_error("Duplicated LocalVariableTable attribute " "entry for '%s' in class file %s", _cp->symbol_at(lvt->name_cp_index)->as_utf8(), - CHECK); + THREAD); + return; } } } @@ -2237,13 +2253,15 @@ void ClassFileParser::copy_localvariable_table(const ConstMethod* cm, classfile_parse_error("LVTT entry for '%s' in class file %s " "does not match any LVT entry", _cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(), - CHECK); + THREAD); + return; } } else if ((*entry)->signature_cp_index != 0 && _need_verify) { classfile_parse_error("Duplicated LocalVariableTypeTable attribute " "entry for '%s' in class file %s", _cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(), - CHECK); + THREAD); + return; } else { // to add generic signatures into LocalVariableTable (*entry)->signature_cp_index = lvtt_elem.descriptor_cp_index; @@ -2361,14 +2379,16 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, } else if ((flags & JVM_ACC_STATIC) == JVM_ACC_STATIC) { flags &= JVM_ACC_STATIC | JVM_ACC_STRICT; } else { - classfile_parse_error("Method is not static in class file %s", CHECK_NULL); + classfile_parse_error("Method is not static in class file %s", THREAD); + return NULL; } } else { verify_legal_method_modifiers(flags, is_interface, name, CHECK_NULL); } if (name == vmSymbols::object_initializer_name() && is_interface) { - classfile_parse_error("Interface cannot have a method named , class file %s", CHECK_NULL); + classfile_parse_error("Interface cannot have a method named , class file %s", THREAD); + return NULL; } int args_size = -1; // only used when _need_verify is true @@ -2376,7 +2396,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, args_size = ((flags & JVM_ACC_STATIC) ? 0 : 1) + verify_legal_method_signature(name, signature, CHECK_NULL); if (args_size > MAX_ARGS_SIZE) { - classfile_parse_error("Too many arguments in method signature in class file %s", CHECK_NULL); + classfile_parse_error("Too many arguments in method signature in class file %s", THREAD); + return NULL; } } @@ -2455,7 +2476,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, } if (parsed_code_attribute) { classfile_parse_error("Multiple Code attributes in class file %s", - CHECK_NULL); + THREAD); + return NULL; } parsed_code_attribute = true; @@ -2587,7 +2609,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_stack_map_table()) { // Stack map is only needed by the new verifier in JDK1.5. if (parsed_stackmap_attribute) { - classfile_parse_error("Multiple StackMapTable attributes in class file %s", CHECK_NULL); + classfile_parse_error("Multiple StackMapTable attributes in class file %s", THREAD); + return NULL; } stackmap_data = parse_stackmap_table(cfs, code_attribute_length, _need_verify, CHECK_NULL); stackmap_data_length = code_attribute_length; @@ -2607,7 +2630,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, // Parse Exceptions attribute if (parsed_checked_exceptions_attribute) { classfile_parse_error("Multiple Exceptions attributes in class file %s", - CHECK_NULL); + THREAD); + return NULL; } parsed_checked_exceptions_attribute = true; checked_exceptions_start = @@ -2619,7 +2643,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, // reject multiple method parameters if (method_parameters_seen) { classfile_parse_error("Multiple MethodParameters attributes in class file %s", - CHECK_NULL); + THREAD); + return NULL; } method_parameters_seen = true; method_parameters_length = cfs->get_u1_fast(); @@ -2627,7 +2652,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, if (method_attribute_length != real_length) { classfile_parse_error( "Invalid MethodParameters method attribute length %u in class file", - method_attribute_length, CHECK_NULL); + method_attribute_length, THREAD); + return NULL; } method_parameters_data = cfs->current(); cfs->skip_u2_fast(method_parameters_length); @@ -2639,7 +2665,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, if (method_attribute_length != 0) { classfile_parse_error( "Invalid Synthetic method attribute length %u in class file %s", - method_attribute_length, CHECK_NULL); + method_attribute_length, THREAD); + return NULL; } // Should we check that there hasn't already been a synthetic attribute? access_flags.set_is_synthetic(); @@ -2647,26 +2674,30 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, if (method_attribute_length != 0) { classfile_parse_error( "Invalid Deprecated method attribute length %u in class file %s", - method_attribute_length, CHECK_NULL); + method_attribute_length, THREAD); + return NULL; } } else if (_major_version >= JAVA_1_5_VERSION) { if (method_attribute_name == vmSymbols::tag_signature()) { if (generic_signature_index != 0) { classfile_parse_error( "Multiple Signature attributes for method in class file %s", - CHECK_NULL); + THREAD); + return NULL; } if (method_attribute_length != 2) { classfile_parse_error( "Invalid Signature attribute length %u in class file %s", - method_attribute_length, CHECK_NULL); + method_attribute_length, THREAD); + return NULL; } generic_signature_index = parse_generic_signature_attribute(cfs, CHECK_NULL); } else if (method_attribute_name == vmSymbols::tag_runtime_visible_annotations()) { if (runtime_visible_annotations != NULL) { classfile_parse_error( "Multiple RuntimeVisibleAnnotations attributes for method in class file %s", - CHECK_NULL); + THREAD); + return NULL; } runtime_visible_annotations_length = method_attribute_length; runtime_visible_annotations = cfs->current(); @@ -2684,7 +2715,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, if (runtime_invisible_annotations_exists) { classfile_parse_error( "Multiple RuntimeInvisibleAnnotations attributes for method in class file %s", - CHECK_NULL); + THREAD); + return NULL; } runtime_invisible_annotations_exists = true; if (PreserveAllAnnotations) { @@ -2697,7 +2729,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, if (runtime_visible_parameter_annotations != NULL) { classfile_parse_error( "Multiple RuntimeVisibleParameterAnnotations attributes for method in class file %s", - CHECK_NULL); + THREAD); + return NULL; } runtime_visible_parameter_annotations_length = method_attribute_length; runtime_visible_parameter_annotations = cfs->current(); @@ -2707,7 +2740,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, if (runtime_invisible_parameter_annotations_exists) { classfile_parse_error( "Multiple RuntimeInvisibleParameterAnnotations attributes for method in class file %s", - CHECK_NULL); + THREAD); + return NULL; } runtime_invisible_parameter_annotations_exists = true; if (PreserveAllAnnotations) { @@ -2721,7 +2755,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, if (annotation_default != NULL) { classfile_parse_error( "Multiple AnnotationDefault attributes for method in class file %s", - CHECK_NULL); + THREAD); + return NULL; } annotation_default_length = method_attribute_length; annotation_default = cfs->current(); @@ -2731,7 +2766,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, if (runtime_visible_type_annotations != NULL) { classfile_parse_error( "Multiple RuntimeVisibleTypeAnnotations attributes for method in class file %s", - CHECK_NULL); + THREAD); + return NULL; } runtime_visible_type_annotations_length = method_attribute_length; runtime_visible_type_annotations = cfs->current(); @@ -2742,7 +2778,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, if (runtime_invisible_type_annotations_exists) { classfile_parse_error( "Multiple RuntimeInvisibleTypeAnnotations attributes for method in class file %s", - CHECK_NULL); + THREAD); + return NULL; } else { runtime_invisible_type_annotations_exists = true; } @@ -2983,7 +3020,7 @@ void ClassFileParser::parse_methods(const ClassFileStream* const cfs, } if (dup) { classfile_parse_error("Duplicate method name \"%s\" with signature \"%s\" in class file %s", - name->as_C_string(), sig->as_klass_external_name(), CHECK); + name->as_C_string(), sig->as_klass_external_name(), THREAD); } } } @@ -3081,8 +3118,83 @@ void ClassFileParser::parse_classfile_source_debug_extension_attribute(const Cla JVM_ACC_STATIC \ ) +// Find index of the InnerClasses entry for the specified inner_class_info_index. +// Return -1 if none is found. +static int inner_classes_find_index(const Array* inner_classes, int inner, const ConstantPool* cp, int length) { + Symbol* cp_klass_name = cp->klass_name_at(inner); + for (int idx = 0; idx < length; idx += InstanceKlass::inner_class_next_offset) { + int idx_inner = inner_classes->at(idx + InstanceKlass::inner_class_inner_class_info_offset); + if (cp->klass_name_at(idx_inner) == cp_klass_name) { + return idx; + } + } + return -1; +} + +// Return the outer_class_info_index for the InnerClasses entry containing the +// specified inner_class_info_index. Return -1 if no InnerClasses entry is found. +static int inner_classes_jump_to_outer(const Array* inner_classes, int inner, const ConstantPool* cp, int length) { + if (inner == 0) return -1; + int idx = inner_classes_find_index(inner_classes, inner, cp, length); + if (idx == -1) return -1; + int result = inner_classes->at(idx + InstanceKlass::inner_class_outer_class_info_offset); + return result; +} + +// Return true if circularity is found, false if no circularity is found. +// Use Floyd's cycle finding algorithm. +static bool inner_classes_check_loop_through_outer(const Array* inner_classes, int idx, const ConstantPool* cp, int length) { + int slow = inner_classes->at(idx + InstanceKlass::inner_class_inner_class_info_offset); + int fast = inner_classes->at(idx + InstanceKlass::inner_class_outer_class_info_offset); + while (fast != -1 && fast != 0) { + if (slow != 0 && (cp->klass_name_at(slow) == cp->klass_name_at(fast))) { + return true; // found a circularity + } + fast = inner_classes_jump_to_outer(inner_classes, fast, cp, length); + if (fast == -1) return false; + fast = inner_classes_jump_to_outer(inner_classes, fast, cp, length); + if (fast == -1) return false; + slow = inner_classes_jump_to_outer(inner_classes, slow, cp, length); + assert(slow != -1, "sanity check"); + } + return false; +} + +// Loop through each InnerClasses entry checking for circularities and duplications +// with other entries. If duplicate entries are found then throw CFE. Otherwise, +// return true if a circularity or entries with duplicate inner_class_info_indexes +// are found. +bool ClassFileParser::check_inner_classes_circularity(const ConstantPool* cp, int length, TRAPS) { + // Loop through each InnerClasses entry. + for (int idx = 0; idx < length; idx += InstanceKlass::inner_class_next_offset) { + // Return true if there are circular entries. + if (inner_classes_check_loop_through_outer(_inner_classes, idx, cp, length)) { + return true; + } + // Check if there are duplicate entries or entries with the same inner_class_info_index. + for (int y = idx + InstanceKlass::inner_class_next_offset; y < length; + y += InstanceKlass::inner_class_next_offset) { + + // To maintain compatibility, throw an exception if duplicate inner classes + // entries are found. + guarantee_property((_inner_classes->at(idx) != _inner_classes->at(y) || + _inner_classes->at(idx+1) != _inner_classes->at(y+1) || + _inner_classes->at(idx+2) != _inner_classes->at(y+2) || + _inner_classes->at(idx+3) != _inner_classes->at(y+3)), + "Duplicate entry in InnerClasses attribute in class file %s", + CHECK_(true)); + // Return true if there are two entries with the same inner_class_info_index. + if (_inner_classes->at(y) == _inner_classes->at(idx)) { + return true; + } + } + } + return false; +} + // Return number of classes in the inner classes attribute table u2 ClassFileParser::parse_classfile_inner_classes_attribute(const ClassFileStream* const cfs, + const ConstantPool* cp, const u1* const inner_classes_attribute_start, bool parsed_enclosingmethod_attribute, u2 enclosing_method_class_index, @@ -3106,7 +3218,7 @@ u2 ClassFileParser::parse_classfile_inner_classes_attribute(const ClassFileStrea // enclosing_method_class_index, // enclosing_method_method_index] const int size = length * 4 + (parsed_enclosingmethod_attribute ? 2 : 0); - Array* const inner_classes = MetadataFactory::new_array(_loader_data, size, CHECK_0); + Array* inner_classes = MetadataFactory::new_array(_loader_data, size, CHECK_0); _inner_classes = inner_classes; int index = 0; @@ -3157,25 +3269,28 @@ u2 ClassFileParser::parse_classfile_inner_classes_attribute(const ClassFileStrea } // 4347400: make sure there's no duplicate entry in the classes array + // Also, check for circular entries. + bool has_circularity = false; if (_need_verify && _major_version >= JAVA_1_5_VERSION) { - for(int i = 0; i < length * 4; i += 4) { - for(int j = i + 4; j < length * 4; j += 4) { - guarantee_property((inner_classes->at(i) != inner_classes->at(j) || - inner_classes->at(i+1) != inner_classes->at(j+1) || - inner_classes->at(i+2) != inner_classes->at(j+2) || - inner_classes->at(i+3) != inner_classes->at(j+3)), - "Duplicate entry in InnerClasses in class file %s", - CHECK_0); + has_circularity = check_inner_classes_circularity(cp, length * 4, CHECK_0); + if (has_circularity) { + // If circularity check failed then ignore InnerClasses attribute. + MetadataFactory::free_array(_loader_data, _inner_classes); + index = 0; + if (parsed_enclosingmethod_attribute) { + inner_classes = MetadataFactory::new_array(_loader_data, 2, CHECK_0); + _inner_classes = inner_classes; + } else { + _inner_classes = Universe::the_empty_short_array(); } } } - // Set EnclosingMethod class and method indexes. if (parsed_enclosingmethod_attribute) { inner_classes->at_put(index++, enclosing_method_class_index); inner_classes->at_put(index++, enclosing_method_method_index); } - assert(index == size, "wrong size"); + assert(index == size || has_circularity, "wrong size"); // Restore buffer's current position. cfs->set_current(current_mark); @@ -3226,7 +3341,8 @@ u2 ClassFileParser::parse_classfile_permitted_subclasses_attribute(const ClassFi length = cfs->get_u2_fast(); } if (length < 1) { - classfile_parse_error("PermittedSubclasses attribute is empty in class file %s", CHECK_0); + classfile_parse_error("PermittedSubclasses attribute is empty in class file %s", THREAD); + return 0; } const int size = length; Array* const permitted_subclasses = MetadataFactory::new_array(_loader_data, size, CHECK_0); @@ -3328,19 +3444,22 @@ u2 ClassFileParser::parse_classfile_record_attribute(const ClassFileStream* cons if (generic_sig_index != 0) { classfile_parse_error( "Multiple Signature attributes for Record component in class file %s", - CHECK_0); + THREAD); + return 0; } if (attribute_length != 2) { classfile_parse_error( "Invalid Signature attribute length %u in Record component in class file %s", - attribute_length, CHECK_0); + attribute_length, THREAD); + return 0; } generic_sig_index = parse_generic_signature_attribute(cfs, CHECK_0); } else if (attribute_name == vmSymbols::tag_runtime_visible_annotations()) { if (runtime_visible_annotations != NULL) { classfile_parse_error( - "Multiple RuntimeVisibleAnnotations attributes for Record component in class file %s", CHECK_0); + "Multiple RuntimeVisibleAnnotations attributes for Record component in class file %s", THREAD); + return 0; } runtime_visible_annotations_length = attribute_length; runtime_visible_annotations = cfs->current(); @@ -3352,7 +3471,8 @@ u2 ClassFileParser::parse_classfile_record_attribute(const ClassFileStream* cons } else if (attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { if (runtime_invisible_annotations_exists) { classfile_parse_error( - "Multiple RuntimeInvisibleAnnotations attributes for Record component in class file %s", CHECK_0); + "Multiple RuntimeInvisibleAnnotations attributes for Record component in class file %s", THREAD); + return 0; } runtime_invisible_annotations_exists = true; if (PreserveAllAnnotations) { @@ -3365,7 +3485,8 @@ u2 ClassFileParser::parse_classfile_record_attribute(const ClassFileStream* cons } else if (attribute_name == vmSymbols::tag_runtime_visible_type_annotations()) { if (runtime_visible_type_annotations != NULL) { classfile_parse_error( - "Multiple RuntimeVisibleTypeAnnotations attributes for Record component in class file %s", CHECK_0); + "Multiple RuntimeVisibleTypeAnnotations attributes for Record component in class file %s", THREAD); + return 0; } runtime_visible_type_annotations_length = attribute_length; runtime_visible_type_annotations = cfs->current(); @@ -3377,7 +3498,8 @@ u2 ClassFileParser::parse_classfile_record_attribute(const ClassFileStream* cons } else if (attribute_name == vmSymbols::tag_runtime_invisible_type_annotations()) { if (runtime_invisible_type_annotations_exists) { classfile_parse_error( - "Multiple RuntimeInvisibleTypeAnnotations attributes for Record component in class file %s", CHECK_0); + "Multiple RuntimeInvisibleTypeAnnotations attributes for Record component in class file %s", THREAD); + return 0; } runtime_invisible_type_annotations_exists = true; if (PreserveAllAnnotations) { @@ -3520,12 +3642,6 @@ bool ClassFileParser::supports_sealed_types() { Arguments::enable_preview(); } -bool ClassFileParser::supports_records() { - return _major_version == JVM_CLASSFILE_MAJOR_VERSION && - _minor_version == JAVA_PREVIEW_MINOR_VERSION && - Arguments::enable_preview(); -} - void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cfs, ConstantPool* cp, ClassFileParser::ClassAnnotationCollector* parsed_annotations, @@ -3588,7 +3704,8 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf guarantee_property(attribute_length == 2, "Wrong SourceFile attribute length in class file %s", CHECK); } if (parsed_sourcefile_attribute) { - classfile_parse_error("Multiple SourceFile attributes in class file %s", CHECK); + classfile_parse_error("Multiple SourceFile attributes in class file %s", THREAD); + return; } else { parsed_sourcefile_attribute = true; } @@ -3596,15 +3713,17 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf } else if (tag == vmSymbols::tag_source_debug_extension()) { // Check for SourceDebugExtension tag if (parsed_source_debug_ext_annotations_exist) { - classfile_parse_error( - "Multiple SourceDebugExtension attributes in class file %s", CHECK); + classfile_parse_error( + "Multiple SourceDebugExtension attributes in class file %s", THREAD); + return; } parsed_source_debug_ext_annotations_exist = true; parse_classfile_source_debug_extension_attribute(cfs, (int)attribute_length, CHECK); } else if (tag == vmSymbols::tag_inner_classes()) { // Check for InnerClasses tag if (parsed_innerclasses_attribute) { - classfile_parse_error("Multiple InnerClasses attributes in class file %s", CHECK); + classfile_parse_error("Multiple InnerClasses attributes in class file %s", THREAD); + return; } else { parsed_innerclasses_attribute = true; } @@ -3617,7 +3736,8 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf if (attribute_length != 0) { classfile_parse_error( "Invalid Synthetic classfile attribute length %u in class file %s", - attribute_length, CHECK); + attribute_length, THREAD); + return; } parse_classfile_synthetic_attribute(CHECK); } else if (tag == vmSymbols::tag_deprecated()) { @@ -3625,24 +3745,28 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf if (attribute_length != 0) { classfile_parse_error( "Invalid Deprecated classfile attribute length %u in class file %s", - attribute_length, CHECK); + attribute_length, THREAD); + return; } } else if (_major_version >= JAVA_1_5_VERSION) { if (tag == vmSymbols::tag_signature()) { if (_generic_signature_index != 0) { classfile_parse_error( - "Multiple Signature attributes in class file %s", CHECK); + "Multiple Signature attributes in class file %s", THREAD); + return; } if (attribute_length != 2) { classfile_parse_error( "Wrong Signature attribute length %u in class file %s", - attribute_length, CHECK); + attribute_length, THREAD); + return; } parse_classfile_signature_attribute(cfs, CHECK); } else if (tag == vmSymbols::tag_runtime_visible_annotations()) { if (runtime_visible_annotations != NULL) { classfile_parse_error( - "Multiple RuntimeVisibleAnnotations attributes in class file %s", CHECK); + "Multiple RuntimeVisibleAnnotations attributes in class file %s", THREAD); + return; } runtime_visible_annotations_length = attribute_length; runtime_visible_annotations = cfs->current(); @@ -3659,7 +3783,8 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf } else if (tag == vmSymbols::tag_runtime_invisible_annotations()) { if (runtime_invisible_annotations_exists) { classfile_parse_error( - "Multiple RuntimeInvisibleAnnotations attributes in class file %s", CHECK); + "Multiple RuntimeInvisibleAnnotations attributes in class file %s", THREAD); + return; } runtime_invisible_annotations_exists = true; if (PreserveAllAnnotations) { @@ -3670,7 +3795,8 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf cfs->skip_u1(attribute_length, CHECK); } else if (tag == vmSymbols::tag_enclosing_method()) { if (parsed_enclosingmethod_attribute) { - classfile_parse_error("Multiple EnclosingMethod attributes in class file %s", CHECK); + classfile_parse_error("Multiple EnclosingMethod attributes in class file %s", THREAD); + return; } else { parsed_enclosingmethod_attribute = true; } @@ -3681,7 +3807,8 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf enclosing_method_class_index = cfs->get_u2_fast(); enclosing_method_method_index = cfs->get_u2_fast(); if (enclosing_method_class_index == 0) { - classfile_parse_error("Invalid class index in EnclosingMethod attribute in class file %s", CHECK); + classfile_parse_error("Invalid class index in EnclosingMethod attribute in class file %s", THREAD); + return; } // Validate the constant pool indices and types check_property(valid_klass_reference_at(enclosing_method_class_index), @@ -3689,19 +3816,22 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf if (enclosing_method_method_index != 0 && (!cp->is_within_bounds(enclosing_method_method_index) || !cp->tag_at(enclosing_method_method_index).is_name_and_type())) { - classfile_parse_error("Invalid or out-of-bounds method index in EnclosingMethod attribute in class file %s", CHECK); + classfile_parse_error("Invalid or out-of-bounds method index in EnclosingMethod attribute in class file %s", THREAD); + return; } } else if (tag == vmSymbols::tag_bootstrap_methods() && _major_version >= Verifier::INVOKEDYNAMIC_MAJOR_VERSION) { if (parsed_bootstrap_methods_attribute) { - classfile_parse_error("Multiple BootstrapMethods attributes in class file %s", CHECK); + classfile_parse_error("Multiple BootstrapMethods attributes in class file %s", THREAD); + return; } parsed_bootstrap_methods_attribute = true; parse_classfile_bootstrap_methods_attribute(cfs, cp, attribute_length, CHECK); } else if (tag == vmSymbols::tag_runtime_visible_type_annotations()) { if (runtime_visible_type_annotations != NULL) { classfile_parse_error( - "Multiple RuntimeVisibleTypeAnnotations attributes in class file %s", CHECK); + "Multiple RuntimeVisibleTypeAnnotations attributes in class file %s", THREAD); + return; } runtime_visible_type_annotations_length = attribute_length; runtime_visible_type_annotations = cfs->current(); @@ -3711,7 +3841,8 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf } else if (tag == vmSymbols::tag_runtime_invisible_type_annotations()) { if (runtime_invisible_type_annotations_exists) { classfile_parse_error( - "Multiple RuntimeInvisibleTypeAnnotations attributes in class file %s", CHECK); + "Multiple RuntimeInvisibleTypeAnnotations attributes in class file %s", THREAD); + return; } else { runtime_invisible_type_annotations_exists = true; } @@ -3725,24 +3856,28 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf if (tag == vmSymbols::tag_nest_members()) { // Check for NestMembers tag if (parsed_nest_members_attribute) { - classfile_parse_error("Multiple NestMembers attributes in class file %s", CHECK); + classfile_parse_error("Multiple NestMembers attributes in class file %s", THREAD); + return; } else { parsed_nest_members_attribute = true; } if (parsed_nest_host_attribute) { - classfile_parse_error("Conflicting NestHost and NestMembers attributes in class file %s", CHECK); + classfile_parse_error("Conflicting NestHost and NestMembers attributes in class file %s", THREAD); + return; } nest_members_attribute_start = cfs->current(); nest_members_attribute_length = attribute_length; cfs->skip_u1(nest_members_attribute_length, CHECK); } else if (tag == vmSymbols::tag_nest_host()) { if (parsed_nest_host_attribute) { - classfile_parse_error("Multiple NestHost attributes in class file %s", CHECK); + classfile_parse_error("Multiple NestHost attributes in class file %s", THREAD); + return; } else { parsed_nest_host_attribute = true; } if (parsed_nest_members_attribute) { - classfile_parse_error("Conflicting NestMembers and NestHost attributes in class file %s", CHECK); + classfile_parse_error("Conflicting NestMembers and NestHost attributes in class file %s", THREAD); + return; } if (_need_verify) { guarantee_property(attribute_length == 2, "Wrong NestHost attribute length in class file %s", CHECK); @@ -3754,63 +3889,34 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf "Nest-host class_info_index %u has bad constant type in class file %s", class_info_index, CHECK); _nest_host = class_info_index; - } else if (_major_version >= JAVA_14_VERSION) { + + } else if (_major_version >= JAVA_16_VERSION) { if (tag == vmSymbols::tag_record()) { - // Skip over Record attribute if not supported or if super class is - // not java.lang.Record. - if (supports_records() && - cp->klass_name_at(_super_class_index) == vmSymbols::java_lang_Record()) { - if (parsed_record_attribute) { - classfile_parse_error("Multiple Record attributes in class file %s", CHECK); - } - // Check that class is final and not abstract. - if (!_access_flags.is_final() || _access_flags.is_abstract()) { - classfile_parse_error("Record attribute in non-final or abstract class file %s", CHECK); - } - parsed_record_attribute = true; - record_attribute_start = cfs->current(); - record_attribute_length = attribute_length; - } else if (log_is_enabled(Info, class, record)) { - // Log why the Record attribute was ignored. Note that if the - // class file version is JVM_CLASSFILE_MAJOR_VERSION.65535 and - // --enable-preview wasn't specified then a java.lang.UnsupportedClassVersionError - // exception would have been thrown. - ResourceMark rm(THREAD); - if (supports_records()) { - log_info(class, record)( - "Ignoring Record attribute in class %s because super type is not java.lang.Record", - _class_name->as_C_string()); - } else { - log_info(class, record)( - "Ignoring Record attribute in class %s because class file version is not %d.65535", - _class_name->as_C_string(), JVM_CLASSFILE_MAJOR_VERSION); - } + if (parsed_record_attribute) { + classfile_parse_error("Multiple Record attributes in class file %s", THREAD); + return; } - cfs->skip_u1(attribute_length, CHECK); - } else if (_major_version >= JAVA_15_VERSION) { - // Check for PermittedSubclasses tag - if (tag == vmSymbols::tag_permitted_subclasses()) { - if (supports_sealed_types()) { - if (parsed_permitted_subclasses_attribute) { - classfile_parse_error("Multiple PermittedSubclasses attributes in class file %s", CHECK); - } - // Classes marked ACC_FINAL cannot have a PermittedSubclasses attribute. - if (_access_flags.is_final()) { - classfile_parse_error("PermittedSubclasses attribute in final class file %s", CHECK); - } - parsed_permitted_subclasses_attribute = true; - permitted_subclasses_attribute_start = cfs->current(); - permitted_subclasses_attribute_length = attribute_length; + parsed_record_attribute = true; + record_attribute_start = cfs->current(); + record_attribute_length = attribute_length; + } else if (tag == vmSymbols::tag_permitted_subclasses()) { + if (supports_sealed_types()) { + if (parsed_permitted_subclasses_attribute) { + classfile_parse_error("Multiple PermittedSubclasses attributes in class file %s", CHECK); + return; } - cfs->skip_u1(attribute_length, CHECK); - } else { - // Unknown attribute - cfs->skip_u1(attribute_length, CHECK); + // Classes marked ACC_FINAL cannot have a PermittedSubclasses attribute. + if (_access_flags.is_final()) { + classfile_parse_error("PermittedSubclasses attribute in final class file %s", CHECK); + return; + } + parsed_permitted_subclasses_attribute = true; + permitted_subclasses_attribute_start = cfs->current(); + permitted_subclasses_attribute_length = attribute_length; } - } else { - // Unknown attribute - cfs->skip_u1(attribute_length, CHECK); } + // Skip attribute_length for any attribute where major_verson >= JAVA_16_VERSION + cfs->skip_u1(attribute_length, CHECK); } else { // Unknown attribute cfs->skip_u1(attribute_length, CHECK); @@ -3838,6 +3944,7 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf if (parsed_innerclasses_attribute || parsed_enclosingmethod_attribute) { const u2 num_of_classes = parse_classfile_inner_classes_attribute( cfs, + cp, inner_classes_attribute_start, parsed_innerclasses_attribute, enclosing_method_class_index, @@ -4295,7 +4402,7 @@ static Array* compute_transitive_interfaces(const InstanceKlass* } } -static void check_super_class_access(const InstanceKlass* this_klass, TRAPS) { +void ClassFileParser::check_super_class_access(const InstanceKlass* this_klass, TRAPS) { assert(this_klass != NULL, "invariant"); const Klass* const super = this_klass->super(); @@ -4303,24 +4410,12 @@ static void check_super_class_access(const InstanceKlass* this_klass, TRAPS) { const InstanceKlass* super_ik = InstanceKlass::cast(super); if (super->is_final()) { - ResourceMark rm(THREAD); - Exceptions::fthrow( - THREAD_AND_LOCATION, - vmSymbols::java_lang_VerifyError(), - "class %s cannot inherit from final class %s", - this_klass->external_name(), - super_ik->external_name()); + classfile_icce_error("class %s cannot inherit from final class %s", super_ik, THREAD); return; } if (super_ik->is_sealed() && !super_ik->has_as_permitted_subclass(this_klass)) { - ResourceMark rm(THREAD); - Exceptions::fthrow( - THREAD_AND_LOCATION, - vmSymbols::java_lang_IncompatibleClassChangeError(), - "class %s cannot inherit from sealed class %s", - this_klass->external_name(), - super_ik->external_name()); + classfile_icce_error("class %s cannot inherit from sealed class %s", super_ik, THREAD); return; } @@ -4376,7 +4471,7 @@ static void check_super_class_access(const InstanceKlass* this_klass, TRAPS) { } -static void check_super_interface_access(const InstanceKlass* this_klass, TRAPS) { +void ClassFileParser::check_super_interface_access(const InstanceKlass* this_klass, TRAPS) { assert(this_klass != NULL, "invariant"); const Array* const local_interfaces = this_klass->local_interfaces(); const int lng = local_interfaces->length(); @@ -4385,14 +4480,10 @@ static void check_super_interface_access(const InstanceKlass* this_klass, TRAPS) assert (k != NULL && k->is_interface(), "invalid interface"); if (k->is_sealed() && !k->has_as_permitted_subclass(this_klass)) { - ResourceMark rm(THREAD); - Exceptions::fthrow( - THREAD_AND_LOCATION, - vmSymbols::java_lang_IncompatibleClassChangeError(), - "class %s cannot %s sealed interface %s", - this_klass->external_name(), - this_klass->is_interface() ? "extend" : "implement", - k->external_name()); + classfile_icce_error(this_klass->is_interface() ? + "class %s cannot extend sealed interface %s" : + "class %s cannot implement sealed interface %s", + k, THREAD); return; } @@ -4464,15 +4555,12 @@ static void check_final_method_override(const InstanceKlass* this_klass, TRAPS) if (can_access) { // this class can access super final method and therefore override ResourceMark rm(THREAD); - Exceptions::fthrow(THREAD_AND_LOCATION, - vmSymbols::java_lang_VerifyError(), - "class %s overrides final method %s.%s%s", - this_klass->external_name(), - super_m->method_holder()->external_name(), - name->as_C_string(), - signature->as_C_string() - ); - return; + THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), + err_msg("class %s overrides final method %s.%s%s", + this_klass->external_name(), + super_m->method_holder()->external_name(), + name->as_C_string(), + signature->as_C_string())); } } @@ -4569,15 +4657,12 @@ static bool has_illegal_visibility(jint flags) { // Major_version >= 56 and major_version <= JVM_CLASSFILE_MAJOR_VERSION and minor_version = 0. // Major_version = JVM_CLASSFILE_MAJOR_VERSION and minor_version = 65535 and --enable-preview is present. // -static void verify_class_version(u2 major, u2 minor, Symbol* class_name, TRAPS){ +void ClassFileParser::verify_class_version(u2 major, u2 minor, Symbol* class_name, TRAPS){ ResourceMark rm(THREAD); const u2 max_version = JVM_CLASSFILE_MAJOR_VERSION; if (major < JAVA_MIN_SUPPORTED_VERSION) { - Exceptions::fthrow( - THREAD_AND_LOCATION, - vmSymbols::java_lang_UnsupportedClassVersionError(), - "%s (class file version %u.%u) was compiled with an invalid major version", - class_name->as_C_string(), major, minor); + classfile_ucve_error("%s (class file version %u.%u) was compiled with an invalid major version", + class_name, major, minor, THREAD); return; } @@ -4607,20 +4692,14 @@ static void verify_class_version(u2 major, u2 minor, Symbol* class_name, TRAPS){ } if (!Arguments::enable_preview()) { - Exceptions::fthrow( - THREAD_AND_LOCATION, - vmSymbols::java_lang_UnsupportedClassVersionError(), - "Preview features are not enabled for %s (class file version %u.%u). Try running with '--enable-preview'", - class_name->as_C_string(), major, minor); + classfile_ucve_error("Preview features are not enabled for %s (class file version %u.%u). Try running with '--enable-preview'", + class_name, major, minor, THREAD); return; } } else { // minor != JAVA_PREVIEW_MINOR_VERSION - Exceptions::fthrow( - THREAD_AND_LOCATION, - vmSymbols::java_lang_UnsupportedClassVersionError(), - "%s (class file version %u.%u) was compiled with an invalid non-zero minor version", - class_name->as_C_string(), major, minor); + classfile_ucve_error("%s (class file version %u.%u) was compiled with an invalid non-zero minor version", + class_name, major, minor, THREAD); } } @@ -4751,7 +4830,7 @@ void ClassFileParser::verify_legal_utf8(const unsigned char* buffer, TRAPS) const { assert(_need_verify, "only called when _need_verify is true"); if (!UTF8::is_legal_utf8(buffer, length, _major_version <= 47)) { - classfile_parse_error("Illegal UTF8 string in constant pool in class file %s", CHECK); + classfile_parse_error("Illegal UTF8 string in constant pool in class file %s", THREAD); } } @@ -4920,7 +4999,7 @@ const char* ClassFileParser::skip_over_field_signature(const char* signature, if (!legal) { classfile_parse_error("Class name is empty or contains illegal character " "in descriptor in class file %s", - CHECK_NULL); + THREAD); return NULL; } return signature + newlen + 1; @@ -4932,7 +5011,8 @@ const char* ClassFileParser::skip_over_field_signature(const char* signature, array_dim++; if (array_dim > 255) { // 4277370: array descriptor is valid only if it represents 255 or fewer dimensions. - classfile_parse_error("Array type descriptor has more than 255 dimensions in class file %s", CHECK_NULL); + classfile_parse_error("Array type descriptor has more than 255 dimensions in class file %s", THREAD); + return NULL; } // The rest of what's there better be a legal signature signature++; @@ -5156,9 +5236,9 @@ static void check_methods_for_intrinsics(const InstanceKlass* ik, // (We used to do this lazily, but now we query it in Rewriter, // which is eagerly done for every method, so we might as well do it now, // when everything is fresh in memory.) - const vmSymbols::SID klass_id = Method::klass_id_for_intrinsics(ik); + const vmSymbolID klass_id = Method::klass_id_for_intrinsics(ik); - if (klass_id != vmSymbols::NO_SID) { + if (klass_id != vmSymbolID::NO_SID) { for (int j = 0; j < methods->length(); ++j) { Method* method = methods->at(j); method->init_intrinsic_id(); @@ -5888,7 +5968,8 @@ void ClassFileParser::parse_stream(const ClassFileStream* const stream, if (bad_constant != 0) { // Do not throw CFE until after the access_flags are checked because if // ACC_MODULE is set in the access flags, then NCDFE must be thrown, not CFE. - classfile_parse_error("Unknown constant tag %u in class file %s", bad_constant, CHECK); + classfile_parse_error("Unknown constant tag %u in class file %s", bad_constant, THREAD); + return; } _access_flags.set_flags(flags); @@ -6056,7 +6137,18 @@ void ClassFileParser::mangle_hidden_class_name(InstanceKlass* const ik) { // use an illegal char such as ';' because that causes serialization issues // and issues with hidden classes that create their own hidden classes. char addr_buf[20]; - jio_snprintf(addr_buf, 20, INTPTR_FORMAT, p2i(ik)); + if (DumpSharedSpaces) { + // We want stable names for the archived hidden classes (only for static + // archive for now). Spaces under default_SharedBaseAddress() will be + // occupied by the archive at run time, so we know that no dynamically + // loaded InstanceKlass will be placed under there. + static volatile size_t counter = 0; + Atomic::cmpxchg(&counter, (size_t)0, Arguments::default_SharedBaseAddress()); // initialize it + size_t new_id = Atomic::add(&counter, (size_t)1); + jio_snprintf(addr_buf, 20, SIZE_FORMAT_HEX, new_id); + } else { + jio_snprintf(addr_buf, 20, INTPTR_FORMAT, p2i(ik)); + } size_t new_name_len = _class_name->utf8_length() + 2 + strlen(addr_buf); char* new_name = NEW_RESOURCE_ARRAY(char, new_name_len); jio_snprintf(new_name, new_name_len, "%s+%s", @@ -6117,14 +6209,7 @@ void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const st } if (_super_klass->is_interface()) { - ResourceMark rm(THREAD); - Exceptions::fthrow( - THREAD_AND_LOCATION, - vmSymbols::java_lang_IncompatibleClassChangeError(), - "class %s has interface %s as super class", - _class_name->as_klass_external_name(), - _super_klass->external_name() - ); + classfile_icce_error("class %s has interface %s as super class", _super_klass, THREAD); return; } } diff --git a/src/hotspot/share/classfile/classFileParser.hpp b/src/hotspot/share/classfile/classFileParser.hpp index 20b72ee2d4a..81ae3ad9597 100644 --- a/src/hotspot/share/classfile/classFileParser.hpp +++ b/src/hotspot/share/classfile/classFileParser.hpp @@ -317,7 +317,11 @@ class ClassFileParser { int length, TRAPS); + // Check for circularity in InnerClasses attribute. + bool check_inner_classes_circularity(const ConstantPool* cp, int length, TRAPS); + u2 parse_classfile_inner_classes_attribute(const ClassFileStream* const cfs, + const ConstantPool* cp, const u1* const inner_classes_attribute_start, bool parsed_enclosingmethod_attribute, u2 enclosing_method_class_index, @@ -374,8 +378,18 @@ class ClassFileParser { const char* signature, TRAPS) const; + void classfile_icce_error(const char* msg, + const Klass* k, + TRAPS) const; + + void classfile_ucve_error(const char* msg, + const Symbol* class_name, + u2 major, + u2 minor, + TRAPS) const; + inline void guarantee_property(bool b, const char* msg, TRAPS) const { - if (!b) { classfile_parse_error(msg, CHECK); } + if (!b) { classfile_parse_error(msg, THREAD); return; } } void report_assert_property_failure(const char* msg, TRAPS) const PRODUCT_RETURN; @@ -420,14 +434,14 @@ class ClassFileParser { const char* msg, int index, TRAPS) const { - if (!b) { classfile_parse_error(msg, index, CHECK); } + if (!b) { classfile_parse_error(msg, index, THREAD); return; } } inline void guarantee_property(bool b, const char* msg, const char *name, TRAPS) const { - if (!b) { classfile_parse_error(msg, name, CHECK); } + if (!b) { classfile_parse_error(msg, name, THREAD); return; } } inline void guarantee_property(bool b, @@ -435,7 +449,7 @@ class ClassFileParser { int index, const char *name, TRAPS) const { - if (!b) { classfile_parse_error(msg, index, name, CHECK); } + if (!b) { classfile_parse_error(msg, index, name, THREAD); return; } } void throwIllegalSignature(const char* type, @@ -460,6 +474,8 @@ class ClassFileParser { const Symbol* signature, TRAPS) const; + void verify_class_version(u2 major, u2 minor, Symbol* class_name, TRAPS); + void verify_legal_class_modifiers(jint flags, TRAPS) const; void verify_legal_field_modifiers(jint flags, bool is_interface, TRAPS) const; void verify_legal_method_modifiers(jint flags, @@ -467,6 +483,12 @@ class ClassFileParser { const Symbol* name, TRAPS) const; + void check_super_class_access(const InstanceKlass* this_klass, + TRAPS); + + void check_super_interface_access(const InstanceKlass* this_klass, + TRAPS); + const char* skip_over_field_signature(const char* signature, bool void_ok, unsigned int length, diff --git a/src/hotspot/share/classfile/classListParser.cpp b/src/hotspot/share/classfile/classListParser.cpp index 638457f4633..2cd89417fd3 100644 --- a/src/hotspot/share/classfile/classListParser.cpp +++ b/src/hotspot/share/classfile/classListParser.cpp @@ -28,13 +28,19 @@ #include "classfile/classListParser.hpp" #include "classfile/classLoaderExt.hpp" #include "classfile/javaClasses.inline.hpp" +#include "classfile/lambdaFormInvokers.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/systemDictionaryShared.hpp" +#include "interpreter/bytecode.hpp" +#include "interpreter/bytecodeStream.hpp" +#include "interpreter/linkResolver.hpp" #include "logging/log.hpp" #include "logging/logTag.hpp" +#include "memory/archiveUtils.hpp" #include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" +#include "oops/constantPool.hpp" #include "runtime/handles.inline.hpp" #include "runtime/java.hpp" #include "runtime/javaCalls.hpp" @@ -64,6 +70,7 @@ ClassListParser::ClassListParser(const char* file) { } _line_no = 0; _interfaces = new (ResourceObj::C_HEAP, mtClass) GrowableArray(10, mtClass); + _indy_items = new (ResourceObj::C_HEAP, mtClass) GrowableArray(9, mtClass); } ClassListParser::~ClassListParser() { @@ -86,36 +93,44 @@ bool ClassListParser::parse_one_line() { if (*_line == '#') { // comment continue; } + + { + int len = (int)strlen(_line); + int i; + // Replace \t\r\n\f with ' ' + for (i=0; i 0) { + if (_line[len-1] == ' ') { + _line[len-1] = '\0'; + len --; + } else { + break; + } + } + _line_len = len; + } + + // valid line break; } + _class_name = _line; _id = _unspecified; _super = _unspecified; _interfaces->clear(); _source = NULL; _interfaces_specified = false; + _indy_items->clear(); + _lambda_form_line = false; - { - int len = (int)strlen(_line); - int i; - // Replace \t\r\n with ' ' - for (i=0; i 0) { - if (_line[len-1] == ' ') { - _line[len-1] = '\0'; - len --; - } else { - break; - } - } - _line_len = len; - _class_name = _line; + if (_line[0] == '@') { + return parse_at_tags(); } if ((_token = strchr(_line, ' ')) == NULL) { @@ -129,14 +144,14 @@ bool ClassListParser::parse_one_line() { while (*_token) { skip_whitespaces(); - if (parse_int_option("id:", &_id)) { + if (parse_uint_option("id:", &_id)) { continue; - } else if (parse_int_option("super:", &_super)) { + } else if (parse_uint_option("super:", &_super)) { check_already_loaded("Super class", _super); continue; } else if (skip_token("interfaces:")) { int i; - while (try_parse_int(&i)) { + while (try_parse_uint(&i)) { check_already_loaded("Interface", i); _interfaces->append(i); } @@ -165,6 +180,62 @@ bool ClassListParser::parse_one_line() { return true; } +void ClassListParser::split_tokens_by_whitespace(int offset) { + int start = offset; + int end; + bool done = false; + while (!done) { + while (_line[start] == ' ' || _line[start] == '\t') start++; + end = start; + while (_line[end] && _line[end] != ' ' && _line[end] != '\t') end++; + if (_line[end] == '\0') { + done = true; + } else { + _line[end] = '\0'; + } + _indy_items->append(_line + start); + start = ++end; + } +} + +int ClassListParser::split_at_tag_from_line() { + _token = _line; + char* ptr; + if ((ptr = strchr(_line, ' ')) == NULL) { + error("Too few items following the @ tag \"%s\" line #%d", _line, _line_no); + return 0; + } + *ptr++ = '\0'; + while (*ptr == ' ' || *ptr == '\t') ptr++; + return (int)(ptr - _line); +} + +bool ClassListParser::parse_at_tags() { + assert(_line[0] == '@', "must be"); + int offset; + if ((offset = split_at_tag_from_line()) == 0) { + return false; + } + + if (strcmp(_token, LAMBDA_PROXY_TAG) == 0) { + split_tokens_by_whitespace(offset); + if (_indy_items->length() < 2) { + error("Line with @ tag has too few items \"%s\" line #%d", _token, _line_no); + return false; + } + // set the class name + _class_name = _indy_items->at(0); + return true; + } else if (strcmp(_token, LAMBDA_FORM_TAG) == 0) { + LambdaFormInvokers::append(os::strdup((const char*)(_line + offset), mtInternal)); + _lambda_form_line = true; + return true; + } else { + error("Invalid @ tag at the beginning of line \"%s\" line #%d", _token, _line_no); + return false; + } +} + void ClassListParser::skip_whitespaces() { while (*_token == ' ' || *_token == '\t') { _token ++; @@ -181,15 +252,19 @@ void ClassListParser::parse_int(int* value) { skip_whitespaces(); if (sscanf(_token, "%i", value) == 1) { skip_non_whitespaces(); - if (*value < 0) { - error("Error: negative integers not allowed (%d)", *value); - } } else { error("Error: expected integer"); } } -bool ClassListParser::try_parse_int(int* value) { +void ClassListParser::parse_uint(int* value) { + parse_int(value); + if (*value < 0) { + error("Error: negative integers not allowed (%d)", *value); + } +} + +bool ClassListParser::try_parse_uint(int* value) { skip_whitespaces(); if (sscanf(_token, "%i", value) == 1) { skip_non_whitespaces(); @@ -220,6 +295,18 @@ bool ClassListParser::parse_int_option(const char* option_name, int* value) { return false; } +bool ClassListParser::parse_uint_option(const char* option_name, int* value) { + if (skip_token(option_name)) { + if (*value != _unspecified) { + error("%s specified twice", option_name); + } else { + parse_uint(value); + return true; + } + } + return false; +} + void ClassListParser::print_specified_interfaces() { const int n = _interfaces->length(); jio_fprintf(defaultStream::error_stream(), "Currently specified interfaces[%d] = {\n", n); @@ -326,9 +413,121 @@ InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS return k; } +void ClassListParser::populate_cds_indy_info(const constantPoolHandle &pool, int cp_index, CDSIndyInfo* cii, TRAPS) { + // Caller needs to allocate ResourceMark. + int type_index = pool->bootstrap_name_and_type_ref_index_at(cp_index); + int name_index = pool->name_ref_index_at(type_index); + cii->add_item(pool->symbol_at(name_index)->as_C_string()); + int sig_index = pool->signature_ref_index_at(type_index); + cii->add_item(pool->symbol_at(sig_index)->as_C_string()); + int argc = pool->bootstrap_argument_count_at(cp_index); + if (argc > 0) { + for (int arg_i = 0; arg_i < argc; arg_i++) { + int arg = pool->bootstrap_argument_index_at(cp_index, arg_i); + jbyte tag = pool->tag_at(arg).value(); + if (tag == JVM_CONSTANT_MethodType) { + cii->add_item(pool->method_type_signature_at(arg)->as_C_string()); + } else if (tag == JVM_CONSTANT_MethodHandle) { + cii->add_ref_kind(pool->method_handle_ref_kind_at(arg)); + int callee_index = pool->method_handle_klass_index_at(arg); + Klass* callee = pool->klass_at(callee_index, THREAD); + if (callee != NULL) { + cii->add_item(callee->name()->as_C_string()); + } + cii->add_item(pool->method_handle_name_ref_at(arg)->as_C_string()); + cii->add_item(pool->method_handle_signature_ref_at(arg)->as_C_string()); + } else { + ShouldNotReachHere(); + } + } + } +} + +bool ClassListParser::is_matching_cp_entry(constantPoolHandle &pool, int cp_index, TRAPS) { + ResourceMark rm(THREAD); + CDSIndyInfo cii; + populate_cds_indy_info(pool, cp_index, &cii, THREAD); + GrowableArray* items = cii.items(); + int indy_info_offset = 1; + if (_indy_items->length() - indy_info_offset != items->length()) { + return false; + } + for (int i = 0; i < items->length(); i++) { + if (strcmp(_indy_items->at(i + indy_info_offset), items->at(i)) != 0) { + return false; + } + } + return true; +} + +void ClassListParser::resolve_indy(Symbol* class_name_symbol, TRAPS) { + Handle class_loader(THREAD, SystemDictionary::java_system_loader()); + Handle protection_domain; + Klass* klass = SystemDictionary::resolve_or_fail(class_name_symbol, class_loader, protection_domain, true, THREAD); // FIXME should really be just a lookup + if (klass != NULL && klass->is_instance_klass()) { + InstanceKlass* ik = InstanceKlass::cast(klass); + MetaspaceShared::try_link_class(ik, THREAD); + assert(!HAS_PENDING_EXCEPTION, "unexpected exception"); + + ConstantPool* cp = ik->constants(); + ConstantPoolCache* cpcache = cp->cache(); + bool found = false; + for (int cpcindex = 0; cpcindex < cpcache->length(); cpcindex ++) { + int indy_index = ConstantPool::encode_invokedynamic_index(cpcindex); + ConstantPoolCacheEntry* cpce = cpcache->entry_at(cpcindex); + int pool_index = cpce->constant_pool_index(); + constantPoolHandle pool(THREAD, cp); + if (pool->tag_at(pool_index).is_invoke_dynamic()) { + BootstrapInfo bootstrap_specifier(pool, pool_index, indy_index); + Handle bsm = bootstrap_specifier.resolve_bsm(THREAD); + if (!SystemDictionaryShared::is_supported_invokedynamic(&bootstrap_specifier)) { + log_debug(cds, lambda)("is_supported_invokedynamic check failed for cp_index %d", pool_index); + continue; + } + if (is_matching_cp_entry(pool, pool_index, THREAD)) { + found = true; + CallInfo info; + bool is_done = bootstrap_specifier.resolve_previously_linked_invokedynamic(info, THREAD); + if (!is_done) { + // resolve it + Handle recv; + LinkResolver::resolve_invoke(info, recv, pool, indy_index, Bytecodes::_invokedynamic, THREAD); + break; + } + cpce->set_dynamic_call(pool, info); + if (HAS_PENDING_EXCEPTION) { + ResourceMark rm(THREAD); + tty->print("resolve_indy for class %s has", class_name_symbol->as_C_string()); + oop message = java_lang_Throwable::message(PENDING_EXCEPTION); + if (message != NULL) { + char* ex_msg = java_lang_String::as_utf8_string(message); + tty->print_cr(" exception pending '%s %s'", + PENDING_EXCEPTION->klass()->external_name(), ex_msg); + } else { + tty->print_cr(" exception pending %s ", + PENDING_EXCEPTION->klass()->external_name()); + } + exit(1); + } + } + } + } + if (!found) { + ResourceMark rm(THREAD); + log_warning(cds)("No invoke dynamic constant pool entry can be found for class %s. The classlist is probably out-of-date.", + class_name_symbol->as_C_string()); + } + } +} + Klass* ClassListParser::load_current_class(TRAPS) { TempNewSymbol class_name_symbol = SymbolTable::new_symbol(_class_name); + if (_indy_items->length() > 0) { + resolve_indy(class_name_symbol, CHECK_NULL); + return NULL; + } + Klass* klass = NULL; if (!is_loading_from_source()) { // Load classes for the boot/platform/app loaders only. @@ -373,6 +572,7 @@ Klass* ClassListParser::load_current_class(TRAPS) { klass = java_lang_Class::as_Klass(obj); } else { // load classes in bootclasspath/a if (HAS_PENDING_EXCEPTION) { + ArchiveUtils::check_for_oom(PENDING_EXCEPTION); // exit on OOM CLEAR_PENDING_EXCEPTION; } @@ -383,6 +583,8 @@ Klass* ClassListParser::load_current_class(TRAPS) { } else { if (!HAS_PENDING_EXCEPTION) { THROW_NULL(vmSymbols::java_lang_ClassNotFoundException()); + } else { + ArchiveUtils::check_for_oom(PENDING_EXCEPTION); // exit on OOM } } } @@ -391,6 +593,9 @@ Klass* ClassListParser::load_current_class(TRAPS) { // If "source:" tag is specified, all super class and super interfaces must be specified in the // class list file. klass = load_class_from_source(class_name_symbol, CHECK_NULL); + if (HAS_PENDING_EXCEPTION) { + ArchiveUtils::check_for_oom(PENDING_EXCEPTION); // exit on OOM + } } if (klass != NULL && klass->is_instance_klass() && is_id_specified()) { diff --git a/src/hotspot/share/classfile/classListParser.hpp b/src/hotspot/share/classfile/classListParser.hpp index 72cdf5c5961..dd5cf0b62ff 100644 --- a/src/hotspot/share/classfile/classListParser.hpp +++ b/src/hotspot/share/classfile/classListParser.hpp @@ -30,11 +30,44 @@ #include "utilities/growableArray.hpp" #include "utilities/hashtable.inline.hpp" +#define LAMBDA_PROXY_TAG "@lambda-proxy" +#define LAMBDA_FORM_TAG "@lambda-form-invoker" + class ID2KlassTable : public KVHashtable { public: ID2KlassTable() : KVHashtable(1987) {} }; +class CDSIndyInfo { + GrowableArray* _items; +public: + CDSIndyInfo() : _items(NULL) {} + void add_item(const char* item) { + if (_items == NULL) { + _items = new GrowableArray(9); + } + assert(_items != NULL, "sanity"); + _items->append(item); + } + void add_ref_kind(int ref_kind) { + switch (ref_kind) { + case JVM_REF_getField : _items->append("REF_getField"); break; + case JVM_REF_getStatic : _items->append("REF_getStatic"); break; + case JVM_REF_putField : _items->append("REF_putField"); break; + case JVM_REF_putStatic : _items->append("REF_putStatic"); break; + case JVM_REF_invokeVirtual : _items->append("REF_invokeVirtual"); break; + case JVM_REF_invokeStatic : _items->append("REF_invokeStatic"); break; + case JVM_REF_invokeSpecial : _items->append("REF_invokeSpecial"); break; + case JVM_REF_newInvokeSpecial : _items->append("REF_newInvokeSpecial"); break; + case JVM_REF_invokeInterface : _items->append("REF_invokeInterface"); break; + default : ShouldNotReachHere(); + } + } + GrowableArray* items() { + return _items; + } +}; + class ClassListParser : public StackObj { enum { _unspecified = -999, @@ -61,13 +94,16 @@ class ClassListParser : public StackObj { int _line_len; // Original length of the input line. int _line_no; // Line number for current line being parsed const char* _class_name; + GrowableArray* _indy_items; // items related to invoke dynamic for archiving lambda proxy classes int _id; int _super; GrowableArray* _interfaces; bool _interfaces_specified; const char* _source; + bool _lambda_form_line; bool parse_int_option(const char* option_name, int* value); + bool parse_uint_option(const char* option_name, int* value); InstanceKlass* load_class_from_source(Symbol* class_name, TRAPS); ID2KlassTable *table() { return &_id2klass_table; @@ -75,6 +111,9 @@ class ClassListParser : public StackObj { InstanceKlass* lookup_class_by_id(int id); void print_specified_interfaces(); void print_actual_interfaces(InstanceKlass *ik); + bool is_matching_cp_entry(constantPoolHandle &pool, int cp_index, TRAPS); + + void resolve_indy(Symbol* class_name_symbol, TRAPS); public: ClassListParser(const char* file); ~ClassListParser(); @@ -83,10 +122,14 @@ class ClassListParser : public StackObj { return _instance; } bool parse_one_line(); + void split_tokens_by_whitespace(int offset); + int split_at_tag_from_line(); + bool parse_at_tags(); char* _token; void error(const char* msg, ...); void parse_int(int* value); - bool try_parse_int(int* value); + void parse_uint(int* value); + bool try_parse_uint(int* value); bool skip_token(const char* option_name); void skip_whitespaces(); void skip_non_whitespaces(); @@ -122,9 +165,13 @@ class ClassListParser : public StackObj { bool is_loading_from_source(); + bool lambda_form_line() { return _lambda_form_line; } + // Look up the super or interface of the current class being loaded // (in this->load_current_class()). InstanceKlass* lookup_super_for_current_class(Symbol* super_name); InstanceKlass* lookup_interface_for_current_class(Symbol* interface_name); + + static void populate_cds_indy_info(const constantPoolHandle &pool, int cp_index, CDSIndyInfo* cii, TRAPS); }; #endif // SHARE_CLASSFILE_CLASSLISTPARSER_HPP diff --git a/src/hotspot/share/classfile/classListWriter.hpp b/src/hotspot/share/classfile/classListWriter.hpp new file mode 100644 index 00000000000..d31512d522d --- /dev/null +++ b/src/hotspot/share/classfile/classListWriter.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_CLASSFILE_CLASSLISTWRITER_HPP +#define SHARE_CLASSFILE_CLASSLISTWRITER_HPP + +#include "runtime/arguments.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/thread.hpp" +#include "utilities/ostream.hpp" + +class ClassListWriter { + friend const char* make_log_name(const char* log_name, const char* force_directory); + + static fileStream* _classlist_file; + MutexLocker _locker; +public: +#if INCLUDE_CDS + ClassListWriter() : _locker(Thread::current(), ClassListFile_lock, Mutex::_no_safepoint_check_flag) {} +#else + ClassListWriter() : _locker(Thread::current(), NULL, Mutex::_no_safepoint_check_flag) {} +#endif + + outputStream* stream() { + return _classlist_file; + } + + static bool is_enabled() { +#if INCLUDE_CDS + return _classlist_file != NULL && _classlist_file->is_open(); +#else + return false; +#endif + } + + static void init() { +#if INCLUDE_CDS + // For -XX:DumpLoadedClassList= option + if (DumpLoadedClassList != NULL) { + const char* list_name = make_log_name(DumpLoadedClassList, NULL); + _classlist_file = new(ResourceObj::C_HEAP, mtInternal) + fileStream(list_name); + _classlist_file->print_cr("# NOTE: Do not modify this file."); + _classlist_file->print_cr("#"); + _classlist_file->print_cr("# This file is generated via the -XX:DumpLoadedClassList= option"); + _classlist_file->print_cr("# and is used at CDS archive dump time (see -Xshare:dump)."); + _classlist_file->print_cr("#"); + FREE_C_HEAP_ARRAY(char, list_name); + } +#endif + } + + static void delete_classlist() { +#if INCLUDE_CDS + if (_classlist_file != NULL) { + delete _classlist_file; + } +#endif + } +}; + +#endif // SHARE_CLASSFILE_CLASSLISTWRITER_HPP diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index 80858f0fe30..ac699ada874 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -58,7 +58,9 @@ #include "logging/log.hpp" #include "logging/logStream.hpp" #include "memory/allocation.inline.hpp" +#include "memory/classLoaderMetaspace.hpp" #include "memory/metadataFactory.hpp" +#include "memory/metaspace.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/access.inline.hpp" @@ -133,7 +135,7 @@ ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool has_class_mirror_ho _metaspace_lock(new Mutex(Mutex::leaf+1, "Metaspace allocation lock", true, Mutex::_safepoint_check_never)), _unloading(false), _has_class_mirror_holder(has_class_mirror_holder), - _modified_oops(true), _accumulated_modified_oops(false), + _modified_oops(true), // An unsafe anonymous class loader data doesn't have anything to keep // it from being unloaded during parsing of the unsafe anonymous class. // The null-class-loader should always be kept alive. @@ -953,9 +955,11 @@ void ClassLoaderData::verify() { guarantee(cl != NULL || this == ClassLoaderData::the_null_class_loader_data() || has_class_mirror_holder(), "must be"); // Verify the integrity of the allocated space. +#ifdef ASSERT if (metaspace_or_null() != NULL) { metaspace_or_null()->verify(); } +#endif for (Klass* k = _klasses; k != NULL; k = k->next_link()) { guarantee(k->class_loader_data() == this, "Must be the same"); diff --git a/src/hotspot/share/classfile/classLoaderData.hpp b/src/hotspot/share/classfile/classLoaderData.hpp index 7615bcb75af..bfad9a5cab9 100644 --- a/src/hotspot/share/classfile/classLoaderData.hpp +++ b/src/hotspot/share/classfile/classLoaderData.hpp @@ -62,6 +62,7 @@ class ModuleEntryTable; class PackageEntryTable; class DictionaryEntry; class Dictionary; +class ClassLoaderMetaspace; // ClassLoaderData class @@ -123,8 +124,7 @@ class ClassLoaderData : public CHeapObj { // to these class loader datas. // Remembered sets support for the oops in the class loader data. - bool _modified_oops; // Card Table Equivalent (YC/CMS support) - bool _accumulated_modified_oops; // Mod Union Equivalent (CMS support) + bool _modified_oops; // Card Table Equivalent int _keep_alive; // if this CLD is kept alive. // Used for non-strong hidden classes, unsafe anonymous classes and the @@ -176,9 +176,6 @@ class ClassLoaderData : public CHeapObj { void record_modified_oops() { _modified_oops = true; } bool has_modified_oops() { return _modified_oops; } - void accumulate_modified_oops() { if (has_modified_oops()) _accumulated_modified_oops = true; } - void clear_accumulated_modified_oops() { _accumulated_modified_oops = false; } - bool has_accumulated_modified_oops() { return _accumulated_modified_oops; } oop holder_no_keepalive() const; oop holder_phantom() const; diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.cpp b/src/hotspot/share/classfile/classLoaderDataGraph.cpp index d5662276ab3..fa07df2d69a 100644 --- a/src/hotspot/share/classfile/classLoaderDataGraph.cpp +++ b/src/hotspot/share/classfile/classLoaderDataGraph.cpp @@ -661,24 +661,6 @@ Klass* ClassLoaderDataGraphKlassIteratorAtomic::next_klass() { return NULL; } -ClassLoaderDataGraphMetaspaceIterator::ClassLoaderDataGraphMetaspaceIterator() { - assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!"); - _data = ClassLoaderDataGraph::_head; -} - -ClassLoaderDataGraphMetaspaceIterator::~ClassLoaderDataGraphMetaspaceIterator() {} - -ClassLoaderMetaspace* ClassLoaderDataGraphMetaspaceIterator::get_next() { - assert(_data != NULL, "Should not be NULL in call to the iterator"); - ClassLoaderMetaspace* result = _data->metaspace_or_null(); - _data = _data->next(); - // This result might be NULL for class loaders without metaspace - // yet. It would be nice to return only non-null results but - // there is no guarantee that there will be a non-null result - // down the list so the caller is going to have to check. - return result; -} - void ClassLoaderDataGraph::verify() { ClassLoaderDataGraphIterator iter; while (ClassLoaderData* cld = iter.get_next()) { diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.hpp b/src/hotspot/share/classfile/classLoaderDataGraph.hpp index 1e8b8f6fab0..8a5e4b44088 100644 --- a/src/hotspot/share/classfile/classLoaderDataGraph.hpp +++ b/src/hotspot/share/classfile/classLoaderDataGraph.hpp @@ -162,12 +162,4 @@ class ClassLoaderDataGraphKlassIteratorAtomic : public StackObj { static Klass* next_klass_in_cldg(Klass* klass); }; -class ClassLoaderDataGraphMetaspaceIterator : public StackObj { - ClassLoaderData* _data; - public: - ClassLoaderDataGraphMetaspaceIterator(); - ~ClassLoaderDataGraphMetaspaceIterator(); - bool repeat() { return _data != NULL; } - ClassLoaderMetaspace* get_next(); -}; #endif // SHARE_CLASSFILE_CLASSLOADERDATAGRAPH_HPP diff --git a/src/hotspot/share/classfile/classLoaderStats.cpp b/src/hotspot/share/classfile/classLoaderStats.cpp index 89c001d0bec..e1655321594 100644 --- a/src/hotspot/share/classfile/classLoaderStats.cpp +++ b/src/hotspot/share/classfile/classLoaderStats.cpp @@ -26,6 +26,7 @@ #include "classfile/classLoaderData.inline.hpp" #include "classfile/classLoaderDataGraph.hpp" #include "classfile/classLoaderStats.hpp" +#include "memory/classLoaderMetaspace.hpp" #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" #include "utilities/globalDefinitions.hpp" @@ -80,15 +81,17 @@ void ClassLoaderStatsClosure::do_cld(ClassLoaderData* cld) { ClassLoaderMetaspace* ms = cld->metaspace_or_null(); if (ms != NULL) { + size_t used_bytes, capacity_bytes; + ms->calculate_jfr_stats(&used_bytes, &capacity_bytes); if(cld->has_class_mirror_holder()) { - cls->_hidden_chunk_sz += ms->allocated_chunks_bytes(); - cls->_hidden_block_sz += ms->allocated_blocks_bytes(); + cls->_hidden_chunk_sz += capacity_bytes; + cls->_hidden_block_sz += used_bytes; } else { - cls->_chunk_sz = ms->allocated_chunks_bytes(); - cls->_block_sz = ms->allocated_blocks_bytes(); + cls->_chunk_sz = capacity_bytes; + cls->_block_sz = used_bytes; } - _total_chunk_sz += ms->allocated_chunks_bytes(); - _total_block_sz += ms->allocated_blocks_bytes(); + _total_chunk_sz += capacity_bytes; + _total_block_sz += used_bytes; } } diff --git a/src/hotspot/share/classfile/compactHashtable.cpp b/src/hotspot/share/classfile/compactHashtable.cpp index b4957a0b449..eac232a914c 100644 --- a/src/hotspot/share/classfile/compactHashtable.cpp +++ b/src/hotspot/share/classfile/compactHashtable.cpp @@ -113,9 +113,11 @@ void CompactHashtableWriter::allocate_table() { _compact_entries = MetaspaceShared::new_ro_array(entries_space); _stats->bucket_count = _num_buckets; - _stats->bucket_bytes = _compact_buckets->size() * BytesPerWord; + _stats->bucket_bytes = align_up(_compact_buckets->size() * BytesPerWord, + SharedSpaceObjectAlignment); _stats->hashentry_count = _num_entries_written; - _stats->hashentry_bytes = _compact_entries->size() * BytesPerWord; + _stats->hashentry_bytes = align_up(_compact_entries->size() * BytesPerWord, + SharedSpaceObjectAlignment); } // Write the compact table's buckets diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index de638695073..8179e92fc91 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -53,6 +53,7 @@ #include "oops/recordComponent.hpp" #include "oops/typeArrayOop.inline.hpp" #include "prims/jvmtiExport.hpp" +#include "prims/methodHandles.hpp" #include "prims/resolvedMethodTable.hpp" #include "runtime/fieldDescriptor.inline.hpp" #include "runtime/frame.inline.hpp" @@ -84,7 +85,7 @@ #endif #define DECLARE_INJECTED_FIELD(klass, name, signature, may_be_java) \ - { SystemDictionary::WK_KLASS_ENUM_NAME(klass), vmSymbols::VM_SYMBOL_ENUM_NAME(name##_name), vmSymbols::VM_SYMBOL_ENUM_NAME(signature), may_be_java }, + { SystemDictionary::WK_KLASS_ENUM_NAME(klass), VM_SYMBOL_ENUM_NAME(name##_name), VM_SYMBOL_ENUM_NAME(signature), may_be_java }, InjectedField JavaClasses::_injected_fields[] = { ALL_INJECTED_FIELDS(DECLARE_INJECTED_FIELD) @@ -112,8 +113,8 @@ int JavaClasses::compute_injected_offset(InjectedFieldID id) { InjectedField* JavaClasses::get_injected(Symbol* class_name, int* field_count) { *field_count = 0; - vmSymbols::SID sid = vmSymbols::find_sid(class_name); - if (sid == vmSymbols::NO_SID) { + vmSymbolID sid = vmSymbols::find_sid(class_name); + if (sid == vmSymbolID::NO_SID) { // Only well known classes can inject fields return NULL; } @@ -122,7 +123,7 @@ InjectedField* JavaClasses::get_injected(Symbol* class_name, int* field_count) { int start = -1; #define LOOKUP_INJECTED_FIELD(klass, name, signature, may_be_java) \ - if (sid == vmSymbols::VM_SYMBOL_ENUM_NAME(klass)) { \ + if (sid == VM_SYMBOL_ENUM_NAME(klass)) { \ count++; \ if (start == -1) start = klass##_##name##_enum; \ } @@ -1244,6 +1245,8 @@ oop java_lang_Class::process_archived_mirror(Klass* k, oop mirror, java_lang_Class:set_init_lock(archived_mirror, NULL); set_protection_domain(archived_mirror, NULL); + set_signers(archived_mirror, NULL); + set_source_file(archived_mirror, NULL); } // clear class loader and mirror_module_field @@ -2436,10 +2439,10 @@ void java_lang_Throwable::fill_in_stack_trace(Handle throwable, const methodHand // The "ASSERT" here is to verify this method generates the exactly same stack // trace as utilizing vframe. #ifdef ASSERT - vframeStream st(thread); + vframeStream st(thread, false /* stop_at_java_call_stub */, false /* process_frames */); #endif int total_count = 0; - RegisterMap map(thread, false); + RegisterMap map(thread, false /* update */, false /* process_frames */); int decode_offset = 0; CompiledMethod* nm = NULL; bool skip_fillInStackTrace_check = false; @@ -2581,7 +2584,7 @@ void java_lang_Throwable::fill_in_stack_trace_of_preallocated_backtrace(Handle t assert(backtrace.not_null(), "backtrace should have been preallocated"); ResourceMark rm(THREAD); - vframeStream st(THREAD); + vframeStream st(THREAD, false /* stop_at_java_call_stub */, false /* process_frames */); BacktraceBuilder bt(THREAD, backtrace); @@ -4536,6 +4539,30 @@ void java_util_concurrent_locks_AbstractOwnableSynchronizer::serialize_offsets(S } #endif +int vector_VectorPayload::_payload_offset; + +#define VECTORPAYLOAD_FIELDS_DO(macro) \ + macro(_payload_offset, k, "payload", object_signature, false) + +void vector_VectorPayload::compute_offsets() { + InstanceKlass* k = SystemDictionary::vector_VectorPayload_klass(); + VECTORPAYLOAD_FIELDS_DO(FIELD_COMPUTE_OFFSET); +} + +#if INCLUDE_CDS +void vector_VectorPayload::serialize_offsets(SerializeClosure* f) { + VECTORPAYLOAD_FIELDS_DO(FIELD_SERIALIZE_OFFSET); +} +#endif + +void vector_VectorPayload::set_payload(oop o, oop val) { + o->obj_field_put(_payload_offset, val); +} + +bool vector_VectorPayload::is_instance(oop obj) { + return obj != NULL && is_subclass(obj->klass()); +} + int java_lang_Integer_IntegerCache::_static_cache_offset; int java_lang_Long_LongCache::_static_cache_offset; int java_lang_Character_CharacterCache::_static_cache_offset; diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index a73be695ac2..2ce053487ae 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -29,7 +29,9 @@ #include "jvmtifiles/jvmti.h" #include "oops/oop.hpp" #include "oops/instanceKlass.hpp" +#include "oops/symbol.hpp" #include "runtime/os.hpp" +#include "utilities/vmEnums.hpp" class RecordComponent; @@ -76,6 +78,7 @@ class RecordComponent; f(java_util_concurrent_locks_AbstractOwnableSynchronizer) \ f(jdk_internal_misc_UnsafeConstants) \ f(java_lang_boxing_object) \ + f(vector_VectorPayload) \ //end #define BASIC_JAVA_CLASSES_DO(f) \ @@ -319,6 +322,8 @@ class java_lang_Class : AllStatic { static oop class_data(oop java_class); static void set_class_data(oop java_class, oop classData); + static int component_mirror_offset() { return _component_mirror_offset; } + static oop class_loader(oop java_class); static void set_module(oop java_class, oop module); static oop module(oop java_class); @@ -899,8 +904,9 @@ class java_lang_ref_Reference: AllStatic { public: // Accessors - static inline oop referent(oop ref); - static inline void set_referent(oop ref, oop value); + static inline oop weak_referent_no_keepalive(oop ref); + static inline oop phantom_referent_no_keepalive(oop ref); + static inline oop unknown_referent_no_keepalive(oop ref); static inline void set_referent_raw(oop ref, oop value); static inline HeapWord* referent_addr_raw(oop ref); static inline oop next(oop ref); @@ -1564,6 +1570,24 @@ class jdk_internal_misc_UnsafeConstants : AllStatic { static void serialize_offsets(SerializeClosure* f) { } }; +// Interface to jdk.internal.vm.vector.VectorSupport.VectorPayload objects + +class vector_VectorPayload : AllStatic { + private: + static int _payload_offset; + public: + static void set_payload(oop o, oop val); + + static void compute_offsets(); + static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN; + + // Testers + static bool is_subclass(Klass* klass) { + return klass->is_subclass_of(SystemDictionary::vector_VectorPayload_klass()); + } + static bool is_instance(oop obj); +}; + class java_lang_Integer : AllStatic { public: static jint value(oop obj); @@ -1680,8 +1704,8 @@ class java_lang_InternalError : AllStatic { class InjectedField { public: const SystemDictionary::WKID klass_id; - const vmSymbols::SID name_index; - const vmSymbols::SID signature_index; + const vmSymbolID name_index; + const vmSymbolID signature_index; const bool may_be_java; @@ -1692,8 +1716,8 @@ class InjectedField { int compute_offset(); // Find the Symbol for this index - static Symbol* lookup_symbol(int symbol_index) { - return vmSymbols::symbol_at((vmSymbols::SID)symbol_index); + static Symbol* lookup_symbol(vmSymbolID symbol_index) { + return Symbol::vm_symbol_at(symbol_index); } }; diff --git a/src/hotspot/share/classfile/javaClasses.inline.hpp b/src/hotspot/share/classfile/javaClasses.inline.hpp index 3e880da8ae5..44d62dab5da 100644 --- a/src/hotspot/share/classfile/javaClasses.inline.hpp +++ b/src/hotspot/share/classfile/javaClasses.inline.hpp @@ -100,12 +100,17 @@ bool java_lang_String::is_instance_inlined(oop obj) { } // Accessors -oop java_lang_ref_Reference::referent(oop ref) { - return ref->obj_field(_referent_offset); + +oop java_lang_ref_Reference::weak_referent_no_keepalive(oop ref) { + return ref->obj_field_access(_referent_offset); +} + +oop java_lang_ref_Reference::phantom_referent_no_keepalive(oop ref) { + return ref->obj_field_access(_referent_offset); } -void java_lang_ref_Reference::set_referent(oop ref, oop value) { - ref->obj_field_put(_referent_offset, value); +oop java_lang_ref_Reference::unknown_referent_no_keepalive(oop ref) { + return ref->obj_field_access(_referent_offset); } void java_lang_ref_Reference::set_referent_raw(oop ref, oop value) { diff --git a/src/hotspot/share/classfile/klassFactory.hpp b/src/hotspot/share/classfile/klassFactory.hpp index 97d49a52294..2b2ee36dfa3 100644 --- a/src/hotspot/share/classfile/klassFactory.hpp +++ b/src/hotspot/share/classfile/klassFactory.hpp @@ -31,11 +31,7 @@ class ClassFileStream; class ClassLoaderData; class ClassLoadInfo; -template -class GrowableArray; -class Klass; class Symbol; -class TempNewSymbol; /* * KlassFactory is an interface to implementations of the following mapping/function: @@ -63,18 +59,12 @@ class TempNewSymbol; class KlassFactory : AllStatic { - // approved clients - friend class ClassLoader; - friend class ClassLoaderExt; - friend class SystemDictionary; - - private: + public: static InstanceKlass* create_from_stream(ClassFileStream* stream, Symbol* name, ClassLoaderData* loader_data, const ClassLoadInfo& cl_info, TRAPS); - public: static InstanceKlass* check_shared_class_file_load_hook( InstanceKlass* ik, Symbol* class_name, diff --git a/src/hotspot/share/classfile/lambdaFormInvokers.cpp b/src/hotspot/share/classfile/lambdaFormInvokers.cpp new file mode 100644 index 00000000000..5d3a0316582 --- /dev/null +++ b/src/hotspot/share/classfile/lambdaFormInvokers.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "classfile/classLoadInfo.hpp" +#include "classfile/classFileStream.hpp" +#include "classfile/dictionary.hpp" +#include "classfile/javaClasses.inline.hpp" +#include "classfile/klassFactory.hpp" +#include "classfile/lambdaFormInvokers.hpp" +#include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" +#include "classfile/systemDictionaryShared.hpp" +#include "classfile/vmSymbols.hpp" +#include "logging/log.hpp" +#include "memory/oopFactory.hpp" +#include "memory/metaspaceShared.hpp" +#include "memory/resourceArea.hpp" +#include "oops/instanceKlass.hpp" +#include "oops/klass.hpp" +#include "oops/objArrayKlass.hpp" +#include "oops/objArrayOop.hpp" +#include "oops/oop.inline.hpp" +#include "oops/typeArrayOop.inline.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/javaCalls.hpp" + +GrowableArray* LambdaFormInvokers::_lambdaform_lines = NULL; + +void LambdaFormInvokers::append(char* line) { + if (_lambdaform_lines == NULL) { + _lambdaform_lines = new GrowableArray(100); + } + _lambdaform_lines->append(line); +} + +void LambdaFormInvokers::regenerate_holder_classes(TRAPS) { + assert(_lambdaform_lines != NULL, "Bad List"); + ResourceMark rm(THREAD); + + Symbol* cds_name = vmSymbols::jdk_internal_misc_CDS(); + Klass* cds_klass = SystemDictionary::resolve_or_null(cds_name, THREAD); + guarantee(cds_klass != NULL, "jdk/internal/misc/CDS must exist!"); + + int len = _lambdaform_lines->length(); + objArrayHandle list_lines = oopFactory::new_objArray_handle(SystemDictionary::String_klass(), len, CHECK); + for (int i = 0; i < len; i++) { + Handle h_line = java_lang_String::create_from_str(_lambdaform_lines->at(i), CHECK); + list_lines->obj_at_put(i, h_line()); + } + + // + // Object[] CDS.generateLambdaFormHolderClasses(String[] lines) + // the returned Object[] layout: + // name, byte[], name, byte[] .... + Symbol* method = vmSymbols::generateLambdaFormHolderClasses(); + Symbol* signrs = vmSymbols::generateLambdaFormHolderClasses_signature(); + + JavaValue result(T_OBJECT); + JavaCalls::call_static(&result, cds_klass, method, signrs, list_lines, THREAD); + + if (HAS_PENDING_EXCEPTION) { + log_info(cds)("%s: %s", THREAD->pending_exception()->klass()->external_name(), + java_lang_String::as_utf8_string(java_lang_Throwable::message(THREAD->pending_exception()))); + CLEAR_PENDING_EXCEPTION; + return; + } + + objArrayHandle h_array(THREAD, (objArrayOop)result.get_jobject()); + int sz = h_array->length(); + assert(sz % 2 == 0 && sz >= 2, "Must be even size of length"); + for (int i = 0; i < sz; i+= 2) { + Handle h_name(THREAD, h_array->obj_at(i)); + typeArrayHandle h_bytes(THREAD, (typeArrayOop)h_array->obj_at(i+1)); + assert(h_name != NULL, "Class name is NULL"); + assert(h_bytes != NULL, "Class bytes is NULL"); + + char *class_name = java_lang_String::as_utf8_string(h_name()); + int len = h_bytes->length(); + // make a copy of class bytes so GC will not affect us. + char *buf = resource_allocate_bytes(THREAD, len); + memcpy(buf, (char*)h_bytes->byte_at_addr(0), len); + ClassFileStream st((u1*)buf, len, NULL, ClassFileStream::verify); + + reload_class(class_name, st, THREAD); + // free buf + resource_free_bytes(buf, len); + + if (HAS_PENDING_EXCEPTION) { + log_info(cds)("Exception happened: %s", PENDING_EXCEPTION->klass()->name()->as_C_string()); + log_info(cds)("Could not create InstanceKlass for class %s", class_name); + CLEAR_PENDING_EXCEPTION; + return; + } + } +} + +// class_handle - the class name, bytes_handle - the class bytes +void LambdaFormInvokers::reload_class(char* name, ClassFileStream& st, TRAPS) { + Symbol* class_name = SymbolTable::new_symbol((const char*)name); + // the class must exist + Klass* klass = SystemDictionary::resolve_or_null(class_name, THREAD); + if (klass == NULL) { + log_info(cds)("Class %s not present, skip", name); + return; + } + assert(klass->is_instance_klass(), "Should be"); + + ClassLoaderData* cld = ClassLoaderData::the_null_class_loader_data(); + Handle protection_domain; + ClassLoadInfo cl_info(protection_domain); + + InstanceKlass* result = KlassFactory::create_from_stream(&st, + class_name, + cld, + cl_info, + CHECK); + + { + MutexLocker mu_r(THREAD, Compile_lock); // add_to_hierarchy asserts this. + SystemDictionary::add_to_hierarchy(result, THREAD); + } + // new class not linked yet. + MetaspaceShared::try_link_class(result, THREAD); + assert(!HAS_PENDING_EXCEPTION, "Invariant"); + + // exclude the existing class from dump + SystemDictionaryShared::set_excluded(InstanceKlass::cast(klass)); + log_info(cds, lambda)("Replaced class %s, old: %p new: %p", name, klass, result); +} diff --git a/src/hotspot/share/classfile/lambdaFormInvokers.hpp b/src/hotspot/share/classfile/lambdaFormInvokers.hpp new file mode 100644 index 00000000000..d21fb8e64ca --- /dev/null +++ b/src/hotspot/share/classfile/lambdaFormInvokers.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_LAMBDAFORMINVOKERS_HPP +#define SHARE_MEMORY_LAMBDAFORMINVOKERS_HPP +#include "memory/allStatic.hpp" +#include "runtime/handles.hpp" + +template +class GrowableArray; +class ClassFileStream; + +class LambdaFormInvokers : public AllStatic { + private: + static GrowableArray* _lambdaform_lines; + static void reload_class(char* name, ClassFileStream& st, TRAPS); + public: + + static void append(char* line); + static void regenerate_holder_classes(TRAPS); + static GrowableArray* lambdaform_lines() { + return _lambdaform_lines; + } +}; +#endif // SHARE_MEMORY_LAMBDAFORMINVOKERS_HPP diff --git a/src/hotspot/share/classfile/moduleEntry.hpp b/src/hotspot/share/classfile/moduleEntry.hpp index ec09fc3eda5..da5438adcd6 100644 --- a/src/hotspot/share/classfile/moduleEntry.hpp +++ b/src/hotspot/share/classfile/moduleEntry.hpp @@ -27,7 +27,6 @@ #include "jni.h" #include "classfile/classLoaderData.hpp" -#include "classfile/vmSymbols.hpp" #include "oops/oopHandle.hpp" #include "oops/symbol.hpp" #include "runtime/jniHandles.hpp" diff --git a/src/hotspot/share/classfile/modules.cpp b/src/hotspot/share/classfile/modules.cpp index b0dd93d0c21..9751d1d68fb 100644 --- a/src/hotspot/share/classfile/modules.cpp +++ b/src/hotspot/share/classfile/modules.cpp @@ -43,6 +43,7 @@ #include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" #include "prims/jvmtiExport.hpp" +#include "runtime/globals_extension.hpp" #include "runtime/handles.inline.hpp" #include "runtime/javaCalls.hpp" #include "runtime/jniHandles.inline.hpp" @@ -452,6 +453,24 @@ void Modules::define_module(jobject module, jboolean is_open, jstring version, if (h_loader.is_null() && !ClassLoader::has_jrt_entry()) { ClassLoader::add_to_exploded_build_list(module_symbol, CHECK); } + +#ifdef COMPILER2 + // Special handling of jdk.incubator.vector + if (strcmp(module_name, "jdk.incubator.vector") == 0) { + if (FLAG_IS_DEFAULT(EnableVectorSupport)) { + FLAG_SET_DEFAULT(EnableVectorSupport, true); + } + if (EnableVectorSupport && FLAG_IS_DEFAULT(EnableVectorReboxing)) { + FLAG_SET_DEFAULT(EnableVectorReboxing, true); + } + if (EnableVectorSupport && EnableVectorReboxing && FLAG_IS_DEFAULT(EnableVectorAggressiveReboxing)) { + FLAG_SET_DEFAULT(EnableVectorAggressiveReboxing, true); + } + log_info(compilation)("EnableVectorSupport=%s", (EnableVectorSupport ? "true" : "false")); + log_info(compilation)("EnableVectorReboxing=%s", (EnableVectorReboxing ? "true" : "false")); + log_info(compilation)("EnableVectorAggressiveReboxing=%s", (EnableVectorAggressiveReboxing ? "true" : "false")); + } +#endif // COMPILER2 } #if INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/classfile/stringTable.cpp b/src/hotspot/share/classfile/stringTable.cpp index fbfd14c1011..de8335efacd 100644 --- a/src/hotspot/share/classfile/stringTable.cpp +++ b/src/hotspot/share/classfile/stringTable.cpp @@ -91,11 +91,11 @@ static size_t _current_size = 0; static volatile size_t _items_count = 0; volatile bool _alt_hash = false; -static juint murmur_seed = 0; +static uint64_t _alt_hash_seed = 0; uintx hash_string(const jchar* s, int len, bool useAlt) { return useAlt ? - AltHashing::murmur3_32(murmur_seed, s, len) : + AltHashing::halfsiphash_32(_alt_hash_seed, s, len) : java_lang_String::hash_code(s, len); } @@ -523,7 +523,7 @@ void StringTable::rehash_table() { return; } - murmur_seed = AltHashing::compute_seed(); + _alt_hash_seed = AltHashing::compute_seed(); { if (do_rehash()) { rehashed = true; diff --git a/src/hotspot/share/classfile/symbolTable.cpp b/src/hotspot/share/classfile/symbolTable.cpp index a98b287519b..bc3faa3fe5f 100644 --- a/src/hotspot/share/classfile/symbolTable.cpp +++ b/src/hotspot/share/classfile/symbolTable.cpp @@ -96,7 +96,7 @@ static volatile bool _lookup_shared_first = false; // Static arena for symbols that are not deallocated Arena* SymbolTable::_arena = NULL; -static juint murmur_seed = 0; +static uint64_t _alt_hash_seed = 0; static inline void log_trace_symboltable_helper(Symbol* sym, const char* msg) { #ifndef PRODUCT @@ -108,7 +108,7 @@ static inline void log_trace_symboltable_helper(Symbol* sym, const char* msg) { // Pick hashing algorithm. static uintx hash_symbol(const char* s, int len, bool useAlt) { return useAlt ? - AltHashing::murmur3_32(murmur_seed, (const jbyte*)s, len) : + AltHashing::halfsiphash_32(_alt_hash_seed, (const uint8_t*)s, len) : java_lang_String::hash_code((const jbyte*)s, len); } @@ -785,7 +785,7 @@ void SymbolTable::rehash_table() { return; } - murmur_seed = AltHashing::compute_seed(); + _alt_hash_seed = AltHashing::compute_seed(); if (do_rehash()) { rehashed = true; diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index 606a84345ef..319110e9e51 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -1353,8 +1353,10 @@ InstanceKlass* SystemDictionary::load_shared_lambda_proxy_class(InstanceKlass* i InstanceKlass* loaded_ik = load_shared_class(ik, class_loader, protection_domain, NULL, pkg_entry, CHECK_NULL); - assert(shared_nest_host->is_same_class_package(ik), - "lambda proxy class and its nest host must be in the same package"); + if (loaded_ik != NULL) { + assert(shared_nest_host->is_same_class_package(ik), + "lambda proxy class and its nest host must be in the same package"); + } return loaded_ik; } @@ -1982,9 +1984,11 @@ void SystemDictionary::initialize(TRAPS) { } // Compact table of directions on the initialization of klasses: +// TODO: we should change the base type of vmSymbolID from int to short. Then we can declare this +// array as vmSymbolID wk_init_info[] anf avoid all the type casts. static const short wk_init_info[] = { #define WK_KLASS_INIT_INFO(name, symbol) \ - ((short)vmSymbols::VM_SYMBOL_ENUM_NAME(symbol)), + ((short)VM_SYMBOL_ENUM_NAME(symbol)), WK_KLASSES_DO(WK_KLASS_INIT_INFO) #undef WK_KLASS_INIT_INFO @@ -1995,7 +1999,7 @@ static const short wk_init_info[] = { bool SystemDictionary::is_well_known_klass(Symbol* class_name) { int sid; for (int i = 0; (sid = wk_init_info[i]) != 0; i++) { - Symbol* symbol = vmSymbols::symbol_at((vmSymbols::SID)sid); + Symbol* symbol = vmSymbols::symbol_at(vmSymbols::as_SID(sid)); if (class_name == symbol) { return true; } @@ -2011,7 +2015,7 @@ bool SystemDictionary::is_well_known_klass(Klass* k) { bool SystemDictionary::resolve_wk_klass(WKID id, TRAPS) { assert(id >= (int)FIRST_WKID && id < (int)WKID_LIMIT, "oob"); int sid = wk_init_info[id - FIRST_WKID]; - Symbol* symbol = vmSymbols::symbol_at((vmSymbols::SID)sid); + Symbol* symbol = vmSymbols::symbol_at(vmSymbols::as_SID(sid)); InstanceKlass** klassp = &_well_known_klasses[id]; #if INCLUDE_CDS diff --git a/src/hotspot/share/classfile/systemDictionary.hpp b/src/hotspot/share/classfile/systemDictionary.hpp index 54c8513b9cb..05f35bb53e7 100644 --- a/src/hotspot/share/classfile/systemDictionary.hpp +++ b/src/hotspot/share/classfile/systemDictionary.hpp @@ -25,7 +25,6 @@ #ifndef SHARE_CLASSFILE_SYSTEMDICTIONARY_HPP #define SHARE_CLASSFILE_SYSTEMDICTIONARY_HPP -#include "classfile/vmSymbols.hpp" #include "oops/oopHandle.hpp" #include "runtime/handles.hpp" #include "runtime/signature.hpp" @@ -226,6 +225,13 @@ class TableStatistics; /* support for records */ \ do_klass(RecordComponent_klass, java_lang_reflect_RecordComponent ) \ \ + /* support for vectors*/ \ + do_klass(vector_VectorSupport_klass, jdk_internal_vm_vector_VectorSupport ) \ + do_klass(vector_VectorPayload_klass, jdk_internal_vm_vector_VectorPayload ) \ + do_klass(vector_Vector_klass, jdk_internal_vm_vector_Vector ) \ + do_klass(vector_VectorMask_klass, jdk_internal_vm_vector_VectorMask ) \ + do_klass(vector_VectorShuffle_klass, jdk_internal_vm_vector_VectorShuffle ) \ + \ /*end*/ class SystemDictionary : AllStatic { @@ -624,9 +630,9 @@ class SystemDictionary : AllStatic { // Return Symbol or throw exception if name given is can not be a valid Symbol. static Symbol* class_name_symbol(const char* name, Symbol* exception, TRAPS); -protected: // Setup link to hierarchy static void add_to_hierarchy(InstanceKlass* k, TRAPS); +protected: // Basic find on loaded classes static InstanceKlass* find_class(unsigned int hash, diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 045910a4f1f..37236801927 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -31,13 +31,16 @@ #include "classfile/classLoaderExt.hpp" #include "classfile/dictionary.hpp" #include "classfile/javaClasses.hpp" +#include "classfile/javaClasses.inline.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/systemDictionaryShared.hpp" #include "classfile/verificationType.hpp" #include "classfile/vmSymbols.hpp" +#include "interpreter/bootstrapInfo.hpp" #include "jfr/jfrEvents.hpp" #include "logging/log.hpp" +#include "logging/logStream.hpp" #include "memory/allocation.hpp" #include "memory/archiveUtils.hpp" #include "memory/dynamicArchive.hpp" @@ -45,6 +48,7 @@ #include "memory/heapShared.hpp" #include "memory/metadataFactory.hpp" #include "memory/metaspaceClosure.hpp" +#include "memory/metaspaceShared.hpp" #include "memory/oopFactory.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" @@ -97,6 +101,7 @@ class DumpTimeSharedClassInfo: public CHeapObj { }; InstanceKlass* _klass; + InstanceKlass* _nest_host; bool _failed_verification; bool _is_archived_lambda_proxy; int _id; @@ -108,6 +113,7 @@ class DumpTimeSharedClassInfo: public CHeapObj { DumpTimeSharedClassInfo() { _klass = NULL; + _nest_host = NULL; _failed_verification = false; _is_archived_lambda_proxy = false; _id = -1; @@ -145,6 +151,7 @@ class DumpTimeSharedClassInfo: public CHeapObj { void metaspace_pointers_do(MetaspaceClosure* it) { it->push(&_klass); + it->push(&_nest_host); if (_verifier_constraints != NULL) { for (int i = 0; i < _verifier_constraints->length(); i++) { DTVerifierConstraint* cons = _verifier_constraints->adr_at(i); @@ -176,6 +183,14 @@ class DumpTimeSharedClassInfo: public CHeapObj { bool failed_verification() { return _failed_verification; } + + void set_nest_host(InstanceKlass* nest_host) { + _nest_host = nest_host; + } + + InstanceKlass* nest_host() { + return _nest_host; + } }; inline unsigned DumpTimeSharedClassTable_hash(InstanceKlass* const& k) { @@ -256,7 +271,9 @@ class DumpTimeSharedClassTable: public ResourceHashtable< class LambdaProxyClassKey { template static void original_to_target(T& field) { if (field != NULL) { - field = DynamicArchive::original_to_target(field); + if (DynamicDumpSharedSpaces) { + field = DynamicArchive::original_to_target(field); + } ArchivePtrMarker::mark_pointer(&field); } } @@ -282,6 +299,15 @@ class LambdaProxyClassKey { _member_method(member_method), _instantiated_method_type(instantiated_method_type) {} + void metaspace_pointers_do(MetaspaceClosure* it) { + it->push(&_caller_ik); + it->push(&_invoked_name); + it->push(&_invoked_type); + it->push(&_method_type); + it->push(&_member_method); + it->push(&_instantiated_method_type); + } + void original_to_target() { original_to_target(_caller_ik); original_to_target(_instantiated_method_type); @@ -308,12 +334,20 @@ class LambdaProxyClassKey { SystemDictionaryShared::hash_for_shared_dictionary(_instantiated_method_type); } + static unsigned int dumptime_hash(Symbol* sym) { + if (sym == NULL) { + // _invoked_name maybe NULL + return 0; + } + return java_lang_String::hash_code((const jbyte*)sym->bytes(), sym->utf8_length()); + } + unsigned int dumptime_hash() const { - return primitive_hash(_caller_ik) + - primitive_hash(_invoked_name) + - primitive_hash(_invoked_type) + - primitive_hash(_method_type) + - primitive_hash(_instantiated_method_type); + return dumptime_hash(_caller_ik->name()) + + dumptime_hash(_invoked_name) + + dumptime_hash(_invoked_type) + + dumptime_hash(_method_type) + + dumptime_hash(_instantiated_method_type); } static inline unsigned int DUMPTIME_HASH(LambdaProxyClassKey const& key) { @@ -338,14 +372,20 @@ class DumpTimeLambdaProxyClassInfo { assert(_proxy_klasses != NULL, "sanity"); _proxy_klasses->append(proxy_klass); } + + void metaspace_pointers_do(MetaspaceClosure* it) { + for (int i=0; i<_proxy_klasses->length(); i++) { + it->push(_proxy_klasses->adr_at(i)); + } + } }; class RunTimeLambdaProxyClassInfo { LambdaProxyClassKey _key; InstanceKlass* _proxy_klass_head; public: - RunTimeLambdaProxyClassInfo(LambdaProxyClassKey key, InstanceKlass* proxy_klass) : - _key(key), _proxy_klass_head(proxy_klass) {} + RunTimeLambdaProxyClassInfo(LambdaProxyClassKey key, InstanceKlass* proxy_klass_head) : + _key(key), _proxy_klass_head(proxy_klass_head) {} InstanceKlass* proxy_klass_head() const { return _proxy_klass_head; } @@ -357,13 +397,18 @@ class RunTimeLambdaProxyClassInfo { void init(LambdaProxyClassKey& key, DumpTimeLambdaProxyClassInfo& info) { _key = key; _key.original_to_target(); - _proxy_klass_head = DynamicArchive::original_to_target(info._proxy_klasses->at(0)); + _proxy_klass_head = DynamicDumpSharedSpaces ? + DynamicArchive::original_to_target(info._proxy_klasses->at(0)) : + info._proxy_klasses->at(0); ArchivePtrMarker::mark_pointer(&_proxy_klass_head); } unsigned int hash() const { return _key.hash(); } + LambdaProxyClassKey key() const { + return _key; + } }; class LambdaProxyClassDictionary : public OffsetCompactHashtable< @@ -373,6 +418,8 @@ class LambdaProxyClassDictionary : public OffsetCompactHashtable< LambdaProxyClassDictionary _lambda_proxy_class_dictionary; +LambdaProxyClassDictionary _dynamic_lambda_proxy_class_dictionary; + class DumpTimeLambdaProxyClassDictionary : public ResourceHashtableat(i)._loader_type2; } } - if (DynamicDumpSharedSpaces) { - if (_klass->is_hidden()) { - Thread* THREAD = Thread::current(); - InstanceKlass* n_h = _klass->nest_host(THREAD); + + if (_klass->is_hidden()) { + InstanceKlass* n_h = info.nest_host(); + if (DynamicDumpSharedSpaces) { n_h = DynamicArchive::original_to_target(n_h); - set_nest_host(n_h); } - _klass = DynamicArchive::original_to_target(info._klass); + set_nest_host(n_h); } + _klass = DynamicDumpSharedSpaces ? DynamicArchive::original_to_target(info._klass) : info._klass; ArchivePtrMarker::mark_pointer(&_klass); } @@ -1133,19 +1180,24 @@ InstanceKlass* SystemDictionaryShared::acquire_class_for_current_thread( return shared_klass; } -static ResourceHashtable< +class LoadedUnregisteredClassesTable : public ResourceHashtable< Symbol*, bool, primitive_hash, primitive_equals, 6661, // prime number - ResourceObj::C_HEAP> _loaded_unregistered_classes; + ResourceObj::C_HEAP> {}; + +static LoadedUnregisteredClassesTable* _loaded_unregistered_classes = NULL; bool SystemDictionaryShared::add_unregistered_class(InstanceKlass* k, TRAPS) { // We don't allow duplicated unregistered classes of the same name. assert(DumpSharedSpaces, "only when dumping"); Symbol* name = k->name(); + if (_loaded_unregistered_classes == NULL) { + _loaded_unregistered_classes = new (ResourceObj::C_HEAP, mtClass)LoadedUnregisteredClassesTable(); + } bool created = false; - _loaded_unregistered_classes.put_if_absent(name, true, &created); + _loaded_unregistered_classes->put_if_absent(name, true, &created); if (created) { MutexLocker mu_r(THREAD, Compile_lock); // add_to_hierarchy asserts this. SystemDictionary::add_to_hierarchy(k, CHECK_false); @@ -1346,7 +1398,8 @@ bool SystemDictionaryShared::should_be_excluded(InstanceKlass* k) { } if (k->is_hidden() && !is_registered_lambda_proxy_class(k)) { - warn_excluded(k, "Hidden class"); + ResourceMark rm; + log_debug(cds)("Skipping %s: %s", k->name()->as_C_string(), "Hidden class"); return true; } @@ -1387,7 +1440,7 @@ void SystemDictionaryShared::validate_before_archiving(InstanceKlass* k) { class ExcludeDumpTimeSharedClasses : StackObj { public: bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { - if (SystemDictionaryShared::should_be_excluded(k)) { + if (SystemDictionaryShared::should_be_excluded(k) || info.is_excluded()) { info.set_excluded(); } return true; // keep on iterating @@ -1407,6 +1460,14 @@ bool SystemDictionaryShared::is_excluded_class(InstanceKlass* k) { return (p == NULL) ? true : p->is_excluded(); } +void SystemDictionaryShared::set_excluded(InstanceKlass* k) { + Arguments::assert_is_dumping_archive(); + DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k); + if (info != NULL) { + info->set_excluded(); + } +} + void SystemDictionaryShared::set_class_has_failed_verification(InstanceKlass* ik) { Arguments::assert_is_dumping_archive(); DumpTimeSharedClassInfo* p = find_or_allocate_info_for(ik); @@ -1439,10 +1500,26 @@ class IterateDumpTimeSharedClassTable : StackObj { } }; +class IterateDumpTimeLambdaProxyClassDictionary : StackObj { + MetaspaceClosure *_it; +public: + IterateDumpTimeLambdaProxyClassDictionary(MetaspaceClosure* it) : _it(it) {} + + bool do_entry(LambdaProxyClassKey& key, DumpTimeLambdaProxyClassInfo& info) { + info.metaspace_pointers_do(_it); + key.metaspace_pointers_do(_it); + return true; + } +}; + void SystemDictionaryShared::dumptime_classes_do(class MetaspaceClosure* it) { assert_locked_or_safepoint(DumpTimeTable_lock); IterateDumpTimeSharedClassTable iter(it); _dumptime_table->iterate(&iter); + if (_dumptime_lambda_proxy_class_dictionary != NULL) { + IterateDumpTimeLambdaProxyClassDictionary iter_lambda(it); + _dumptime_lambda_proxy_class_dictionary->iterate(&iter_lambda); + } } bool SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbol* name, @@ -1513,7 +1590,8 @@ void SystemDictionaryShared::add_lambda_proxy_class(InstanceKlass* caller_ik, Symbol* invoked_type, Symbol* method_type, Method* member_method, - Symbol* instantiated_method_type) { + Symbol* instantiated_method_type, + TRAPS) { assert(caller_ik->class_loader() == lambda_ik->class_loader(), "mismatched class loader"); assert(caller_ik->class_loader_data() == lambda_ik->class_loader_data(), "mismatched class loader data"); @@ -1523,12 +1601,14 @@ void SystemDictionaryShared::add_lambda_proxy_class(InstanceKlass* caller_ik, lambda_ik->assign_class_loader_type(); lambda_ik->set_shared_classpath_index(caller_ik->shared_classpath_index()); + InstanceKlass* nest_host = caller_ik->nest_host(THREAD); DumpTimeSharedClassInfo* info = _dumptime_table->get(lambda_ik); if (info != NULL && !lambda_ik->is_non_strong_hidden() && is_builtin(lambda_ik) && is_builtin(caller_ik)) { // Set _is_archived_lambda_proxy in DumpTimeSharedClassInfo so that the lambda_ik // won't be excluded during dumping of shared archive. See ExcludeDumpTimeSharedClasses. info->_is_archived_lambda_proxy = true; + info->set_nest_host(nest_host); LambdaProxyClassKey key(caller_ik, invoked_name, @@ -1550,6 +1630,10 @@ InstanceKlass* SystemDictionaryShared::get_shared_lambda_proxy_class(InstanceKla LambdaProxyClassKey key(caller_ik, invoked_name, invoked_type, method_type, member_method, instantiated_method_type); const RunTimeLambdaProxyClassInfo* info = _lambda_proxy_class_dictionary.lookup(&key, key.hash(), 0); + if (info == NULL) { + // Try lookup from the dynamic lambda proxy class dictionary. + info = _dynamic_lambda_proxy_class_dictionary.lookup(&key, key.hash(), 0); + } InstanceKlass* proxy_klass = NULL; if (info != NULL) { InstanceKlass* curr_klass = info->proxy_klass_head(); @@ -1567,7 +1651,7 @@ InstanceKlass* SystemDictionaryShared::get_shared_lambda_proxy_class(InstanceKla proxy_klass->clear_lambda_proxy_is_available(); if (log_is_enabled(Debug, cds)) { ResourceMark rm; - log_debug(cds)("Loaded lambda proxy: %s", proxy_klass->external_name()); + log_debug(cds)("Loaded lambda proxy: %s ", proxy_klass->external_name()); } } else { if (log_is_enabled(Debug, cds)) { @@ -1602,6 +1686,10 @@ InstanceKlass* SystemDictionaryShared::prepare_shared_lambda_proxy_class(Instanc InstanceKlass* loaded_lambda = SystemDictionary::load_shared_lambda_proxy_class(lambda_ik, class_loader, protection_domain, pkg_entry, CHECK_NULL); + if (loaded_lambda == NULL) { + return NULL; + } + // Ensures the nest host is the same as the lambda proxy's // nest host recorded at dump time. InstanceKlass* nest_host = caller_ik->nest_host(THREAD); @@ -1836,6 +1924,51 @@ bool SystemDictionaryShared::check_linking_constraints(InstanceKlass* klass, TRA return false; } +bool SystemDictionaryShared::is_supported_invokedynamic(BootstrapInfo* bsi) { + LogTarget(Debug, cds, lambda) log; + if (bsi->arg_values() == NULL || !bsi->arg_values()->is_objArray()) { + if (log.is_enabled()) { + LogStream log_stream(log); + log.print("bsi check failed"); + log.print(" bsi->arg_values().not_null() %d", bsi->arg_values().not_null()); + if (bsi->arg_values().not_null()) { + log.print(" bsi->arg_values()->is_objArray() %d", bsi->arg_values()->is_objArray()); + bsi->print_msg_on(&log_stream); + } + } + return false; + } + + Handle bsm = bsi->bsm(); + if (bsm.is_null() || !java_lang_invoke_DirectMethodHandle::is_instance(bsm())) { + if (log.is_enabled()) { + log.print("bsm check failed"); + log.print(" bsm.is_null() %d", bsm.is_null()); + log.print(" java_lang_invoke_DirectMethodHandle::is_instance(bsm()) %d", + java_lang_invoke_DirectMethodHandle::is_instance(bsm())); + } + return false; + } + + oop mn = java_lang_invoke_DirectMethodHandle::member(bsm()); + Method* method = java_lang_invoke_MemberName::vmtarget(mn); + if (method->klass_name()->equals("java/lang/invoke/LambdaMetafactory") && + method->name()->equals("metafactory") && + method->signature()->equals("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;")) { + return true; + } else { + if (log.is_enabled()) { + ResourceMark rm; + log.print("method check failed"); + log.print(" klass_name() %s", method->klass_name()->as_C_string()); + log.print(" name() %s", method->name()->as_C_string()); + log.print(" signature() %s", method->signature()->as_C_string()); + } + } + + return false; +} + class EstimateSizeForArchive : StackObj { size_t _shared_class_info_size; int _num_builtin_klasses; @@ -1851,7 +1984,7 @@ class EstimateSizeForArchive : StackObj { bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { if (!info.is_excluded()) { size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_verifier_constraints(), info.num_loader_constraints()); - _shared_class_info_size += align_up(byte_size, BytesPerWord); + _shared_class_info_size += align_up(byte_size, SharedSpaceObjectAlignment); } return true; // keep on iterating } @@ -1868,8 +2001,9 @@ size_t SystemDictionaryShared::estimate_size_for_archive() { CompactHashtableWriter::estimate_size(_dumptime_table->count_of(true)) + CompactHashtableWriter::estimate_size(_dumptime_table->count_of(false)); if (_dumptime_lambda_proxy_class_dictionary != NULL) { + size_t bytesize = align_up(sizeof(RunTimeLambdaProxyClassInfo), SharedSpaceObjectAlignment); total_size += - (sizeof(RunTimeLambdaProxyClassInfo) * _dumptime_lambda_proxy_class_dictionary->_count) + + (bytesize * _dumptime_lambda_proxy_class_dictionary->_count) + CompactHashtableWriter::estimate_size(_dumptime_lambda_proxy_class_dictionary->_count); } else { total_size += CompactHashtableWriter::estimate_size(0); @@ -1883,9 +2017,15 @@ class CopyLambdaProxyClassInfoToArchive : StackObj { CopyLambdaProxyClassInfoToArchive(CompactHashtableWriter* writer) : _writer(writer) {} bool do_entry(LambdaProxyClassKey& key, DumpTimeLambdaProxyClassInfo& info) { - if (SystemDictionaryShared::is_excluded_class(info._proxy_klasses->at(0))) { - return true; - } + // In static dump, info._proxy_klasses->at(0) is already relocated to point to the archived class + // (not the original class). + // + // The following check has been moved to SystemDictionaryShared::check_excluded_classes(), which + // happens before the classes are copied. + // + // if (SystemDictionaryShared::is_excluded_class(info._proxy_klasses->at(0))) { + // return true; + //} ResourceMark rm; log_info(cds,dynamic)("Archiving hidden %s", info._proxy_klasses->at(0)->external_name()); size_t byte_size = sizeof(RunTimeLambdaProxyClassInfo); @@ -1893,7 +2033,9 @@ class CopyLambdaProxyClassInfoToArchive : StackObj { (RunTimeLambdaProxyClassInfo*)MetaspaceShared::read_only_space_alloc(byte_size); runtime_info->init(key, info); unsigned int hash = runtime_info->hash(); // Fields in runtime_info->_key already point to target space. - u4 delta = MetaspaceShared::object_delta_u4(DynamicArchive::buffer_to_target(runtime_info)); + u4 delta = DynamicDumpSharedSpaces ? + MetaspaceShared::object_delta_u4((void*)DynamicArchive::buffer_to_target(runtime_info)) : + MetaspaceShared::object_delta_u4((void*)runtime_info); _writer->add(hash, delta); return true; } @@ -1903,16 +2045,13 @@ class AdjustLambdaProxyClassInfo : StackObj { public: AdjustLambdaProxyClassInfo() {} bool do_entry(LambdaProxyClassKey& key, DumpTimeLambdaProxyClassInfo& info) { - if (SystemDictionaryShared::is_excluded_class(info._proxy_klasses->at(0))) { - return true; - } int len = info._proxy_klasses->length(); if (len > 1) { for (int i = 0; i < len-1; i++) { InstanceKlass* ok0 = info._proxy_klasses->at(i+0); // this is original klass InstanceKlass* ok1 = info._proxy_klasses->at(i+1); // this is original klass - InstanceKlass* bk0 = DynamicArchive::original_to_buffer(ok0); - InstanceKlass* bk1 = DynamicArchive::original_to_buffer(ok1); + InstanceKlass* bk0 = DynamicDumpSharedSpaces ? DynamicArchive::original_to_buffer(ok0) : ok0; + InstanceKlass* bk1 = DynamicDumpSharedSpaces ? DynamicArchive::original_to_buffer(ok1) : ok1; assert(bk0->next_link() == 0, "must be called after Klass::remove_unshareable_info()"); assert(bk1->next_link() == 0, "must be called after Klass::remove_unshareable_info()"); bk0->set_next_link(bk1); @@ -1920,7 +2059,11 @@ class AdjustLambdaProxyClassInfo : StackObj { ArchivePtrMarker::mark_pointer(bk0->next_link_addr()); } } - DynamicArchive::original_to_buffer(info._proxy_klasses->at(0))->set_lambda_proxy_is_available(); + if (DynamicDumpSharedSpaces) { + DynamicArchive::original_to_buffer(info._proxy_klasses->at(0))->set_lambda_proxy_is_available(); + } else { + info._proxy_klasses->at(0)->set_lambda_proxy_is_available(); + } return true; } }; @@ -2011,13 +2154,21 @@ void SystemDictionaryShared::adjust_lambda_proxy_class_dictionary() { void SystemDictionaryShared::serialize_dictionary_headers(SerializeClosure* soc, bool is_static_archive) { + FileMapInfo *dynamic_mapinfo = FileMapInfo::dynamic_info(); if (is_static_archive) { _builtin_dictionary.serialize_header(soc); _unregistered_dictionary.serialize_header(soc); + if (dynamic_mapinfo == NULL || DynamicDumpSharedSpaces || (dynamic_mapinfo != NULL && UseSharedSpaces)) { + _lambda_proxy_class_dictionary.serialize_header(soc); + } } else { _dynamic_builtin_dictionary.serialize_header(soc); _dynamic_unregistered_dictionary.serialize_header(soc); - _lambda_proxy_class_dictionary.serialize_header(soc); + if (DynamicDumpSharedSpaces) { + _lambda_proxy_class_dictionary.serialize_header(soc); + } else { + _dynamic_lambda_proxy_class_dictionary.serialize_header(soc); + } } } @@ -2095,20 +2246,28 @@ class SharedLambdaDictionaryPrinter : StackObj { } }; +void SystemDictionaryShared::print_on(const char* prefix, + RunTimeSharedDictionary* builtin_dictionary, + RunTimeSharedDictionary* unregistered_dictionary, + LambdaProxyClassDictionary* lambda_dictionary, + outputStream* st) { + st->print_cr("%sShared Dictionary", prefix); + SharedDictionaryPrinter p(st); + builtin_dictionary->iterate(&p); + unregistered_dictionary->iterate(&p); + if (!lambda_dictionary->empty()) { + st->print_cr("%sShared Lambda Dictionary", prefix); + SharedLambdaDictionaryPrinter ldp(st); + lambda_dictionary->iterate(&ldp); + } +} + void SystemDictionaryShared::print_on(outputStream* st) { if (UseSharedSpaces) { - st->print_cr("Shared Dictionary"); - SharedDictionaryPrinter p(st); - _builtin_dictionary.iterate(&p); - _unregistered_dictionary.iterate(&p); + print_on("", &_builtin_dictionary, &_unregistered_dictionary, &_lambda_proxy_class_dictionary, st); if (DynamicArchive::is_mapped()) { - _dynamic_builtin_dictionary.iterate(&p); - _unregistered_dictionary.iterate(&p); - if (!_lambda_proxy_class_dictionary.empty()) { - st->print_cr("Shared Lambda Dictionary"); - SharedLambdaDictionaryPrinter ldp(st); - _lambda_proxy_class_dictionary.iterate(&ldp); - } + print_on("", &_dynamic_builtin_dictionary, &_dynamic_unregistered_dictionary, + &_dynamic_lambda_proxy_class_dictionary, st); } } } @@ -2117,10 +2276,11 @@ void SystemDictionaryShared::print_table_statistics(outputStream* st) { if (UseSharedSpaces) { _builtin_dictionary.print_table_statistics(st, "Builtin Shared Dictionary"); _unregistered_dictionary.print_table_statistics(st, "Unregistered Shared Dictionary"); + _lambda_proxy_class_dictionary.print_table_statistics(st, "Lambda Shared Dictionary"); if (DynamicArchive::is_mapped()) { _dynamic_builtin_dictionary.print_table_statistics(st, "Dynamic Builtin Shared Dictionary"); _dynamic_unregistered_dictionary.print_table_statistics(st, "Unregistered Shared Dictionary"); - _lambda_proxy_class_dictionary.print_table_statistics(st, "Lambda Shared Dictionary"); + _dynamic_lambda_proxy_class_dictionary.print_table_statistics(st, "Dynamic Lambda Shared Dictionary"); } } } @@ -2139,6 +2299,7 @@ bool SystemDictionaryShared::empty_dumptime_table() { #if INCLUDE_CDS_JAVA_HEAP class ArchivedMirrorPatcher { +protected: static void update(Klass* k) { if (k->has_raw_archived_mirror()) { oop m = HeapShared::materialize_archived_object(k->archived_java_mirror_raw_narrow()); @@ -2163,11 +2324,28 @@ class ArchivedMirrorPatcher { } }; +class ArchivedLambdaMirrorPatcher : public ArchivedMirrorPatcher { +public: + void do_value(const RunTimeLambdaProxyClassInfo* info) { + InstanceKlass* ik = info->proxy_klass_head(); + while (ik != NULL) { + update(ik); + Klass* k = ik->next_link(); + ik = (k != NULL) ? InstanceKlass::cast(k) : NULL; + } + } +}; + void SystemDictionaryShared::update_archived_mirror_native_pointers_for(RunTimeSharedDictionary* dict) { ArchivedMirrorPatcher patcher; dict->iterate(&patcher); } +void SystemDictionaryShared::update_archived_mirror_native_pointers_for(LambdaProxyClassDictionary* dict) { + ArchivedLambdaMirrorPatcher patcher; + dict->iterate(&patcher); +} + void SystemDictionaryShared::update_archived_mirror_native_pointers() { if (!HeapShared::open_archive_heap_region_mapped()) { return; @@ -2177,6 +2355,7 @@ void SystemDictionaryShared::update_archived_mirror_native_pointers() { } update_archived_mirror_native_pointers_for(&_builtin_dictionary); update_archived_mirror_native_pointers_for(&_unregistered_dictionary); + update_archived_mirror_native_pointers_for(&_lambda_proxy_class_dictionary); for (int t = T_BOOLEAN; t <= T_LONG; t++) { Klass* k = Universe::typeArrayKlassObj((BasicType)t); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp index fe55b7a5cd7..0b913e8374f 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -101,6 +101,7 @@ ===============================================================================*/ #define UNREGISTERED_INDEX -9999 +class BootstrapInfo; class ClassFileStream; class Dictionary; class DumpTimeSharedClassInfo; @@ -220,6 +221,11 @@ class SystemDictionaryShared: public SystemDictionary { static bool _dump_in_progress; DEBUG_ONLY(static bool _no_class_loading_should_happen;) + static void print_on(const char* prefix, + RunTimeSharedDictionary* builtin_dictionary, + RunTimeSharedDictionary* unregistered_dictionary, + LambdaProxyClassDictionary* lambda_dictionary, + outputStream* st) NOT_CDS_RETURN; public: static bool is_hidden_lambda_proxy(InstanceKlass* ik); @@ -288,7 +294,7 @@ class SystemDictionaryShared: public SystemDictionary { Symbol* invoked_type, Symbol* method_type, Method* member_method, - Symbol* instantiated_method_type) NOT_CDS_RETURN; + Symbol* instantiated_method_type, TRAPS) NOT_CDS_RETURN; static InstanceKlass* get_shared_lambda_proxy_class(InstanceKlass* caller_ik, Symbol* invoked_name, Symbol* invoked_type, @@ -308,6 +314,7 @@ class SystemDictionaryShared: public SystemDictionary { static void check_excluded_classes(); static void validate_before_archiving(InstanceKlass* k); static bool is_excluded_class(InstanceKlass* k); + static void set_excluded(InstanceKlass* k); static void dumptime_classes_do(class MetaspaceClosure* it); static size_t estimate_size_for_archive(); static void write_to_archive(bool is_static_archive = true); @@ -321,6 +328,7 @@ class SystemDictionaryShared: public SystemDictionary { static bool empty_dumptime_table() NOT_CDS_RETURN_(true); static void start_dumping() NOT_CDS_RETURN; static Handle create_jar_manifest(const char* man, size_t size, TRAPS) NOT_CDS_RETURN_(Handle()); + static bool is_supported_invokedynamic(BootstrapInfo* bsi) NOT_CDS_RETURN_(false); DEBUG_ONLY(static bool no_class_loading_should_happen() {return _no_class_loading_should_happen;}) @@ -347,6 +355,7 @@ class SystemDictionaryShared: public SystemDictionary { #if INCLUDE_CDS_JAVA_HEAP private: static void update_archived_mirror_native_pointers_for(RunTimeSharedDictionary* dict); + static void update_archived_mirror_native_pointers_for(LambdaProxyClassDictionary* dict); public: static void update_archived_mirror_native_pointers() NOT_CDS_RETURN; #endif diff --git a/src/hotspot/share/classfile/vmIntrinsics.cpp b/src/hotspot/share/classfile/vmIntrinsics.cpp index aa5c44baeb5..ea7f5da0b6c 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.cpp +++ b/src/hotspot/share/classfile/vmIntrinsics.cpp @@ -59,53 +59,6 @@ inline bool match_F_SN(jshort flags) { return (flags & (req | neg)) == req; } -inline bool match_F_RNY(jshort flags) { - const int req = JVM_ACC_NATIVE | JVM_ACC_SYNCHRONIZED; - const int neg = JVM_ACC_STATIC; - return (flags & (req | neg)) == req; -} - -static vmIntrinsics::ID wrapper_intrinsic(BasicType type, bool unboxing) { -#define TYPE2(type, unboxing) ((int)(type)*2 + ((unboxing) ? 1 : 0)) - switch (TYPE2(type, unboxing)) { -#define BASIC_TYPE_CASE(type, box, unbox) \ - case TYPE2(type, false): return vmIntrinsics::box; \ - case TYPE2(type, true): return vmIntrinsics::unbox - BASIC_TYPE_CASE(T_BOOLEAN, _Boolean_valueOf, _booleanValue); - BASIC_TYPE_CASE(T_BYTE, _Byte_valueOf, _byteValue); - BASIC_TYPE_CASE(T_CHAR, _Character_valueOf, _charValue); - BASIC_TYPE_CASE(T_SHORT, _Short_valueOf, _shortValue); - BASIC_TYPE_CASE(T_INT, _Integer_valueOf, _intValue); - BASIC_TYPE_CASE(T_LONG, _Long_valueOf, _longValue); - BASIC_TYPE_CASE(T_FLOAT, _Float_valueOf, _floatValue); - BASIC_TYPE_CASE(T_DOUBLE, _Double_valueOf, _doubleValue); -#undef BASIC_TYPE_CASE - } -#undef TYPE2 - return vmIntrinsics::_none; -} - -vmIntrinsics::ID vmIntrinsics::for_boxing(BasicType type) { - return wrapper_intrinsic(type, false); -} -vmIntrinsics::ID vmIntrinsics::for_unboxing(BasicType type) { - return wrapper_intrinsic(type, true); -} - -vmIntrinsics::ID vmIntrinsics::for_raw_conversion(BasicType src, BasicType dest) { -#define SRC_DEST(s,d) (((int)(s) << 4) + (int)(d)) - switch (SRC_DEST(src, dest)) { - case SRC_DEST(T_INT, T_FLOAT): return vmIntrinsics::_intBitsToFloat; - case SRC_DEST(T_FLOAT, T_INT): return vmIntrinsics::_floatToRawIntBits; - - case SRC_DEST(T_LONG, T_DOUBLE): return vmIntrinsics::_longBitsToDouble; - case SRC_DEST(T_DOUBLE, T_LONG): return vmIntrinsics::_doubleToRawLongBits; - } -#undef SRC_DEST - - return vmIntrinsics::_none; -} - bool vmIntrinsics::preserves_state(vmIntrinsics::ID id) { assert(id != vmIntrinsics::_none, "must be a VM intrinsic"); switch(id) { @@ -226,7 +179,7 @@ int vmIntrinsics::predicates_needed(vmIntrinsics::ID id) { case vmIntrinsics::_counterMode_AESCrypt: return 1; case vmIntrinsics::_digestBase_implCompressMB: - return 4; + return 5; default: return 0; } @@ -246,6 +199,7 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) { case vmIntrinsics::_indexOfIU: case vmIntrinsics::_indexOfIUL: case vmIntrinsics::_indexOfU_char: + case vmIntrinsics::_indexOfL_char: case vmIntrinsics::_compareToL: case vmIntrinsics::_compareToU: case vmIntrinsics::_compareToLU: @@ -482,8 +436,11 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) { case vmIntrinsics::_sha5_implCompress: if (!UseSHA512Intrinsics) return true; break; + case vmIntrinsics::_sha3_implCompress: + if (!UseSHA3Intrinsics) return true; + break; case vmIntrinsics::_digestBase_implCompressMB: - if (!(UseMD5Intrinsics || UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics)) return true; + if (!(UseMD5Intrinsics || UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics || UseSHA3Intrinsics)) return true; break; case vmIntrinsics::_ghash_processBlocks: if (!UseGHASHIntrinsics) return true; @@ -531,6 +488,7 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) { case vmIntrinsics::_indexOfIU: case vmIntrinsics::_indexOfIUL: case vmIntrinsics::_indexOfU_char: + case vmIntrinsics::_indexOfL_char: if (!SpecialStringIndexOf) return true; break; case vmIntrinsics::_equalsL: @@ -692,13 +650,13 @@ bool vmIntrinsics::is_disabled_by_flags(vmIntrinsics::ID id) { #define ID3(x, y, z) (( jlong)(z) + \ ((jlong)(y) << vmSymbols::log2_SID_LIMIT) + \ ((jlong)(x) << (2*vmSymbols::log2_SID_LIMIT)) ) -#define SID_ENUM(n) vmSymbols::VM_SYMBOL_ENUM_NAME(n) +#define SID_ENUM(n) VM_SYMBOL_ENUM_NAME(n) -vmIntrinsics::ID vmIntrinsics::find_id_impl(vmSymbols::SID holder, - vmSymbols::SID name, - vmSymbols::SID sig, +vmIntrinsics::ID vmIntrinsics::find_id_impl(vmSymbolID holder, + vmSymbolID name, + vmSymbolID sig, jshort flags) { - assert((int)vmSymbols::SID_LIMIT <= (1<> shift) & mask) == 1021, ""); - return vmSymbols::SID( (info >> shift) & mask ); + return vmSymbols::as_SID( (info >> shift) & mask ); } -vmSymbols::SID vmIntrinsics::name_for(vmIntrinsics::ID id) { +vmSymbolID vmIntrinsics::name_for(vmIntrinsics::ID id) { jlong info = intrinsic_info(id); int shift = vmSymbols::log2_SID_LIMIT + log2_FLAG_LIMIT, mask = right_n_bits(vmSymbols::log2_SID_LIMIT); assert(((ID4(1021,1022,1023,15) >> shift) & mask) == 1022, ""); - return vmSymbols::SID( (info >> shift) & mask ); + return vmSymbols::as_SID( (info >> shift) & mask ); } -vmSymbols::SID vmIntrinsics::signature_for(vmIntrinsics::ID id) { +vmSymbolID vmIntrinsics::signature_for(vmIntrinsics::ID id) { jlong info = intrinsic_info(id); int shift = log2_FLAG_LIMIT, mask = right_n_bits(vmSymbols::log2_SID_LIMIT); assert(((ID4(1021,1022,1023,15) >> shift) & mask) == 1023, ""); - return vmSymbols::SID( (info >> shift) & mask ); + return vmSymbols::as_SID( (info >> shift) & mask ); } vmIntrinsics::Flags vmIntrinsics::flags_for(vmIntrinsics::ID id) { @@ -788,66 +746,4 @@ vmIntrinsics::Flags vmIntrinsics::flags_for(vmIntrinsics::ID id) { assert(((ID4(1021,1022,1023,15) >> shift) & mask) == 15, ""); return Flags( (info >> shift) & mask ); } - - -#ifndef PRODUCT -// verify_method performs an extra check on a matched intrinsic method - -static bool match_method(Method* m, Symbol* n, Symbol* s) { - return (m->name() == n && - m->signature() == s); -} - -static vmIntrinsics::ID match_method_with_klass(Method* m, Symbol* mk) { -#define VM_INTRINSIC_MATCH(id, klassname, namepart, sigpart, flags) \ - { Symbol* k = vmSymbols::klassname(); \ - if (mk == k) { \ - Symbol* n = vmSymbols::namepart(); \ - Symbol* s = vmSymbols::sigpart(); \ - if (match_method(m, n, s)) \ - return vmIntrinsics::id; \ - } } - VM_INTRINSICS_DO(VM_INTRINSIC_MATCH, - VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE); - return vmIntrinsics::_none; -#undef VM_INTRINSIC_MATCH -} - -void vmIntrinsics::verify_method(ID actual_id, Method* m) { - Symbol* mk = m->method_holder()->name(); - ID declared_id = match_method_with_klass(m, mk); - - if (declared_id == actual_id) return; // success - - if (declared_id == _none && actual_id != _none && mk == vmSymbols::java_lang_StrictMath()) { - // Here are a few special cases in StrictMath not declared in vmSymbols.hpp. - switch (actual_id) { - case _min: - case _max: - case _dsqrt: - declared_id = match_method_with_klass(m, vmSymbols::java_lang_Math()); - if (declared_id == actual_id) return; // acceptable alias - break; - default: - break; - } - } - - const char* declared_name = name_at(declared_id); - const char* actual_name = name_at(actual_id); - m = NULL; - ttyLocker ttyl; - if (xtty != NULL) { - xtty->begin_elem("intrinsic_misdeclared actual='%s' declared='%s'", - actual_name, declared_name); - xtty->method(m); - xtty->end_elem("%s", ""); - } - if (PrintMiscellaneous && (WizardMode || Verbose)) { - tty->print_cr("*** misidentified method; %s(%d) should be %s(%d):", - declared_name, declared_id, actual_name, actual_id); - m->print_short_name(tty); - tty->cr(); - } -} -#endif //PRODUCT +#endif // ASSERT diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 98427627f16..249bbbc7395 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -25,6 +25,14 @@ #ifndef SHARE_CLASSFILE_VMINTRINSICS_HPP #define SHARE_CLASSFILE_VMINTRINSICS_HPP +#include "jfr/support/jfrIntrinsics.hpp" +#include "memory/allStatic.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/vmEnums.hpp" + +class Method; +class methodHandle; + // Here are all the intrinsics known to the runtime and the CI. // Each intrinsic consists of a public enum name (like _hashCode), // followed by a specification of its klass, name, and signature: @@ -309,6 +317,7 @@ do_intrinsic(_indexOfIU, java_lang_StringUTF16, indexOf_name, indexOfI_signature, F_S) \ do_intrinsic(_indexOfIUL, java_lang_StringUTF16, indexOfUL_name, indexOfI_signature, F_S) \ do_intrinsic(_indexOfU_char, java_lang_StringUTF16, indexOfChar_name, indexOfChar_signature, F_S) \ + do_intrinsic(_indexOfL_char, java_lang_StringLatin1,indexOfChar_name, indexOfChar_signature, F_S) \ do_name( indexOf_name, "indexOf") \ do_name( indexOfChar_name, "indexOfChar") \ do_name( indexOfUL_name, "indexOfLatin1") \ @@ -423,6 +432,10 @@ do_class(sun_security_provider_sha5, "sun/security/provider/SHA5") \ do_intrinsic(_sha5_implCompress, sun_security_provider_sha5, implCompress_name, implCompress_signature, F_R) \ \ + /* support for sun.security.provider.SHA3 */ \ + do_class(sun_security_provider_sha3, "sun/security/provider/SHA3") \ + do_intrinsic(_sha3_implCompress, sun_security_provider_sha3, implCompress_name, implCompress_signature, F_R) \ + \ /* support for sun.security.provider.DigestBase */ \ do_class(sun_security_provider_digestbase, "sun/security/provider/DigestBase") \ do_intrinsic(_digestBase_implCompressMB, sun_security_provider_digestbase, implCompressMB_name, implCompressMB_signature, F_R) \ @@ -777,6 +790,122 @@ do_intrinsic(_getAndSetReference, jdk_internal_misc_Unsafe, getAndSetReference_name, getAndSetReference_signature, F_R) \ do_name( getAndSetReference_name, "getAndSetReference") \ do_signature(getAndSetReference_signature, "(Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;" ) \ + \ + /* Vector API intrinsification support */ \ + \ + do_intrinsic(_VectorUnaryOp, jdk_internal_vm_vector_VectorSupport, vector_unary_op_name, vector_unary_op_sig, F_S) \ + do_signature(vector_unary_op_sig, "(ILjava/lang/Class;Ljava/lang/Class;ILjava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object;") \ + do_name(vector_unary_op_name, "unaryOp") \ + \ + do_intrinsic(_VectorBinaryOp, jdk_internal_vm_vector_VectorSupport, vector_binary_op_name, vector_binary_op_sig, F_S) \ + do_signature(vector_binary_op_sig, "(ILjava/lang/Class;Ljava/lang/Class;ILjava/lang/Object;Ljava/lang/Object;" \ + "Ljava/util/function/BiFunction;)Ljava/lang/Object;") \ + do_name(vector_binary_op_name, "binaryOp") \ + \ + do_intrinsic(_VectorTernaryOp, jdk_internal_vm_vector_VectorSupport, vector_ternary_op_name, vector_ternary_op_sig, F_S) \ + do_signature(vector_ternary_op_sig, "(ILjava/lang/Class;Ljava/lang/Class;ILjava/lang/Object;Ljava/lang/Object;" \ + "Ljava/lang/Object;Ljdk/internal/vm/vector/VectorSupport$TernaryOperation;)Ljava/lang/Object;") \ + do_name(vector_ternary_op_name, "ternaryOp") \ + \ + do_intrinsic(_VectorBroadcastCoerced, jdk_internal_vm_vector_VectorSupport, vector_broadcast_coerced_name, vector_broadcast_coerced_sig, F_S)\ + do_signature(vector_broadcast_coerced_sig, "(Ljava/lang/Class;Ljava/lang/Class;IJLjdk/internal/vm/vector/VectorSupport$VectorSpecies;" \ + "Ljdk/internal/vm/vector/VectorSupport$BroadcastOperation;)Ljava/lang/Object;") \ + do_name(vector_broadcast_coerced_name, "broadcastCoerced") \ + \ + do_intrinsic(_VectorShuffleIota, jdk_internal_vm_vector_VectorSupport, vector_shuffle_step_iota_name, vector_shuffle_step_iota_sig, F_S) \ + do_signature(vector_shuffle_step_iota_sig, "(Ljava/lang/Class;Ljava/lang/Class;Ljdk/internal/vm/vector/VectorSupport$VectorSpecies;" \ + "IIIILjdk/internal/vm/vector/VectorSupport$ShuffleIotaOperation;)Ljdk/internal/vm/vector/VectorSupport$VectorShuffle;") \ + do_name(vector_shuffle_step_iota_name, "shuffleIota") \ + \ + do_intrinsic(_VectorShuffleToVector, jdk_internal_vm_vector_VectorSupport, vector_shuffle_to_vector_name, vector_shuffle_to_vector_sig, F_S) \ + do_signature(vector_shuffle_to_vector_sig, "(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Class;Ljdk/internal/vm/vector/VectorSupport$VectorShuffle;" \ + "ILjdk/internal/vm/vector/VectorSupport$ShuffleToVectorOperation;)Ljava/lang/Object;") \ + do_name(vector_shuffle_to_vector_name, "shuffleToVector") \ + \ + do_intrinsic(_VectorLoadOp, jdk_internal_vm_vector_VectorSupport, vector_load_op_name, vector_load_op_sig, F_S) \ + do_signature(vector_load_op_sig, "(Ljava/lang/Class;Ljava/lang/Class;ILjava/lang/Object;JLjava/lang/Object;" \ + "ILjdk/internal/vm/vector/VectorSupport$VectorSpecies;Ljdk/internal/vm/vector/VectorSupport$LoadOperation;)Ljava/lang/Object;") \ + do_name(vector_load_op_name, "load") \ + \ + do_intrinsic(_VectorStoreOp, jdk_internal_vm_vector_VectorSupport, vector_store_op_name, vector_store_op_sig, F_S) \ + do_signature(vector_store_op_sig, "(Ljava/lang/Class;Ljava/lang/Class;ILjava/lang/Object;JLjdk/internal/vm/vector/VectorSupport$Vector;" \ + "Ljava/lang/Object;ILjdk/internal/vm/vector/VectorSupport$StoreVectorOperation;)V") \ + do_name(vector_store_op_name, "store") \ + \ + do_intrinsic(_VectorReductionCoerced, jdk_internal_vm_vector_VectorSupport, vector_reduction_coerced_name, vector_reduction_coerced_sig, F_S) \ + do_signature(vector_reduction_coerced_sig, "(ILjava/lang/Class;Ljava/lang/Class;ILjdk/internal/vm/vector/VectorSupport$Vector;Ljava/util/function/Function;)J") \ + do_name(vector_reduction_coerced_name, "reductionCoerced") \ + \ + do_intrinsic(_VectorTest, jdk_internal_vm_vector_VectorSupport, vector_test_name, vector_test_sig, F_S) \ + do_signature(vector_test_sig, "(ILjava/lang/Class;Ljava/lang/Class;ILjava/lang/Object;Ljava/lang/Object;Ljava/util/function/BiFunction;)Z") \ + do_name(vector_test_name, "test") \ + \ + do_intrinsic(_VectorBlend, jdk_internal_vm_vector_VectorSupport, vector_blend_name, vector_blend_sig, F_S) \ + do_signature(vector_blend_sig, "(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Class;I" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;Ljdk/internal/vm/vector/VectorSupport$Vector;Ljdk/internal/vm/vector/VectorSupport$VectorMask;" \ + "Ljdk/internal/vm/vector/VectorSupport$VectorBlendOp;)Ljdk/internal/vm/vector/VectorSupport$Vector;") \ + do_name(vector_blend_name, "blend") \ + \ + do_intrinsic(_VectorCompare, jdk_internal_vm_vector_VectorSupport, vector_compare_name, vector_compare_sig, F_S) \ + do_signature(vector_compare_sig, "(ILjava/lang/Class;Ljava/lang/Class;Ljava/lang/Class;I" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;" "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ + "Ljdk/internal/vm/vector/VectorSupport$VectorCompareOp;" ")" "Ljdk/internal/vm/vector/VectorSupport$VectorMask;") \ + do_name(vector_compare_name, "compare") \ + \ + do_intrinsic(_VectorRearrange, jdk_internal_vm_vector_VectorSupport, vector_rearrange_name, vector_rearrange_sig, F_S) \ + do_signature(vector_rearrange_sig, "(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Class;I" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;Ljdk/internal/vm/vector/VectorSupport$VectorShuffle;" \ + "Ljdk/internal/vm/vector/VectorSupport$VectorRearrangeOp;)Ljdk/internal/vm/vector/VectorSupport$Vector;") \ + do_name(vector_rearrange_name, "rearrangeOp") \ + \ + do_intrinsic(_VectorExtract, jdk_internal_vm_vector_VectorSupport, vector_extract_name, vector_extract_sig, F_S) \ + do_signature(vector_extract_sig, "(Ljava/lang/Class;Ljava/lang/Class;I" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;I" \ + "Ljdk/internal/vm/vector/VectorSupport$VecExtractOp;)J") \ + do_name(vector_extract_name, "extract") \ + \ + do_intrinsic(_VectorInsert, jdk_internal_vm_vector_VectorSupport, vector_insert_name, vector_insert_sig, F_S) \ + do_signature(vector_insert_sig, "(Ljava/lang/Class;Ljava/lang/Class;I" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;IJ" \ + "Ljdk/internal/vm/vector/VectorSupport$VecInsertOp;)Ljdk/internal/vm/vector/VectorSupport$Vector;") \ + do_name(vector_insert_name, "insert") \ + \ + do_intrinsic(_VectorBroadcastInt, jdk_internal_vm_vector_VectorSupport, vector_broadcast_int_name, vector_broadcast_int_sig, F_S) \ + do_signature(vector_broadcast_int_sig, "(ILjava/lang/Class;Ljava/lang/Class;I" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;I" \ + "Ljdk/internal/vm/vector/VectorSupport$VectorBroadcastIntOp;)Ljdk/internal/vm/vector/VectorSupport$Vector;") \ + do_name(vector_broadcast_int_name, "broadcastInt") \ + \ + do_intrinsic(_VectorConvert, jdk_internal_vm_vector_VectorSupport, vector_convert_name, vector_convert_sig, F_S) \ + do_signature(vector_convert_sig, "(ILjava/lang/Class;Ljava/lang/Class;I" \ + "Ljava/lang/Class;Ljava/lang/Class;I" \ + "Ljdk/internal/vm/vector/VectorSupport$VectorPayload;" \ + "Ljdk/internal/vm/vector/VectorSupport$VectorSpecies;" \ + "Ljdk/internal/vm/vector/VectorSupport$VectorConvertOp;)Ljdk/internal/vm/vector/VectorSupport$VectorPayload;") \ + do_name(vector_convert_name, "convert") \ + \ + do_intrinsic(_VectorGatherOp, jdk_internal_vm_vector_VectorSupport, vector_gather_name, vector_gather_sig, F_S) \ + do_signature(vector_gather_sig, "(Ljava/lang/Class;Ljava/lang/Class;ILjava/lang/Class;" \ + "Ljava/lang/Object;J" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ + "Ljava/lang/Object;I[II" \ + "Ljdk/internal/vm/vector/VectorSupport$VectorSpecies;" \ + "Ljdk/internal/vm/vector/VectorSupport$LoadVectorOperationWithMap;)" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;") \ + do_name(vector_gather_name, "loadWithMap") \ + \ + do_intrinsic(_VectorScatterOp, jdk_internal_vm_vector_VectorSupport, vector_scatter_name, vector_scatter_sig, F_S) \ + do_signature(vector_scatter_sig, "(Ljava/lang/Class;Ljava/lang/Class;ILjava/lang/Class;" \ + "Ljava/lang/Object;J" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;Ljdk/internal/vm/vector/VectorSupport$Vector;" \ + "Ljava/lang/Object;I[II" \ + "Ljdk/internal/vm/vector/VectorSupport$StoreVectorOperationWithMap;)V") \ + do_name(vector_scatter_name, "storeWithMap") \ + \ + do_intrinsic(_VectorRebox, jdk_internal_vm_vector_VectorSupport, vector_rebox_name, vector_rebox_sig, F_S) \ + do_alias(vector_rebox_sig, object_object_signature) \ + do_name(vector_rebox_name, "maybeRebox") \ + \ \ /* (2) Bytecode intrinsics */ \ \ @@ -870,4 +999,134 @@ /*end*/ +// VM Intrinsic ID's uniquely identify some very special methods +class vmIntrinsics : AllStatic { + friend class vmSymbols; + friend class ciObjectFactory; + + public: + // Accessing + enum ID { + _none = 0, // not an intrinsic (default answer) + + #define VM_INTRINSIC_ENUM(id, klass, name, sig, flags) id, + #define __IGNORE_CLASS(id, name) /*ignored*/ + #define __IGNORE_NAME(id, name) /*ignored*/ + #define __IGNORE_SIGNATURE(id, name) /*ignored*/ + #define __IGNORE_ALIAS(id, name) /*ignored*/ + + VM_INTRINSICS_DO(VM_INTRINSIC_ENUM, + __IGNORE_CLASS, __IGNORE_NAME, __IGNORE_SIGNATURE, __IGNORE_ALIAS) + #undef VM_INTRINSIC_ENUM + #undef __IGNORE_CLASS + #undef __IGNORE_NAME + #undef __IGNORE_SIGNATURE + #undef __IGNORE_ALIAS + + ID_LIMIT, + LAST_COMPILER_INLINE = _VectorScatterOp, + FIRST_MH_SIG_POLY = _invokeGeneric, + FIRST_MH_STATIC = _linkToVirtual, + LAST_MH_SIG_POLY = _linkToInterface, + + FIRST_ID = _none + 1 + }; + + enum Flags { + // AccessFlags syndromes relevant to intrinsics. + F_none = 0, + F_R, // !static ?native !synchronized (R="regular") + F_S, // static ?native !synchronized + F_Y, // !static ?native synchronized + F_RN, // !static native !synchronized + F_SN, // static native !synchronized + + FLAG_LIMIT + }; + enum { + log2_FLAG_LIMIT = 3 // checked by an assert at start-up + }; + +public: + static ID ID_from(int raw_id) { + assert(raw_id >= (int)_none && raw_id < (int)ID_LIMIT, + "must be a valid intrinsic ID"); + return (ID)raw_id; + } + + static const char* name_at(ID id); + +private: + static ID find_id_impl(vmSymbolID holder, + vmSymbolID name, + vmSymbolID sig, + jshort flags); + + // check if the intrinsic is disabled by course-grained flags. + static bool disabled_by_jvm_flags(vmIntrinsics::ID id); +public: + static ID find_id(const char* name); + // Given a method's class, name, signature, and access flags, report its ID. + static ID find_id(vmSymbolID holder, + vmSymbolID name, + vmSymbolID sig, + jshort flags) { + ID id = find_id_impl(holder, name, sig, flags); +#ifdef ASSERT + // ID _none does not hold the following asserts. + if (id == _none) return id; +#endif + assert( class_for(id) == holder, "correct id"); + assert( name_for(id) == name, "correct id"); + assert(signature_for(id) == sig, "correct id"); + return id; + } + +#ifdef ASSERT + // Find out the symbols behind an intrinsic: + static vmSymbolID class_for(ID id); + static vmSymbolID name_for(ID id); + static vmSymbolID signature_for(ID id); + static Flags flags_for(ID id); +#endif + + static const char* short_name_as_C_string(ID id, char* buf, int size); + + // The methods below provide information related to compiling intrinsics. + + // (1) Information needed by the C1 compiler. + + static bool preserves_state(vmIntrinsics::ID id); + static bool can_trap(vmIntrinsics::ID id); + static bool should_be_pinned(vmIntrinsics::ID id); + + // (2) Information needed by the C2 compiler. + + // Returns true if the intrinsic for method 'method' will perform a virtual dispatch. + static bool does_virtual_dispatch(vmIntrinsics::ID id); + // A return value larger than 0 indicates that the intrinsic for method + // 'method' requires predicated logic. + static int predicates_needed(vmIntrinsics::ID id); + + // There are 2 kinds of JVM options to control intrinsics. + // 1. Disable/Control Intrinsic accepts a list of intrinsic IDs. + // ControlIntrinsic is recommended. DisableIntrinic will be deprecated. + // Currently, the DisableIntrinsic list prevails if an intrinsic appears on + // both lists. + // + // 2. Explicit UseXXXIntrinsics options. eg. UseAESIntrinsics, UseCRC32Intrinsics etc. + // Each option can control a group of intrinsics. The user can specify them but + // their final values are subject to hardware inspection (VM_Version::initialize). + // Stub generators are controlled by them. + // + // An intrinsic is enabled if and only if neither the fine-grained control(1) nor + // the corresponding coarse-grained control(2) disables it. + static bool is_disabled_by_flags(vmIntrinsics::ID id); + + static bool is_disabled_by_flags(const methodHandle& method); + static bool is_intrinsic_available(vmIntrinsics::ID id) { + return !is_disabled_by_flags(id); + } +}; + #endif // SHARE_CLASSFILE_VMINTRINSICS_HPP diff --git a/src/hotspot/share/classfile/vmSymbols.cpp b/src/hotspot/share/classfile/vmSymbols.cpp index cb02bc5be3b..4702fc95f48 100644 --- a/src/hotspot/share/classfile/vmSymbols.cpp +++ b/src/hotspot/share/classfile/vmSymbols.cpp @@ -36,8 +36,6 @@ #include "utilities/xmlstream.hpp" -Symbol* vmSymbols::_symbols[vmSymbols::SID_LIMIT]; - Symbol* vmSymbols::_type_signatures[T_VOID+1] = { NULL /*, NULL...*/ }; inline int compare_symbol(const Symbol* a, const Symbol* b) { @@ -46,11 +44,11 @@ inline int compare_symbol(const Symbol* a, const Symbol* b) { return (address)a > (address)b ? +1 : -1; } -static vmSymbols::SID vm_symbol_index[vmSymbols::SID_LIMIT]; +static vmSymbolID vm_symbol_index[vmSymbols::number_of_symbols()]; extern "C" { static int compare_vmsymbol_sid(const void* void_a, const void* void_b) { - const Symbol* a = vmSymbols::symbol_at(*((vmSymbols::SID*) void_a)); - const Symbol* b = vmSymbols::symbol_at(*((vmSymbols::SID*) void_b)); + const Symbol* a = Symbol::vm_symbol_at(*((vmSymbolID*) void_a)); + const Symbol* b = Symbol::vm_symbol_at(*((vmSymbolID*) void_b)); return compare_symbol(a, b); } } @@ -60,9 +58,9 @@ extern "C" { static const char* vm_symbol_enum_names = VM_SYMBOLS_DO(VM_SYMBOL_ENUM_NAME_BODY, VM_ALIAS_IGNORE) "\0"; -static const char* vm_symbol_enum_name(vmSymbols::SID sid) { +static const char* vm_symbol_enum_name(vmSymbolID sid) { const char* string = &vm_symbol_enum_names[0]; - int skip = (int)sid - (int)vmSymbols::FIRST_SID; + int skip = vmSymbols::as_int(sid) - vmSymbols::as_int(vmSymbolID::FIRST_SID); for (; skip != 0; skip--) { size_t skiplen = strlen(string); if (skiplen == 0) return ""; // overflow @@ -78,15 +76,16 @@ static const char* vm_symbol_enum_name(vmSymbols::SID sid) { static const char* vm_symbol_bodies = VM_SYMBOLS_DO(VM_SYMBOL_BODY, VM_ALIAS_IGNORE); void vmSymbols::initialize(TRAPS) { - assert((int)SID_LIMIT <= (1< (1< (1<print("*** Duplicate VM symbol SIDs %s(%d) and %s(%d): \"", - vm_symbol_enum_name((SID)i2), i2, - vm_symbol_enum_name((SID)i1), i1); + vm_symbol_enum_name(i2), as_int(i2), + vm_symbol_enum_name(i1), as_int(i1)); sym->print_symbol_on(tty); tty->print_cr("\""); } @@ -129,8 +130,9 @@ void vmSymbols::initialize(TRAPS) { // Create an index for find_id: { - for (int index = (int)FIRST_SID; index < (int)SID_LIMIT; index++) { - vm_symbol_index[index] = (SID)index; + for (vmSymbolsIterator it = vmSymbolsRange.begin(); it != vmSymbolsRange.end(); ++it) { + vmSymbolID index = *it; + vm_symbol_index[as_int(index)] = index; } int num_sids = SID_LIMIT-FIRST_SID; qsort(&vm_symbol_index[FIRST_SID], num_sids, sizeof(vm_symbol_index[0]), @@ -140,20 +142,21 @@ void vmSymbols::initialize(TRAPS) { #ifdef ASSERT { // Spot-check correspondence between strings, symbols, and enums: - assert(_symbols[NO_SID] == NULL, "must be"); + assert(Symbol::_vm_symbols[NO_SID] == NULL, "must be"); const char* str = "java/lang/Object"; TempNewSymbol jlo = SymbolTable::new_permanent_symbol(str); assert(strncmp(str, (char*)jlo->base(), jlo->utf8_length()) == 0, ""); assert(jlo == java_lang_Object(), ""); - SID sid = VM_SYMBOL_ENUM_NAME(java_lang_Object); + vmSymbolID sid = VM_SYMBOL_ENUM_NAME(java_lang_Object); assert(find_sid(jlo) == sid, ""); assert(symbol_at(sid) == jlo, ""); // Make sure find_sid produces the right answer in each case. - for (int index = (int)FIRST_SID; index < (int)SID_LIMIT; index++) { - Symbol* sym = symbol_at((SID)index); + for (vmSymbolsIterator it = vmSymbolsRange.begin(); it != vmSymbolsRange.end(); ++it) { + vmSymbolID index = *it; + Symbol* sym = symbol_at(index); sid = find_sid(sym); - assert(sid == (SID)index, "symbol index works"); + assert(sid == index, "symbol index works"); // Note: If there are duplicates, this assert will fail. // A "Duplicate VM symbol" message will have already been printed. } @@ -163,19 +166,20 @@ void vmSymbols::initialize(TRAPS) { str = "format"; TempNewSymbol fmt = SymbolTable::new_permanent_symbol(str); sid = find_sid(fmt); - assert(sid == NO_SID, "symbol index works (negative test)"); + assert(sid == vmSymbolID::NO_SID, "symbol index works (negative test)"); } #endif } #ifndef PRODUCT -const char* vmSymbols::name_for(vmSymbols::SID sid) { - if (sid == NO_SID) +const char* vmSymbols::name_for(vmSymbolID sid) { + if (sid == vmSymbolID::NO_SID) return "NO_SID"; const char* string = &vm_symbol_bodies[0]; - for (int index = (int)FIRST_SID; index < (int)SID_LIMIT; index++) { - if (index == (int)sid) + for (vmSymbolsIterator it = vmSymbolsRange.begin(); it != vmSymbolsRange.end(); ++it) { + vmSymbolID index = *it; + if (index == sid) return string; string += strlen(string); // skip string body string += 1; // skip trailing null @@ -187,48 +191,49 @@ const char* vmSymbols::name_for(vmSymbols::SID sid) { void vmSymbols::symbols_do(SymbolClosure* f) { - for (int index = (int)FIRST_SID; index < (int)SID_LIMIT; index++) { - f->do_symbol(&_symbols[index]); + for (vmSymbolsIterator it = vmSymbolsRange.begin(); it != vmSymbolsRange.end(); ++it) { + vmSymbolID index = *it; + f->do_symbol(&Symbol::_vm_symbols[as_int(index)]); } for (int i = 0; i < T_VOID+1; i++) { f->do_symbol(&_type_signatures[i]); } } -void vmSymbols::metaspace_pointers_do(MetaspaceClosure *it) { - for (int index = (int)FIRST_SID; index < (int)SID_LIMIT; index++) { - it->push(&_symbols[index]); +void vmSymbols::metaspace_pointers_do(MetaspaceClosure *closure) { + for (vmSymbolsIterator it = vmSymbolsRange.begin(); it != vmSymbolsRange.end(); ++it) { + vmSymbolID index = *it; + closure->push(&Symbol::_vm_symbols[as_int(index)]); } for (int i = 0; i < T_VOID+1; i++) { - it->push(&_type_signatures[i]); + closure->push(&_type_signatures[i]); } } void vmSymbols::serialize(SerializeClosure* soc) { - soc->do_region((u_char*)&_symbols[FIRST_SID], - (SID_LIMIT - FIRST_SID) * sizeof(_symbols[0])); + soc->do_region((u_char*)&Symbol::_vm_symbols[FIRST_SID], + (SID_LIMIT - FIRST_SID) * sizeof(Symbol::_vm_symbols[0])); soc->do_region((u_char*)_type_signatures, sizeof(_type_signatures)); } -static int mid_hint = (int)vmSymbols::FIRST_SID+1; - #ifndef PRODUCT static int find_sid_calls, find_sid_probes; // (Typical counts are calls=7000 and probes=17000.) #endif -vmSymbols::SID vmSymbols::find_sid(const Symbol* symbol) { +vmSymbolID vmSymbols::find_sid(const Symbol* symbol) { + static int mid_hint = FIRST_SID+1; // Handle the majority of misses by a bounds check. // Then, use a binary search over the index. // Expected trip count is less than log2_SID_LIMIT, about eight. // This is slow but acceptable, given that calls are not // dynamically common. (Method*::intrinsic_id has a cache.) NOT_PRODUCT(find_sid_calls++); - int min = (int)FIRST_SID, max = (int)SID_LIMIT - 1; - SID sid = NO_SID, sid1; + int min = FIRST_SID, max = SID_LIMIT - 1; + vmSymbolID sid = vmSymbolID::NO_SID, sid1; int cmp1; sid1 = vm_symbol_index[min]; - cmp1 = compare_symbol(symbol, symbol_at(sid1)); + cmp1 = compare_symbol(symbol, Symbol::vm_symbol_at(sid1)); if (cmp1 <= 0) { // before the first if (cmp1 == 0) sid = sid1; } else { @@ -262,6 +267,10 @@ vmSymbols::SID vmSymbols::find_sid(const Symbol* symbol) { } #ifdef ASSERT + if (sid == vmSymbolID::NO_SID) { + return sid; + } + // Perform the exhaustive self-check the first 1000 calls, // and every 100 calls thereafter. static int find_sid_check_count = -2000; @@ -270,16 +279,17 @@ vmSymbols::SID vmSymbols::find_sid(const Symbol* symbol) { // Make sure this is the right answer, using linear search. // (We have already proven that there are no duplicates in the list.) - SID sid2 = NO_SID; - for (int index = (int)FIRST_SID; index < (int)SID_LIMIT; index++) { - Symbol* sym2 = symbol_at((SID)index); + vmSymbolID sid2 = vmSymbolID::NO_SID; + for (vmSymbolsIterator it = vmSymbolsRange.begin(); it != vmSymbolsRange.end(); ++it) { + vmSymbolID index = *it; + Symbol* sym2 = symbol_at(index); if (sym2 == symbol) { - sid2 = (SID)index; + sid2 = index; break; } } // Unless it's a duplicate, assert that the sids are the same. - if (_symbols[sid] != _symbols[sid2]) { + if (Symbol::_vm_symbols[as_int(sid)] != Symbol::_vm_symbols[as_int(sid2)]) { assert(sid == sid2, "binary same as linear search"); } } @@ -288,8 +298,8 @@ vmSymbols::SID vmSymbols::find_sid(const Symbol* symbol) { return sid; } -vmSymbols::SID vmSymbols::find_sid(const char* symbol_name) { +vmSymbolID vmSymbols::find_sid(const char* symbol_name) { Symbol* symbol = SymbolTable::probe(symbol_name, (int) strlen(symbol_name)); - if (symbol == NULL) return NO_SID; + if (symbol == NULL) return vmSymbolID::NO_SID; return find_sid(symbol); } diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index d3289e5e9ed..df2737f743f 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -25,13 +25,12 @@ #ifndef SHARE_CLASSFILE_VMSYMBOLS_HPP #define SHARE_CLASSFILE_VMSYMBOLS_HPP -#include "jfr/support/jfrIntrinsics.hpp" +#include "classfile/vmIntrinsics.hpp" #include "jvmci/vmSymbols_jvmci.hpp" #include "memory/iterator.hpp" #include "oops/symbol.hpp" #include "utilities/macros.hpp" -#include "vmIntrinsics.hpp" - +#include "utilities/enumIterator.hpp" // The class vmSymbols is a name space for fast lookup of // symbols commonly used in the VM. @@ -43,7 +42,8 @@ // Useful sub-macros exported by this header file: -#define VM_SYMBOL_ENUM_NAME(name) name##_enum +#define VM_SYMBOL_ENUM_NAME_(name) name##_enum +#define VM_SYMBOL_ENUM_NAME(name) vmSymbolID::VM_SYMBOL_ENUM_NAME_(name) #define VM_INTRINSIC_IGNORE(id, class, name, sig, flags) /*ignored*/ #define VM_SYMBOL_IGNORE(id, name) /*ignored*/ #define VM_ALIAS_IGNORE(id, id2) /*ignored*/ @@ -81,6 +81,16 @@ template(java_lang_Integer_IntegerCache, "java/lang/Integer$IntegerCache") \ template(java_lang_Long, "java/lang/Long") \ template(java_lang_Long_LongCache, "java/lang/Long$LongCache") \ + \ + template(jdk_internal_vm_vector_VectorSupport, "jdk/internal/vm/vector/VectorSupport") \ + template(jdk_internal_vm_vector_VectorPayload, "jdk/internal/vm/vector/VectorSupport$VectorPayload") \ + template(jdk_internal_vm_vector_Vector, "jdk/internal/vm/vector/VectorSupport$Vector") \ + template(jdk_internal_vm_vector_VectorMask, "jdk/internal/vm/vector/VectorSupport$VectorMask") \ + template(jdk_internal_vm_vector_VectorShuffle, "jdk/internal/vm/vector/VectorSupport$VectorShuffle") \ + template(payload_name, "payload") \ + template(ETYPE_name, "ETYPE") \ + template(VLENGTH_name, "VLENGTH") \ + \ template(java_lang_Shutdown, "java/lang/Shutdown") \ template(java_lang_ref_Reference, "java/lang/ref/Reference") \ template(java_lang_ref_SoftReference, "java/lang/ref/SoftReference") \ @@ -137,6 +147,7 @@ \ /* Java runtime version access */ \ template(java_lang_VersionProps, "java/lang/VersionProps") \ + template(java_version_name, "java_version") \ template(java_runtime_name_name, "java_runtime_name") \ template(java_runtime_version_name, "java_runtime_version") \ template(java_runtime_vendor_version_name, "VENDOR_VERSION") \ @@ -282,6 +293,10 @@ template(base_name, "base") \ /* Type Annotations (JDK 8 and above) */ \ template(type_annotations_name, "typeAnnotations") \ + /* used by CDS */ \ + template(jdk_internal_misc_CDS, "jdk/internal/misc/CDS") \ + template(generateLambdaFormHolderClasses, "generateLambdaFormHolderClasses") \ + template(generateLambdaFormHolderClasses_signature, "([Ljava/lang/String;)[Ljava/lang/Object;") \ \ /* Intrinsic Annotation (JDK 9 and above) */ \ template(jdk_internal_vm_annotation_DontInline_signature, "Ljdk/internal/vm/annotation/DontInline;") \ @@ -289,7 +304,6 @@ template(jdk_internal_vm_annotation_Hidden_signature, "Ljdk/internal/vm/annotation/Hidden;") \ template(jdk_internal_vm_annotation_IntrinsicCandidate_signature, "Ljdk/internal/vm/annotation/IntrinsicCandidate;") \ template(jdk_internal_vm_annotation_Stable_signature, "Ljdk/internal/vm/annotation/Stable;") \ - \ /* Support for JSR 292 & invokedynamic (JDK 1.7 and above) */ \ template(java_lang_invoke_CallSite, "java/lang/invoke/CallSite") \ template(java_lang_invoke_ConstantCallSite, "java/lang/invoke/ConstantCallSite") \ @@ -675,38 +689,70 @@ \ /*end*/ +// enum for figuring positions and size of Symbol::_vm_symbols[] +enum class vmSymbolID : int { + // [FIRST_SID ... LAST_SID] is the iteration range for the *valid* symbols. + // NO_SID is used to indicate an invalid symbol. Some implementation code + // *may* read _vm_symbols[NO_SID], so it must be a valid array index. + NO_SID = 0, // exclusive lower limit + #define VM_SYMBOL_ENUM(name, string) VM_SYMBOL_ENUM_NAME_(name), + VM_SYMBOLS_DO(VM_SYMBOL_ENUM, VM_ALIAS_IGNORE) + #undef VM_SYMBOL_ENUM -// Class vmSymbols + SID_LIMIT, // exclusive upper limit + + #define VM_ALIAS_ENUM(name, def) VM_SYMBOL_ENUM_NAME_(name) = VM_SYMBOL_ENUM_NAME_(def), + VM_SYMBOLS_DO(VM_SYMBOL_IGNORE, VM_ALIAS_ENUM) + #undef VM_ALIAS_ENUM + + FIRST_SID = NO_SID + 1, // inclusive lower limit + LAST_SID = SID_LIMIT - 1, // inclusive upper limit +}; + +ENUMERATOR_RANGE(vmSymbolID, vmSymbolID::FIRST_SID, vmSymbolID::LAST_SID) +constexpr EnumRange vmSymbolsRange; // the default range of all valid vmSymbolIDs +using vmSymbolsIterator = EnumIterator; // convenience class vmSymbols: AllStatic { friend class vmIntrinsics; friend class VMStructs; friend class JVMCIVMStructs; + + static const int NO_SID = static_cast(vmSymbolID::NO_SID); // exclusive lower limit + static const int FIRST_SID = static_cast(vmSymbolID::FIRST_SID); // inclusive lower limit + static const int LAST_SID = static_cast(vmSymbolID::FIRST_SID); // inclusive upper limit + static const int SID_LIMIT = static_cast(vmSymbolID::SID_LIMIT); // exclusive upper limit + public: - // enum for figuring positions and size of array holding Symbol*s - enum SID { - NO_SID = 0, + static constexpr bool is_valid_id(int id) { + return (id >= FIRST_SID && id < SID_LIMIT); + } + static constexpr bool is_valid_id(vmSymbolID sid) { + return is_valid_id(static_cast(sid)); + } - #define VM_SYMBOL_ENUM(name, string) VM_SYMBOL_ENUM_NAME(name), - VM_SYMBOLS_DO(VM_SYMBOL_ENUM, VM_ALIAS_IGNORE) - #undef VM_SYMBOL_ENUM + static constexpr vmSymbolID as_SID(int id) { + assert(is_valid_id(id), "must be"); + return static_cast(id); + } - SID_LIMIT, + static constexpr int as_int(vmSymbolID sid) { + assert(is_valid_id(sid), "must be"); + return static_cast(sid); + } - #define VM_ALIAS_ENUM(name, def) VM_SYMBOL_ENUM_NAME(name) = VM_SYMBOL_ENUM_NAME(def), - VM_SYMBOLS_DO(VM_SYMBOL_IGNORE, VM_ALIAS_ENUM) - #undef VM_ALIAS_ENUM + static constexpr int number_of_symbols() { + static_assert(NO_SID == 0, "must be a valid array index"); + static_assert(FIRST_SID == 1, "must not be the same as NO_SID"); + return SID_LIMIT; + } - FIRST_SID = NO_SID + 1 - }; enum { log2_SID_LIMIT = 11 // checked by an assert at start-up }; private: - // The symbol array - static Symbol* _symbols[]; // Field signatures indexed by BasicType. static Symbol* _type_signatures[T_VOID+1]; @@ -717,7 +763,7 @@ class vmSymbols: AllStatic { // Accessing #define VM_SYMBOL_DECLARE(name, ignore) \ static Symbol* name() { \ - return _symbols[VM_SYMBOL_ENUM_NAME(name)]; \ + return Symbol::_vm_symbols[static_cast(VM_SYMBOL_ENUM_NAME(name))]; \ } VM_SYMBOLS_DO(VM_SYMBOL_DECLARE, VM_SYMBOL_DECLARE) #undef VM_SYMBOL_DECLARE @@ -733,149 +779,18 @@ class vmSymbols: AllStatic { return _type_signatures[t]; } - static Symbol* symbol_at(SID id) { - assert(id >= FIRST_SID && id < SID_LIMIT, "oob"); - assert(_symbols[id] != NULL, "init"); - return _symbols[id]; + static Symbol* symbol_at(vmSymbolID id) { + return Symbol::vm_symbol_at(id); } - // Returns symbol's SID if one is assigned, else NO_SID. - static SID find_sid(const Symbol* symbol); - static SID find_sid(const char* symbol_name); + // Returns symbol's vmSymbolID if one is assigned, else vmSymbolID::NO_SID. + static vmSymbolID find_sid(const Symbol* symbol); + static vmSymbolID find_sid(const char* symbol_name); #ifndef PRODUCT // No need for this in the product: - static const char* name_for(SID sid); + static const char* name_for(vmSymbolID sid); #endif //PRODUCT }; -// VM Intrinsic ID's uniquely identify some very special methods -class vmIntrinsics: AllStatic { - friend class vmSymbols; - friend class ciObjectFactory; - - public: - // Accessing - enum ID { - _none = 0, // not an intrinsic (default answer) - - #define VM_INTRINSIC_ENUM(id, klass, name, sig, flags) id, - VM_INTRINSICS_DO(VM_INTRINSIC_ENUM, - VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE) - #undef VM_INTRINSIC_ENUM - - ID_LIMIT, - LAST_COMPILER_INLINE = _getAndSetReference, - FIRST_MH_SIG_POLY = _invokeGeneric, - FIRST_MH_STATIC = _linkToVirtual, - LAST_MH_SIG_POLY = _linkToInterface, - - FIRST_ID = _none + 1 - }; - - enum Flags { - // AccessFlags syndromes relevant to intrinsics. - F_none = 0, - F_R, // !static ?native !synchronized (R="regular") - F_S, // static ?native !synchronized - F_Y, // !static ?native synchronized - F_RN, // !static native !synchronized - F_SN, // static native !synchronized - F_RNY, // !static native synchronized - - FLAG_LIMIT - }; - enum { - log2_FLAG_LIMIT = 4 // checked by an assert at start-up - }; - -public: - static ID ID_from(int raw_id) { - assert(raw_id >= (int)_none && raw_id < (int)ID_LIMIT, - "must be a valid intrinsic ID"); - return (ID)raw_id; - } - - static const char* name_at(ID id); - -private: - static ID find_id_impl(vmSymbols::SID holder, - vmSymbols::SID name, - vmSymbols::SID sig, - jshort flags); - - // check if the intrinsic is disabled by course-grained flags. - static bool disabled_by_jvm_flags(vmIntrinsics::ID id); -public: - static ID find_id(const char* name); - // Given a method's class, name, signature, and access flags, report its ID. - static ID find_id(vmSymbols::SID holder, - vmSymbols::SID name, - vmSymbols::SID sig, - jshort flags) { - ID id = find_id_impl(holder, name, sig, flags); -#ifdef ASSERT - // ID _none does not hold the following asserts. - if (id == _none) return id; -#endif - assert( class_for(id) == holder, "correct id"); - assert( name_for(id) == name, "correct id"); - assert(signature_for(id) == sig, "correct id"); - return id; - } - - static void verify_method(ID actual_id, Method* m) PRODUCT_RETURN; - - // Find out the symbols behind an intrinsic: - static vmSymbols::SID class_for(ID id); - static vmSymbols::SID name_for(ID id); - static vmSymbols::SID signature_for(ID id); - static Flags flags_for(ID id); - - static const char* short_name_as_C_string(ID id, char* buf, int size); - - // Wrapper object methods: - static ID for_boxing(BasicType type); - static ID for_unboxing(BasicType type); - - // Raw conversion: - static ID for_raw_conversion(BasicType src, BasicType dest); - - // The methods below provide information related to compiling intrinsics. - - // (1) Information needed by the C1 compiler. - - static bool preserves_state(vmIntrinsics::ID id); - static bool can_trap(vmIntrinsics::ID id); - static bool should_be_pinned(vmIntrinsics::ID id); - - // (2) Information needed by the C2 compiler. - - // Returns true if the intrinsic for method 'method' will perform a virtual dispatch. - static bool does_virtual_dispatch(vmIntrinsics::ID id); - // A return value larger than 0 indicates that the intrinsic for method - // 'method' requires predicated logic. - static int predicates_needed(vmIntrinsics::ID id); - - // There are 2 kinds of JVM options to control intrinsics. - // 1. Disable/Control Intrinsic accepts a list of intrinsic IDs. - // ControlIntrinsic is recommended. DisableIntrinic will be deprecated. - // Currently, the DisableIntrinsic list prevails if an intrinsic appears on - // both lists. - // - // 2. Explicit UseXXXIntrinsics options. eg. UseAESIntrinsics, UseCRC32Intrinsics etc. - // Each option can control a group of intrinsics. The user can specify them but - // their final values are subject to hardware inspection (VM_Version::initialize). - // Stub generators are controlled by them. - // - // An intrinsic is enabled if and only if neither the fine-grained control(1) nor - // the corresponding coarse-grained control(2) disables it. - static bool is_disabled_by_flags(vmIntrinsics::ID id); - - static bool is_disabled_by_flags(const methodHandle& method); - static bool is_intrinsic_available(vmIntrinsics::ID id) { - return !is_disabled_by_flags(id); - } -}; - #endif // SHARE_CLASSFILE_VMSYMBOLS_HPP diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index e3c3aba226f..42a4fc0f769 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -55,9 +55,8 @@ const char* CodeBlob::compiler_name() const { unsigned int CodeBlob::align_code_offset(int offset) { // align the size to CodeEntryAlignment - return - ((offset + (int)CodeHeap::header_size() + (CodeEntryAlignment-1)) & ~(CodeEntryAlignment-1)) - - (int)CodeHeap::header_size(); + int header_size = (int)CodeHeap::header_size(); + return align_up(offset + header_size, CodeEntryAlignment) - header_size; } @@ -88,8 +87,8 @@ CodeBlob::CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& la _relocation_end(layout.relocation_end()), _oop_maps(oop_maps), _caller_must_gc_arguments(caller_must_gc_arguments), - _strings(CodeStrings()), _name(name) + NOT_PRODUCT(COMMA _strings(CodeStrings())) { assert(is_aligned(layout.size(), oopSize), "unaligned size"); assert(is_aligned(layout.header_size(), oopSize), "unaligned size"); @@ -116,8 +115,8 @@ CodeBlob::CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& la _relocation_begin(layout.relocation_begin()), _relocation_end(layout.relocation_end()), _caller_must_gc_arguments(caller_must_gc_arguments), - _strings(CodeStrings()), _name(name) + NOT_PRODUCT(COMMA _strings(CodeStrings())) { assert(is_aligned(_size, oopSize), "unaligned size"); assert(is_aligned(_header_size, oopSize), "unaligned size"); @@ -159,7 +158,7 @@ RuntimeBlob::RuntimeBlob( void CodeBlob::flush() { FREE_C_HEAP_ARRAY(unsigned char, _oop_maps); _oop_maps = NULL; - _strings.free(); + NOT_PRODUCT(_strings.free();) } void CodeBlob::set_oop_maps(OopMapSet* p) { diff --git a/src/hotspot/share/code/codeBlob.hpp b/src/hotspot/share/code/codeBlob.hpp index f8c45e92f95..d9bd003a52d 100644 --- a/src/hotspot/share/code/codeBlob.hpp +++ b/src/hotspot/share/code/codeBlob.hpp @@ -110,10 +110,12 @@ class CodeBlob { ImmutableOopMapSet* _oop_maps; // OopMap for this CodeBlob bool _caller_must_gc_arguments; - CodeStrings _strings; + const char* _name; S390_ONLY(int _ctable_offset;) + NOT_PRODUCT(CodeStrings _strings;) + CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& layout, int frame_complete_offset, int frame_size, ImmutableOopMapSet* oop_maps, bool caller_must_gc_arguments); CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& layout, CodeBuffer* cb, int frame_complete_offset, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments); @@ -231,29 +233,20 @@ class CodeBlob { void dump_for_addr(address addr, outputStream* st, bool verbose) const; void print_code(); - bool has_block_comment(address block_begin) const { - intptr_t offset = (intptr_t)(block_begin - code_begin()); - return _strings.has_block_comment(offset); - } // Print the comment associated with offset on stream, if there is one virtual void print_block_comment(outputStream* stream, address block_begin) const { + #ifndef PRODUCT intptr_t offset = (intptr_t)(block_begin - code_begin()); _strings.print_block_comment(stream, offset); + #endif } - // Transfer ownership of comments to this CodeBlob +#ifndef PRODUCT void set_strings(CodeStrings& strings) { assert(!is_aot(), "invalid on aot"); - _strings.assign(strings); - } - - static ByteSize name_field_offset() { - return byte_offset_of(CodeBlob, _name); - } - - static ByteSize oop_maps_field_offset() { - return byte_offset_of(CodeBlob, _oop_maps); + _strings.copy(strings); } +#endif }; class CodeBlobLayout : public StackObj { diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index 3b589844232..7e4c88e65e2 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -1559,6 +1559,34 @@ void CodeCache::log_state(outputStream* st) { unallocated_capacity()); } +#ifdef LINUX +void CodeCache::write_perf_map() { + MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + + // Perf expects to find the map file at /tmp/perf-.map. + char fname[32]; + jio_snprintf(fname, sizeof(fname), "/tmp/perf-%d.map", os::current_process_id()); + + fileStream fs(fname, "w"); + if (!fs.is_open()) { + log_warning(codecache)("Failed to create %s for perf map", fname); + return; + } + + AllCodeBlobsIterator iter(AllCodeBlobsIterator::only_alive_and_not_unloading); + while (iter.next()) { + CodeBlob *cb = iter.method(); + ResourceMark rm; + const char* method_name = + cb->is_compiled() ? cb->as_compiled_method()->method()->external_name() + : cb->name(); + fs.print_cr(INTPTR_FORMAT " " INTPTR_FORMAT " %s", + (intptr_t)cb->code_begin(), (intptr_t)cb->code_size(), + method_name); + } +} +#endif // LINUX + //---< BEGIN >--- CodeHeap State Analytics. void CodeCache::aggregate(outputStream *out, size_t granularity) { diff --git a/src/hotspot/share/code/codeCache.hpp b/src/hotspot/share/code/codeCache.hpp index 4a3e29810f3..959396f8699 100644 --- a/src/hotspot/share/code/codeCache.hpp +++ b/src/hotspot/share/code/codeCache.hpp @@ -191,6 +191,7 @@ class CodeCache : AllStatic { static void print_trace(const char* event, CodeBlob* cb, int size = 0) PRODUCT_RETURN; static void print_summary(outputStream* st, bool detailed = true); // Prints a summary of the code cache usage static void log_state(outputStream* st); + LINUX_ONLY(static void write_perf_map();) static const char* get_code_heap_name(int code_blob_type) { return (heap_available(code_blob_type) ? get_code_heap(code_blob_type)->name() : "Unused"); } static void report_codemem_full(int code_blob_type, bool print); @@ -409,7 +410,13 @@ struct NMethodFilter { static const GrowableArray* heaps() { return CodeCache::nmethod_heaps(); } }; +struct AllCodeBlobsFilter { + static bool apply(CodeBlob* cb) { return true; } + static const GrowableArray* heaps() { return CodeCache::heaps(); } +}; + typedef CodeBlobIterator CompiledMethodIterator; typedef CodeBlobIterator NMethodIterator; +typedef CodeBlobIterator AllCodeBlobsIterator; #endif // SHARE_CODE_CODECACHE_HPP diff --git a/src/hotspot/share/code/compiledMethod.cpp b/src/hotspot/share/code/compiledMethod.cpp index 78614548f63..d36ae21366f 100644 --- a/src/hotspot/share/code/compiledMethod.cpp +++ b/src/hotspot/share/code/compiledMethod.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -78,7 +78,6 @@ void CompiledMethod::init_defaults() { } _has_unsafe_access = 0; _has_method_handle_invokes = 0; - _lazy_critical_native = 0; _has_wide_vectors = 0; } @@ -293,17 +292,13 @@ void CompiledMethod::verify_oop_relocations() { ScopeDesc* CompiledMethod::scope_desc_at(address pc) { PcDesc* pd = pc_desc_at(pc); guarantee(pd != NULL, "scope must be present"); - return new ScopeDesc(this, pd->scope_decode_offset(), - pd->obj_decode_offset(), pd->should_reexecute(), pd->rethrow_exception(), - pd->return_oop()); + return new ScopeDesc(this, pd); } ScopeDesc* CompiledMethod::scope_desc_near(address pc) { PcDesc* pd = pc_desc_near(pc); guarantee(pd != NULL, "scope must be present"); - return new ScopeDesc(this, pd->scope_decode_offset(), - pd->obj_decode_offset(), pd->should_reexecute(), pd->rethrow_exception(), - pd->return_oop()); + return new ScopeDesc(this, pd); } address CompiledMethod::oops_reloc_begin() const { diff --git a/src/hotspot/share/code/compiledMethod.hpp b/src/hotspot/share/code/compiledMethod.hpp index 09a329c2fb0..fb4e9dd35c2 100644 --- a/src/hotspot/share/code/compiledMethod.hpp +++ b/src/hotspot/share/code/compiledMethod.hpp @@ -157,7 +157,6 @@ class CompiledMethod : public CodeBlob { // set during construction unsigned int _has_unsafe_access:1; // May fault due to unsafe access. unsigned int _has_method_handle_invokes:1; // Has this method MethodHandle invokes? - unsigned int _lazy_critical_native:1; // Lazy JNI critical native unsigned int _has_wide_vectors:1; // Preserve wide vectors at safepoints Method* _method; @@ -196,9 +195,6 @@ class CompiledMethod : public CodeBlob { bool has_method_handle_invokes() const { return _has_method_handle_invokes; } void set_has_method_handle_invokes(bool z) { _has_method_handle_invokes = z; } - bool is_lazy_critical_native() const { return _lazy_critical_native; } - void set_lazy_critical_native(bool z) { _lazy_critical_native = z; } - bool has_wide_vectors() const { return _has_wide_vectors; } void set_has_wide_vectors(bool z) { _has_wide_vectors = z; } diff --git a/src/hotspot/share/code/debugInfo.hpp b/src/hotspot/share/code/debugInfo.hpp index dae79905a93..bfba1523b08 100644 --- a/src/hotspot/share/code/debugInfo.hpp +++ b/src/hotspot/share/code/debugInfo.hpp @@ -42,6 +42,7 @@ // - ConstantValue describes a constant class ConstantOopReadValue; +class LocationValue; class ObjectValue; class ScopeValue: public ResourceObj { @@ -67,6 +68,11 @@ class ScopeValue: public ResourceObj { return (ObjectValue*)this; } + LocationValue* as_LocationValue() { + assert(is_location(), "must be"); + return (LocationValue*)this; + } + // Serialization of debugging information virtual void write_on(DebugInfoWriteStream* stream) = 0; static ScopeValue* read_from(DebugInfoReadStream* stream); diff --git a/src/hotspot/share/code/debugInfoRec.cpp b/src/hotspot/share/code/debugInfoRec.cpp index b19b9defc4d..887150eb3d1 100644 --- a/src/hotspot/share/code/debugInfoRec.cpp +++ b/src/hotspot/share/code/debugInfoRec.cpp @@ -288,6 +288,8 @@ void DebugInformationRecorder::describe_scope(int pc_offset, bool rethrow_exception, bool is_method_handle_invoke, bool return_oop, + bool has_ea_local_in_scope, + bool arg_escape, DebugToken* locals, DebugToken* expressions, DebugToken* monitors) { @@ -304,6 +306,8 @@ void DebugInformationRecorder::describe_scope(int pc_offset, last_pd->set_rethrow_exception(rethrow_exception); last_pd->set_is_method_handle_invoke(is_method_handle_invoke); last_pd->set_return_oop(return_oop); + last_pd->set_has_ea_local_in_scope(has_ea_local_in_scope); + last_pd->set_arg_escape(arg_escape); // serialize sender stream offest stream()->write_int(sender_stream_offset); diff --git a/src/hotspot/share/code/debugInfoRec.hpp b/src/hotspot/share/code/debugInfoRec.hpp index 2f64d6b1bd4..1225ed06ad8 100644 --- a/src/hotspot/share/code/debugInfoRec.hpp +++ b/src/hotspot/share/code/debugInfoRec.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -105,6 +105,8 @@ class DebugInformationRecorder: public ResourceObj { bool rethrow_exception = false, bool is_method_handle_invoke = false, bool return_oop = false, + bool has_ea_local_in_scope = false, + bool arg_escape = false, DebugToken* locals = NULL, DebugToken* expressions = NULL, DebugToken* monitors = NULL); diff --git a/src/hotspot/share/code/location.hpp b/src/hotspot/share/code/location.hpp index b90dd939512..41575f2d77b 100644 --- a/src/hotspot/share/code/location.hpp +++ b/src/hotspot/share/code/location.hpp @@ -58,6 +58,7 @@ class Location { lng, // Long held in one register float_in_dbl, // Float held in double register dbl, // Double held in one register + vector, // Vector in one register addr, // JSR return address narrowoop // Narrow Oop (please GC me!) }; diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 57e8802ecb8..4e21b69836b 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -50,6 +50,7 @@ #include "oops/oop.inline.hpp" #include "prims/jvmtiImpl.hpp" #include "prims/jvmtiThreadState.hpp" +#include "prims/methodHandles.hpp" #include "runtime/atomic.hpp" #include "runtime/deoptimization.hpp" #include "runtime/flags/flagSetting.hpp" @@ -2418,9 +2419,7 @@ void nmethod::verify_interrupt_point(address call_site) { PcDesc* pd = pc_desc_at(nativeCall_at(call_site)->return_address()); assert(pd != NULL, "PcDesc must exist"); - for (ScopeDesc* sd = new ScopeDesc(this, pd->scope_decode_offset(), - pd->obj_decode_offset(), pd->should_reexecute(), pd->rethrow_exception(), - pd->return_oop()); + for (ScopeDesc* sd = new ScopeDesc(this, pd); !sd->is_top(); sd = sd->sender()) { sd->verify(); } @@ -3055,9 +3054,7 @@ const char* nmethod::reloc_string_for(u_char* begin, u_char* end) { ScopeDesc* nmethod::scope_desc_in(address begin, address end) { PcDesc* p = pc_desc_near(begin+1); if (p != NULL && p->real_pc(this) <= end) { - return new ScopeDesc(this, p->scope_decode_offset(), - p->obj_decode_offset(), p->should_reexecute(), p->rethrow_exception(), - p->return_oop()); + return new ScopeDesc(this, p); } return NULL; } diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index 92b1e72aef8..6bc04b44045 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -695,9 +695,7 @@ class nmethod : public CompiledMethod { CodeBlob::print_block_comment(stream, block_begin); #endif } - bool has_block_comment(address block_begin) { - return CodeBlob::has_block_comment(block_begin); - } + void print_nmethod_labels(outputStream* stream, address block_begin, bool print_section_labels=true) const; const char* nmethod_section_label(address pos) const; diff --git a/src/hotspot/share/code/pcDesc.hpp b/src/hotspot/share/code/pcDesc.hpp index 263a13a353c..8fddefd790b 100644 --- a/src/hotspot/share/code/pcDesc.hpp +++ b/src/hotspot/share/code/pcDesc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,7 +42,9 @@ class PcDesc { PCDESC_reexecute = 1 << 0, PCDESC_is_method_handle_invoke = 1 << 1, PCDESC_return_oop = 1 << 2, - PCDESC_rethrow_exception = 1 << 3 + PCDESC_rethrow_exception = 1 << 3, + PCDESC_has_ea_local_in_scope = 1 << 4, + PCDESC_arg_escape = 1 << 5 }; int _flags; @@ -89,6 +91,16 @@ class PcDesc { bool return_oop() const { return (_flags & PCDESC_return_oop) != 0; } void set_return_oop(bool z) { set_flag(PCDESC_return_oop, z); } + // Indicates if there are objects in scope that, based on escape analysis, are local to the + // compiled method or local to the current thread, i.e. NoEscape or ArgEscape + bool has_ea_local_in_scope() const { return (_flags & PCDESC_has_ea_local_in_scope) != 0; } + void set_has_ea_local_in_scope(bool z) { set_flag(PCDESC_has_ea_local_in_scope, z); } + + // Indicates if this pc descriptor is at a call site where objects that do not escape the + // current thread are passed as arguments. + bool arg_escape() const { return (_flags & PCDESC_arg_escape) != 0; } + void set_arg_escape(bool z) { set_flag(PCDESC_arg_escape, z); } + // Returns the real pc address real_pc(const CompiledMethod* code) const; diff --git a/src/hotspot/share/code/scopeDesc.cpp b/src/hotspot/share/code/scopeDesc.cpp index 5ebe4b6c570..799d11a2f91 100644 --- a/src/hotspot/share/code/scopeDesc.cpp +++ b/src/hotspot/share/code/scopeDesc.cpp @@ -31,23 +31,16 @@ #include "oops/oop.inline.hpp" #include "runtime/handles.inline.hpp" -ScopeDesc::ScopeDesc(const CompiledMethod* code, int decode_offset, int obj_decode_offset, bool reexecute, bool rethrow_exception, bool return_oop) { +ScopeDesc::ScopeDesc(const CompiledMethod* code, PcDesc* pd, bool ignore_objects) { + int obj_decode_offset = ignore_objects ? DebugInformationRecorder::serialized_null : pd->obj_decode_offset(); _code = code; - _decode_offset = decode_offset; + _decode_offset = pd->scope_decode_offset(); _objects = decode_object_values(obj_decode_offset); - _reexecute = reexecute; - _rethrow_exception = rethrow_exception; - _return_oop = return_oop; - decode_body(); -} - -ScopeDesc::ScopeDesc(const CompiledMethod* code, int decode_offset, bool reexecute, bool rethrow_exception, bool return_oop) { - _code = code; - _decode_offset = decode_offset; - _objects = decode_object_values(DebugInformationRecorder::serialized_null); - _reexecute = reexecute; - _rethrow_exception = rethrow_exception; - _return_oop = return_oop; + _reexecute = pd->should_reexecute(); + _rethrow_exception = pd->rethrow_exception(); + _return_oop = pd->return_oop(); + _has_ea_local_in_scope = ignore_objects ? false : pd->has_ea_local_in_scope(); + _arg_escape = ignore_objects ? false : pd->arg_escape(); decode_body(); } @@ -59,6 +52,8 @@ void ScopeDesc::initialize(const ScopeDesc* parent, int decode_offset) { _reexecute = false; //reexecute only applies to the first scope _rethrow_exception = false; _return_oop = false; + _has_ea_local_in_scope = parent->has_ea_local_in_scope(); + _arg_escape = false; decode_body(); } diff --git a/src/hotspot/share/code/scopeDesc.hpp b/src/hotspot/share/code/scopeDesc.hpp index 44c7f160156..00022c745ff 100644 --- a/src/hotspot/share/code/scopeDesc.hpp +++ b/src/hotspot/share/code/scopeDesc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,12 +60,7 @@ class SimpleScopeDesc : public StackObj { class ScopeDesc : public ResourceObj { public: // Constructor - ScopeDesc(const CompiledMethod* code, int decode_offset, int obj_decode_offset, bool reexecute, bool rethrow_exception, bool return_oop); - - // Calls above, giving default value of "serialized_null" to the - // "obj_decode_offset" argument. (We don't use a default argument to - // avoid a .hpp-.hpp dependency.) - ScopeDesc(const CompiledMethod* code, int decode_offset, bool reexecute, bool rethrow_exception, bool return_oop); + ScopeDesc(const CompiledMethod* code, PcDesc* pd, bool ignore_objects = false); // Direct access to scope ScopeDesc* at_offset(int decode_offset) { return new ScopeDesc(this, decode_offset); } @@ -76,6 +71,10 @@ class ScopeDesc : public ResourceObj { bool should_reexecute() const { return _reexecute; } bool rethrow_exception() const { return _rethrow_exception; } bool return_oop() const { return _return_oop; } + // Returns true if one or more NoEscape or ArgEscape objects exist in + // any of the scopes at compiled pc. + bool has_ea_local_in_scope() const { return _has_ea_local_in_scope; } + bool arg_escape() const { return _arg_escape; } GrowableArray* locals(); GrowableArray* expressions(); @@ -105,6 +104,9 @@ class ScopeDesc : public ResourceObj { bool _reexecute; bool _rethrow_exception; bool _return_oop; + bool _has_ea_local_in_scope; // One or more NoEscape or ArgEscape objects exist in + // any of the scopes at compiled pc. + bool _arg_escape; // Compiled Java call in youngest scope passes ArgEscape // Decoding offsets int _decode_offset; diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp index d204b9dc408..336a0c4f95a 100644 --- a/src/hotspot/share/compiler/compileBroker.cpp +++ b/src/hotspot/share/compiler/compileBroker.cpp @@ -50,6 +50,7 @@ #include "prims/whitebox.hpp" #include "runtime/arguments.hpp" #include "runtime/atomic.hpp" +#include "runtime/escapeBarrier.hpp" #include "runtime/globals_extension.hpp" #include "runtime/handles.inline.hpp" #include "runtime/init.hpp" @@ -798,19 +799,98 @@ Handle CompileBroker::create_thread_oop(const char* name, TRAPS) { CHECK_NH); } +#if defined(ASSERT) && COMPILER2_OR_JVMCI +// Stress testing. Dedicated threads revert optimizations based on escape analysis concurrently to +// the running java application. Configured with vm options DeoptimizeObjectsALot*. +class DeoptimizeObjectsALotThread : public JavaThread { -JavaThread* CompileBroker::make_thread(jobject thread_handle, CompileQueue* queue, AbstractCompiler* comp, Thread* THREAD) { + static void deopt_objs_alot_thread_entry(JavaThread* thread, TRAPS); + void deoptimize_objects_alot_loop_single(); + void deoptimize_objects_alot_loop_all(); + +public: + DeoptimizeObjectsALotThread() : JavaThread(&deopt_objs_alot_thread_entry) { } + + bool is_hidden_from_external_view() const { return true; } +}; + +// Entry for DeoptimizeObjectsALotThread. The threads are started in +// CompileBroker::init_compiler_sweeper_threads() iff DeoptimizeObjectsALot is enabled +void DeoptimizeObjectsALotThread::deopt_objs_alot_thread_entry(JavaThread* thread, TRAPS) { + DeoptimizeObjectsALotThread* dt = ((DeoptimizeObjectsALotThread*) thread); + bool enter_single_loop; + { + MonitorLocker ml(dt, EscapeBarrier_lock, Mutex::_no_safepoint_check_flag); + static int single_thread_count = 0; + enter_single_loop = single_thread_count++ < DeoptimizeObjectsALotThreadCountSingle; + } + if (enter_single_loop) { + dt->deoptimize_objects_alot_loop_single(); + } else { + dt->deoptimize_objects_alot_loop_all(); + } + } + +// Execute EscapeBarriers in an endless loop to revert optimizations based on escape analysis. Each +// barrier targets a single thread which is selected round robin. +void DeoptimizeObjectsALotThread::deoptimize_objects_alot_loop_single() { + HandleMark hm(this); + while (true) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *deoptee_thread = jtiwh.next(); ) { + { // Begin new scope for escape barrier + HandleMarkCleaner hmc(this); + ResourceMark rm(this); + EscapeBarrier eb(true, this, deoptee_thread); + eb.deoptimize_objects(100); + } + // Now sleep after the escape barriers destructor resumed deoptee_thread. + sleep(DeoptimizeObjectsALotInterval); + } + } +} + +// Execute EscapeBarriers in an endless loop to revert optimizations based on escape analysis. Each +// barrier targets all java threads in the vm at once. +void DeoptimizeObjectsALotThread::deoptimize_objects_alot_loop_all() { + HandleMark hm(this); + while (true) { + { // Begin new scope for escape barrier + HandleMarkCleaner hmc(this); + ResourceMark rm(this); + EscapeBarrier eb(true, this); + eb.deoptimize_objects_all_threads(); + } + // Now sleep after the escape barriers destructor resumed the java threads. + sleep(DeoptimizeObjectsALotInterval); + } +} +#endif // defined(ASSERT) && COMPILER2_OR_JVMCI + + +JavaThread* CompileBroker::make_thread(ThreadType type, jobject thread_handle, CompileQueue* queue, AbstractCompiler* comp, Thread* THREAD) { JavaThread* new_thread = NULL; { MutexLocker mu(THREAD, Threads_lock); - if (comp != NULL) { - if (!InjectCompilerCreationFailure || comp->num_compiler_threads() == 0) { - CompilerCounters* counters = new CompilerCounters(); - new_thread = new CompilerThread(queue, counters); - } - } else { - new_thread = new CodeCacheSweeperThread(); + switch (type) { + case compiler_t: + assert(comp != NULL, "Compiler instance missing."); + if (!InjectCompilerCreationFailure || comp->num_compiler_threads() == 0) { + CompilerCounters* counters = new CompilerCounters(); + new_thread = new CompilerThread(queue, counters); + } + break; + case sweeper_t: + new_thread = new CodeCacheSweeperThread(); + break; +#if defined(ASSERT) && COMPILER2_OR_JVMCI + case deoptimizer_t: + new_thread = new DeoptimizeObjectsALotThread(); + break; +#endif // ASSERT + default: + ShouldNotReachHere(); } + // At this point the new CompilerThread data-races with this startup // thread (which I believe is the primoridal thread and NOT the VM // thread). This means Java bytecodes being executed at startup can @@ -852,7 +932,7 @@ JavaThread* CompileBroker::make_thread(jobject thread_handle, CompileQueue* queu java_lang_Thread::set_daemon(JNIHandles::resolve_non_null(thread_handle)); new_thread->set_threadObj(JNIHandles::resolve_non_null(thread_handle)); - if (comp != NULL) { + if (type == compiler_t) { new_thread->as_CompilerThread()->set_compiler(comp); } Threads::add(new_thread); @@ -862,7 +942,7 @@ JavaThread* CompileBroker::make_thread(jobject thread_handle, CompileQueue* queu // First release lock before aborting VM. if (new_thread == NULL || new_thread->osthread() == NULL) { - if (UseDynamicNumberOfCompilerThreads && comp != NULL && comp->num_compiler_threads() > 0) { + if (UseDynamicNumberOfCompilerThreads && type == compiler_t && comp->num_compiler_threads() > 0) { if (new_thread != NULL) { new_thread->smr_delete(); } @@ -917,7 +997,7 @@ void CompileBroker::init_compiler_sweeper_threads() { _compiler2_logs[i] = NULL; if (!UseDynamicNumberOfCompilerThreads || i == 0) { - JavaThread *ct = make_thread(thread_handle, _c2_compile_queue, _compilers[1], THREAD); + JavaThread *ct = make_thread(compiler_t, thread_handle, _c2_compile_queue, _compilers[1], THREAD); assert(ct != NULL, "should have been handled for initial thread"); _compilers[1]->set_num_compiler_threads(i + 1); if (TraceCompilerThreads) { @@ -937,7 +1017,7 @@ void CompileBroker::init_compiler_sweeper_threads() { _compiler1_logs[i] = NULL; if (!UseDynamicNumberOfCompilerThreads || i == 0) { - JavaThread *ct = make_thread(thread_handle, _c1_compile_queue, _compilers[0], THREAD); + JavaThread *ct = make_thread(compiler_t, thread_handle, _c1_compile_queue, _compilers[0], THREAD); assert(ct != NULL, "should have been handled for initial thread"); _compilers[0]->set_num_compiler_threads(i + 1); if (TraceCompilerThreads) { @@ -956,8 +1036,20 @@ void CompileBroker::init_compiler_sweeper_threads() { // Initialize the sweeper thread Handle thread_oop = create_thread_oop("Sweeper thread", CHECK); jobject thread_handle = JNIHandles::make_local(THREAD, thread_oop()); - make_thread(thread_handle, NULL, NULL, THREAD); + make_thread(sweeper_t, thread_handle, NULL, NULL, THREAD); + } + +#if defined(ASSERT) && COMPILER2_OR_JVMCI + if (DeoptimizeObjectsALot) { + // Initialize and start the object deoptimizer threads + const int total_count = DeoptimizeObjectsALotThreadCountSingle + DeoptimizeObjectsALotThreadCountAll; + for (int count = 0; count < total_count; count++) { + Handle thread_oop = create_thread_oop("Deoptimize objects a lot single mode", CHECK); + jobject thread_handle = JNIHandles::make_local(THREAD, thread_oop()); + make_thread(deoptimizer_t, thread_handle, NULL, NULL, THREAD); + } } +#endif // defined(ASSERT) && COMPILER2_OR_JVMCI } void CompileBroker::possibly_add_compiler_threads(Thread* THREAD) { @@ -1011,7 +1103,7 @@ void CompileBroker::possibly_add_compiler_threads(Thread* THREAD) { _compiler2_objects[i] = thread_handle; } #endif - JavaThread *ct = make_thread(compiler2_object(i), _c2_compile_queue, _compilers[1], THREAD); + JavaThread *ct = make_thread(compiler_t, compiler2_object(i), _c2_compile_queue, _compilers[1], THREAD); if (ct == NULL) break; _compilers[1]->set_num_compiler_threads(i + 1); if (TraceCompilerThreads) { @@ -1031,7 +1123,7 @@ void CompileBroker::possibly_add_compiler_threads(Thread* THREAD) { (int)(available_cc_p / (128*K))); for (int i = old_c1_count; i < new_c1_count; i++) { - JavaThread *ct = make_thread(compiler1_object(i), _c1_compile_queue, _compilers[0], THREAD); + JavaThread *ct = make_thread(compiler_t, compiler1_object(i), _c1_compile_queue, _compilers[0], THREAD); if (ct == NULL) break; _compilers[0]->set_num_compiler_threads(i + 1); if (TraceCompilerThreads) { diff --git a/src/hotspot/share/compiler/compileBroker.hpp b/src/hotspot/share/compiler/compileBroker.hpp index 2da97cf107e..367fcbc617c 100644 --- a/src/hotspot/share/compiler/compileBroker.hpp +++ b/src/hotspot/share/compiler/compileBroker.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -228,8 +228,14 @@ class CompileBroker: AllStatic { static volatile int _print_compilation_warning; + enum ThreadType { + compiler_t, + sweeper_t, + deoptimizer_t + }; + static Handle create_thread_oop(const char* name, TRAPS); - static JavaThread* make_thread(jobject thread_oop, CompileQueue* queue, AbstractCompiler* comp, Thread* THREAD); + static JavaThread* make_thread(ThreadType type, jobject thread_oop, CompileQueue* queue, AbstractCompiler* comp, Thread* THREAD); static void init_compiler_sweeper_threads(); static void possibly_add_compiler_threads(Thread* THREAD); static bool compilation_is_prohibited(const methodHandle& method, int osr_bci, int comp_level, bool excluded); diff --git a/src/hotspot/share/compiler/compilerDirectives.cpp b/src/hotspot/share/compiler/compilerDirectives.cpp index b940a7e8e36..753f9798a83 100644 --- a/src/hotspot/share/compiler/compilerDirectives.cpp +++ b/src/hotspot/share/compiler/compilerDirectives.cpp @@ -322,7 +322,6 @@ class DirectiveSetPtr { // - if some option is changed we need to copy directiveset since it no longer can be shared // - Need to free copy after use // - Requires a modified bit so we don't overwrite options that is set by directives - DirectiveSet* DirectiveSet::compilecommand_compatibility_init(const methodHandle& method) { // Early bail out - checking all options is expensive - we rely on them not being used // Only set a flag if it has not been modified and value changes. @@ -360,6 +359,7 @@ DirectiveSet* DirectiveSet::compilecommand_compatibility_init(const methodHandle } // inline and dontinline (including exclude) are implemented in the directiveset accessors + // ignore flags whose cc_flags are X #define init_default_cc(name, type, dvalue, cc_flag) { type v; if (!_modified[name##Index] && CompilerOracle::has_option_value(method, #cc_flag, v) && v != this->name##Option) { set.cloned()->name##Option = v; } } compilerdirectives_common_flags(init_default_cc) compilerdirectives_c2_flags(init_default_cc) diff --git a/src/hotspot/share/compiler/compilerDirectives.hpp b/src/hotspot/share/compiler/compilerDirectives.hpp index a04c13ea061..a67e534a9b2 100644 --- a/src/hotspot/share/compiler/compilerDirectives.hpp +++ b/src/hotspot/share/compiler/compilerDirectives.hpp @@ -36,8 +36,8 @@ #define compilerdirectives_common_flags(cflags) \ cflags(Enable, bool, false, X) \ cflags(Exclude, bool, false, X) \ - cflags(BreakAtExecute, bool, false, X) \ - cflags(BreakAtCompile, bool, false, X) \ + cflags(BreakAtExecute, bool, false, BreakAtExecute) \ + cflags(BreakAtCompile, bool, false, BreakAtCompile) \ cflags(Log, bool, LogCompilation, X) \ cflags(PrintAssembly, bool, PrintAssembly, PrintAssembly) \ cflags(PrintInlining, bool, PrintInlining, PrintInlining) \ diff --git a/src/hotspot/share/compiler/compiler_globals.hpp b/src/hotspot/share/compiler/compiler_globals.hpp index 51ac7179bb0..c28c0e86eb5 100644 --- a/src/hotspot/share/compiler/compiler_globals.hpp +++ b/src/hotspot/share/compiler/compiler_globals.hpp @@ -38,7 +38,6 @@ #if !defined(COMPILER1) && !defined(COMPILER2) && !INCLUDE_JVMCI define_pd_global(bool, BackgroundCompilation, false); -define_pd_global(bool, UseTLAB, false); define_pd_global(bool, CICompileOSR, false); define_pd_global(bool, UseTypeProfile, false); define_pd_global(bool, UseOnStackReplacement, false); @@ -51,7 +50,6 @@ define_pd_global(bool, TieredCompilation, false); define_pd_global(intx, CompileThreshold, 0); define_pd_global(intx, OnStackReplacePercentage, 0); -define_pd_global(bool, ResizeTLAB, false); define_pd_global(size_t, NewSizeThreadIncrease, 4*K); define_pd_global(bool, InlineClassNatives, true); define_pd_global(bool, InlineUnsafeOps, true); diff --git a/src/hotspot/share/compiler/disassembler.cpp b/src/hotspot/share/compiler/disassembler.cpp index 5a9f94276c9..61f0b23575f 100644 --- a/src/hotspot/share/compiler/disassembler.cpp +++ b/src/hotspot/share/compiler/disassembler.cpp @@ -48,12 +48,9 @@ bool Disassembler::_library_usable = false; // This routine is in the shared library: Disassembler::decode_func_virtual Disassembler::_decode_instructions_virtual = NULL; -Disassembler::decode_func Disassembler::_decode_instructions = NULL; static const char hsdis_library_name[] = "hsdis-" HOTSPOT_LIB_ARCH; static const char decode_instructions_virtual_name[] = "decode_instructions_virtual"; -static const char decode_instructions_name[] = "decode_instructions"; -static bool use_new_version = true; #define COMMENT_COLUMN 52 LP64_ONLY(+8) /*could be an option*/ #define BYTES_COMMENT ";..." /* funky byte display comment */ @@ -63,7 +60,7 @@ class decode_env { CodeBuffer* _codeBuffer; // != NULL only when decoding a CodeBuffer CodeBlob* _codeBlob; // != NULL only when decoding a CodeBlob nmethod* _nm; // != NULL only when decoding a nmethod - CodeStrings _strings; + address _start; // != NULL when decoding a range of unknown type address _end; // != NULL when decoding a range of unknown type @@ -77,6 +74,7 @@ class decode_env { bool _print_help; bool _helpPrinted; static bool _optionsParsed; + NOT_PRODUCT(const CodeStrings* _strings;) enum { tabspacing = 8 @@ -202,17 +200,23 @@ class decode_env { 15889, // prime number ResourceObj::C_HEAP> SourceFileInfoTable; - static SourceFileInfoTable _src_table; + static SourceFileInfoTable* _src_table; static const char* _cached_src; static GrowableArray* _cached_src_lines; + static SourceFileInfoTable& src_table() { + if (_src_table == NULL) { + _src_table = new (ResourceObj::C_HEAP, mtCode)SourceFileInfoTable(); + } + return *_src_table; + } + public: - decode_env(CodeBuffer* code, outputStream* output); - decode_env(CodeBlob* code, outputStream* output, CodeStrings c = CodeStrings() /* , ptrdiff_t offset */); - decode_env(nmethod* code, outputStream* output, CodeStrings c = CodeStrings()); + decode_env(CodeBlob* code, outputStream* output); + decode_env(nmethod* code, outputStream* output); // Constructor for a 'decode_env' to decode an arbitrary // piece of memory, hopefully containing code. - decode_env(address start, address end, outputStream* output); + decode_env(address start, address end, outputStream* output, const CodeStrings* strings = NULL); // Add 'original_start' argument which is the the original address // the instructions were located at (if this is not equal to 'start'). @@ -229,7 +233,7 @@ class decode_env { bool decode_env::_optionsParsed = false; -decode_env::SourceFileInfoTable decode_env::_src_table; +decode_env::SourceFileInfoTable* decode_env::_src_table = NULL; const char* decode_env::_cached_src = NULL; GrowableArray* decode_env::_cached_src_lines = NULL; @@ -238,17 +242,17 @@ void decode_env::hook(const char* file, int line, address pc) { // necessary as we add to the table only when PrintInterpreter is true, // which means we are debugging the VM and a little bit of extra // memory usage doesn't matter. - SourceFileInfo* found = _src_table.get(pc); + SourceFileInfo* found = src_table().get(pc); if (found != NULL) { found->append(file, line); } else { SourceFileInfo sfi(file, line); - _src_table.put(pc, sfi); // sfi is copied by value + src_table().put(pc, sfi); // sfi is copied by value } } void decode_env::print_hook_comments(address pc, bool newline) { - SourceFileInfo* found = _src_table.get(pc); + SourceFileInfo* found = src_table().get(pc); outputStream* st = output(); if (found != NULL) { for (SourceFileInfo::Link *link = found->head; link; link = link->next) { @@ -315,34 +319,10 @@ void decode_env::print_hook_comments(address pc, bool newline) { } } -decode_env::decode_env(CodeBuffer* code, outputStream* output) : - _output(output ? output : tty), - _codeBuffer(code), - _codeBlob(NULL), - _nm(NULL), - _strings(), - _start(NULL), - _end(NULL), - _option_buf(), - _print_raw(0), - _cur_insn(NULL), - _bytes_per_line(0), - _pre_decode_alignment(0), - _post_decode_alignment(0), - _print_file_name(false), - _print_help(false), - _helpPrinted(false) { - - memset(_option_buf, 0, sizeof(_option_buf)); - process_options(_output); -} - -decode_env::decode_env(CodeBlob* code, outputStream* output, CodeStrings c) : +decode_env::decode_env(CodeBlob* code, outputStream* output) : _output(output ? output : tty), - _codeBuffer(NULL), _codeBlob(code), _nm(_codeBlob != NULL && _codeBlob->is_nmethod() ? (nmethod*) code : NULL), - _strings(), _start(NULL), _end(NULL), _option_buf(), @@ -353,19 +333,18 @@ decode_env::decode_env(CodeBlob* code, outputStream* output, CodeStrings c) : _post_decode_alignment(0), _print_file_name(false), _print_help(false), - _helpPrinted(false) { + _helpPrinted(false) + NOT_PRODUCT(COMMA _strings(NULL)) { memset(_option_buf, 0, sizeof(_option_buf)); - _strings.copy(c); process_options(_output); + } -decode_env::decode_env(nmethod* code, outputStream* output, CodeStrings c) : +decode_env::decode_env(nmethod* code, outputStream* output) : _output(output ? output : tty), - _codeBuffer(NULL), _codeBlob(NULL), _nm(code), - _strings(), _start(_nm->code_begin()), _end(_nm->code_end()), _option_buf(), @@ -376,21 +355,19 @@ decode_env::decode_env(nmethod* code, outputStream* output, CodeStrings c) : _post_decode_alignment(0), _print_file_name(false), _print_help(false), - _helpPrinted(false) { + _helpPrinted(false) + NOT_PRODUCT(COMMA _strings(NULL)) { memset(_option_buf, 0, sizeof(_option_buf)); - _strings.copy(c); process_options(_output); } // Constructor for a 'decode_env' to decode a memory range [start, end) // of unknown origin, assuming it contains code. -decode_env::decode_env(address start, address end, outputStream* output) : +decode_env::decode_env(address start, address end, outputStream* output, const CodeStrings* c) : _output(output ? output : tty), - _codeBuffer(NULL), _codeBlob(NULL), _nm(NULL), - _strings(), _start(start), _end(end), _option_buf(), @@ -401,7 +378,8 @@ decode_env::decode_env(address start, address end, outputStream* output) : _post_decode_alignment(0), _print_file_name(false), _print_help(false), - _helpPrinted(false) { + _helpPrinted(false) + NOT_PRODUCT(COMMA _strings(c)) { assert(start < end, "Range must have a positive size, [" PTR_FORMAT ".." PTR_FORMAT ").", p2i(start), p2i(end)); memset(_option_buf, 0, sizeof(_option_buf)); @@ -675,10 +653,11 @@ void decode_env::print_insn_labels() { if (_codeBlob != NULL) { _codeBlob->print_block_comment(st, p); } - if (_codeBuffer != NULL) { - _codeBuffer->print_block_comment(st, p); +#ifndef PRODUCT + if (_strings != NULL) { + _strings->print_block_comment(st, (intptr_t)(p - _start)); } - _strings.print_block_comment(st, (intptr_t)(p - _start)); +#endif } } @@ -754,34 +733,22 @@ address decode_env::decode_instructions(address start, address end, address orig // This is mainly for debugging the library itself. FILE* out = stdout; FILE* xmlout = (_print_raw > 1 ? out : NULL); - return use_new_version ? + return (address) (*Disassembler::_decode_instructions_virtual)((uintptr_t)start, (uintptr_t)end, start, end - start, NULL, (void*) xmlout, NULL, (void*) out, - options(), 0/*nice new line*/) - : - (address) - (*Disassembler::_decode_instructions)(start, end, - NULL, (void*) xmlout, - NULL, (void*) out, - options()); + options(), 0/*nice new line*/); } - return use_new_version ? + return (address) (*Disassembler::_decode_instructions_virtual)((uintptr_t)start, (uintptr_t)end, start, end - start, &event_to_env, (void*) this, &printf_to_env, (void*) this, - options(), 0/*nice new line*/) - : - (address) - (*Disassembler::_decode_instructions)(start, end, - &event_to_env, (void*) this, - &printf_to_env, (void*) this, - options()); + options(), 0/*nice new line*/); } // ---------------------------------------------------------------------------- @@ -871,21 +838,13 @@ bool Disassembler::load_library(outputStream* st) { _library = os::dll_load(buf, ebuf, sizeof ebuf); } - // load the decoder function to use (new or old version). + // load the decoder function to use. if (_library != NULL) { _decode_instructions_virtual = CAST_TO_FN_PTR(Disassembler::decode_func_virtual, os::dll_lookup(_library, decode_instructions_virtual_name)); } - if (_decode_instructions_virtual == NULL && _library != NULL) { - // could not spot in new version, try old version - _decode_instructions = CAST_TO_FN_PTR(Disassembler::decode_func, - os::dll_lookup(_library, decode_instructions_name)); - use_new_version = false; - } else { - use_new_version = true; - } _tried_to_load_library = true; - _library_usable = _decode_instructions_virtual != NULL || _decode_instructions != NULL; + _library_usable = _decode_instructions_virtual != NULL; // Create a dummy environment to initialize PrintAssemblyOptions. // The PrintAssemblyOptions must be known for abstract disassemblies as well. @@ -912,49 +871,13 @@ bool Disassembler::load_library(outputStream* st) { } -// Directly disassemble code buffer. -void Disassembler::decode(CodeBuffer* cb, address start, address end, outputStream* st) { -#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) - //---< Test memory before decoding >--- - if (!(cb->contains(start) && cb->contains(end))) { - //---< Allow output suppression, but prevent writing to a NULL stream. Could happen with +PrintStubCode. >--- - if (st != NULL) { - st->print("Memory range [" PTR_FORMAT ".." PTR_FORMAT "] not contained in CodeBuffer", p2i(start), p2i(end)); - } - return; - } - if (!os::is_readable_range(start, end)) { - //---< Allow output suppression, but prevent writing to a NULL stream. Could happen with +PrintStubCode. >--- - if (st != NULL) { - st->print("Memory range [" PTR_FORMAT ".." PTR_FORMAT "] not readable", p2i(start), p2i(end)); - } - return; - } - - decode_env env(cb, st); - env.output()->print_cr("--------------------------------------------------------------------------------"); - env.output()->print("Decoding CodeBuffer (" PTR_FORMAT ")", p2i(cb)); - if (cb->name() != NULL) { - env.output()->print(", name: %s,", cb->name()); - } - env.output()->print_cr(" at [" PTR_FORMAT ", " PTR_FORMAT "] " JLONG_FORMAT " bytes", p2i(start), p2i(end), ((jlong)(end - start))); - - if (is_abstract()) { - AbstractDisassembler::decode_abstract(start, end, env.output(), Assembler::instr_maxlen()); - } else { - env.decode_instructions(start, end); - } - env.output()->print_cr("--------------------------------------------------------------------------------"); -#endif -} - // Directly disassemble code blob. -void Disassembler::decode(CodeBlob* cb, outputStream* st, CodeStrings c) { +void Disassembler::decode(CodeBlob* cb, outputStream* st) { #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) if (cb->is_nmethod()) { // If we have an nmethod at hand, // call the specialized decoder directly. - decode((nmethod*)cb, st, c); + decode((nmethod*)cb, st); return; } @@ -992,7 +915,7 @@ void Disassembler::decode(CodeBlob* cb, outputStream* st, CodeStrings c) { // Decode a nmethod. // This includes printing the constant pool and all code segments. // The nmethod data structures (oop maps, relocations and the like) are not printed. -void Disassembler::decode(nmethod* nm, outputStream* st, CodeStrings c) { +void Disassembler::decode(nmethod* nm, outputStream* st) { #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) ttyLocker ttyl; @@ -1011,7 +934,7 @@ void Disassembler::decode(nmethod* nm, outputStream* st, CodeStrings c) { } // Decode a range, given as [start address, end address) -void Disassembler::decode(address start, address end, outputStream* st, CodeStrings c /*, ptrdiff_t offset */) { +void Disassembler::decode(address start, address end, outputStream* st, const CodeStrings* c) { #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) //---< Test memory before decoding >--- if (!os::is_readable_range(start, end)) { @@ -1039,7 +962,7 @@ void Disassembler::decode(address start, address end, outputStream* st, CodeStri #endif { // This seems to be just a chunk of memory. - decode_env env(start, end, st); + decode_env env(start, end, st, c); env.output()->print_cr("--------------------------------------------------------------------------------"); env.decode_instructions(start, end); env.output()->print_cr("--------------------------------------------------------------------------------"); diff --git a/src/hotspot/share/compiler/disassembler.hpp b/src/hotspot/share/compiler/disassembler.hpp index 523b423d9c2..93228a565b4 100644 --- a/src/hotspot/share/compiler/disassembler.hpp +++ b/src/hotspot/share/compiler/disassembler.hpp @@ -28,7 +28,8 @@ #include "utilities/globalDefinitions.hpp" #include "asm/assembler.hpp" -#include "asm/codeBuffer.hpp" +#include "code/codeBlob.hpp" +#include "code/nmethod.hpp" #include "compiler/abstractDisassembler.hpp" #include "runtime/globals.hpp" #include "utilities/macros.hpp" @@ -51,13 +52,6 @@ class Disassembler : public AbstractDisassembler { void* printf_stream, const char* options, int newline); - // this is the type of the dll entry point for old version: - typedef void* (*decode_func)(void* start_va, void* end_va, - void* (*event_callback)(void*, const char*, void*), - void* event_stream, - int (*printf_callback)(void*, const char*, ...), - void* printf_stream, - const char* options); // points to the library. static void* _library; // bailout @@ -65,7 +59,6 @@ class Disassembler : public AbstractDisassembler { static bool _library_usable; // points to the decode function. static decode_func_virtual _decode_instructions_virtual; - static decode_func _decode_instructions; // tries to load library and return whether it succeeded. // Allow (diagnostic) output redirection. @@ -88,10 +81,10 @@ class Disassembler : public AbstractDisassembler { // about which decoding format is used. // We can also enforce using the abstract disassembler. static bool is_abstract() { - if (!_tried_to_load_library /* && !UseAbstractDisassembler */) { + if (!_tried_to_load_library) { load_library(); } - return ! _library_usable /* || UseAbstractDisassembler */; // Not available until DecodeErrorFile is supported. + return ! _library_usable; } // Check out if we are doing a live disassembly or a post-mortem @@ -105,14 +98,12 @@ class Disassembler : public AbstractDisassembler { #endif } - // Directly disassemble code buffer. - static void decode(CodeBuffer* cb, address start, address end, outputStream* st = NULL); // Directly disassemble code blob. - static void decode(CodeBlob *cb, outputStream* st = NULL, CodeStrings c = CodeStrings()); + static void decode(CodeBlob *cb, outputStream* st = NULL); // Directly disassemble nmethod. - static void decode(nmethod* nm, outputStream* st = NULL, CodeStrings c = CodeStrings()); + static void decode(nmethod* nm, outputStream* st = NULL); // Disassemble an arbitrary memory range. - static void decode(address start, address end, outputStream* st = NULL, CodeStrings c = CodeStrings() /* , ptrdiff_t offset */); + static void decode(address start, address end, outputStream* st = NULL, const CodeStrings* = NULL); static void _hook(const char* file, int line, class MacroAssembler* masm); diff --git a/src/hotspot/share/compiler/oopMap.cpp b/src/hotspot/share/compiler/oopMap.cpp index 48c4c73fe35..35e678025ce 100644 --- a/src/hotspot/share/compiler/oopMap.cpp +++ b/src/hotspot/share/compiler/oopMap.cpp @@ -37,6 +37,7 @@ #include "runtime/frame.inline.hpp" #include "runtime/handles.inline.hpp" #include "runtime/signature.hpp" +#include "runtime/stackWatermarkSet.inline.hpp" #include "utilities/align.hpp" #include "utilities/lockFreeStack.hpp" #ifdef COMPILER1 @@ -189,15 +190,25 @@ void OopMapSet::add_gc_map(int pc_offset, OopMap *map ) { add(map); } -static void add_derived_oop(oop* base, oop* derived) { -#if !defined(TIERED) && !INCLUDE_JVMCI - COMPILER1_PRESENT(ShouldNotReachHere();) -#endif // !defined(TIERED) && !INCLUDE_JVMCI +static void add_derived_oop(oop* base, oop* derived, OopClosure* oop_fn) { #if COMPILER2_OR_JVMCI DerivedPointerTable::add(derived, base); #endif // COMPILER2_OR_JVMCI } +static void ignore_derived_oop(oop* base, oop* derived, OopClosure* oop_fn) { +} + +static void process_derived_oop(oop* base, oop* derived, OopClosure* oop_fn) { + // All derived pointers must be processed before the base pointer of any derived pointer is processed. + // Otherwise, if two derived pointers use the same base, the second derived pointer will get an obscured + // offset, if the base pointer is processed in the first derived pointer. + uintptr_t offset = cast_from_oop(*derived) - cast_from_oop(*base); + *derived = *base; + oop_fn->do_oop(derived); + *derived = cast_to_oop(cast_from_oop(*derived) + offset); +} + #ifndef PRODUCT static void trace_codeblob_maps(const frame *fr, const RegisterMap *reg_map) { @@ -227,14 +238,23 @@ static void trace_codeblob_maps(const frame *fr, const RegisterMap *reg_map) { } #endif // PRODUCT -void OopMapSet::oops_do(const frame *fr, const RegisterMap* reg_map, OopClosure* f) { - // add derived oops to a table - all_do(fr, reg_map, f, add_derived_oop, &do_nothing_cl); +void OopMapSet::oops_do(const frame *fr, const RegisterMap* reg_map, OopClosure* f, DerivedPointerIterationMode mode) { + switch (mode) { + case DerivedPointerIterationMode::_directly: + all_do(fr, reg_map, f, process_derived_oop, &do_nothing_cl); + break; + case DerivedPointerIterationMode::_with_table: + all_do(fr, reg_map, f, add_derived_oop, &do_nothing_cl); + break; + case DerivedPointerIterationMode::_ignore: + all_do(fr, reg_map, f, ignore_derived_oop, &do_nothing_cl); + break; + } } void OopMapSet::all_do(const frame *fr, const RegisterMap *reg_map, - OopClosure* oop_fn, void derived_oop_fn(oop*, oop*), + OopClosure* oop_fn, void derived_oop_fn(oop*, oop*, OopClosure*), OopClosure* value_fn) { CodeBlob* cb = fr->cb(); assert(cb != NULL, "no codeblob"); @@ -271,7 +291,7 @@ void OopMapSet::all_do(const frame *fr, const RegisterMap *reg_map, // The narrow_oop_base could be NULL or be the address // of the page below heap depending on compressed oops mode. if (base_loc != NULL && *base_loc != NULL && !CompressedOops::is_base(*base_loc)) { - derived_oop_fn(base_loc, derived_loc); + derived_oop_fn(base_loc, derived_loc, oop_fn); } } } @@ -296,20 +316,6 @@ void OopMapSet::all_do(const frame *fr, const RegisterMap *reg_map, // of the page below heap depending on compressed oops mode. continue; } -#ifdef ASSERT - if ((((uintptr_t)loc & (sizeof(*loc)-1)) != 0) || - !Universe::heap()->is_in_or_null(*loc)) { - tty->print_cr("# Found non oop pointer. Dumping state at failure"); - // try to dump out some helpful debugging information - trace_codeblob_maps(fr, reg_map); - omv.print(); - tty->print_cr("register r"); - omv.reg()->print(); - tty->print_cr("loc = %p *loc = %p\n", loc, cast_from_oop
    (*loc)); - // do the real assert. - assert(Universe::heap()->is_in_or_null(*loc), "found non oop pointer"); - } -#endif // ASSERT oop_fn->do_oop(loc); } else if ( omv.type() == OopMapValue::narrowoop_value ) { narrowOop *nl = (narrowOop*)loc; @@ -694,27 +700,26 @@ inline intptr_t value_of_loc(oop *pointer) { void DerivedPointerTable::add(oop *derived_loc, oop *base_loc) { assert(Universe::heap()->is_in_or_null(*base_loc), "not an oop"); assert(derived_loc != base_loc, "Base and derived in same location"); - if (_active) { - assert(*derived_loc != (void*)base_loc, "location already added"); - assert(Entry::_list != NULL, "list must exist"); - intptr_t offset = value_of_loc(derived_loc) - value_of_loc(base_loc); - // This assert is invalid because derived pointers can be - // arbitrarily far away from their base. - // assert(offset >= -1000000, "wrong derived pointer info"); - - if (TraceDerivedPointers) { - tty->print_cr( - "Add derived pointer@" INTPTR_FORMAT - " - Derived: " INTPTR_FORMAT - " Base: " INTPTR_FORMAT " (@" INTPTR_FORMAT ") (Offset: " INTX_FORMAT ")", - p2i(derived_loc), p2i(*derived_loc), p2i(*base_loc), p2i(base_loc), offset - ); - } - // Set derived oop location to point to base. - *derived_loc = (oop)base_loc; - Entry* entry = new Entry(derived_loc, offset); - Entry::_list->push(*entry); + assert(*derived_loc != (void*)base_loc, "location already added"); + assert(Entry::_list != NULL, "list must exist"); + assert(is_active(), "table must be active here"); + intptr_t offset = value_of_loc(derived_loc) - value_of_loc(base_loc); + // This assert is invalid because derived pointers can be + // arbitrarily far away from their base. + // assert(offset >= -1000000, "wrong derived pointer info"); + + if (TraceDerivedPointers) { + tty->print_cr( + "Add derived pointer@" INTPTR_FORMAT + " - Derived: " INTPTR_FORMAT + " Base: " INTPTR_FORMAT " (@" INTPTR_FORMAT ") (Offset: " INTX_FORMAT ")", + p2i(derived_loc), p2i(*derived_loc), p2i(*base_loc), p2i(base_loc), offset + ); } + // Set derived oop location to point to base. + *derived_loc = (oop)base_loc; + Entry* entry = new Entry(derived_loc, offset); + Entry::_list->push(*entry); } void DerivedPointerTable::update_pointers() { diff --git a/src/hotspot/share/compiler/oopMap.hpp b/src/hotspot/share/compiler/oopMap.hpp index cbf5ad7ed87..04a7ba92ecb 100644 --- a/src/hotspot/share/compiler/oopMap.hpp +++ b/src/hotspot/share/compiler/oopMap.hpp @@ -40,6 +40,7 @@ // // OopMapValue describes a single OopMap entry +enum class DerivedPointerIterationMode; class frame; class RegisterMap; class OopClosure; @@ -196,7 +197,6 @@ class OopMap: public ResourceObj { bool equals(const OopMap* other) const; }; - class OopMapSet : public ResourceObj { friend class VMStructs; private: @@ -221,13 +221,15 @@ class OopMapSet : public ResourceObj { // Iterates through frame for a compiled method static void oops_do (const frame* fr, - const RegisterMap* reg_map, OopClosure* f); + const RegisterMap* reg_map, + OopClosure* f, + DerivedPointerIterationMode mode); static void update_register_map(const frame* fr, RegisterMap *reg_map); // Iterates through frame for a compiled method for dead ones and values, too static void all_do(const frame* fr, const RegisterMap* reg_map, OopClosure* oop_fn, - void derived_oop_fn(oop* base, oop* derived), + void derived_oop_fn(oop* base, oop* derived, OopClosure* oop_fn), OopClosure* value_fn); // Printing diff --git a/src/hotspot/share/compiler/tieredThresholdPolicy.cpp b/src/hotspot/share/compiler/tieredThresholdPolicy.cpp index e6836879fc1..3af2dbb1d4f 100644 --- a/src/hotspot/share/compiler/tieredThresholdPolicy.cpp +++ b/src/hotspot/share/compiler/tieredThresholdPolicy.cpp @@ -294,7 +294,7 @@ void TieredThresholdPolicy::initialize() { // Some inlining tuning #ifdef X86 if (FLAG_IS_DEFAULT(InlineSmallCode)) { - FLAG_SET_DEFAULT(InlineSmallCode, 2000); + FLAG_SET_DEFAULT(InlineSmallCode, 2500); } #endif diff --git a/src/hotspot/share/gc/g1/g1Allocator.cpp b/src/hotspot/share/gc/g1/g1Allocator.cpp index cb172b5f5b5..1bf55c51579 100644 --- a/src/hotspot/share/gc/g1/g1Allocator.cpp +++ b/src/hotspot/share/gc/g1/g1Allocator.cpp @@ -281,22 +281,9 @@ HeapWord* G1Allocator::old_attempt_allocation(size_t min_word_size, return result; } -uint G1PLABAllocator::calc_survivor_alignment_bytes() { - assert(SurvivorAlignmentInBytes >= ObjectAlignmentInBytes, "sanity"); - if (SurvivorAlignmentInBytes == ObjectAlignmentInBytes) { - // No need to align objects in the survivors differently, return 0 - // which means "survivor alignment is not used". - return 0; - } else { - assert(SurvivorAlignmentInBytes > 0, "sanity"); - return SurvivorAlignmentInBytes; - } -} - G1PLABAllocator::G1PLABAllocator(G1Allocator* allocator) : _g1h(G1CollectedHeap::heap()), - _allocator(allocator), - _survivor_alignment_bytes(calc_survivor_alignment_bytes()) { + _allocator(allocator) { for (region_type_t state = 0; state < G1HeapRegionAttr::Num; state++) { _direct_allocated[state] = 0; uint length = alloc_buffers_length(state); @@ -411,15 +398,8 @@ size_t G1PLABAllocator::undo_waste() const { return result; } -bool G1ArchiveAllocator::_archive_check_enabled = false; -G1ArchiveRegionMap G1ArchiveAllocator::_archive_region_map; - G1ArchiveAllocator* G1ArchiveAllocator::create_allocator(G1CollectedHeap* g1h, bool open) { - // Create the archive allocator, and also enable archive object checking - // in mark-sweep, since we will be creating archive regions. - G1ArchiveAllocator* result = new G1ArchiveAllocator(g1h, open); - enable_archive_object_check(); - return result; + return new G1ArchiveAllocator(g1h, open); } bool G1ArchiveAllocator::alloc_new_region() { @@ -447,9 +427,6 @@ bool G1ArchiveAllocator::alloc_new_region() { _bottom = hr->bottom(); _max = _bottom + HeapRegion::min_region_size_in_words(); - // Tell mark-sweep that objects in this region are not to be marked. - set_range_archive(MemRegion(_bottom, HeapRegion::GrainWords), _open); - // Since we've modified the old set, call update_sizes. _g1h->g1mm()->update_sizes(); return true; diff --git a/src/hotspot/share/gc/g1/g1Allocator.hpp b/src/hotspot/share/gc/g1/g1Allocator.hpp index f0702b4d6c2..fdd3f90d4a2 100644 --- a/src/hotspot/share/gc/g1/g1Allocator.hpp +++ b/src/hotspot/share/gc/g1/g1Allocator.hpp @@ -149,13 +149,6 @@ class G1PLABAllocator : public CHeapObj { PLAB** _alloc_buffers[G1HeapRegionAttr::Num]; - // The survivor alignment in effect in bytes. - // == 0 : don't align survivors - // != 0 : align survivors to that alignment - // These values were chosen to favor the non-alignment case since some - // architectures have a special compare against zero instructions. - const uint _survivor_alignment_bytes; - // Number of words allocated directly (not counting PLAB allocation). size_t _direct_allocated[G1HeapRegionAttr::Num]; @@ -168,10 +161,6 @@ class G1PLABAllocator : public CHeapObj { // active NUMA nodes. inline uint alloc_buffers_length(region_type_t dest) const; - // Calculate the survivor space object alignment in bytes. Returns that or 0 if - // there are no restrictions on survivor alignment. - static uint calc_survivor_alignment_bytes(); - bool may_throw_away_buffer(size_t const allocation_word_sz, size_t const buffer_size) const; public: G1PLABAllocator(G1Allocator* allocator); @@ -203,19 +192,6 @@ class G1PLABAllocator : public CHeapObj { void undo_allocation(G1HeapRegionAttr dest, HeapWord* obj, size_t word_sz, uint node_index); }; -// G1ArchiveRegionMap is an array used to mark G1 regions as -// archive regions. This allows a quick check for whether an object -// should not be marked because it is in an archive region. -class G1ArchiveRegionMap : public G1BiasedMappedArray { -public: - static const uint8_t NoArchive = 0; - static const uint8_t OpenArchive = 1; - static const uint8_t ClosedArchive = 2; - -protected: - uint8_t default_value() const { return NoArchive; } -}; - // G1ArchiveAllocator is used to allocate memory in archive // regions. Such regions are not scavenged nor compacted by GC. // There are two types of archive regions, which are @@ -289,33 +265,6 @@ class G1ArchiveAllocator : public CHeapObj { void clear_used() { _summary_bytes_used = 0; } - - // Create the _archive_region_map which is used to identify archive objects. - static inline void enable_archive_object_check(); - - // Mark regions containing the specified address range as archive/non-archive. - static inline void set_range_archive(MemRegion range, bool open); - static inline void clear_range_archive(MemRegion range); - - // Check if the object is in closed archive - static inline bool is_closed_archive_object(oop object); - // Check if the object is in open archive - static inline bool is_open_archive_object(oop object); - // Check if the object is either in closed archive or open archive - static inline bool is_archived_object(oop object); - -private: - static bool _archive_check_enabled; - static G1ArchiveRegionMap _archive_region_map; - - // Check if an object is in a closed archive region using the _closed_archive_region_map. - static inline bool in_closed_archive_range(oop object); - // Check if an object is in open archive region using the _open_archive_region_map. - static inline bool in_open_archive_range(oop object); - - // Check if archive object checking is enabled, to avoid calling in_open/closed_archive_range - // unnecessarily. - static inline bool archive_check_enabled(); }; #endif // SHARE_GC_G1_G1ALLOCATOR_HPP diff --git a/src/hotspot/share/gc/g1/g1Allocator.inline.hpp b/src/hotspot/share/gc/g1/g1Allocator.inline.hpp index 5b6ab355c4d..73a07828378 100644 --- a/src/hotspot/share/gc/g1/g1Allocator.inline.hpp +++ b/src/hotspot/share/gc/g1/g1Allocator.inline.hpp @@ -105,11 +105,7 @@ inline HeapWord* G1PLABAllocator::plab_allocate(G1HeapRegionAttr dest, size_t word_sz, uint node_index) { PLAB* buffer = alloc_buffer(dest, node_index); - if (_survivor_alignment_bytes == 0 || !dest.is_young()) { - return buffer->allocate(word_sz); - } else { - return buffer->allocate_aligned(word_sz, _survivor_alignment_bytes); - } + return buffer->allocate(word_sz); } inline HeapWord* G1PLABAllocator::allocate(G1HeapRegionAttr dest, @@ -123,63 +119,4 @@ inline HeapWord* G1PLABAllocator::allocate(G1HeapRegionAttr dest, return allocate_direct_or_new_plab(dest, word_sz, refill_failed, node_index); } -// Create the maps which is used to identify archive objects. -inline void G1ArchiveAllocator::enable_archive_object_check() { - if (_archive_check_enabled) { - return; - } - - _archive_check_enabled = true; - _archive_region_map.initialize(G1CollectedHeap::heap()->reserved(), - HeapRegion::GrainBytes); -} - -// Set the regions containing the specified address range as archive. -inline void G1ArchiveAllocator::set_range_archive(MemRegion range, bool open) { - assert(_archive_check_enabled, "archive range check not enabled"); - log_info(gc, cds)("Mark %s archive regions in map: [" PTR_FORMAT ", " PTR_FORMAT "]", - open ? "open" : "closed", - p2i(range.start()), - p2i(range.last())); - uint8_t const value = open ? G1ArchiveRegionMap::OpenArchive : G1ArchiveRegionMap::ClosedArchive; - _archive_region_map.set_by_address(range, value); -} - -// Clear the archive regions map containing the specified address range. -inline void G1ArchiveAllocator::clear_range_archive(MemRegion range) { - assert(_archive_check_enabled, "archive range check not enabled"); - log_info(gc, cds)("Clear archive regions in map: [" PTR_FORMAT ", " PTR_FORMAT "]", - p2i(range.start()), - p2i(range.last())); - _archive_region_map.set_by_address(range, G1ArchiveRegionMap::NoArchive); -} - -// Check if an object is in a closed archive region using the _archive_region_map. -inline bool G1ArchiveAllocator::in_closed_archive_range(oop object) { - return _archive_region_map.get_by_address(cast_from_oop(object)) == G1ArchiveRegionMap::ClosedArchive; -} - -inline bool G1ArchiveAllocator::in_open_archive_range(oop object) { - return _archive_region_map.get_by_address(cast_from_oop(object)) == G1ArchiveRegionMap::OpenArchive; -} - -// Check if archive object checking is enabled, to avoid calling in_open/closed_archive_range -// unnecessarily. -inline bool G1ArchiveAllocator::archive_check_enabled() { - return _archive_check_enabled; -} - -inline bool G1ArchiveAllocator::is_closed_archive_object(oop object) { - return (archive_check_enabled() && in_closed_archive_range(object)); -} - -inline bool G1ArchiveAllocator::is_open_archive_object(oop object) { - return (archive_check_enabled() && in_open_archive_range(object)); -} - -inline bool G1ArchiveAllocator::is_archived_object(oop object) { - return archive_check_enabled() && - (_archive_region_map.get_by_address(cast_from_oop(object)) != G1ArchiveRegionMap::NoArchive); -} - #endif // SHARE_GC_G1_G1ALLOCATOR_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/g1BiasedArray.cpp b/src/hotspot/share/gc/g1/g1BiasedArray.cpp index 0d9a3caf4eb..ce0196ee5bb 100644 --- a/src/hotspot/share/gc/g1/g1BiasedArray.cpp +++ b/src/hotspot/share/gc/g1/g1BiasedArray.cpp @@ -26,11 +26,23 @@ #include "gc/g1/g1BiasedArray.hpp" #include "memory/padded.inline.hpp" +G1BiasedMappedArrayBase::G1BiasedMappedArrayBase() : + _alloc_base(NULL), + _base(NULL), + _length(0), + _biased_base(NULL), + _bias(0), + _shift_by(0) { } + +G1BiasedMappedArrayBase::~G1BiasedMappedArrayBase() { + FreeHeap(_alloc_base); +} + // Allocate a new array, generic version. address G1BiasedMappedArrayBase::create_new_base_array(size_t length, size_t elem_size) { assert(length > 0, "just checking"); assert(elem_size > 0, "just checking"); - return PaddedPrimitiveArray::create_unfreeable(length * elem_size); + return PaddedPrimitiveArray::create(length * elem_size, &_alloc_base); } #ifndef PRODUCT diff --git a/src/hotspot/share/gc/g1/g1BiasedArray.hpp b/src/hotspot/share/gc/g1/g1BiasedArray.hpp index 248abbabf1e..7c604706566 100644 --- a/src/hotspot/share/gc/g1/g1BiasedArray.hpp +++ b/src/hotspot/share/gc/g1/g1BiasedArray.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_GC_G1_G1BIASEDARRAY_HPP #define SHARE_GC_G1_G1BIASEDARRAY_HPP +#include "memory/allocation.hpp" #include "memory/memRegion.hpp" #include "utilities/debug.hpp" #include "utilities/powerOfTwo.hpp" @@ -32,10 +33,14 @@ // Implements the common base functionality for arrays that contain provisions // for accessing its elements using a biased index. // The element type is defined by the instantiating the template. -class G1BiasedMappedArrayBase { +class G1BiasedMappedArrayBase : public CHeapObj { friend class VMStructs; + + void* _alloc_base; // the address the unpadded array has been allocated to + public: typedef size_t idx_t; + protected: address _base; // the real base address size_t _length; // the length of the array @@ -44,12 +49,10 @@ class G1BiasedMappedArrayBase { uint _shift_by; // the amount of bits to shift right when mapping to an index of the array. protected: - - G1BiasedMappedArrayBase() : _base(NULL), _length(0), _biased_base(NULL), - _bias(0), _shift_by(0) { } + G1BiasedMappedArrayBase(); // Allocate a new array, generic version. - static address create_new_base_array(size_t length, size_t elem_size); + address create_new_base_array(size_t length, size_t elem_size); // Initialize the members of this class. The biased start address of this array // is the bias (in elements) multiplied by the element size. @@ -90,8 +93,10 @@ class G1BiasedMappedArrayBase { void verify_biased_index_inclusive_end(idx_t biased_index) const PRODUCT_RETURN; public: - // Return the length of the array in elements. - size_t length() const { return _length; } + virtual ~G1BiasedMappedArrayBase(); + + // Return the length of the array in elements. + size_t length() const { return _length; } }; // Array that provides biased access and mapping from (valid) addresses in the diff --git a/src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp b/src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp index 9f489c0b8bc..277c9e38228 100644 --- a/src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp +++ b/src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp @@ -60,7 +60,7 @@ void G1BlockOffsetTable::check_index(size_t index, const char* msg) const { assert((index) < (_reserved.word_size() >> BOTConstants::LogN_words), "%s - index: " SIZE_FORMAT ", _vs.committed_size: " SIZE_FORMAT, msg, (index), (_reserved.word_size() >> BOTConstants::LogN_words)); - assert(G1CollectedHeap::heap()->is_in_exact(address_for_index_raw(index)), + assert(G1CollectedHeap::heap()->is_in(address_for_index_raw(index)), "Index " SIZE_FORMAT " corresponding to " PTR_FORMAT " (%u) is not in committed area.", (index), diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index d55d10a25eb..70b534b6ff1 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -570,10 +570,6 @@ bool G1CollectedHeap::alloc_archive_regions(MemRegion* ranges, // when mmap'ing archived heap data in, so pre-touching is wasted. FlagSetting fs(AlwaysPreTouch, false); - // Enable archive object checking used by G1MarkSweep. We have to let it know - // about each archive range, so that objects in those ranges aren't marked. - G1ArchiveAllocator::enable_archive_object_check(); - // For each specified MemRegion range, allocate the corresponding G1 // regions and mark them as archive regions. We expect the ranges // in ascending starting address order, without overlap. @@ -649,9 +645,6 @@ bool G1CollectedHeap::alloc_archive_regions(MemRegion* ranges, curr_region->set_top(top); curr_region = next_region; } - - // Notify mark-sweep of the archive - G1ArchiveAllocator::set_range_archive(curr_range, open); } return true; } @@ -802,9 +795,6 @@ void G1CollectedHeap::dealloc_archive_regions(MemRegion* ranges, size_t count) { _hrm->shrink_at(curr_index, 1); uncommitted_regions++; } - - // Notify mark-sweep that this is no longer an archive range. - G1ArchiveAllocator::clear_range_archive(ranges[i]); } if (uncommitted_regions != 0) { @@ -815,8 +805,7 @@ void G1CollectedHeap::dealloc_archive_regions(MemRegion* ranges, size_t count) { } oop G1CollectedHeap::materialize_archived_object(oop obj) { - assert(obj != NULL, "archived obj is NULL"); - assert(G1ArchiveAllocator::is_archived_object(obj), "must be archived object"); + assert(is_archived_object(obj), "not an archived obj"); // Loading an archived object makes it strongly reachable. If it is // loaded during concurrent marking, it must be enqueued to the SATB @@ -1016,8 +1005,7 @@ void G1CollectedHeap::prepare_heap_for_full_collection() { // after this full GC. abandon_collection_set(collection_set()); - tear_down_region_sets(false /* free_list_only */); - + hrm()->remove_all_free_regions(); hrm()->prepare_for_full_collection_start(); } @@ -1034,7 +1022,7 @@ void G1CollectedHeap::prepare_heap_for_mutators() { // Delete metaspaces for unloaded class loaders and clean up loader_data graph ClassLoaderDataGraph::purge(/*at_safepoint*/true); - MetaspaceUtils::verify_metrics(); + DEBUG_ONLY(MetaspaceUtils::verify();) // Prepare heap for normal collections. assert(num_free_regions() == 0, "we should not have added any free regions"); @@ -1073,17 +1061,7 @@ void G1CollectedHeap::verify_after_full_collection() { _hrm->verify_optional(); _verifier->verify_region_sets_optional(); _verifier->verify_after_gc(G1HeapVerifier::G1VerifyFull); - // Clear the previous marking bitmap, if needed for bitmap verification. - // Note we cannot do this when we clear the next marking bitmap in - // G1ConcurrentMark::abort() above since VerifyDuringGC verifies the - // objects marked during a full GC against the previous bitmap. - // But we need to clear it before calling check_bitmaps below since - // the full GC has compacted objects and updated TAMS but not updated - // the prev bitmap. - if (G1VerifyBitmaps) { - GCTraceTime(Debug, gc) tm("Clear Prev Bitmap for Verification"); - _cm->clear_prev_bitmap(workers()); - } + // This call implicitly verifies that the next bitmap is clear after Full GC. _verifier->check_bitmaps("Full GC End"); @@ -1347,7 +1325,7 @@ void G1CollectedHeap::shrink(size_t shrink_bytes) { // Instead of tearing down / rebuilding the free lists here, we // could instead use the remove_all_pending() method on free_list to // remove only the ones that we need to remove. - tear_down_region_sets(true /* free_list_only */); + hrm()->remove_all_free_regions(); shrink_helper(shrink_bytes); rebuild_region_sets(true /* free_list_only */); @@ -2260,29 +2238,9 @@ bool G1CollectedHeap::try_collect(GCCause::Cause cause) { } bool G1CollectedHeap::is_in(const void* p) const { - if (_hrm->reserved().contains(p)) { - // Given that we know that p is in the reserved space, - // heap_region_containing() should successfully - // return the containing region. - HeapRegion* hr = heap_region_containing(p); - return hr->is_in(p); - } else { - return false; - } + return is_in_reserved(p) && _hrm->is_available(addr_to_region((HeapWord*)p)); } -#ifdef ASSERT -bool G1CollectedHeap::is_in_exact(const void* p) const { - bool contains = reserved().contains(p); - bool available = _hrm->is_available(addr_to_region((HeapWord*)p)); - if (contains && available) { - return true; - } else { - return false; - } -} -#endif - // Iteration functions. // Iterates an ObjectClosure over all objects within a HeapRegion. @@ -2415,6 +2373,10 @@ bool G1CollectedHeap::is_heterogeneous_heap() const { return G1Arguments::is_heterogeneous_heap(); } +bool G1CollectedHeap::is_archived_object(oop object) const { + return object != NULL && heap_region_containing(object)->is_archive(); +} + class PrintRegionClosure: public HeapRegionClosure { outputStream* _st; public: @@ -2457,10 +2419,11 @@ void G1CollectedHeap::print_heap_regions() const { } void G1CollectedHeap::print_on(outputStream* st) const { + size_t heap_used = Heap_lock->owned_by_self() ? used() : used_unlocked(); st->print(" %-20s", "garbage-first heap"); if (_hrm != NULL) { st->print(" total " SIZE_FORMAT "K, used " SIZE_FORMAT "K", - capacity()/K, used_unlocked()/K); + capacity()/K, heap_used/K); st->print(" [" PTR_FORMAT ", " PTR_FORMAT ")", p2i(_hrm->reserved().start()), p2i(_hrm->reserved().end())); @@ -2659,9 +2622,6 @@ void G1CollectedHeap::gc_epilogue(bool full) { // We are at the end of the GC. Total collections has already been increased. rem_set()->print_periodic_summary_info("After GC RS summary", total_collections() - 1); - // FIXME: what is this about? - // I'm ignoring the "fill_newgen()" call if "alloc_event_enabled" - // is set. #if COMPILER2_OR_JVMCI assert(DerivedPointerTable::is_empty(), "derived pointer present"); #endif @@ -4587,45 +4547,23 @@ bool G1CollectedHeap::check_young_list_empty() { #endif // ASSERT -class TearDownRegionSetsClosure : public HeapRegionClosure { - HeapRegionSet *_old_set; - -public: - TearDownRegionSetsClosure(HeapRegionSet* old_set) : _old_set(old_set) { } - - bool do_heap_region(HeapRegion* r) { - if (r->is_old()) { - _old_set->remove(r); - } else if(r->is_young()) { - r->uninstall_surv_rate_group(); - } else { - // We ignore free regions, we'll empty the free list afterwards. - // We ignore humongous and archive regions, we're not tearing down these - // sets. - assert(r->is_archive() || r->is_free() || r->is_humongous(), - "it cannot be another type"); - } - return false; - } - - ~TearDownRegionSetsClosure() { - assert(_old_set->is_empty(), "post-condition"); - } -}; - -void G1CollectedHeap::tear_down_region_sets(bool free_list_only) { - assert_at_safepoint_on_vm_thread(); - - if (!free_list_only) { - TearDownRegionSetsClosure cl(&_old_set); - heap_region_iterate(&cl); - + // Remove the given HeapRegion from the appropriate region set. +void G1CollectedHeap::prepare_region_for_full_compaction(HeapRegion* hr) { + if (hr->is_old()) { + _old_set.remove(hr); + } else if (hr->is_young()) { // Note that emptying the _young_list is postponed and instead done as // the first step when rebuilding the regions sets again. The reason for // this is that during a full GC string deduplication needs to know if // a collected region was young or old when the full GC was initiated. + hr->uninstall_surv_rate_group(); + } else { + // We ignore free regions, we'll empty the free list afterwards. + // We ignore humongous and archive regions, we're not tearing down these + // sets. + assert(hr->is_archive() || hr->is_free() || hr->is_humongous(), + "it cannot be another type"); } - _hrm->remove_all_free_regions(); } void G1CollectedHeap::increase_used(size_t bytes) { diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 4c98d5f38d9..3048f9ceae1 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -177,20 +177,14 @@ class G1CollectedHeap : public CollectedHeap { // The block offset table for the G1 heap. G1BlockOffsetTable* _bot; - // Tears down the region sets / lists so that they are empty and the - // regions on the heap do not belong to a region set / list. The - // only exception is the humongous set which we leave unaltered. If - // free_list_only is true, it will only tear down the master free - // list. It is called before a Full GC (free_list_only == false) or - // before heap shrinking (free_list_only == true). - void tear_down_region_sets(bool free_list_only); +public: + void prepare_region_for_full_compaction(HeapRegion* hr); +private: // Rebuilds the region sets / lists so that they are repopulated to // reflect the contents of the heap. The only exception is the // humongous set which was not torn down in the first place. If - // free_list_only is true, it will only rebuild the master free - // list. It is called after a Full GC (free_list_only == false) or - // after heap shrinking (free_list_only == true). + // free_list_only is true, it will only rebuild the free list. void rebuild_region_sets(bool free_list_only); // Callback for region mapping changed events. @@ -1136,11 +1130,6 @@ class G1CollectedHeap : public CollectedHeap { void decrement_summary_bytes(size_t bytes); virtual bool is_in(const void* p) const; -#ifdef ASSERT - // Returns whether p is in one of the available areas of the heap. Slow but - // extensive version. - bool is_in_exact(const void* p) const; -#endif // Return "TRUE" iff the given object address is within the collection // set. Assumes that the reference points into the heap. @@ -1429,6 +1418,8 @@ class G1CollectedHeap : public CollectedHeap { virtual WorkGang* safepoint_workers() { return _workers; } + virtual bool is_archived_object(oop object) const; + // The methods below are here for convenience and dispatch the // appropriate method depending on value of the given VerifyOption // parameter. The values for that parameter, and their meanings, diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp index 310cb7bc066..d9904f2fc8f 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,7 +54,12 @@ void G1CollectionSetCandidates::verify() const { for (; idx < _num_regions; idx++) { HeapRegion *cur = _regions[idx]; guarantee(cur != NULL, "Regions after _front_idx %u cannot be NULL but %u is", _front_idx, idx); - guarantee(G1CollectionSetChooser::should_add(cur), "Region %u should be eligible for addition.", cur->hrm_index()); + // The first disjunction filters out regions with objects that were explicitly + // pinned after being added to the collection set candidates. Archive regions + // should never have been added to the collection set though. + guarantee((cur->is_pinned() && !cur->is_archive()) || + G1CollectionSetChooser::should_add(cur), + "Region %u should be eligible for addition.", cur->hrm_index()); if (prev != NULL) { guarantee(prev->gc_efficiency() >= cur->gc_efficiency(), "GC efficiency for region %u: %1.4f smaller than for region %u: %1.4f", diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index fa350232846..eca44ecdff9 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -594,15 +594,19 @@ class G1ClearBitMapTask : public AbstractGangTask { HeapWord* const end = r->end(); while (cur < end) { + // Abort iteration if necessary. + if (_cm != NULL) { + _cm->do_yield_check(); + if (_cm->has_aborted()) { + return true; + } + } + MemRegion mr(cur, MIN2(cur + chunk_size_in_words, end)); _bitmap->clear_range(mr); cur += chunk_size_in_words; - // Abort iteration if after yielding the marking has been aborted. - if (_cm != NULL && _cm->do_yield_check() && _cm->has_aborted()) { - return true; - } // Repeat the asserts from before the start of the closure. We will do them // as asserts here to minimize their overhead on the product. However, we // will have them as guarantees at the beginning / end of the bitmap @@ -671,9 +675,9 @@ void G1ConcurrentMark::cleanup_for_next_mark() { guarantee(!_g1h->collector_state()->mark_or_rebuild_in_progress(), "invariant"); } -void G1ConcurrentMark::clear_prev_bitmap(WorkGang* workers) { +void G1ConcurrentMark::clear_next_bitmap(WorkGang* workers) { assert_at_safepoint_on_vm_thread(); - clear_bitmap(_prev_mark_bitmap, workers, false); + clear_bitmap(_next_mark_bitmap, workers, false); } class NoteStartOfMarkHRClosure : public HeapRegionClosure { @@ -1128,6 +1132,8 @@ void G1ConcurrentMark::remark() { // Install newly created mark bitmap as "prev". swap_mark_bitmaps(); + + _g1h->collector_state()->set_clearing_next_bitmap(true); { GCTraceTime(Debug, gc, phases) debug("Update Remembered Set Tracking Before Rebuild", _gc_timer_cm); @@ -1692,7 +1698,6 @@ void G1ConcurrentMark::swap_mark_bitmaps() { G1CMBitMap* temp = _prev_mark_bitmap; _prev_mark_bitmap = _next_mark_bitmap; _next_mark_bitmap = temp; - _g1h->collector_state()->set_clearing_next_bitmap(true); } // Closure for marking entries in SATB buffers. @@ -1971,7 +1976,7 @@ void G1ConcurrentMark::concurrent_cycle_abort() { // concurrent bitmap clearing. { GCTraceTime(Debug, gc) debug("Clear Next Bitmap"); - clear_bitmap(_next_mark_bitmap, _g1h->workers(), false); + clear_next_bitmap(_g1h->workers()); } // Note we cannot clear the previous marking bitmap here // since VerifyDuringGC verifies the objects marked during diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 6ac83ba4953..2e0171a72ee 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -374,8 +374,6 @@ class G1ConcurrentMark : public CHeapObj { void report_object_count(bool mark_completed); - void swap_mark_bitmaps(); - void reclaim_empty_regions(); // After reclaiming empty regions, update heap sizes. @@ -539,8 +537,8 @@ class G1ConcurrentMark : public CHeapObj { // to be called concurrently to the mutator. It will yield to safepoint requests. void cleanup_for_next_mark(); - // Clear the previous marking bitmap during safepoint. - void clear_prev_bitmap(WorkGang* workers); + // Clear the next marking bitmap during safepoint. + void clear_next_bitmap(WorkGang* workers); // These two methods do the work that needs to be done at the start and end of the // concurrent start pause. @@ -563,6 +561,8 @@ class G1ConcurrentMark : public CHeapObj { void remark(); + void swap_mark_bitmaps(); + void cleanup(); // Mark in the previous bitmap. Caution: the prev bitmap is usually read-only, so use // this carefully. diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkBitMap.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkBitMap.cpp index 4b8d04f4932..a2b0ce26b04 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkBitMap.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkBitMap.cpp @@ -51,7 +51,7 @@ void G1CMBitMap::clear_region(HeapRegion* region) { #ifdef ASSERT void G1CMBitMap::check_mark(HeapWord* addr) { - assert(G1CollectedHeap::heap()->is_in_exact(addr), + assert(G1CollectedHeap::heap()->is_in(addr), "Trying to access bitmap " PTR_FORMAT " for address " PTR_FORMAT " not in the heap.", p2i(this), p2i(addr)); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp index 23879d7bbed..b58d8c66d8b 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp @@ -108,7 +108,7 @@ void G1ConcurrentMarkThread::delay_to_keep_mmu(bool remark) { jlong sleep_time_ms = ceil(sleep_time_sec * MILLIUNITS); if (sleep_time_ms <= 0) { break; // Passed end time. - } else if (ml.wait(sleep_time_ms, Monitor::_no_safepoint_check_flag)) { + } else if (ml.wait(sleep_time_ms)) { break; // Timeout => reached end time. } // Other (possibly spurious) wakeup. Retry with updated sleep time. diff --git a/src/hotspot/share/gc/g1/g1EvacFailure.cpp b/src/hotspot/share/gc/g1/g1EvacFailure.cpp index 1c17dd0f7b2..7c850249b10 100644 --- a/src/hotspot/share/gc/g1/g1EvacFailure.cpp +++ b/src/hotspot/share/gc/g1/g1EvacFailure.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -259,5 +259,10 @@ G1ParRemoveSelfForwardPtrsTask::G1ParRemoveSelfForwardPtrsTask(G1RedirtyCardsQue void G1ParRemoveSelfForwardPtrsTask::work(uint worker_id) { RemoveSelfForwardPtrHRClosure rsfp_cl(_rdcqs, worker_id); - _g1h->collection_set_iterate_increment_from(&rsfp_cl, &_hrclaimer, worker_id); + // We need to check all collection set regions whether they need self forward + // removals, not only the last collection set increment. The reason is that + // reference processing (e.g. finalizers) can make it necessary to resurrect an + // otherwise unreachable object at the very end of the collection. That object + // might cause an evacuation failure in any region in the collection set. + _g1h->collection_set_par_iterate_all(&rsfp_cl, &_hrclaimer, worker_id); } diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index 9846ba62593..33cd49fff3d 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -25,7 +25,7 @@ #include "precompiled.hpp" #include "code/codeCache.hpp" #include "gc/g1/g1CollectedHeap.hpp" -#include "gc/g1/g1FullCollector.hpp" +#include "gc/g1/g1FullCollector.inline.hpp" #include "gc/g1/g1FullGCAdjustTask.hpp" #include "gc/g1/g1FullGCCompactTask.hpp" #include "gc/g1/g1FullGCMarker.inline.hpp" @@ -111,21 +111,23 @@ G1FullCollector::G1FullCollector(G1CollectedHeap* heap, bool explicit_gc, bool c _array_queue_set(_num_workers), _preserved_marks_set(true), _serial_compaction_point(), - _is_alive(heap->concurrent_mark()->next_mark_bitmap()), + _is_alive(this, heap->concurrent_mark()->next_mark_bitmap()), _is_alive_mutator(heap->ref_processor_stw(), &_is_alive), _always_subject_to_discovery(), - _is_subject_mutator(heap->ref_processor_stw(), &_always_subject_to_discovery) { + _is_subject_mutator(heap->ref_processor_stw(), &_always_subject_to_discovery), + _region_attr_table() { assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint"); _preserved_marks_set.init(_num_workers); _markers = NEW_C_HEAP_ARRAY(G1FullGCMarker*, _num_workers, mtGC); _compaction_points = NEW_C_HEAP_ARRAY(G1FullGCCompactionPoint*, _num_workers, mtGC); for (uint i = 0; i < _num_workers; i++) { - _markers[i] = new G1FullGCMarker(i, _preserved_marks_set.get(i), mark_bitmap()); + _markers[i] = new G1FullGCMarker(this, i, _preserved_marks_set.get(i)); _compaction_points[i] = new G1FullGCCompactionPoint(); _oop_queue_set.register_queue(i, marker(i)->oop_stack()); _array_queue_set.register_queue(i, marker(i)->objarray_stack()); } + _region_attr_table.initialize(heap->reserved(), HeapRegion::GrainBytes); } G1FullCollector::~G1FullCollector() { @@ -137,6 +139,19 @@ G1FullCollector::~G1FullCollector() { FREE_C_HEAP_ARRAY(G1FullGCCompactionPoint*, _compaction_points); } +class PrepareRegionsClosure : public HeapRegionClosure { + G1FullCollector* _collector; + +public: + PrepareRegionsClosure(G1FullCollector* collector) : _collector(collector) { } + + bool do_heap_region(HeapRegion* hr) { + G1CollectedHeap::heap()->prepare_region_for_full_compaction(hr); + _collector->update_attribute_table(hr); + return false; + } +}; + void G1FullCollector::prepare_collection() { _heap->policy()->record_full_collection_start(); @@ -149,6 +164,9 @@ void G1FullCollector::prepare_collection() { _heap->gc_prologue(true); _heap->prepare_heap_for_full_collection(); + PrepareRegionsClosure cl(this); + _heap->heap_region_iterate(&cl); + reference_processor()->enable_discovery(); reference_processor()->setup_policy(scope()->should_clear_soft_refs()); @@ -184,6 +202,10 @@ void G1FullCollector::complete_collection() { BiasedLocking::restore_marks(); + _heap->concurrent_mark()->swap_mark_bitmaps(); + // Prepare the bitmap for the next (potentially concurrent) marking. + _heap->concurrent_mark()->clear_next_bitmap(_heap->workers()); + _heap->prepare_heap_for_mutators(); _heap->policy()->record_full_collection_end(); @@ -194,6 +216,19 @@ void G1FullCollector::complete_collection() { _heap->print_heap_after_full_collection(scope()->heap_transition()); } +void G1FullCollector::update_attribute_table(HeapRegion* hr) { + if (hr->is_free()) { + return; + } + if (hr->is_closed_archive()) { + _region_attr_table.set_closed_archive(hr->hrm_index()); + } else if (hr->is_pinned()) { + _region_attr_table.set_pinned(hr->hrm_index()); + } else { + _region_attr_table.set_normal(hr->hrm_index()); + } +} + void G1FullCollector::phase1_mark_live_objects() { // Recursively traverse all live objects and mark them. GCTraceTime(Info, gc, phases) info("Phase 1: Mark live objects", scope()->timer()); diff --git a/src/hotspot/share/gc/g1/g1FullCollector.hpp b/src/hotspot/share/gc/g1/g1FullCollector.hpp index 9c21f65fc21..66b97ad40a7 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.hpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1FULLCOLLECTOR_HPP #include "gc/g1/g1FullGCCompactionPoint.hpp" +#include "gc/g1/g1FullGCHeapRegionAttr.hpp" #include "gc/g1/g1FullGCMarker.hpp" #include "gc/g1/g1FullGCOopClosures.hpp" #include "gc/g1/g1FullGCScope.hpp" @@ -33,6 +34,7 @@ #include "gc/shared/referenceProcessor.hpp" #include "gc/shared/taskqueue.hpp" #include "memory/allocation.hpp" +#include "oops/oopsHierarchy.hpp" class AbstractGangTask; class G1CMBitMap; @@ -71,6 +73,8 @@ class G1FullCollector : StackObj { G1FullGCSubjectToDiscoveryClosure _always_subject_to_discovery; ReferenceProcessorSubjectToDiscoveryMutator _is_subject_mutator; + G1FullGCHeapRegionAttr _region_attr_table; + public: G1FullCollector(G1CollectedHeap* heap, bool explicit_gc, bool clear_soft_refs); ~G1FullCollector(); @@ -90,6 +94,12 @@ class G1FullCollector : StackObj { G1CMBitMap* mark_bitmap(); ReferenceProcessor* reference_processor(); + void update_attribute_table(HeapRegion* hr); + + inline bool is_in_pinned_or_closed(oop obj) const; + inline bool is_in_pinned(oop obj) const; + inline bool is_in_closed(oop obj) const; + private: void phase1_mark_live_objects(); void phase2_prepare_compaction(); diff --git a/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp b/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp new file mode 100644 index 00000000000..1d45f67ed1a --- /dev/null +++ b/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_GC_G1_G1FULLCOLLECTOR_INLINE_HPP +#define SHARE_GC_G1_G1FULLCOLLECTOR_INLINE_HPP + +#include "gc/g1/g1FullCollector.hpp" +#include "gc/g1/g1FullGCHeapRegionAttr.hpp" +#include "oops/oopsHierarchy.hpp" + +bool G1FullCollector::is_in_pinned_or_closed(oop obj) const { + return _region_attr_table.is_pinned_or_closed(cast_from_oop(obj)); +} + +bool G1FullCollector::is_in_pinned(oop obj) const { + return _region_attr_table.is_pinned(cast_from_oop(obj)); +} + +bool G1FullCollector::is_in_closed(oop obj) const { + return _region_attr_table.is_closed_archive(cast_from_oop(obj)); +} + +#endif // SHARE_GC_G1_G1FULLCOLLECTOR_INLINE_HPP + diff --git a/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp b/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp index 3dfd30f67b2..ac5ba8834fb 100644 --- a/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ #include "classfile/classLoaderDataGraph.hpp" #include "gc/g1/g1CollectedHeap.hpp" #include "gc/g1/g1ConcurrentMarkBitMap.inline.hpp" -#include "gc/g1/g1FullCollector.hpp" +#include "gc/g1/g1FullCollector.inline.hpp" #include "gc/g1/g1FullGCAdjustTask.hpp" #include "gc/g1/g1FullGCCompactionPoint.hpp" #include "gc/g1/g1FullGCMarker.hpp" @@ -51,27 +51,26 @@ class G1AdjustLiveClosure : public StackObj { }; class G1AdjustRegionClosure : public HeapRegionClosure { + G1FullCollector* _collector; G1CMBitMap* _bitmap; uint _worker_id; public: - G1AdjustRegionClosure(G1CMBitMap* bitmap, uint worker_id) : - _bitmap(bitmap), + G1AdjustRegionClosure(G1FullCollector* collector, uint worker_id) : + _collector(collector), + _bitmap(collector->mark_bitmap()), _worker_id(worker_id) { } bool do_heap_region(HeapRegion* r) { - G1AdjustClosure cl; + G1AdjustClosure cl(_collector); if (r->is_humongous()) { + // Special handling for humongous regions to get somewhat better + // work distribution. oop obj = oop(r->humongous_start_region()->bottom()); obj->oop_iterate(&cl, MemRegion(r->bottom(), r->top())); - } else if (r->is_open_archive()) { - // Only adjust the open archive regions, the closed ones - // never change. - G1AdjustLiveClosure adjust(&cl); - r->apply_to_marked_objects(_bitmap, &adjust); - // Open archive regions will not be compacted and the marking information is - // no longer needed. Clear it here to avoid having to do it later. - _bitmap->clear_region(r); - } else { + } else if (!r->is_closed_archive() && !r->is_free()) { + // Closed archive regions never change references and only contain + // references into other closed regions and are always live. Free + // regions do not contain objects to iterate. So skip both. G1AdjustLiveClosure adjust(&cl); r->apply_to_marked_objects(_bitmap, &adjust); } @@ -85,7 +84,7 @@ G1FullGCAdjustTask::G1FullGCAdjustTask(G1FullCollector* collector) : _references_done(0), _weak_proc_task(collector->workers()), _hrclaimer(collector->workers()), - _adjust(), + _adjust(collector), _string_dedup_cleaning_task(NULL, &_adjust, false) { // Need cleared claim bits for the roots processing ClassLoaderDataGraph::clear_claimed_marks(); @@ -116,7 +115,7 @@ void G1FullGCAdjustTask::work(uint worker_id) { _string_dedup_cleaning_task.work(worker_id); // Now adjust pointers region by region - G1AdjustRegionClosure blk(collector()->mark_bitmap(), worker_id); + G1AdjustRegionClosure blk(collector(), worker_id); G1CollectedHeap::heap()->heap_region_par_iterate_from_worker_offset(&blk, &_hrclaimer, worker_id); log_task("Adjust task", worker_id, start); } diff --git a/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp b/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp index be35978ba17..8a0a5c4c0eb 100644 --- a/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,27 +34,19 @@ #include "oops/oop.inline.hpp" #include "utilities/ticks.hpp" -class G1ResetHumongousClosure : public HeapRegionClosure { +class G1ResetPinnedClosure : public HeapRegionClosure { G1CMBitMap* _bitmap; public: - G1ResetHumongousClosure(G1CMBitMap* bitmap) : - _bitmap(bitmap) { } + G1ResetPinnedClosure(G1CMBitMap* bitmap) : _bitmap(bitmap) { } - bool do_heap_region(HeapRegion* current) { - if (current->is_humongous()) { - if (current->is_starts_humongous()) { - oop obj = oop(current->bottom()); - if (_bitmap->is_marked(obj)) { - // Clear bitmap and fix mark word. - _bitmap->clear(obj); - obj->init_mark(); - } else { - assert(current->is_empty(), "Should have been cleared in phase 2."); - } - } - current->reset_humongous_during_compaction(); + bool do_heap_region(HeapRegion* r) { + if (!r->is_pinned()) { + return false; } + assert(!r->is_starts_humongous() || _bitmap->is_marked((oop)r->bottom()), + "must be, otherwise reclaimed earlier"); + r->reset_pinned_after_full_gc(); return false; } }; @@ -78,13 +70,16 @@ size_t G1FullGCCompactTask::G1CompactRegionClosure::apply(oop obj) { } void G1FullGCCompactTask::compact_region(HeapRegion* hr) { + assert(!hr->is_pinned(), "Should be no pinned region in compaction queue"); assert(!hr->is_humongous(), "Should be no humongous regions in compaction queue"); G1CompactRegionClosure compact(collector()->mark_bitmap()); hr->apply_to_marked_objects(collector()->mark_bitmap(), &compact); - // Once all objects have been moved the liveness information - // needs be cleared. - collector()->mark_bitmap()->clear_region(hr); - hr->complete_compaction(); + // Clear the liveness information for this region if necessary i.e. if we actually look at it + // for bitmap verification. Otherwise it is sufficient that we move the TAMS to bottom(). + if (G1VerifyBitmaps) { + collector()->mark_bitmap()->clear_region(hr); + } + hr->reset_compacted_after_full_gc(); } void G1FullGCCompactTask::work(uint worker_id) { @@ -96,7 +91,7 @@ void G1FullGCCompactTask::work(uint worker_id) { compact_region(*it); } - G1ResetHumongousClosure hc(collector()->mark_bitmap()); + G1ResetPinnedClosure hc(collector()->mark_bitmap()); G1CollectedHeap::heap()->heap_region_par_iterate_from_worker_offset(&hc, &_claimer, worker_id); log_task("Compaction task", worker_id, start); } diff --git a/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.cpp b/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.cpp index 2799ac5109e..c70f1f02f00 100644 --- a/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.cpp @@ -136,10 +136,6 @@ void G1FullGCCompactionPoint::add(HeapRegion* hr) { _compaction_regions->append(hr); } -void G1FullGCCompactionPoint::merge(G1FullGCCompactionPoint* other) { - _compaction_regions->appendAll(other->regions()); -} - HeapRegion* G1FullGCCompactionPoint::remove_last() { return _compaction_regions->pop(); } diff --git a/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.hpp b/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.hpp index 0ec0b324aab..5f732dcad89 100644 --- a/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.hpp @@ -53,7 +53,6 @@ class G1FullGCCompactionPoint : public CHeapObj { void update(); void forward(oop object, size_t size); void add(HeapRegion* hr); - void merge(G1FullGCCompactionPoint* other); HeapRegion* remove_last(); HeapRegion* current_region(); diff --git a/src/hotspot/share/gc/g1/g1FullGCHeapRegionAttr.hpp b/src/hotspot/share/gc/g1/g1FullGCHeapRegionAttr.hpp new file mode 100644 index 00000000000..8297bd03811 --- /dev/null +++ b/src/hotspot/share/gc/g1/g1FullGCHeapRegionAttr.hpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_GC_G1_G1FULLGCHEAPREGIONATTR_HPP +#define SHARE_GC_G1_G1FULLGCHEAPREGIONATTR_HPP + +#include "gc/g1/g1BiasedArray.hpp" + +// This table is used to store attribute values of all HeapRegions that need +// fast access during the full collection. In particular some parts of the region +// type information is encoded in these per-region bytes. +// Value encoding has been specifically chosen to make required accesses fast. +class G1FullGCHeapRegionAttr : public G1BiasedMappedArray { + static const uint8_t Normal = 0; // Other kind of region + static const uint8_t Pinned = 1; // Region is a pinned (non-Closed Archive) region + static const uint8_t ClosedArchive = 2; // Region is a (pinned) Closed Archive region + + STATIC_ASSERT(ClosedArchive > Pinned); + + static const uint8_t Invalid = 255; + + bool is_invalid(HeapWord* obj) const { + return get_by_address(obj) == Invalid; + } + +protected: + uint8_t default_value() const { return Invalid; } + +public: + void set_closed_archive(uint idx) { set_by_index(idx, ClosedArchive); } + + bool is_closed_archive(HeapWord* obj) const { + assert(!is_invalid(obj), "not initialized yet"); + return get_by_address(obj) == ClosedArchive; + } + + void set_pinned(uint idx) { set_by_index(idx, Pinned); } + + bool is_pinned_or_closed(HeapWord* obj) const { + assert(!is_invalid(obj), "not initialized yet"); + return get_by_address(obj) >= Pinned; + } + + bool is_pinned(HeapWord* obj) const { + assert(!is_invalid(obj), "not initialized yet"); + return get_by_address(obj) == Pinned; + } + + void set_normal(uint idx) { set_by_index(idx, Normal); } + + bool is_normal(HeapWord* obj) const { + assert(!is_invalid(obj), "not initialized yet"); + return get_by_address(obj) == Normal; + } +}; + +#endif // SHARE_GC_G1_G1FULLGCHEAPREGIONATTR_HPP diff --git a/src/hotspot/share/gc/g1/g1FullGCMarker.cpp b/src/hotspot/share/gc/g1/g1FullGCMarker.cpp index 3bbf74adfc6..ed97bbfe949 100644 --- a/src/hotspot/share/gc/g1/g1FullGCMarker.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCMarker.cpp @@ -30,9 +30,12 @@ #include "gc/shared/verifyOption.hpp" #include "memory/iterator.inline.hpp" -G1FullGCMarker::G1FullGCMarker(uint worker_id, PreservedMarks* preserved_stack, G1CMBitMap* bitmap) : +G1FullGCMarker::G1FullGCMarker(G1FullCollector* collector, + uint worker_id, + PreservedMarks* preserved_stack) : + _collector(collector), _worker_id(worker_id), - _bitmap(bitmap), + _bitmap(collector->mark_bitmap()), _oop_stack(), _objarray_stack(), _preserved_stack(preserved_stack), diff --git a/src/hotspot/share/gc/g1/g1FullGCMarker.hpp b/src/hotspot/share/gc/g1/g1FullGCMarker.hpp index fc130fdc66f..28b4946c359 100644 --- a/src/hotspot/share/gc/g1/g1FullGCMarker.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCMarker.hpp @@ -43,8 +43,11 @@ typedef GenericTaskQueueSet OopQueueSet; typedef GenericTaskQueueSet ObjArrayTaskQueueSet; class G1CMBitMap; +class G1FullCollector; class G1FullGCMarker : public CHeapObj { + G1FullCollector* _collector; + uint _worker_id; // Backing mark bitmap G1CMBitMap* _bitmap; @@ -71,7 +74,7 @@ class G1FullGCMarker : public CHeapObj { inline void follow_array(objArrayOop array); inline void follow_array_chunk(objArrayOop array, int index); public: - G1FullGCMarker(uint worker_id, PreservedMarks* preserved_stack, G1CMBitMap* bitmap); + G1FullGCMarker(G1FullCollector* collector, uint worker_id, PreservedMarks* preserved_stack); ~G1FullGCMarker(); // Stack getters diff --git a/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp b/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp index efa0fb91bbf..75d8f656305 100644 --- a/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,10 @@ #ifndef SHARE_GC_G1_G1FULLGCMARKER_INLINE_HPP #define SHARE_GC_G1_G1FULLGCMARKER_INLINE_HPP +#include "classfile/javaClasses.inline.hpp" #include "gc/g1/g1Allocator.inline.hpp" #include "gc/g1/g1ConcurrentMarkBitMap.inline.hpp" +#include "gc/g1/g1FullCollector.inline.hpp" #include "gc/g1/g1FullGCMarker.hpp" #include "gc/g1/g1FullGCOopClosures.inline.hpp" #include "gc/g1/g1StringDedup.hpp" @@ -38,8 +40,7 @@ #include "utilities/debug.hpp" inline bool G1FullGCMarker::mark_object(oop obj) { - // Not marking closed archive objects. - if (G1ArchiveAllocator::is_closed_archive_object(obj)) { + if (_collector->is_in_closed(obj)) { return false; } @@ -52,12 +53,15 @@ inline bool G1FullGCMarker::mark_object(oop obj) { // Marked by us, preserve if needed. markWord mark = obj->mark(); if (obj->mark_must_be_preserved(mark) && - !G1ArchiveAllocator::is_open_archive_object(obj)) { + // It is not necessary to preserve marks for objects in pinned regions because + // we do not change their headers (i.e. forward them). + !_collector->is_in_pinned(obj)) { preserved_stack()->push(obj, mark); } // Check if deduplicatable string. - if (G1StringDedup::is_enabled()) { + if (G1StringDedup::is_enabled() && + java_lang_String::is_instance_inlined(obj)) { G1StringDedup::enqueue_from_mark(obj, _worker_id); } return true; @@ -71,7 +75,7 @@ template inline void G1FullGCMarker::mark_and_push(T* p) { _oop_stack.push(obj); assert(_bitmap->is_marked(obj), "Must be marked now - map self"); } else { - assert(_bitmap->is_marked(obj) || G1ArchiveAllocator::is_closed_archive_object(obj), + assert(_bitmap->is_marked(obj) || _collector->is_in_closed(obj), "Must be marked by other or closed archive object"); } } diff --git a/src/hotspot/share/gc/g1/g1FullGCOopClosures.cpp b/src/hotspot/share/gc/g1/g1FullGCOopClosures.cpp index 876def6f550..ba3b50e5a96 100644 --- a/src/hotspot/share/gc/g1/g1FullGCOopClosures.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCOopClosures.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "gc/g1/g1CollectedHeap.hpp" +#include "gc/g1/g1FullCollector.hpp" #include "gc/g1/g1FullGCMarker.inline.hpp" #include "gc/g1/g1FullGCOopClosures.inline.hpp" #include "logging/logStream.hpp" @@ -32,6 +33,9 @@ #include "oops/compressedOops.inline.hpp" #include "oops/oop.inline.hpp" +G1IsAliveClosure::G1IsAliveClosure(G1FullCollector* collector) : + G1IsAliveClosure(collector, collector->mark_bitmap()) { } + void G1FollowStackClosure::do_void() { _marker->drain_stack(); } void G1FullKeepAliveClosure::do_oop(oop* p) { do_oop_work(p); } diff --git a/src/hotspot/share/gc/g1/g1FullGCOopClosures.hpp b/src/hotspot/share/gc/g1/g1FullGCOopClosures.hpp index baca67dabd2..1690c99ea24 100644 --- a/src/hotspot/share/gc/g1/g1FullGCOopClosures.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCOopClosures.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,10 +35,13 @@ class G1FullGCMarker; // Below are closures used by the G1 Full GC. class G1IsAliveClosure : public BoolObjectClosure { + G1FullCollector* _collector; G1CMBitMap* _bitmap; public: - G1IsAliveClosure(G1CMBitMap* bitmap) : _bitmap(bitmap) { } + G1IsAliveClosure(G1FullCollector* collector); + G1IsAliveClosure(G1FullCollector* collector, G1CMBitMap* bitmap) : + _collector(collector), _bitmap(bitmap) { } virtual bool do_object_b(oop p); }; @@ -75,8 +78,11 @@ class G1MarkAndPushClosure : public OopIterateClosure { }; class G1AdjustClosure : public BasicOopIterateClosure { - template static inline void adjust_pointer(T* p); + G1FullCollector* _collector; + + template inline void adjust_pointer(T* p); public: + G1AdjustClosure(G1FullCollector* collector) : _collector(collector) { } template void do_oop_work(T* p) { adjust_pointer(p); } virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); diff --git a/src/hotspot/share/gc/g1/g1FullGCOopClosures.inline.hpp b/src/hotspot/share/gc/g1/g1FullGCOopClosures.inline.hpp index 16c34eace8d..dfb56d50ca4 100644 --- a/src/hotspot/share/gc/g1/g1FullGCOopClosures.inline.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCOopClosures.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1FULLGCOOPCLOSURES_INLINE_HPP #include "gc/g1/g1Allocator.inline.hpp" +#include "gc/g1/g1FullCollector.inline.hpp" #include "gc/g1/g1ConcurrentMarkBitMap.inline.hpp" #include "gc/g1/g1FullGCMarker.inline.hpp" #include "gc/g1/g1FullGCOopClosures.hpp" @@ -69,8 +70,9 @@ template inline void G1AdjustClosure::adjust_pointer(T* p) { oop obj = CompressedOops::decode_not_null(heap_oop); assert(Universe::heap()->is_in(obj), "should be in heap"); - if (G1ArchiveAllocator::is_archived_object(obj)) { - // We never forward archive objects. + if (_collector->is_in_pinned_or_closed(obj)) { + // We never forward objects in pinned regions so there is no need to + // process them further. return; } @@ -94,7 +96,7 @@ inline void G1AdjustClosure::do_oop(oop* p) { do_oop_work(p); } inline void G1AdjustClosure::do_oop(narrowOop* p) { do_oop_work(p); } inline bool G1IsAliveClosure::do_object_b(oop p) { - return _bitmap->is_marked(p) || G1ArchiveAllocator::is_closed_archive_object(p); + return _bitmap->is_marked(p) || _collector->is_in_closed(p); } template diff --git a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.cpp b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.cpp index 3658ae200d9..732a040ffb0 100644 --- a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,22 +40,28 @@ #include "utilities/ticks.hpp" bool G1FullGCPrepareTask::G1CalculatePointersClosure::do_heap_region(HeapRegion* hr) { - if (hr->is_humongous()) { - oop obj = oop(hr->humongous_start_region()->bottom()); - if (_bitmap->is_marked(obj)) { - if (hr->is_starts_humongous()) { - obj->forward_to(obj); + if (hr->is_pinned()) { + // There is no need to iterate and forward objects in pinned regions ie. + // prepare them for compaction. The adjust pointers phase will skip + // work for them. + if (hr->is_humongous()) { + oop obj = oop(hr->humongous_start_region()->bottom()); + if (!_bitmap->is_marked(obj)) { + free_humongous_region(hr); } } else { - free_humongous_region(hr); + assert(hr->is_archive(), "Only archive regions can also be pinned."); } - } else if (!hr->is_pinned()) { + } else { + assert(!hr->is_humongous(), "moving humongous objects not supported."); prepare_for_compaction(hr); } // Reset data structures not valid after Full GC. reset_region_metadata(hr); + _collector->update_attribute_table(hr); + return false; } @@ -78,7 +84,7 @@ bool G1FullGCPrepareTask::has_freed_regions() { void G1FullGCPrepareTask::work(uint worker_id) { Ticks start = Ticks::now(); G1FullGCCompactionPoint* compaction_point = collector()->compaction_point(worker_id); - G1CalculatePointersClosure closure(collector()->mark_bitmap(), compaction_point); + G1CalculatePointersClosure closure(collector(), compaction_point); G1CollectedHeap::heap()->heap_region_par_iterate_from_start(&closure, &_hrclaimer); // Update humongous region sets @@ -92,15 +98,18 @@ void G1FullGCPrepareTask::work(uint worker_id) { log_task("Prepare compaction task", worker_id, start); } -G1FullGCPrepareTask::G1CalculatePointersClosure::G1CalculatePointersClosure(G1CMBitMap* bitmap, +G1FullGCPrepareTask::G1CalculatePointersClosure::G1CalculatePointersClosure(G1FullCollector* collector, G1FullGCCompactionPoint* cp) : _g1h(G1CollectedHeap::heap()), - _bitmap(bitmap), + _collector(collector), + _bitmap(collector->mark_bitmap()), _cp(cp), _humongous_regions_removed(0) { } void G1FullGCPrepareTask::G1CalculatePointersClosure::free_humongous_region(HeapRegion* hr) { - FreeRegionList dummy_free_list("Dummy Free List for G1MarkSweep"); + assert(hr->is_humongous(), "must be but region %u is %s", hr->hrm_index(), hr->get_short_type_str()); + + FreeRegionList dummy_free_list("Humongous Dummy Free List for G1MarkSweep"); hr->set_containing_set(NULL); _humongous_regions_removed++; diff --git a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.hpp b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.hpp index fcaf797a12f..124fb619c8e 100644 --- a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ #include "gc/shared/referenceProcessor.hpp" class G1CMBitMap; +class G1FullCollector; class G1FullGCPrepareTask : public G1FullGCTask { protected: @@ -52,6 +53,7 @@ class G1FullGCPrepareTask : public G1FullGCTask { class G1CalculatePointersClosure : public HeapRegionClosure { protected: G1CollectedHeap* _g1h; + G1FullCollector* _collector; G1CMBitMap* _bitmap; G1FullGCCompactionPoint* _cp; uint _humongous_regions_removed; @@ -62,7 +64,7 @@ class G1FullGCPrepareTask : public G1FullGCTask { void reset_region_metadata(HeapRegion* hr); public: - G1CalculatePointersClosure(G1CMBitMap* bitmap, + G1CalculatePointersClosure(G1FullCollector* collector, G1FullGCCompactionPoint* cp); void update_sets(); diff --git a/src/hotspot/share/gc/g1/g1FullGCReferenceProcessorExecutor.cpp b/src/hotspot/share/gc/g1/g1FullGCReferenceProcessorExecutor.cpp index 1e180192612..103fb4eca88 100644 --- a/src/hotspot/share/gc/g1/g1FullGCReferenceProcessorExecutor.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCReferenceProcessorExecutor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,7 +57,7 @@ G1FullGCReferenceProcessingExecutor::G1RefProcTaskProxy::G1RefProcTaskProxy(Proc void G1FullGCReferenceProcessingExecutor::G1RefProcTaskProxy::work(uint worker_id) { G1FullGCMarker* marker = _collector->marker(worker_id); - G1IsAliveClosure is_alive(_collector->mark_bitmap()); + G1IsAliveClosure is_alive(_collector); G1FullKeepAliveClosure keep_alive(marker); _proc_task.work(worker_id, is_alive, @@ -82,7 +82,7 @@ void G1FullGCReferenceProcessingExecutor::execute(STWGCTimer* timer, G1FullGCTra GCTraceTime(Debug, gc, phases) debug("Phase 1: Reference Processing", timer); // Process reference objects found during marking. G1FullGCMarker* marker = _collector->marker(0); - G1IsAliveClosure is_alive(_collector->mark_bitmap()); + G1IsAliveClosure is_alive(_collector); G1FullKeepAliveClosure keep_alive(marker); ReferenceProcessorPhaseTimes pt(timer, _reference_processor->max_num_queues()); AbstractRefProcTaskExecutor* executor = _reference_processor->processing_is_mt() ? this : NULL; diff --git a/src/hotspot/share/gc/g1/g1FullGCScope.hpp b/src/hotspot/share/gc/g1/g1FullGCScope.hpp index 8d62e1f9b9b..a3bf9cbb37b 100644 --- a/src/hotspot/share/gc/g1/g1FullGCScope.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCScope.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ #include "gc/shared/gcVMOperations.hpp" #include "gc/shared/isGCActiveMark.hpp" #include "memory/allocation.hpp" +#include "memory/resourceArea.hpp" #include "services/memoryService.hpp" class GCMemoryManager; diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp index 62f0b5d41f5..d96bd5c211c 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp @@ -68,7 +68,8 @@ class VerifyRootsClosure: public OopClosure { oop obj = CompressedOops::decode_not_null(heap_oop); if (_g1h->is_obj_dead_cond(obj, _vo)) { Log(gc, verify) log; - log.error("Root location " PTR_FORMAT " points to dead obj " PTR_FORMAT, p2i(p), p2i(obj)); + log.error("Root location " PTR_FORMAT " points to dead obj " PTR_FORMAT " in region " HR_FORMAT, + p2i(p), p2i(obj), HR_FORMAT_PARAMS(_g1h->heap_region_containing(obj))); ResourceMark rm; LogStream ls(log.error()); obj->print_on(&ls); @@ -239,8 +240,7 @@ class VerifyObjsInRegionClosure: public ObjectClosure { class VerifyArchiveOopClosure: public BasicOopIterateClosure { HeapRegion* _hr; public: - VerifyArchiveOopClosure(HeapRegion *hr) - : _hr(hr) { } + VerifyArchiveOopClosure(HeapRegion *hr) : _hr(hr) { } void do_oop(narrowOop *p) { do_oop_work(p); } void do_oop( oop *p) { do_oop_work(p); } @@ -248,12 +248,12 @@ class VerifyArchiveOopClosure: public BasicOopIterateClosure { oop obj = RawAccess<>::oop_load(p); if (_hr->is_open_archive()) { - guarantee(obj == NULL || G1ArchiveAllocator::is_archived_object(obj), + guarantee(obj == NULL || G1CollectedHeap::heap()->heap_region_containing(obj)->is_archive(), "Archive object at " PTR_FORMAT " references a non-archive object at " PTR_FORMAT, p2i(p), p2i(obj)); } else { assert(_hr->is_closed_archive(), "should be closed archive region"); - guarantee(obj == NULL || G1ArchiveAllocator::is_closed_archive_object(obj), + guarantee(obj == NULL || G1CollectedHeap::heap()->heap_region_containing(obj)->is_closed_archive(), "Archive object at " PTR_FORMAT " references a non-archive object at " PTR_FORMAT, p2i(p), p2i(obj)); } diff --git a/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp b/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp index be7fa937ed2..58eea806fbb 100644 --- a/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp +++ b/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "gc/g1/g1PageBasedVirtualSpace.hpp" +#include "gc/shared/pretouchTask.hpp" #include "gc/shared/workgroup.hpp" #include "oops/markWord.hpp" #include "oops/oop.inline.hpp" @@ -234,56 +235,10 @@ void G1PageBasedVirtualSpace::uncommit(size_t start_page, size_t size_in_pages) _committed.clear_range(start_page, end_page); } -class G1PretouchTask : public AbstractGangTask { -private: - char* volatile _cur_addr; - char* const _start_addr; - char* const _end_addr; - size_t _page_size; -public: - G1PretouchTask(char* start_address, char* end_address, size_t page_size) : - AbstractGangTask("G1 PreTouch"), - _cur_addr(start_address), - _start_addr(start_address), - _end_addr(end_address), - _page_size(0) { -#ifdef LINUX - _page_size = UseTransparentHugePages ? (size_t)os::vm_page_size(): page_size; -#else - _page_size = page_size; -#endif - } - - virtual void work(uint worker_id) { - size_t const actual_chunk_size = MAX2(chunk_size(), _page_size); - while (true) { - char* touch_addr = Atomic::fetch_and_add(&_cur_addr, actual_chunk_size); - if (touch_addr < _start_addr || touch_addr >= _end_addr) { - break; - } - char* end_addr = touch_addr + MIN2(actual_chunk_size, pointer_delta(_end_addr, touch_addr, sizeof(char))); - os::pretouch_memory(touch_addr, end_addr, _page_size); - } - } - - static size_t chunk_size() { return PreTouchParallelChunkSize; } -}; - void G1PageBasedVirtualSpace::pretouch(size_t start_page, size_t size_in_pages, WorkGang* pretouch_gang) { - G1PretouchTask cl(page_start(start_page), bounded_end_addr(start_page + size_in_pages), _page_size); - - if (pretouch_gang != NULL) { - size_t num_chunks = MAX2((size_t)1, size_in_pages * _page_size / MAX2(G1PretouchTask::chunk_size(), _page_size)); - uint num_workers = MIN2((uint)num_chunks, pretouch_gang->total_workers()); - log_debug(gc, heap)("Running %s with %u workers for " SIZE_FORMAT " work units pre-touching " SIZE_FORMAT "B.", - cl.name(), num_workers, num_chunks, size_in_pages * _page_size); - pretouch_gang->run_task(&cl, num_workers); - } else { - log_debug(gc, heap)("Running %s pre-touching " SIZE_FORMAT "B.", - cl.name(), size_in_pages * _page_size); - cl.work(0); - } + PretouchTask::pretouch("G1 PreTouch", page_start(start_page), bounded_end_addr(start_page + size_in_pages), + _page_size, pretouch_gang); } bool G1PageBasedVirtualSpace::contains(const void* p) const { diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index 40149f91336..c9ca7daed32 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "classfile/systemDictionary.hpp" #include "gc/g1/g1Allocator.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.hpp" @@ -75,10 +76,17 @@ G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, _old_gen_is_full(false), _partial_objarray_chunk_size(ParGCArrayScanChunk), _partial_array_stepper(n_workers), + _string_klass_or_null(G1StringDedup::is_enabled() + ? SystemDictionary::String_klass() + : nullptr), _num_optional_regions(optional_cset_length), _numa(g1h->numa()), _obj_alloc_stat(NULL) { + // Verify klass comparison with _string_klass_or_null is sufficient + // to determine whether dedup is enabled and the object is a String. + assert(SystemDictionary::String_klass()->is_final(), "precondition"); + // We allocate number of young gen regions in the collection set plus one // entries, since entry 0 keeps track of surviving bytes for non-young regions. // We also add a few elements at the beginning and at the end in @@ -482,12 +490,11 @@ oop G1ParScanThreadState::do_copy_to_survivor_space(G1HeapRegionAttr const regio age++; } if (old_mark.has_displaced_mark_helper()) { - // In this case, we have to install the mark word first, - // otherwise obj looks to be forwarded (the old mark word, - // which contains the forward pointer, was copied) - obj->set_mark(old_mark); + // In this case, we have to install the old mark word containing the + // displacement tag, and update the age in the displaced mark word. markWord new_mark = old_mark.displaced_mark_helper().set_age(age); old_mark.set_displaced_mark_helper(new_mark); + obj->set_mark(old_mark); } else { obj->set_mark(old_mark.set_age(age)); } @@ -510,7 +517,10 @@ oop G1ParScanThreadState::do_copy_to_survivor_space(G1HeapRegionAttr const regio return obj; } - if (G1StringDedup::is_enabled()) { + // StringDedup::is_enabled() and java_lang_String::is_instance_inline + // test of the obj, combined into a single comparison, using the klass + // already in hand and avoiding the null check in is_instance. + if (klass == _string_klass_or_null) { const bool is_from_young = region_attr.is_young(); const bool is_to_young = dest_attr.is_young(); assert(is_from_young == from_region->is_young(), diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp index 0f6a4f211bc..8b1c1b65700 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp @@ -42,6 +42,7 @@ class G1OopStarChunkedList; class G1PLABAllocator; class G1EvacuationRootClosures; class HeapRegion; +class Klass; class outputStream; class G1ParScanThreadState : public CHeapObj { @@ -83,6 +84,8 @@ class G1ParScanThreadState : public CHeapObj { // Size (in elements) of a partial objArray task chunk. int _partial_objarray_chunk_size; PartialArrayTaskStepper _partial_array_stepper; + // Used to check whether string dedup should be applied to an object. + Klass* _string_klass_or_null; G1RedirtyCardsQueue& redirty_cards_queue() { return _rdcq; } G1CardTable* ct() { return _ct; } diff --git a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp index 88907a6f87f..69e0778a904 100644 --- a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp +++ b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp @@ -220,7 +220,7 @@ static bool map_nvdimm_space(ReservedSpace rs) { return false; } // commit this memory in nv-dimm - char* ret = os::attempt_reserve_memory_at(rs.base(), rs.size(), _backing_fd); + char* ret = os::attempt_map_memory_to_file_at(rs.base(), rs.size(), _backing_fd); if (ret != rs.base()) { if (ret != NULL) { diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index 98809fc08b4..a6a55984db8 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -906,14 +906,27 @@ void G1RemSet::scan_collection_set_regions(G1ParScanThreadState* pss, } } -void G1RemSet::prepare_region_for_scan(HeapRegion* region) { - uint hrm_index = region->hrm_index(); +#ifdef ASSERT +void G1RemSet::assert_scan_top_is_null(uint hrm_index) { + assert(_scan_state->scan_top(hrm_index) == NULL, + "scan_top of region %u is unexpectedly " PTR_FORMAT, + hrm_index, p2i(_scan_state->scan_top(hrm_index))); +} +#endif + +void G1RemSet::prepare_region_for_scan(HeapRegion* r) { + uint hrm_index = r->hrm_index(); - if (region->is_old_or_humongous_or_archive()) { - _scan_state->set_scan_top(hrm_index, region->top()); + // Only update non-collection set old regions, others must have already been set + // to NULL (don't scan) in the initialization. + if (r->in_collection_set()) { + assert_scan_top_is_null(hrm_index); + } else if (r->is_old_or_humongous_or_archive()) { + _scan_state->set_scan_top(hrm_index, r->top()); } else { - assert(region->in_collection_set() || region->is_free(), - "Should only be free or in the collection set at this point %s", region->get_type_str()); + assert_scan_top_is_null(hrm_index); + assert(r->is_free(), + "Region %u should be free region but is %s", hrm_index, r->get_type_str()); } } @@ -1291,7 +1304,7 @@ void G1RemSet::cleanup_after_scan_heap_roots() { inline void check_card_ptr(CardTable::CardValue* card_ptr, G1CardTable* ct) { #ifdef ASSERT G1CollectedHeap* g1h = G1CollectedHeap::heap(); - assert(g1h->is_in_exact(ct->addr_for(card_ptr)), + assert(g1h->is_in(ct->addr_for(card_ptr)), "Card at " PTR_FORMAT " index " SIZE_FORMAT " representing heap at " PTR_FORMAT " (%u) must be in committed heap", p2i(card_ptr), ct->index_for(ct->addr_for(card_ptr)), diff --git a/src/hotspot/share/gc/g1/g1RemSet.hpp b/src/hotspot/share/gc/g1/g1RemSet.hpp index c44ea34c739..f1a090d9b5b 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.hpp +++ b/src/hotspot/share/gc/g1/g1RemSet.hpp @@ -67,6 +67,8 @@ class G1RemSet: public CHeapObj { G1HotCardCache* _hot_card_cache; void print_merge_heap_roots_stats(); + + void assert_scan_top_is_null(uint hrm_index) PRODUCT_RETURN; public: typedef CardTable::CardValue CardValue; diff --git a/src/hotspot/share/gc/g1/g1ServiceThread.cpp b/src/hotspot/share/gc/g1/g1ServiceThread.cpp index 0c7f91ab6d7..02762b375a0 100644 --- a/src/hotspot/share/gc/g1/g1ServiceThread.cpp +++ b/src/hotspot/share/gc/g1/g1ServiceThread.cpp @@ -36,92 +36,69 @@ #include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" -G1ServiceThread::G1ServiceThread() : - ConcurrentGCThread(), - _monitor(Mutex::nonleaf, - "G1ServiceThread monitor", - true, - Monitor::_safepoint_check_never), - _last_periodic_gc_attempt_s(os::elapsedTime()), - _vtime_accum(0) { - set_name("G1 Service"); - create_and_start(); +G1SentinelTask::G1SentinelTask() : G1ServiceTask("Sentinel Task") { + set_time(max_jlong); + set_next(this); } -void G1ServiceThread::sleep_before_next_cycle() { - MonitorLocker ml(&_monitor, Mutex::_no_safepoint_check_flag); - if (!should_terminate()) { - uintx waitms = G1ConcRefinementServiceIntervalMillis; - ml.wait(waitms); - } +void G1SentinelTask::execute() { + guarantee(false, "Sentinel service task should never be executed."); } -bool G1ServiceThread::should_start_periodic_gc() { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - // If we are currently in a concurrent mark we are going to uncommit memory soon. - if (g1h->concurrent_mark()->cm_thread()->in_progress()) { - log_debug(gc, periodic)("Concurrent cycle in progress. Skipping."); - return false; - } +// Task handling periodic GCs +class G1PeriodicGCTask : public G1ServiceTask { + bool should_start_periodic_gc() { + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + // If we are currently in a concurrent mark we are going to uncommit memory soon. + if (g1h->concurrent_mark()->cm_thread()->in_progress()) { + log_debug(gc, periodic)("Concurrent cycle in progress. Skipping."); + return false; + } - // Check if enough time has passed since the last GC. - uintx time_since_last_gc = (uintx)g1h->time_since_last_collection().milliseconds(); - if ((time_since_last_gc < G1PeriodicGCInterval)) { - log_debug(gc, periodic)("Last GC occurred " UINTX_FORMAT "ms before which is below threshold " UINTX_FORMAT "ms. Skipping.", - time_since_last_gc, G1PeriodicGCInterval); - return false; - } + // Check if enough time has passed since the last GC. + uintx time_since_last_gc = (uintx)g1h->time_since_last_collection().milliseconds(); + if ((time_since_last_gc < G1PeriodicGCInterval)) { + log_debug(gc, periodic)("Last GC occurred " UINTX_FORMAT "ms before which is below threshold " UINTX_FORMAT "ms. Skipping.", + time_since_last_gc, G1PeriodicGCInterval); + return false; + } - // Check if load is lower than max. - double recent_load; - if ((G1PeriodicGCSystemLoadThreshold > 0.0f) && - (os::loadavg(&recent_load, 1) == -1 || recent_load > G1PeriodicGCSystemLoadThreshold)) { - log_debug(gc, periodic)("Load %1.2f is higher than threshold %1.2f. Skipping.", - recent_load, G1PeriodicGCSystemLoadThreshold); - return false; + // Check if load is lower than max. + double recent_load; + if ((G1PeriodicGCSystemLoadThreshold > 0.0f) && + (os::loadavg(&recent_load, 1) == -1 || recent_load > G1PeriodicGCSystemLoadThreshold)) { + log_debug(gc, periodic)("Load %1.2f is higher than threshold %1.2f. Skipping.", + recent_load, G1PeriodicGCSystemLoadThreshold); + return false; + } + return true; } - return true; -} + void check_for_periodic_gc(){ + // If disabled, just return. + if (G1PeriodicGCInterval == 0) { + return; + } -void G1ServiceThread::check_for_periodic_gc(){ - // If disabled, just return. - if (G1PeriodicGCInterval == 0) { - return; - } - if ((os::elapsedTime() - _last_periodic_gc_attempt_s) > (G1PeriodicGCInterval / 1000.0)) { log_debug(gc, periodic)("Checking for periodic GC."); if (should_start_periodic_gc()) { if (!G1CollectedHeap::heap()->try_collect(GCCause::_g1_periodic_collection)) { log_debug(gc, periodic)("GC request denied. Skipping."); } } - _last_periodic_gc_attempt_s = os::elapsedTime(); } -} - -void G1ServiceThread::run_service() { - double vtime_start = os::elapsedVTime(); - - while (!should_terminate()) { - sample_young_list_rs_length(); - - if (os::supports_vtime()) { - _vtime_accum = (os::elapsedVTime() - vtime_start); - } else { - _vtime_accum = 0.0; - } +public: + G1PeriodicGCTask(const char* name) : G1ServiceTask(name) { } + virtual void execute() { check_for_periodic_gc(); - - sleep_before_next_cycle(); + // G1PeriodicGCInterval is a manageable flag and can be updated + // during runtime. If no value is set, wait a second and run it + // again to see if the value has been updated. Otherwise use the + // real value provided. + schedule(G1PeriodicGCInterval == 0 ? 1000 : G1PeriodicGCInterval); } -} - -void G1ServiceThread::stop_service() { - MutexLocker x(&_monitor, Mutex::_no_safepoint_check_flag); - _monitor.notify(); -} +}; class G1YoungRemSetSamplingClosure : public HeapRegionClosure { SuspendibleThreadSetJoiner* _sts; @@ -155,19 +132,278 @@ class G1YoungRemSetSamplingClosure : public HeapRegionClosure { size_t sampled_rs_length() const { return _sampled_rs_length; } }; -void G1ServiceThread::sample_young_list_rs_length() { - SuspendibleThreadSetJoiner sts; - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - G1Policy* policy = g1h->policy(); +// Task handling young gen remembered set sampling. +class G1RemSetSamplingTask : public G1ServiceTask { + // Sample the current length of remembered sets for young. + // + // At the end of the GC G1 determines the length of the young gen based on + // how much time the next GC can take, and when the next GC may occur + // according to the MMU. + // + // The assumption is that a significant part of the GC is spent on scanning + // the remembered sets (and many other components), so this thread constantly + // reevaluates the prediction for the remembered set scanning costs, and potentially + // G1Policy resizes the young gen. This may do a premature GC or even + // increase the young gen size to keep pause time length goal. + void sample_young_list_rs_length(){ + SuspendibleThreadSetJoiner sts; + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + G1Policy* policy = g1h->policy(); - if (policy->use_adaptive_young_list_length()) { - G1YoungRemSetSamplingClosure cl(&sts); + if (policy->use_adaptive_young_list_length()) { + G1YoungRemSetSamplingClosure cl(&sts); - G1CollectionSet* g1cs = g1h->collection_set(); - g1cs->iterate(&cl); + G1CollectionSet* g1cs = g1h->collection_set(); + g1cs->iterate(&cl); - if (cl.is_complete()) { - policy->revise_young_list_target_length_if_necessary(cl.sampled_rs_length()); + if (cl.is_complete()) { + policy->revise_young_list_target_length_if_necessary(cl.sampled_rs_length()); + } } } +public: + G1RemSetSamplingTask(const char* name) : G1ServiceTask(name) { } + virtual void execute() { + sample_young_list_rs_length(); + schedule(G1ConcRefinementServiceIntervalMillis); + } +}; + +G1ServiceThread::G1ServiceThread() : + ConcurrentGCThread(), + _monitor(Mutex::nonleaf, + "G1ServiceThread monitor", + true, + Monitor::_safepoint_check_never), + _task_queue(), + _remset_task(new G1RemSetSamplingTask("Remembered Set Sampling Task")), + _periodic_gc_task(new G1PeriodicGCTask("Periodic GC Task")), + _vtime_accum(0) { + set_name("G1 Service"); + create_and_start(); +} + +G1ServiceThread::~G1ServiceThread() { + delete _remset_task; + delete _periodic_gc_task; +} + +void G1ServiceThread::register_task(G1ServiceTask* task, jlong delay) { + guarantee(!task->is_registered(), "Task already registered"); + guarantee(task->next() == NULL, "Task already in queue"); + + // Make sure the service thread is still up and running, there is a race + // during shutdown where the service thread has been stopped, but other + // GC threads might still be running and trying to add tasks. + if (has_terminated()) { + log_debug(gc, task)("G1 Service Thread (%s) (terminated)", task->name()); + return; + } + + log_debug(gc, task)("G1 Service Thread (%s) (register)", task->name()); + + // Associate the task with the service thread. + task->set_service_thread(this); + + // Schedule the task to run after the given delay. + schedule_task(task, delay); + + // Notify the service thread that there is a new task, thread might + // be waiting and the newly added task might be first in the list. + MonitorLocker ml(&_monitor, Mutex::_no_safepoint_check_flag); + ml.notify(); +} + +void G1ServiceThread::schedule_task(G1ServiceTask* task, jlong delay_ms) { + guarantee(task->is_registered(), "Must be registered before scheduled"); + guarantee(task->next() == NULL, "Task already in queue"); + + // Schedule task by setting the task time and adding it to queue. + jlong delay = TimeHelper::millis_to_counter(delay_ms); + task->set_time(os::elapsed_counter() + delay); + + MutexLocker ml(&_monitor, Mutex::_no_safepoint_check_flag); + _task_queue.add_ordered(task); + + log_trace(gc, task)("G1 Service Thread (%s) (schedule) @%1.3fs", + task->name(), TimeHelper::counter_to_seconds(task->time())); +} + +int64_t G1ServiceThread::time_to_next_task_ms() { + assert(_monitor.owned_by_self(), "Must be owner of lock"); + assert(!_task_queue.is_empty(), "Should not be called for empty list"); + + jlong time_diff = _task_queue.peek()->time() - os::elapsed_counter(); + if (time_diff < 0) { + // Run without sleeping. + return 0; + } + + // Return sleep time in milliseconds. + return (int64_t) TimeHelper::counter_to_millis(time_diff); +} + +void G1ServiceThread::sleep_before_next_cycle() { + if (should_terminate()) { + return; + } + + MonitorLocker ml(&_monitor, Mutex::_no_safepoint_check_flag); + if (_task_queue.is_empty()) { + // Sleep until new task is registered if no tasks available. + log_trace(gc, task)("G1 Service Thread (wait for new tasks)"); + ml.wait(0); + } else { + int64_t sleep_ms = time_to_next_task_ms(); + if (sleep_ms > 0) { + log_trace(gc, task)("G1 Service Thread (wait) %1.3fs", sleep_ms / 1000.0); + ml.wait(sleep_ms); + } + } +} + +G1ServiceTask* G1ServiceThread::pop_due_task() { + MutexLocker ml(&_monitor, Mutex::_no_safepoint_check_flag); + if (_task_queue.is_empty() || time_to_next_task_ms() != 0) { + return NULL; + } + + return _task_queue.pop(); +} + +void G1ServiceThread::run_task(G1ServiceTask* task) { + double start = os::elapsedTime(); + double vstart = os::elapsedVTime(); + + log_debug(gc, task, start)("G1 Service Thread (%s) (run)", task->name()); + task->execute(); + + double duration = os::elapsedTime() - start; + double vduration = os::elapsedVTime() - vstart; + log_debug(gc, task)("G1 Service Thread (%s) (run) %1.3fms (cpu: %1.3fms)", + task->name(), duration * MILLIUNITS, vduration * MILLIUNITS); +} + +void G1ServiceThread::run_service() { + double vtime_start = os::elapsedVTime(); + + // Register the tasks handled by the service thread. + register_task(_periodic_gc_task); + register_task(_remset_task); + + while (!should_terminate()) { + G1ServiceTask* task = pop_due_task(); + if (task != NULL) { + run_task(task); + } + + if (os::supports_vtime()) { + _vtime_accum = (os::elapsedVTime() - vtime_start); + } else { + _vtime_accum = 0.0; + } + sleep_before_next_cycle(); + } + + log_debug(gc, task)("G1 Service Thread (stopping)"); +} + +void G1ServiceThread::stop_service() { + MonitorLocker ml(&_monitor, Mutex::_no_safepoint_check_flag); + ml.notify(); +} + +G1ServiceTask::G1ServiceTask(const char* name) : + _time(), + _name(name), + _next(NULL), + _service_thread(NULL) { } + +void G1ServiceTask::set_service_thread(G1ServiceThread* thread) { + _service_thread = thread; +} + +bool G1ServiceTask::is_registered() { + return _service_thread != NULL; +} + +void G1ServiceTask::schedule(jlong delay_ms) { + _service_thread->schedule_task(this, delay_ms); +} + +const char* G1ServiceTask::name() { + return _name; +} + +void G1ServiceTask::set_time(jlong time) { + assert(_next == NULL, "Not allowed to update time while in queue"); + _time = time; +} + +jlong G1ServiceTask::time() { + return _time; +} + +void G1ServiceTask::set_next(G1ServiceTask* next) { + _next = next; +} + +G1ServiceTask* G1ServiceTask::next() { + return _next; +} + +G1ServiceTaskQueue::G1ServiceTaskQueue() : _sentinel() { } + +G1ServiceTask* G1ServiceTaskQueue::pop() { + verify_task_queue(); + + G1ServiceTask* task = _sentinel.next(); + _sentinel.set_next(task->next()); + task->set_next(NULL); + + return task; +} + +G1ServiceTask* G1ServiceTaskQueue::peek() { + verify_task_queue(); + return _sentinel.next(); +} + +bool G1ServiceTaskQueue::is_empty() { + return &_sentinel == _sentinel.next(); +} + +void G1ServiceTaskQueue::add_ordered(G1ServiceTask* task) { + assert(task != NULL, "not a valid task"); + assert(task->next() == NULL, "invariant"); + assert(task->time() != max_jlong, "invalid time for task"); + + G1ServiceTask* current = &_sentinel; + while (task->time() >= current->next()->time()) { + assert(task != current, "Task should only be added once."); + current = current->next(); + } + + // Update the links. + task->set_next(current->next()); + current->set_next(task); + + verify_task_queue(); +} + +#ifdef ASSERT +void G1ServiceTaskQueue::verify_task_queue() { + G1ServiceTask* cur = _sentinel.next(); + + assert(cur != &_sentinel, "Should never try to verify empty queue"); + while (cur != &_sentinel) { + G1ServiceTask* next = cur->next(); + assert(cur->time() <= next->time(), + "Tasks out of order, prev: %s (%1.3fs), next: %s (%1.3fs)", + cur->name(), TimeHelper::counter_to_seconds(cur->time()), next->name(), TimeHelper::counter_to_seconds(next->time())); + + assert(cur != next, "Invariant"); + cur = next; + } } +#endif diff --git a/src/hotspot/share/gc/g1/g1ServiceThread.hpp b/src/hotspot/share/gc/g1/g1ServiceThread.hpp index 3454f65d02e..ec86dead879 100644 --- a/src/hotspot/share/gc/g1/g1ServiceThread.hpp +++ b/src/hotspot/share/gc/g1/g1ServiceThread.hpp @@ -28,43 +28,111 @@ #include "gc/shared/concurrentGCThread.hpp" #include "runtime/mutex.hpp" +class G1PeriodicGCTask; +class G1RemSetSamplingTask; +class G1ServiceTaskQueue; +class G1ServiceThread; + +class G1ServiceTask : public CHeapObj { + friend class G1ServiceTaskQueue; + friend class G1ServiceThread; + + // The next absolute time this task should be executed. + jlong _time; + // Name of the task. + const char* _name; + // Next task in the task queue. + G1ServiceTask* _next; + // The service thread this task is registered with. + G1ServiceThread* _service_thread; + + void set_service_thread(G1ServiceThread* thread); + bool is_registered(); + +public: + G1ServiceTask(const char* name); + + jlong time(); + const char* name(); + G1ServiceTask* next(); + + // Do the actual work for the task. To get added back to the + // execution queue a task can call schedule(delay_ms). + virtual void execute() = 0; + +protected: + // Schedule the task on the associated service thread + // using the provided delay in milliseconds. + void schedule(jlong delay_ms); + + // These setters are protected for use by testing and the + // sentinel task only. + void set_time(jlong time); + void set_next(G1ServiceTask* next); +}; + +class G1SentinelTask : public G1ServiceTask { +public: + G1SentinelTask(); + virtual void execute(); +}; + +class G1ServiceTaskQueue { + // The sentinel task is the entry point of this priority queue holding the + // service tasks. The queue is ordered by the time the tasks are scheduled + // to run. To simplify list management the sentinel task has its time set + // to max_jlong, guaranteeing it to be the last task in the queue. + G1SentinelTask _sentinel; + + // Verify that the queue is ordered. + void verify_task_queue() NOT_DEBUG_RETURN; +public: + G1ServiceTaskQueue(); + G1ServiceTask* pop(); + G1ServiceTask* peek(); + void add_ordered(G1ServiceTask* task); + bool is_empty(); +}; + // The G1ServiceThread is used to periodically do a number of different tasks: // - re-assess the validity of the prediction for the // remembered set lengths of the young generation. // - check if a periodic GC should be scheduled. class G1ServiceThread: public ConcurrentGCThread { -private: + friend class G1ServiceTask; + // The monitor is used to ensure thread safety for the task queue + // and allow other threads to signal the service thread to wake up. Monitor _monitor; + G1ServiceTaskQueue _task_queue; - double _last_periodic_gc_attempt_s; + G1RemSetSamplingTask* _remset_task; + G1PeriodicGCTask* _periodic_gc_task; double _vtime_accum; // Accumulated virtual time. - // Sample the current length of remembered sets for young. - // - // At the end of the GC G1 determines the length of the young gen based on - // how much time the next GC can take, and when the next GC may occur - // according to the MMU. - // - // The assumption is that a significant part of the GC is spent on scanning - // the remembered sets (and many other components), so this thread constantly - // reevaluates the prediction for the remembered set scanning costs, and potentially - // G1Policy resizes the young gen. This may do a premature GC or even - // increase the young gen size to keep pause time length goal. - void sample_young_list_rs_length(); - void run_service(); - void check_for_periodic_gc(); - void stop_service(); + // Returns the time in milliseconds until the next task is due. + // Used both to determine if there are tasks ready to run and + // how long to sleep when nothing is ready. + int64_t time_to_next_task_ms(); void sleep_before_next_cycle(); - bool should_start_periodic_gc(); + G1ServiceTask* pop_due_task(); + void run_task(G1ServiceTask* task); + + // Schedule a registered task to run after the given delay. + void schedule_task(G1ServiceTask* task, jlong delay); public: G1ServiceThread(); + ~G1ServiceThread(); + double vtime_accum() { return _vtime_accum; } + // Register a task with the service thread and schedule it. If + // no delay is specified the task is scheduled to run directly. + void register_task(G1ServiceTask* task, jlong delay = 0); }; #endif // SHARE_GC_G1_G1SERVICETHREAD_HPP diff --git a/src/hotspot/share/gc/g1/g1StringDedup.cpp b/src/hotspot/share/gc/g1/g1StringDedup.cpp index 2da35163fad..122f1a7e9c7 100644 --- a/src/hotspot/share/gc/g1/g1StringDedup.cpp +++ b/src/hotspot/share/gc/g1/g1StringDedup.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,29 +41,23 @@ void G1StringDedup::initialize() { } bool G1StringDedup::is_candidate_from_mark(oop obj) { - if (java_lang_String::is_instance_inlined(obj)) { - bool from_young = G1CollectedHeap::heap()->heap_region_containing(obj)->is_young(); - if (from_young && obj->age() < StringDeduplicationAgeThreshold) { - // Candidate found. String is being evacuated from young to old but has not - // reached the deduplication age threshold, i.e. has not previously been a - // candidate during its life in the young generation. - return true; - } - } - - // Not a candidate - return false; + bool from_young = G1CollectedHeap::heap()->heap_region_containing(obj)->is_young(); + // Candidate if string is being evacuated from young to old but has not + // reached the deduplication age threshold, i.e. has not previously been a + // candidate during its life in the young generation. + return from_young && (obj->age() < StringDeduplicationAgeThreshold); } void G1StringDedup::enqueue_from_mark(oop java_string, uint worker_id) { assert(is_enabled(), "String deduplication not enabled"); + assert(java_lang_String::is_instance(java_string), "not a String"); if (is_candidate_from_mark(java_string)) { G1StringDedupQueue::push(worker_id, java_string); } } bool G1StringDedup::is_candidate_from_evacuation(bool from_young, bool to_young, oop obj) { - if (from_young && java_lang_String::is_instance_inlined(obj)) { + if (from_young) { if (to_young && obj->age() == StringDeduplicationAgeThreshold) { // Candidate found. String is being evacuated from young to young and just // reached the deduplication age threshold. @@ -83,6 +77,7 @@ bool G1StringDedup::is_candidate_from_evacuation(bool from_young, bool to_young, void G1StringDedup::enqueue_from_evacuation(bool from_young, bool to_young, uint worker_id, oop java_string) { assert(is_enabled(), "String deduplication not enabled"); + assert(java_lang_String::is_instance(java_string), "not a String"); if (is_candidate_from_evacuation(from_young, to_young, java_string)) { G1StringDedupQueue::push(worker_id, java_string); } diff --git a/src/hotspot/share/gc/g1/g1StringDedup.hpp b/src/hotspot/share/gc/g1/g1StringDedup.hpp index 7c0d3e46eaf..2f03a890ca6 100644 --- a/src/hotspot/share/gc/g1/g1StringDedup.hpp +++ b/src/hotspot/share/gc/g1/g1StringDedup.hpp @@ -65,8 +65,8 @@ class G1StringDedup : public StringDedup { // Candidate selection policies, returns true if the given object is // candidate for string deduplication. - static bool is_candidate_from_mark(oop obj); - static bool is_candidate_from_evacuation(bool from_young, bool to_young, oop obj); + static bool is_candidate_from_mark(oop java_string); + static bool is_candidate_from_evacuation(bool from_young, bool to_young, oop java_string); public: // Initialize string deduplication. @@ -75,6 +75,7 @@ class G1StringDedup : public StringDedup { // Enqueues a deduplication candidate for later processing by the deduplication // thread. Before enqueuing, these functions apply the appropriate candidate // selection policy to filters out non-candidates. + // Precondition for both is that java_string is a String. static void enqueue_from_mark(oop java_string, uint worker_id); static void enqueue_from_evacuation(bool from_young, bool to_young, unsigned int queue, oop java_string); diff --git a/src/hotspot/share/gc/g1/heapRegion.cpp b/src/hotspot/share/gc/g1/heapRegion.cpp index eadde715f6f..b901ceb15fd 100644 --- a/src/hotspot/share/gc/g1/heapRegion.cpp +++ b/src/hotspot/share/gc/g1/heapRegion.cpp @@ -516,9 +516,6 @@ class G1VerificationClosure : public BasicOopIterateClosure { obj->print_on(out); #endif // PRODUCT } - - // This closure provides its own oop verification code. - debug_only(virtual bool should_verify_oops() { return false; }) }; class VerifyLiveClosure : public G1VerificationClosure { @@ -655,9 +652,6 @@ class G1Mux2Closure : public BasicOopIterateClosure { } virtual inline void do_oop(oop* p) { do_oop_work(p); } virtual inline void do_oop(narrowOop* p) { do_oop_work(p); } - - // This closure provides its own oop verification code. - debug_only(virtual bool should_verify_oops() { return false; }) }; void HeapRegion::verify(VerifyOption vo, diff --git a/src/hotspot/share/gc/g1/heapRegion.hpp b/src/hotspot/share/gc/g1/heapRegion.hpp index 3756d5fe094..a7566037b63 100644 --- a/src/hotspot/share/gc/g1/heapRegion.hpp +++ b/src/hotspot/share/gc/g1/heapRegion.hpp @@ -123,7 +123,9 @@ class HeapRegion : public CHeapObj { bool is_empty() const { return used() == 0; } private: - void reset_after_compaction() { set_top(compaction_top()); } + void reset_compaction_top_after_compaction(); + + void reset_after_full_gc_common(); void clear(bool mangle_space); @@ -165,16 +167,11 @@ class HeapRegion : public CHeapObj { HeapWord* initialize_threshold(); HeapWord* cross_threshold(HeapWord* start, HeapWord* end); - // Update heap region to be consistent after Full GC compaction. - void reset_humongous_during_compaction() { - assert(is_humongous(), - "should only be called for humongous regions"); - zero_marked_bytes(); - init_top_at_mark_start(); - } - // Update heap region to be consistent after Full GC compaction. - void complete_compaction(); + // Update heap region that has been compacted to be consistent after Full GC. + void reset_compacted_after_full_gc(); + // Update pinned heap region (not compacted) to be consistent after Full GC. + void reset_pinned_after_full_gc(); // All allocated blocks are occupied by objects in a HeapRegion bool block_is_obj(const HeapWord* p) const; diff --git a/src/hotspot/share/gc/g1/heapRegion.inline.hpp b/src/hotspot/share/gc/g1/heapRegion.inline.hpp index 1736d4b512d..31b787869c5 100644 --- a/src/hotspot/share/gc/g1/heapRegion.inline.hpp +++ b/src/hotspot/share/gc/g1/heapRegion.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -181,18 +181,44 @@ inline size_t HeapRegion::block_size(const HeapWord *addr) const { return block_size_using_bitmap(addr, G1CollectedHeap::heap()->concurrent_mark()->prev_mark_bitmap()); } -inline void HeapRegion::complete_compaction() { - // Reset space and bot after compaction is complete if needed. - reset_after_compaction(); - if (is_empty()) { - reset_bot(); - } +inline void HeapRegion::reset_compaction_top_after_compaction() { + set_top(compaction_top()); + _compaction_top = bottom(); +} + +inline void HeapRegion::reset_compacted_after_full_gc() { + assert(!is_pinned(), "must be"); - // After a compaction the mark bitmap is invalid, so we must - // treat all objects as being inside the unmarked area. + reset_compaction_top_after_compaction(); + // After a compaction the mark bitmap in a non-pinned regions is invalid. + // We treat all objects as being above PTAMS. zero_marked_bytes(); init_top_at_mark_start(); + reset_after_full_gc_common(); +} + +inline void HeapRegion::reset_pinned_after_full_gc() { + assert(!is_free(), "should not have compacted free region"); + assert(is_pinned(), "must be"); + + assert(compaction_top() == bottom(), + "region %u compaction_top " PTR_FORMAT " must not be different from bottom " PTR_FORMAT, + hrm_index(), p2i(compaction_top()), p2i(bottom())); + + _prev_top_at_mark_start = top(); // Keep existing top and usage. + _prev_marked_bytes = used(); + _next_top_at_mark_start = bottom(); + _next_marked_bytes = 0; + + reset_after_full_gc_common(); +} + +inline void HeapRegion::reset_after_full_gc_common() { + if (is_empty()) { + reset_bot(); + } + // Clear unused heap memory in debug builds. if (ZapUnusedHeapArea) { mangle_unused_area(); diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp index 6b27e7de06f..ce7b2152659 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp @@ -26,6 +26,7 @@ #include "gc/parallel/mutableNUMASpace.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/spaceDecorator.hpp" +#include "gc/shared/workgroup.hpp" #include "memory/allocation.inline.hpp" #include "oops/oop.inline.hpp" #include "runtime/atomic.hpp" @@ -572,7 +573,8 @@ void MutableNUMASpace::merge_regions(MemRegion new_region, MemRegion* intersecti void MutableNUMASpace::initialize(MemRegion mr, bool clear_space, bool mangle_space, - bool setup_pages) { + bool setup_pages, + WorkGang* pretouch_gang) { assert(clear_space, "Reallocation will destroy data!"); assert(lgrp_spaces()->length() > 0, "There should be at least one space"); diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp index bc4b73639ad..d9c6afe0cc5 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -195,7 +195,11 @@ class MutableNUMASpace : public MutableSpace { MutableNUMASpace(size_t alignment); virtual ~MutableNUMASpace(); // Space initialization. - virtual void initialize(MemRegion mr, bool clear_space, bool mangle_space, bool setup_pages = SetupPages); + virtual void initialize(MemRegion mr, + bool clear_space, + bool mangle_space, + bool setup_pages = SetupPages, + WorkGang* pretouch_gang = NULL); // Update space layout if necessary. Do all adaptive resizing job. virtual void update(); // Update allocation rate averages. diff --git a/src/hotspot/share/gc/parallel/mutableSpace.cpp b/src/hotspot/share/gc/parallel/mutableSpace.cpp index e98128101e1..5d575ca7e45 100644 --- a/src/hotspot/share/gc/parallel/mutableSpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableSpace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "gc/parallel/mutableSpace.hpp" +#include "gc/shared/pretouchTask.hpp" #include "gc/shared/spaceDecorator.inline.hpp" #include "memory/iterator.inline.hpp" #include "memory/universe.hpp" @@ -60,14 +61,11 @@ void MutableSpace::numa_setup_pages(MemRegion mr, bool clear_space) { } } -void MutableSpace::pretouch_pages(MemRegion mr) { - os::pretouch_memory(mr.start(), mr.end()); -} - void MutableSpace::initialize(MemRegion mr, bool clear_space, bool mangle_space, - bool setup_pages) { + bool setup_pages, + WorkGang* pretouch_gang) { assert(Universe::on_page_boundary(mr.start()) && Universe::on_page_boundary(mr.end()), "invalid space boundaries"); @@ -114,8 +112,13 @@ void MutableSpace::initialize(MemRegion mr, } if (AlwaysPreTouch) { - pretouch_pages(head); - pretouch_pages(tail); + size_t page_size = UseLargePages ? os::large_page_size() : os::vm_page_size(); + + PretouchTask::pretouch("ParallelGC PreTouch head", (char*)head.start(), (char*)head.end(), + page_size, pretouch_gang); + + PretouchTask::pretouch("ParallelGC PreTouch tail", (char*)tail.start(), (char*)tail.end(), + page_size, pretouch_gang); } // Remember where we stopped so that we can continue later. diff --git a/src/hotspot/share/gc/parallel/mutableSpace.hpp b/src/hotspot/share/gc/parallel/mutableSpace.hpp index acc4285f576..84fe8dba86d 100644 --- a/src/hotspot/share/gc/parallel/mutableSpace.hpp +++ b/src/hotspot/share/gc/parallel/mutableSpace.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,8 @@ #include "memory/memRegion.hpp" #include "utilities/copy.hpp" +class WorkGang; + // A MutableSpace is a subtype of ImmutableSpace that supports the // concept of allocation. This includes the concepts that a space may // be only partially full, and the query methods that go with such @@ -56,7 +58,6 @@ class MutableSpace: public ImmutableSpace { MutableSpaceMangler* mangler() { return _mangler; } void numa_setup_pages(MemRegion mr, bool clear_space); - void pretouch_pages(MemRegion mr); void set_last_setup_region(MemRegion mr) { _last_setup_region = mr; } MemRegion last_setup_region() const { return _last_setup_region; } @@ -87,7 +88,8 @@ class MutableSpace: public ImmutableSpace { virtual void initialize(MemRegion mr, bool clear_space, bool mangle_space, - bool setup_pages = SetupPages); + bool setup_pages = SetupPages, + WorkGang* pretouch_gang = NULL); virtual void clear(bool mangle_space); // Does the usual initialization but optionally resets top to bottom. diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index ec2cce62fbe..4f3f41e5ccf 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -88,6 +88,9 @@ jint ParallelScavengeHeap::initialize() { ReservedSpace young_rs = heap_rs.last_part(MaxOldSize); assert(young_rs.size() == MaxNewSize, "Didn't reserve all of the heap"); + // Set up WorkGang + _workers.initialize_workers(); + // Create and initialize the generations. _young_gen = new PSYoungGen( young_rs, @@ -132,9 +135,6 @@ jint ParallelScavengeHeap::initialize() { return JNI_ENOMEM; } - // Set up WorkGang - _workers.initialize_workers(); - GCInitLogger::print(); return JNI_OK; @@ -539,6 +539,71 @@ void ParallelScavengeHeap::object_iterate(ObjectClosure* cl) { old_gen()->object_iterate(cl); } +// The HeapBlockClaimer is used during parallel iteration over the heap, +// allowing workers to claim heap areas ("blocks"), gaining exclusive rights to these. +// The eden and survivor spaces are treated as single blocks as it is hard to divide +// these spaces. +// The old space is divided into fixed-size blocks. +class HeapBlockClaimer : public StackObj { + size_t _claimed_index; + +public: + static const size_t InvalidIndex = SIZE_MAX; + static const size_t EdenIndex = 0; + static const size_t SurvivorIndex = 1; + static const size_t NumNonOldGenClaims = 2; + + HeapBlockClaimer() : _claimed_index(EdenIndex) { } + // Claim the block and get the block index. + size_t claim_and_get_block() { + size_t block_index; + block_index = Atomic::fetch_and_add(&_claimed_index, 1u); + + PSOldGen* old_gen = ParallelScavengeHeap::heap()->old_gen(); + size_t num_claims = old_gen->num_iterable_blocks() + NumNonOldGenClaims; + + return block_index < num_claims ? block_index : InvalidIndex; + } +}; + +void ParallelScavengeHeap::object_iterate_parallel(ObjectClosure* cl, + HeapBlockClaimer* claimer) { + size_t block_index = claimer->claim_and_get_block(); + // Iterate until all blocks are claimed + if (block_index == HeapBlockClaimer::EdenIndex) { + young_gen()->eden_space()->object_iterate(cl); + block_index = claimer->claim_and_get_block(); + } + if (block_index == HeapBlockClaimer::SurvivorIndex) { + young_gen()->from_space()->object_iterate(cl); + young_gen()->to_space()->object_iterate(cl); + block_index = claimer->claim_and_get_block(); + } + while (block_index != HeapBlockClaimer::InvalidIndex) { + old_gen()->object_iterate_block(cl, block_index - HeapBlockClaimer::NumNonOldGenClaims); + block_index = claimer->claim_and_get_block(); + } +} + +class PSScavengeParallelObjectIterator : public ParallelObjectIterator { +private: + ParallelScavengeHeap* _heap; + HeapBlockClaimer _claimer; + +public: + PSScavengeParallelObjectIterator() : + _heap(ParallelScavengeHeap::heap()), + _claimer() {} + + virtual void object_iterate(ObjectClosure* cl, uint worker_id) { + _heap->object_iterate_parallel(cl, &_claimer); + } +}; + +ParallelObjectIterator* ParallelScavengeHeap::parallel_object_iterator(uint thread_num) { + return new PSScavengeParallelObjectIterator(); +} + HeapWord* ParallelScavengeHeap::block_start(const void* addr) const { if (young_gen()->is_in_reserved(addr)) { assert(young_gen()->is_in(addr), diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp index ed72efea866..77f9ce1dc43 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp @@ -45,6 +45,7 @@ class AdjoiningGenerations; class GCHeapSummary; +class HeapBlockClaimer; class MemoryManager; class MemoryPool; class PSAdaptiveSizePolicy; @@ -207,6 +208,8 @@ class ParallelScavengeHeap : public CollectedHeap { size_t unsafe_max_tlab_alloc(Thread* thr) const; void object_iterate(ObjectClosure* cl); + void object_iterate_parallel(ObjectClosure* cl, HeapBlockClaimer* claimer); + virtual ParallelObjectIterator* parallel_object_iterator(uint thread_num); HeapWord* block_start(const void* addr) const; bool block_is_obj(const HeapWord* addr) const; diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp b/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp index 979db9e2870..48b9875672f 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp @@ -47,9 +47,6 @@ class PCMarkAndPushClosure: public OopClosure { template void do_oop_nv(T* p) { _compaction_manager->mark_and_push(p); } virtual void do_oop(oop* p) { do_oop_nv(p); } virtual void do_oop(narrowOop* p) { do_oop_nv(p); } - - // This closure provides its own oop verification code. - debug_only(virtual bool should_verify_oops() { return false; }) }; class PCIterateMarkAndPushClosure: public MetadataVisitingOopIterateClosure { @@ -64,9 +61,6 @@ class PCIterateMarkAndPushClosure: public MetadataVisitingOopIterateClosure { void do_klass_nv(Klass* k) { _compaction_manager->follow_klass(k); } void do_cld_nv(ClassLoaderData* cld) { _compaction_manager->follow_class_loader(cld); } - - // This closure provides its own oop verification code. - debug_only(virtual bool should_verify_oops() { return false; }) }; inline bool ParCompactionManager::steal(int queue_num, oop& t) { diff --git a/src/hotspot/share/gc/parallel/psOldGen.cpp b/src/hotspot/share/gc/parallel/psOldGen.cpp index fe4480294ca..8506079d5f0 100644 --- a/src/hotspot/share/gc/parallel/psOldGen.cpp +++ b/src/hotspot/share/gc/parallel/psOldGen.cpp @@ -131,7 +131,9 @@ void PSOldGen::initialize_work(const char* perf_data_name, int level) { _object_space = new MutableSpace(virtual_space()->alignment()); object_space()->initialize(cmr, SpaceDecorator::Clear, - SpaceDecorator::Mangle); + SpaceDecorator::Mangle, + MutableSpace::SetupPages, + &ParallelScavengeHeap::heap()->workers()); // Update the start_array start_array()->set_covered_region(cmr); @@ -171,6 +173,38 @@ HeapWord* PSOldGen::allocate(size_t word_size) { return res; } +size_t PSOldGen::num_iterable_blocks() const { + return (object_space()->used_in_bytes() + IterateBlockSize - 1) / IterateBlockSize; +} + +void PSOldGen::object_iterate_block(ObjectClosure* cl, size_t block_index) { + size_t block_word_size = IterateBlockSize / HeapWordSize; + assert((block_word_size % (ObjectStartArray::block_size)) == 0, + "Block size not a multiple of start_array block"); + + MutableSpace *space = object_space(); + + HeapWord* begin = space->bottom() + block_index * block_word_size; + HeapWord* end = MIN2(space->top(), begin + block_word_size); + + if (!start_array()->object_starts_in_range(begin, end)) { + return; + } + + // Get object starting at or reaching into this block. + HeapWord* start = start_array()->object_start(begin); + if (start < begin) { + start += oop(start)->size(); + } + assert(start >= begin, + "Object address" PTR_FORMAT " must be larger or equal to block address at " PTR_FORMAT, + p2i(start), p2i(begin)); + // Iterate all objects until the end. + for (HeapWord* p = start; p < end; p += oop(p)->size()) { + cl->do_object(oop(p)); + } +} + HeapWord* PSOldGen::expand_and_allocate(size_t word_size) { expand(word_size*HeapWordSize); if (GCExpandToAllocateDelayMillis > 0) { @@ -351,10 +385,15 @@ void PSOldGen::post_resize() { start_array()->set_covered_region(new_memregion); ParallelScavengeHeap::heap()->card_table()->resize_covered_region(new_memregion); + WorkGang* workers = Thread::current()->is_VM_thread() ? + &ParallelScavengeHeap::heap()->workers() : NULL; + // ALWAYS do this last!! object_space()->initialize(new_memregion, SpaceDecorator::DontClear, - SpaceDecorator::DontMangle); + SpaceDecorator::DontMangle, + MutableSpace::SetupPages, + workers); assert(new_word_size == heap_word_size(object_space()->capacity_in_bytes()), "Sanity"); diff --git a/src/hotspot/share/gc/parallel/psOldGen.hpp b/src/hotspot/share/gc/parallel/psOldGen.hpp index f7e43af064f..d882bab114b 100644 --- a/src/hotspot/share/gc/parallel/psOldGen.hpp +++ b/src/hotspot/share/gc/parallel/psOldGen.hpp @@ -52,6 +52,9 @@ class PSOldGen : public CHeapObj { const size_t _min_gen_size; const size_t _max_gen_size; + // Block size for parallel iteration + static const size_t IterateBlockSize = 1024 * 1024; + #ifdef ASSERT void assert_block_in_covered_region(MemRegion new_memregion) { // Explictly capture current covered_region in a local @@ -163,6 +166,14 @@ class PSOldGen : public CHeapObj { void oop_iterate(OopIterateClosure* cl) { object_space()->oop_iterate(cl); } void object_iterate(ObjectClosure* cl) { object_space()->object_iterate(cl); } + // Number of blocks to be iterated over in the used part of old gen. + size_t num_iterable_blocks() const; + // Iterate the objects starting in block block_index within [bottom, top) of the + // old gen. The object just reaching into this block is not iterated over. + // A block is an evenly sized non-overlapping part of the old gen of + // IterateBlockSize bytes. + void object_iterate_block(ObjectClosure* cl, size_t block_index); + // Debugging - do not use for time critical operations void print() const; virtual void print_on(outputStream* st) const; diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index ff001d14ccd..41e0d28d691 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -1058,7 +1058,7 @@ void PSParallelCompact::post_compact() // Delete metaspaces for unloaded class loaders and clean up loader_data graph ClassLoaderDataGraph::purge(/*at_safepoint*/true); - MetaspaceUtils::verify_metrics(); + DEBUG_ONLY(MetaspaceUtils::verify();) heap->prune_scavengable_nmethods(); diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.inline.hpp b/src/hotspot/share/gc/parallel/psParallelCompact.inline.hpp index df4f1c41cda..688da81e9c2 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.inline.hpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.inline.hpp @@ -134,8 +134,6 @@ class PCAdjustPointerClosure: public BasicOopIterateClosure { virtual void do_oop(oop* p) { do_oop_nv(p); } virtual void do_oop(narrowOop* p) { do_oop_nv(p); } - // This closure provides its own oop verification code. - debug_only(virtual bool should_verify_oops() { return false; }) virtual ReferenceIterationMode reference_iteration_mode() { return DO_FIELDS; } private: ParCompactionManager* _cm; diff --git a/src/hotspot/share/gc/parallel/psPromotionLAB.inline.hpp b/src/hotspot/share/gc/parallel/psPromotionLAB.inline.hpp index 1c883058310..b852324e877 100644 --- a/src/hotspot/share/gc/parallel/psPromotionLAB.inline.hpp +++ b/src/hotspot/share/gc/parallel/psPromotionLAB.inline.hpp @@ -32,20 +32,14 @@ HeapWord* PSYoungPromotionLAB::allocate(size_t size) { // Can't assert this, when young fills, we keep the LAB around, but flushed. // assert(_state != flushed, "Sanity"); - HeapWord* obj = CollectedHeap::align_allocation_or_fail(top(), end(), SurvivorAlignmentInBytes); - if (obj == NULL) { - return NULL; - } - + HeapWord* obj = top(); HeapWord* new_top = obj + size; // The 'new_top>obj' check is needed to detect overflow of obj+size. if (new_top > obj && new_top <= end()) { set_top(new_top); - assert(is_aligned(obj, SurvivorAlignmentInBytes) && is_object_aligned(new_top), - "checking alignment"); + assert(is_object_aligned(new_top), "checking alignment"); return obj; } else { - set_top(obj); return NULL; } } diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp b/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp index 3dc587da9e0..d97694466d5 100644 --- a/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp +++ b/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp @@ -97,9 +97,6 @@ class PSPushContentsClosure: public BasicOopIterateClosure { virtual void do_oop(oop* p) { do_oop_nv(p); } virtual void do_oop(narrowOop* p) { do_oop_nv(p); } - - // Don't use the oop verification code in the oop_oop_iterate framework. - debug_only(virtual bool should_verify_oops() { return false; }) }; // diff --git a/src/hotspot/share/gc/parallel/psYoungGen.cpp b/src/hotspot/share/gc/parallel/psYoungGen.cpp index 97362dfe615..47cca3a0996 100644 --- a/src/hotspot/share/gc/parallel/psYoungGen.cpp +++ b/src/hotspot/share/gc/parallel/psYoungGen.cpp @@ -189,9 +189,10 @@ void PSYoungGen::set_space_boundaries(size_t eden_size, size_t survivor_size) { MemRegion to_mr ((HeapWord*)to_start, (HeapWord*)from_start); MemRegion from_mr((HeapWord*)from_start, (HeapWord*)from_end); - eden_space()->initialize(eden_mr, true, ZapUnusedHeapArea); - to_space()->initialize(to_mr , true, ZapUnusedHeapArea); - from_space()->initialize(from_mr, true, ZapUnusedHeapArea); + WorkGang& pretouch_workers = ParallelScavengeHeap::heap()->workers(); + eden_space()->initialize(eden_mr, true, ZapUnusedHeapArea, MutableSpace::SetupPages, &pretouch_workers); + to_space()->initialize(to_mr , true, ZapUnusedHeapArea, MutableSpace::SetupPages, &pretouch_workers); + from_space()->initialize(from_mr, true, ZapUnusedHeapArea, MutableSpace::SetupPages, &pretouch_workers); } #ifndef PRODUCT @@ -636,17 +637,26 @@ void PSYoungGen::resize_spaces(size_t requested_eden_size, from_space()->check_mangled_unused_area(limit); to_space()->check_mangled_unused_area(limit); } + + WorkGang* workers = &ParallelScavengeHeap::heap()->workers(); + // When an existing space is being initialized, it is not // mangled because the space has been previously mangled. eden_space()->initialize(edenMR, SpaceDecorator::Clear, - SpaceDecorator::DontMangle); + SpaceDecorator::DontMangle, + MutableSpace::SetupPages, + workers); to_space()->initialize(toMR, SpaceDecorator::Clear, - SpaceDecorator::DontMangle); + SpaceDecorator::DontMangle, + MutableSpace::SetupPages, + workers); from_space()->initialize(fromMR, SpaceDecorator::DontClear, - SpaceDecorator::DontMangle); + SpaceDecorator::DontMangle, + MutableSpace::SetupPages, + workers); assert(from_space()->top() == old_from_top, "from top changed!"); @@ -783,9 +793,12 @@ void PSYoungGen::reset_survivors_after_shrink() { // Was there a shrink of the survivor space? if (new_end < space_shrinking->end()) { MemRegion mr(space_shrinking->bottom(), new_end); + space_shrinking->initialize(mr, SpaceDecorator::DontClear, - SpaceDecorator::Mangle); + SpaceDecorator::Mangle, + MutableSpace::SetupPages, + &ParallelScavengeHeap::heap()->workers()); } } diff --git a/src/hotspot/share/gc/serial/defNewGeneration.cpp b/src/hotspot/share/gc/serial/defNewGeneration.cpp index 4234adbc091..296415542ad 100644 --- a/src/hotspot/share/gc/serial/defNewGeneration.cpp +++ b/src/hotspot/share/gc/serial/defNewGeneration.cpp @@ -115,9 +115,6 @@ void CLDScanClosure::do_cld(ClassLoaderData* cld) { // If the cld has not been dirtied we know that there's // no references into the young gen and we can skip it. if (cld->has_modified_oops()) { - if (_accumulate_modified_oops) { - cld->accumulate_modified_oops(); - } // Tell the closure which CLD is being scanned so that it can be dirtied // if oops are left pointing into the young gen. @@ -567,8 +564,7 @@ void DefNewGeneration::collect(bool full, DefNewScanClosure scan_closure(this); DefNewYoungerGenClosure younger_gen_closure(this, _old_gen); - CLDScanClosure cld_scan_closure(&scan_closure, - heap->rem_set()->cld_rem_set()->accumulate_modified_oops()); + CLDScanClosure cld_scan_closure(&scan_closure); set_promo_failure_scan_stack_closure(&scan_closure); FastEvacuateFollowersClosure evacuate_followers(heap, @@ -715,7 +711,7 @@ oop DefNewGeneration::copy_to_survivor_space(oop old) { // Try allocating obj in to-space (unless too old) if (old->age() < tenuring_threshold()) { - obj = (oop) to()->allocate_aligned(s); + obj = (oop) to()->allocate(s); } // Otherwise try allocating obj tenured diff --git a/src/hotspot/share/gc/serial/defNewGeneration.inline.hpp b/src/hotspot/share/gc/serial/defNewGeneration.inline.hpp index 744d03c088d..67dce43e7b6 100644 --- a/src/hotspot/share/gc/serial/defNewGeneration.inline.hpp +++ b/src/hotspot/share/gc/serial/defNewGeneration.inline.hpp @@ -45,7 +45,7 @@ inline void DefNewGeneration::KeepAliveClosure::do_oop_work(T* p) { } #endif // ASSERT - Devirtualizer::do_oop_no_verify(_cl, p); + Devirtualizer::do_oop(_cl, p); // Card marking is trickier for weak refs. // This oop is a 'next' field which was filled in while we @@ -77,7 +77,7 @@ inline void DefNewGeneration::FastKeepAliveClosure::do_oop_work(T* p) { } #endif // ASSERT - Devirtualizer::do_oop_no_verify(_cl, p); + Devirtualizer::do_oop(_cl, p); // Optimized for Defnew generation if it's the youngest generation: // we set a younger_gen card if we have an older->youngest diff --git a/src/hotspot/share/gc/serial/markSweep.hpp b/src/hotspot/share/gc/serial/markSweep.hpp index 58a4128d8cf..e09d7d06833 100644 --- a/src/hotspot/share/gc/serial/markSweep.hpp +++ b/src/hotspot/share/gc/serial/markSweep.hpp @@ -191,9 +191,6 @@ class AdjustPointerClosure: public BasicOopIterateClosure { virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); virtual ReferenceIterationMode reference_iteration_mode() { return DO_FIELDS; } - - // This closure provides its own oop verification code. - debug_only(virtual bool should_verify_oops() { return false; }) }; class PreservedMark { diff --git a/src/hotspot/share/gc/shared/cardTableRS.cpp b/src/hotspot/share/gc/shared/cardTableRS.cpp index df347af8b72..952e7afb268 100644 --- a/src/hotspot/share/gc/shared/cardTableRS.cpp +++ b/src/hotspot/share/gc/shared/cardTableRS.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,46 +38,6 @@ #include "runtime/os.hpp" #include "utilities/macros.hpp" -class HasAccumulatedModifiedOopsClosure : public CLDClosure { - bool _found; - public: - HasAccumulatedModifiedOopsClosure() : _found(false) {} - void do_cld(ClassLoaderData* cld) { - if (_found) { - return; - } - - if (cld->has_accumulated_modified_oops()) { - _found = true; - } - } - bool found() { - return _found; - } -}; - -bool CLDRemSet::mod_union_is_clear() { - HasAccumulatedModifiedOopsClosure closure; - ClassLoaderDataGraph::cld_do(&closure); - - return !closure.found(); -} - - -class ClearCLDModUnionClosure : public CLDClosure { - public: - void do_cld(ClassLoaderData* cld) { - if (cld->has_accumulated_modified_oops()) { - cld->clear_accumulated_modified_oops(); - } - } -}; - -void CLDRemSet::clear_mod_union() { - ClearCLDModUnionClosure closure; - ClassLoaderDataGraph::cld_do(&closure); -} - CardTable::CardValue CardTableRS::find_unused_youngergenP_card_value() { for (CardValue v = youngergenP1_card; v < cur_youngergen_and_prev_nonclean_card; diff --git a/src/hotspot/share/gc/shared/cardTableRS.hpp b/src/hotspot/share/gc/shared/cardTableRS.hpp index 3d29f1c56a8..6a15fb56942 100644 --- a/src/hotspot/share/gc/shared/cardTableRS.hpp +++ b/src/hotspot/share/gc/shared/cardTableRS.hpp @@ -33,17 +33,6 @@ class DirtyCardToOopClosure; class Generation; class Space; -// Helper to remember modified oops in all clds. -class CLDRemSet { - bool _accumulate_modified_oops; - public: - CLDRemSet() : _accumulate_modified_oops(false) {} - void set_accumulate_modified_oops(bool value) { _accumulate_modified_oops = value; } - bool accumulate_modified_oops() { return _accumulate_modified_oops; } - bool mod_union_is_clear(); - void clear_mod_union(); -}; - // This RemSet uses a card table both as shared data structure // for a mod ref barrier set and for the rem set information. @@ -53,8 +42,6 @@ class CardTableRS: public CardTable { friend class VerifyCTSpaceClosure; friend class ClearNoncleanCardWrapper; - CLDRemSet _cld_rem_set; - void verify_space(Space* s, HeapWord* gen_start); enum ExtendedCardValue { @@ -101,8 +88,6 @@ class CardTableRS: public CardTable { CardTableRS(MemRegion whole_heap, bool scanned_concurrently); ~CardTableRS(); - CLDRemSet* cld_rem_set() { return &_cld_rem_set; } - void younger_refs_in_space_iterate(Space* sp, HeapWord* gen_boundary, OopIterateClosure* cl, uint n_threads); virtual void verify_used_region_at_save_marks(Space* sp) const NOT_DEBUG_RETURN; diff --git a/src/hotspot/share/gc/shared/collectedHeap.cpp b/src/hotspot/share/gc/shared/collectedHeap.cpp index 2e41655da99..dd3c5e27316 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.cpp +++ b/src/hotspot/share/gc/shared/collectedHeap.cpp @@ -37,7 +37,7 @@ #include "gc/shared/memAllocator.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" -#include "memory/metaspace.hpp" +#include "memory/classLoaderMetaspace.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/instanceMirrorKlass.hpp" @@ -599,6 +599,10 @@ void CollectedHeap::unpin_object(JavaThread* thread, oop obj) { ShouldNotReachHere(); } +bool CollectedHeap::is_archived_object(oop object) const { + return false; +} + void CollectedHeap::deduplicate_string(oop str) { // Do nothing, unless overridden in subclass. } diff --git a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp index 328cfbd43b3..0a6b1062f1c 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.hpp +++ b/src/hotspot/share/gc/shared/collectedHeap.hpp @@ -310,12 +310,6 @@ class CollectedHeap : public CHeapObj { virtual size_t min_dummy_object_size() const; size_t tlab_alloc_reserve() const; - // Return the address "addr" aligned by "alignment_in_bytes" if such - // an address is below "end". Return NULL otherwise. - inline static HeapWord* align_allocation_or_fail(HeapWord* addr, - HeapWord* end, - unsigned short alignment_in_bytes); - // Some heaps may offer a contiguous region for shared non-blocking // allocation, via inlined code (by exporting the address of the top and // end fields defining the extent of the contiguous allocation region.) @@ -370,6 +364,11 @@ class CollectedHeap : public CHeapObj { return 0; } + // If a GC uses a stack watermark barrier, the stack processing is lazy, concurrent, + // incremental and cooperative. In order for that to work well, mechanisms that stop + // another thread might want to ensure its roots are in a sane state. + virtual bool uses_stack_watermark_barrier() const { return false; } + // Perform a collection of the heap; intended for use in implementing // "System.gc". This probably implies as full a collection as the // "CollectedHeap" supports. @@ -511,11 +510,13 @@ class CollectedHeap : public CHeapObj { virtual oop pin_object(JavaThread* thread, oop obj); virtual void unpin_object(JavaThread* thread, oop obj); + // Is the given object inside a CDS archive area? + virtual bool is_archived_object(oop object) const; + // Deduplicate the string, iff the GC supports string deduplication. virtual void deduplicate_string(oop str); virtual bool is_oop(oop object) const; - // Non product verification and debugging. #ifndef PRODUCT // Support for PromotionFailureALot. Return true if it's time to cause a diff --git a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp b/src/hotspot/share/gc/shared/collectedHeap.inline.hpp index 2d03f7a36b4..94b3b50238f 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp +++ b/src/hotspot/share/gc/shared/collectedHeap.inline.hpp @@ -30,43 +30,6 @@ #include "oops/oop.inline.hpp" #include "utilities/align.hpp" -inline HeapWord* CollectedHeap::align_allocation_or_fail(HeapWord* addr, - HeapWord* end, - unsigned short alignment_in_bytes) { - if (alignment_in_bytes <= ObjectAlignmentInBytes) { - return addr; - } - - assert(is_aligned(addr, HeapWordSize), - "Address " PTR_FORMAT " is not properly aligned.", p2i(addr)); - assert(is_aligned(alignment_in_bytes, HeapWordSize), - "Alignment size %u is incorrect.", alignment_in_bytes); - - HeapWord* new_addr = align_up(addr, alignment_in_bytes); - size_t padding = pointer_delta(new_addr, addr); - - if (padding == 0) { - return addr; - } - - if (padding < CollectedHeap::min_fill_size()) { - padding += alignment_in_bytes / HeapWordSize; - assert(padding >= CollectedHeap::min_fill_size(), - "alignment_in_bytes %u is expect to be larger " - "than the minimum object size", alignment_in_bytes); - new_addr = addr + padding; - } - - assert(new_addr > addr, "Unexpected arithmetic overflow " - PTR_FORMAT " not greater than " PTR_FORMAT, p2i(new_addr), p2i(addr)); - if(new_addr < end) { - CollectedHeap::fill_with_object(addr, padding); - return new_addr; - } else { - return NULL; - } -} - inline oop CollectedHeap::obj_allocate(Klass* klass, int size, TRAPS) { ObjAllocator allocator(klass, size, THREAD); return allocator.allocate(); diff --git a/src/hotspot/share/gc/shared/gcLocker.hpp b/src/hotspot/share/gc/shared/gcLocker.hpp index a54bd91692f..4b776058da8 100644 --- a/src/hotspot/share/gc/shared/gcLocker.hpp +++ b/src/hotspot/share/gc/shared/gcLocker.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -149,8 +149,6 @@ class GCLocker: public AllStatic { // is set, the slow path is always taken, till _needs_gc is cleared. inline static void lock_critical(JavaThread* thread); inline static void unlock_critical(JavaThread* thread); - - static address needs_gc_address() { return (address) &_needs_gc; } }; #endif // SHARE_GC_SHARED_GCLOCKER_HPP diff --git a/src/hotspot/share/gc/shared/gcVMOperations.cpp b/src/hotspot/share/gc/shared/gcVMOperations.cpp index 111a4582a46..60b97e33a09 100644 --- a/src/hotspot/share/gc/shared/gcVMOperations.cpp +++ b/src/hotspot/share/gc/shared/gcVMOperations.cpp @@ -32,6 +32,7 @@ #include "gc/shared/genCollectedHeap.hpp" #include "interpreter/oopMapCache.hpp" #include "logging/log.hpp" +#include "memory/classLoaderMetaspace.hpp" #include "memory/oopFactory.hpp" #include "memory/universe.hpp" #include "runtime/handles.inline.hpp" diff --git a/src/hotspot/share/gc/shared/gcVMOperations.hpp b/src/hotspot/share/gc/shared/gcVMOperations.hpp index fe269f32c0e..d071b50e2cd 100644 --- a/src/hotspot/share/gc/shared/gcVMOperations.hpp +++ b/src/hotspot/share/gc/shared/gcVMOperations.hpp @@ -28,6 +28,7 @@ #include "gc/shared/collectedHeap.hpp" #include "gc/shared/genCollectedHeap.hpp" #include "memory/heapInspection.hpp" +#include "memory/metaspace.hpp" #include "prims/jvmtiExport.hpp" #include "runtime/handles.hpp" #include "runtime/jniHandles.hpp" diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp index 956634b2066..064e0da22a1 100644 --- a/src/hotspot/share/gc/shared/gc_globals.hpp +++ b/src/hotspot/share/gc/shared/gc_globals.hpp @@ -293,9 +293,10 @@ product(bool, ExecutingUnitTests, false, \ "Whether the JVM is running unit tests or not") \ \ - product_pd(bool, UseTLAB, "Use thread-local object allocation") \ + product(bool, UseTLAB, true, \ + "Use thread-local object allocation") \ \ - product_pd(bool, ResizeTLAB, \ + product(bool, ResizeTLAB, true, \ "Dynamically resize TLAB size for threads") \ \ product(bool, ZeroTLAB, false, \ diff --git a/src/hotspot/share/gc/shared/genCollectedHeap.cpp b/src/hotspot/share/gc/shared/genCollectedHeap.cpp index 48c7060a10f..884a5bf8902 100644 --- a/src/hotspot/share/gc/shared/genCollectedHeap.cpp +++ b/src/hotspot/share/gc/shared/genCollectedHeap.cpp @@ -58,6 +58,7 @@ #include "gc/shared/workgroup.hpp" #include "memory/filemap.hpp" #include "memory/iterator.hpp" +#include "memory/metaspace/metaspaceSizesSnapshot.hpp" #include "memory/metaspaceCounters.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" @@ -662,7 +663,7 @@ void GenCollectedHeap::do_collection(bool full, // Delete metaspaces for unloaded class loaders and clean up loader_data graph ClassLoaderDataGraph::purge(/*at_safepoint*/true); - MetaspaceUtils::verify_metrics(); + DEBUG_ONLY(MetaspaceUtils::verify();) // Resize the metaspace capacity after full collections MetaspaceGC::compute_new_size(); update_full_collections_completed(); diff --git a/src/hotspot/share/gc/shared/genOopClosures.hpp b/src/hotspot/share/gc/shared/genOopClosures.hpp index ad3973453a0..e1e8104dfa5 100644 --- a/src/hotspot/share/gc/shared/genOopClosures.hpp +++ b/src/hotspot/share/gc/shared/genOopClosures.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -93,12 +93,9 @@ class DefNewScanClosure : public FastScanClosure { class CLDScanClosure: public CLDClosure { DefNewScanClosure* _scavenge_closure; - // true if the the modified oops state should be saved. - bool _accumulate_modified_oops; public: - CLDScanClosure(DefNewScanClosure* scavenge_closure, - bool accumulate_modified_oops) : - _scavenge_closure(scavenge_closure), _accumulate_modified_oops(accumulate_modified_oops) {} + CLDScanClosure(DefNewScanClosure* scavenge_closure) : + _scavenge_closure(scavenge_closure) {} void do_cld(ClassLoaderData* cld); }; diff --git a/src/hotspot/share/gc/shared/generationSpec.cpp b/src/hotspot/share/gc/shared/generationSpec.cpp index 2f10d521907..67c1305e730 100644 --- a/src/hotspot/share/gc/shared/generationSpec.cpp +++ b/src/hotspot/share/gc/shared/generationSpec.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,6 @@ #include "precompiled.hpp" #include "gc/shared/cardTableRS.hpp" #include "gc/shared/generationSpec.hpp" -#include "memory/binaryTreeDictionary.hpp" #include "memory/filemap.hpp" #include "runtime/java.hpp" #include "utilities/macros.hpp" diff --git a/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp b/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp index a77cc509ff7..70043c430b4 100644 --- a/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp +++ b/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -262,18 +262,6 @@ JVMFlag::Error GCPauseIntervalMillisConstraintFunc(uintx value, bool verbose) { return JVMFlag::SUCCESS; } -JVMFlag::Error InitialBootClassLoaderMetaspaceSizeConstraintFunc(size_t value, bool verbose) { - size_t aligned_max = align_down(max_uintx/2, Metaspace::reserve_alignment_words()); - if (value > aligned_max) { - JVMFlag::printError(verbose, - "InitialBootClassLoaderMetaspaceSize (" SIZE_FORMAT ") must be " - "less than or equal to aligned maximum value (" SIZE_FORMAT ")\n", - value, aligned_max); - return JVMFlag::VIOLATES_CONSTRAINT; - } - return JVMFlag::SUCCESS; -} - // To avoid an overflow by 'align_up(value, alignment)'. static JVMFlag::Error MaxSizeForAlignment(const char* name, size_t value, size_t alignment, bool verbose) { size_t aligned_max = ((max_uintx - alignment) & ~(alignment-1)); @@ -450,22 +438,3 @@ JVMFlag::Error MaxMetaspaceSizeConstraintFunc(size_t value, bool verbose) { } } -JVMFlag::Error SurvivorAlignmentInBytesConstraintFunc(intx value, bool verbose) { - if (value != 0) { - if (!is_power_of_2(value)) { - JVMFlag::printError(verbose, - "SurvivorAlignmentInBytes (" INTX_FORMAT ") must be " - "power of 2\n", - value); - return JVMFlag::VIOLATES_CONSTRAINT; - } - if (value < ObjectAlignmentInBytes) { - JVMFlag::printError(verbose, - "SurvivorAlignmentInBytes (" INTX_FORMAT ") must be " - "greater than or equal to ObjectAlignmentInBytes (" INTX_FORMAT ")\n", - value, ObjectAlignmentInBytes); - return JVMFlag::VIOLATES_CONSTRAINT; - } - } - return JVMFlag::SUCCESS; -} diff --git a/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.hpp b/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.hpp index f2962b41c63..5bbfac68b66 100644 --- a/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.hpp +++ b/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.hpp @@ -56,7 +56,6 @@ \ f(uintx, MaxGCPauseMillisConstraintFunc) \ f(uintx, GCPauseIntervalMillisConstraintFunc) \ - f(size_t, InitialBootClassLoaderMetaspaceSizeConstraintFunc) \ f(size_t, MinHeapSizeConstraintFunc) \ f(size_t, InitialHeapSizeConstraintFunc) \ f(size_t, MaxHeapSizeConstraintFunc) \ @@ -68,8 +67,7 @@ f(uintx, TLABWasteIncrementConstraintFunc) \ f(uintx, SurvivorRatioConstraintFunc) \ f(size_t, MetaspaceSizeConstraintFunc) \ - f(size_t, MaxMetaspaceSizeConstraintFunc) \ - f(intx, SurvivorAlignmentInBytesConstraintFunc) + f(size_t, MaxMetaspaceSizeConstraintFunc) SHARED_GC_CONSTRAINTS(DECLARE_CONSTRAINT) diff --git a/src/hotspot/share/gc/shared/plab.inline.hpp b/src/hotspot/share/gc/shared/plab.inline.hpp index 38ef672f7c8..b0698da8a33 100644 --- a/src/hotspot/share/gc/shared/plab.inline.hpp +++ b/src/hotspot/share/gc/shared/plab.inline.hpp @@ -30,18 +30,6 @@ #include "memory/allocation.inline.hpp" #include "runtime/atomic.hpp" -inline HeapWord* PLAB::allocate_aligned(size_t word_sz, unsigned short alignment_in_bytes) { - HeapWord* res = CollectedHeap::align_allocation_or_fail(_top, _end, alignment_in_bytes); - if (res == NULL) { - return NULL; - } - - // Set _top so that allocate(), which expects _top to be correctly set, - // can be used below. - _top = res; - return allocate(word_sz); -} - void PLABStats::add_allocated(size_t v) { Atomic::add(&_allocated, v); } diff --git a/src/hotspot/share/gc/shared/pretouchTask.cpp b/src/hotspot/share/gc/shared/pretouchTask.cpp new file mode 100644 index 00000000000..4398d3924cc --- /dev/null +++ b/src/hotspot/share/gc/shared/pretouchTask.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "gc/shared/pretouchTask.hpp" +#include "runtime/atomic.hpp" +#include "runtime/globals.hpp" +#include "runtime/os.hpp" + +PretouchTask::PretouchTask(const char* task_name, + char* start_address, + char* end_address, + size_t page_size, + size_t chunk_size) : + AbstractGangTask(task_name), + _cur_addr(start_address), + _start_addr(start_address), + _end_addr(end_address), + _page_size(page_size), + _chunk_size(chunk_size) { + + assert(chunk_size >= page_size, + "Chunk size " SIZE_FORMAT " is smaller than page size " SIZE_FORMAT, + chunk_size, page_size); +} + +size_t PretouchTask::chunk_size() { + return PreTouchParallelChunkSize; +} + +void PretouchTask::work(uint worker_id) { + while (true) { + char* touch_addr = Atomic::fetch_and_add(&_cur_addr, _chunk_size); + if (touch_addr < _start_addr || touch_addr >= _end_addr) { + break; + } + + char* end_addr = touch_addr + MIN2(_chunk_size, pointer_delta(_end_addr, touch_addr, sizeof(char))); + + os::pretouch_memory(touch_addr, end_addr, _page_size); + } +} + +void PretouchTask::pretouch(const char* task_name, char* start_address, char* end_address, + size_t page_size, WorkGang* pretouch_gang) { + +#ifdef LINUX + // When using THP we need to always pre-touch using small pages as the OS will + // initially always use small pages. + page_size = UseTransparentHugePages ? (size_t)os::vm_page_size() : page_size; +#endif + size_t chunk_size = MAX2(PretouchTask::chunk_size(), page_size); + + PretouchTask task(task_name, start_address, end_address, page_size, chunk_size); + size_t total_bytes = pointer_delta(end_address, start_address, sizeof(char)); + + if (total_bytes == 0) { + return; + } + + if (pretouch_gang != NULL) { + size_t num_chunks = (total_bytes + chunk_size - 1) / chunk_size; + + uint num_workers = (uint)MIN2(num_chunks, (size_t)pretouch_gang->total_workers()); + log_debug(gc, heap)("Running %s with %u workers for " SIZE_FORMAT " work units pre-touching " SIZE_FORMAT "B.", + task.name(), num_workers, num_chunks, total_bytes); + + pretouch_gang->run_task(&task, num_workers); + } else { + log_debug(gc, heap)("Running %s pre-touching " SIZE_FORMAT "B.", + task.name(), total_bytes); + task.work(0); + } +} + diff --git a/src/hotspot/share/gc/shared/pretouchTask.hpp b/src/hotspot/share/gc/shared/pretouchTask.hpp new file mode 100644 index 00000000000..49cd48dc9cf --- /dev/null +++ b/src/hotspot/share/gc/shared/pretouchTask.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_GC_SHARED_PRETOUCH_HPP +#define SHARE_GC_SHARED_PRETOUCH_HPP + +#include "gc/shared/workgroup.hpp" + +class PretouchTask : public AbstractGangTask { + char* volatile _cur_addr; + char* const _start_addr; + char* const _end_addr; + size_t _page_size; + size_t _chunk_size; + +public: + PretouchTask(const char* task_name, char* start_address, char* end_address, size_t page_size, size_t chunk_size); + + virtual void work(uint worker_id); + + static size_t chunk_size(); + + static void pretouch(const char* task_name, char* start_address, char* end_address, + size_t page_size, WorkGang* pretouch_gang); + +}; + +#endif // SHARE_GC_SHARED_PRETOUCH_HPP diff --git a/src/hotspot/share/gc/shared/referenceProcessor.cpp b/src/hotspot/share/gc/shared/referenceProcessor.cpp index 820839dae61..d634ffa9efd 100644 --- a/src/hotspot/share/gc/shared/referenceProcessor.cpp +++ b/src/hotspot/share/gc/shared/referenceProcessor.cpp @@ -262,7 +262,7 @@ void DiscoveredListIterator::load_ptrs(DEBUG_ONLY(bool allow_null_referent)) { _next_discovered = discovered; _referent_addr = java_lang_ref_Reference::referent_addr_raw(_current_discovered); - _referent = java_lang_ref_Reference::referent(_current_discovered); + _referent = java_lang_ref_Reference::unknown_referent_no_keepalive(_current_discovered); assert(Universe::heap()->is_in_or_null(_referent), "Wrong oop found in java.lang.Reference object"); assert(allow_null_referent ? @@ -1058,7 +1058,7 @@ ReferenceProcessor::add_to_discovered_list_mt(DiscoveredList& refs_list, // cleared concurrently by mutators during (or after) discovery. void ReferenceProcessor::verify_referent(oop obj) { bool da = discovery_is_atomic(); - oop referent = java_lang_ref_Reference::referent(obj); + oop referent = java_lang_ref_Reference::unknown_referent_no_keepalive(obj); assert(da ? oopDesc::is_oop(referent) : oopDesc::is_oop_or_null(referent), "Bad referent " INTPTR_FORMAT " found in Reference " INTPTR_FORMAT " during %satomic discovery ", @@ -1119,7 +1119,8 @@ bool ReferenceProcessor::discover_reference(oop obj, ReferenceType rt) { // known to be strongly reachable. if (is_alive_non_header() != NULL) { verify_referent(obj); - if (is_alive_non_header()->do_object_b(java_lang_ref_Reference::referent(obj))) { + oop referent = java_lang_ref_Reference::unknown_referent_no_keepalive(obj); + if (is_alive_non_header()->do_object_b(referent)) { return false; // referent is reachable } } @@ -1169,7 +1170,7 @@ bool ReferenceProcessor::discover_reference(oop obj, ReferenceType rt) { // .. we are an atomic collector and referent is in our span if (is_subject_to_discovery(obj) || (discovery_is_atomic() && - is_subject_to_discovery(java_lang_ref_Reference::referent(obj)))) { + is_subject_to_discovery(java_lang_ref_Reference::unknown_referent_no_keepalive(obj)))) { } else { return false; } diff --git a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp index 76335788d0a..71117893119 100644 --- a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp +++ b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ #include "logging/log.hpp" #include "logging/logStream.hpp" #include "memory/allocation.inline.hpp" +#include "memory/resourceArea.hpp" #include "runtime/atomic.hpp" #define ASSERT_REF_TYPE(ref_type) assert((ref_type) >= REF_SOFT && (ref_type) <= REF_PHANTOM, \ diff --git a/src/hotspot/share/gc/shared/space.cpp b/src/hotspot/share/gc/shared/space.cpp index c267f1c0c3e..7b099a7d01a 100644 --- a/src/hotspot/share/gc/shared/space.cpp +++ b/src/hotspot/share/gc/shared/space.cpp @@ -567,27 +567,6 @@ inline HeapWord* ContiguousSpace::par_allocate_impl(size_t size) { } while (true); } -HeapWord* ContiguousSpace::allocate_aligned(size_t size) { - assert(Heap_lock->owned_by_self() || (SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread()), "not locked"); - HeapWord* end_value = end(); - - HeapWord* obj = CollectedHeap::align_allocation_or_fail(top(), end_value, SurvivorAlignmentInBytes); - if (obj == NULL) { - return NULL; - } - - if (pointer_delta(end_value, obj) >= size) { - HeapWord* new_top = obj + size; - set_top(new_top); - assert(::is_aligned(obj, SurvivorAlignmentInBytes) && is_aligned(new_top), - "checking alignment"); - return obj; - } else { - set_top(obj); - return NULL; - } -} - // Requires locking. HeapWord* ContiguousSpace::allocate(size_t size) { return allocate_impl(size); diff --git a/src/hotspot/share/gc/shared/space.hpp b/src/hotspot/share/gc/shared/space.hpp index 0b760364bbc..48eadafaf80 100644 --- a/src/hotspot/share/gc/shared/space.hpp +++ b/src/hotspot/share/gc/shared/space.hpp @@ -560,7 +560,6 @@ class ContiguousSpace: public CompactibleSpace { // Allocation (return NULL if full) virtual HeapWord* allocate(size_t word_size); virtual HeapWord* par_allocate(size_t word_size); - HeapWord* allocate_aligned(size_t word_size); // Iteration void oop_iterate(OopIterateClosure* cl); diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp index 27fe53f6868..2deafa3ff0d 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp @@ -224,7 +224,7 @@ StringDedupTable* StringDedupTable::_resized_table = NULL; StringDedupTable* StringDedupTable::_rehashed_table = NULL; volatile size_t StringDedupTable::_claimed_index = 0; -StringDedupTable::StringDedupTable(size_t size, jint hash_seed) : +StringDedupTable::StringDedupTable(size_t size, uint64_t hash_seed) : _size(size), _entries(0), _shrink_threshold((uintx)(size * _shrink_load_factor)), @@ -278,7 +278,7 @@ typeArrayOop StringDedupTable::lookup(typeArrayOop value, bool latin1, unsigned if (entry->hash() == hash && entry->latin1() == latin1) { oop* obj_addr = (oop*)entry->obj_addr(); oop obj = NativeAccess::oop_load(obj_addr); - if (java_lang_String::value_equals(value, static_cast(obj))) { + if (obj != NULL && java_lang_String::value_equals(value, static_cast(obj))) { obj = NativeAccess::oop_load(obj_addr); return static_cast(obj); } @@ -322,7 +322,7 @@ unsigned int StringDedupTable::hash_code(typeArrayOop value, bool latin1) { if (use_java_hash()) { hash = java_lang_String::hash_code(data, length); } else { - hash = AltHashing::murmur3_32(_table->_hash_seed, data, length); + hash = AltHashing::halfsiphash_32(_table->_hash_seed, (const uint8_t*)data, length); } } else { length /= sizeof(jchar) / sizeof(jbyte); // Convert number of bytes to number of chars @@ -330,7 +330,7 @@ unsigned int StringDedupTable::hash_code(typeArrayOop value, bool latin1) { if (use_java_hash()) { hash = java_lang_String::hash_code(data, length); } else { - hash = AltHashing::murmur3_32(_table->_hash_seed, data, length); + hash = AltHashing::halfsiphash_32(_table->_hash_seed, (const uint16_t*)data, length); } } @@ -647,6 +647,6 @@ void StringDedupTable::print_statistics() { _table->_entries, percent_of((size_t)_table->_entries, _table->_size), _entry_cache->size(), _entries_added, _entries_removed); log.debug(" Resize Count: " UINTX_FORMAT ", Shrink Threshold: " UINTX_FORMAT "(" STRDEDUP_PERCENT_FORMAT_NS "), Grow Threshold: " UINTX_FORMAT "(" STRDEDUP_PERCENT_FORMAT_NS ")", _resize_count, _table->_shrink_threshold, _shrink_load_factor * 100.0, _table->_grow_threshold, _grow_load_factor * 100.0); - log.debug(" Rehash Count: " UINTX_FORMAT ", Rehash Threshold: " UINTX_FORMAT ", Hash Seed: 0x%x", _rehash_count, _rehash_threshold, _table->_hash_seed); + log.debug(" Rehash Count: " UINTX_FORMAT ", Rehash Threshold: " UINTX_FORMAT ", Hash Seed: " UINT64_FORMAT, _rehash_count, _rehash_threshold, _table->_hash_seed); log.debug(" Age Threshold: " UINTX_FORMAT, StringDeduplicationAgeThreshold); } diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.hpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.hpp index e332c3c366c..6579c1964e8 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.hpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.hpp @@ -131,7 +131,7 @@ class StringDedupTable : public CHeapObj { // zero hash seed means we will use the Java compatible hash // function (which doesn't use a seed), and a non-zero hash // seed means we use the murmur3 hash function. - jint _hash_seed; + uint64_t _hash_seed; // Constants governing table resize/rehash/cache. static const size_t _min_size; @@ -153,7 +153,7 @@ class StringDedupTable : public CHeapObj { static StringDedupTable* _resized_table; static StringDedupTable* _rehashed_table; - StringDedupTable(size_t size, jint hash_seed = 0); + StringDedupTable(size_t size, uint64_t hash_seed = 0); ~StringDedupTable(); // Returns the hash bucket at the given index. diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp index 9754f495034..f0c034d1401 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp @@ -51,7 +51,9 @@ void ShenandoahLoadReferenceBarrierStub::emit_code(LIR_Assembler* ce) { ShenandoahBarrierSetC1::ShenandoahBarrierSetC1() : _pre_barrier_c1_runtime_code_blob(NULL), - _load_reference_barrier_rt_code_blob(NULL) {} + _load_reference_barrier_normal_rt_code_blob(NULL), + _load_reference_barrier_native_rt_code_blob(NULL), + _load_reference_barrier_weak_rt_code_blob(NULL) {} void ShenandoahBarrierSetC1::pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, DecoratorSet decorators, LIR_Opr addr_opr, LIR_Opr pre_val) { // First we test whether marking is in progress. @@ -107,15 +109,15 @@ void ShenandoahBarrierSetC1::pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, __ branch_destination(slow->continuation()); } -LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, bool is_native) { +LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, ShenandoahBarrierSet::AccessKind kind) { if (ShenandoahLoadRefBarrier) { - return load_reference_barrier_impl(gen, obj, addr, is_native); + return load_reference_barrier_impl(gen, obj, addr, kind); } else { return obj; } } -LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, bool is_native) { +LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, ShenandoahBarrierSet::AccessKind kind) { assert(ShenandoahLoadRefBarrier, "Should be enabled"); obj = ensure_in_register(gen, obj, T_OBJECT); @@ -148,7 +150,7 @@ LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier_impl(LIRGenerator* gen, L } __ cmp(lir_cond_notEqual, flag_val, LIR_OprFact::intConst(0)); - CodeStub* slow = new ShenandoahLoadReferenceBarrierStub(obj, addr, result, tmp1, tmp2, is_native); + CodeStub* slow = new ShenandoahLoadReferenceBarrierStub(obj, addr, result, tmp1, tmp2, kind); __ branch(lir_cond_notEqual, slow); __ branch_destination(slow->continuation()); @@ -211,8 +213,8 @@ void ShenandoahBarrierSetC1::load_at_resolved(LIRAccess& access, LIR_Opr result) if (ShenandoahBarrierSet::need_load_reference_barrier(decorators, type)) { LIR_Opr tmp = gen->new_register(T_OBJECT); BarrierSetC1::load_at_resolved(access, tmp); - bool is_native = ShenandoahBarrierSet::use_load_reference_barrier_native(decorators, type); - tmp = load_reference_barrier(gen, tmp, access.resolved_addr(), is_native); + ShenandoahBarrierSet::AccessKind kind = ShenandoahBarrierSet::access_kind(decorators, type); + tmp = load_reference_barrier(gen, tmp, access.resolved_addr(), kind); __ move(tmp, result); } else { BarrierSetC1::load_at_resolved(access, result); @@ -251,14 +253,14 @@ class C1ShenandoahPreBarrierCodeGenClosure : public StubAssemblerCodeGenClosure class C1ShenandoahLoadReferenceBarrierCodeGenClosure : public StubAssemblerCodeGenClosure { private: - const bool _is_native; + const ShenandoahBarrierSet::AccessKind _kind; public: - C1ShenandoahLoadReferenceBarrierCodeGenClosure(bool is_native) : _is_native(is_native) {} + C1ShenandoahLoadReferenceBarrierCodeGenClosure(ShenandoahBarrierSet::AccessKind kind) : _kind(kind) {} virtual OopMapSet* generate_code(StubAssembler* sasm) { ShenandoahBarrierSetAssembler* bs = (ShenandoahBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); - bs->generate_c1_load_reference_barrier_runtime_stub(sasm, _is_native); + bs->generate_c1_load_reference_barrier_runtime_stub(sasm, _kind); return NULL; } }; @@ -269,14 +271,19 @@ void ShenandoahBarrierSetC1::generate_c1_runtime_stubs(BufferBlob* buffer_blob) "shenandoah_pre_barrier_slow", false, &pre_code_gen_cl); if (ShenandoahLoadRefBarrier) { - C1ShenandoahLoadReferenceBarrierCodeGenClosure lrb_code_gen_cl(false); - _load_reference_barrier_rt_code_blob = Runtime1::generate_blob(buffer_blob, -1, + C1ShenandoahLoadReferenceBarrierCodeGenClosure lrb_code_gen_cl(ShenandoahBarrierSet::AccessKind::NORMAL); + _load_reference_barrier_normal_rt_code_blob = Runtime1::generate_blob(buffer_blob, -1, "shenandoah_load_reference_barrier_slow", false, &lrb_code_gen_cl); - C1ShenandoahLoadReferenceBarrierCodeGenClosure lrb_native_code_gen_cl(true); + C1ShenandoahLoadReferenceBarrierCodeGenClosure lrb_native_code_gen_cl(ShenandoahBarrierSet::AccessKind::NATIVE); _load_reference_barrier_native_rt_code_blob = Runtime1::generate_blob(buffer_blob, -1, - "shenandoah_load_reference_barrier_native_slow", - false, &lrb_native_code_gen_cl); + "shenandoah_load_reference_barrier_native_slow", + false, &lrb_native_code_gen_cl); + + C1ShenandoahLoadReferenceBarrierCodeGenClosure lrb_weak_code_gen_cl(ShenandoahBarrierSet::AccessKind::WEAK); + _load_reference_barrier_weak_rt_code_blob = Runtime1::generate_blob(buffer_blob, -1, + "shenandoah_load_reference_barrier_weak_slow", + false, &lrb_weak_code_gen_cl); } } diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp index e97f3af0926..eb65475e609 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp @@ -94,10 +94,10 @@ class ShenandoahLoadReferenceBarrierStub: public CodeStub { LIR_Opr _result; LIR_Opr _tmp1; LIR_Opr _tmp2; - bool _is_native; + ShenandoahBarrierSet::AccessKind _kind; public: - ShenandoahLoadReferenceBarrierStub(LIR_Opr obj, LIR_Opr addr, LIR_Opr result, LIR_Opr tmp1, LIR_Opr tmp2, bool is_native) : - _obj(obj), _addr(addr), _result(result), _tmp1(tmp1), _tmp2(tmp2), _is_native(is_native) + ShenandoahLoadReferenceBarrierStub(LIR_Opr obj, LIR_Opr addr, LIR_Opr result, LIR_Opr tmp1, LIR_Opr tmp2, ShenandoahBarrierSet::AccessKind kind) : + _obj(obj), _addr(addr), _result(result), _tmp1(tmp1), _tmp2(tmp2), _kind(kind) { assert(_obj->is_register(), "should be register"); assert(_addr->is_register(), "should be register"); @@ -111,7 +111,7 @@ class ShenandoahLoadReferenceBarrierStub: public CodeStub { LIR_Opr result() const { return _result; } LIR_Opr tmp1() const { return _tmp1; } LIR_Opr tmp2() const { return _tmp2; } - bool is_native() const { return _is_native; } + ShenandoahBarrierSet::AccessKind kind() const { return _kind; } virtual void emit_code(LIR_Assembler* e); virtual void visit(LIR_OpVisitState* visitor) { @@ -190,15 +190,16 @@ class LIR_OpShenandoahCompareAndSwap : public LIR_Op { class ShenandoahBarrierSetC1 : public BarrierSetC1 { private: CodeBlob* _pre_barrier_c1_runtime_code_blob; - CodeBlob* _load_reference_barrier_rt_code_blob; + CodeBlob* _load_reference_barrier_normal_rt_code_blob; CodeBlob* _load_reference_barrier_native_rt_code_blob; + CodeBlob* _load_reference_barrier_weak_rt_code_blob; void pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, DecoratorSet decorators, LIR_Opr addr_opr, LIR_Opr pre_val); - LIR_Opr load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, bool is_native); + LIR_Opr load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, ShenandoahBarrierSet::AccessKind kind); LIR_Opr storeval_barrier(LIRGenerator* gen, LIR_Opr obj, CodeEmitInfo* info, DecoratorSet decorators); - LIR_Opr load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, bool is_native); + LIR_Opr load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, ShenandoahBarrierSet::AccessKind kind); LIR_Opr ensure_in_register(LIRGenerator* gen, LIR_Opr obj, BasicType type); @@ -210,15 +211,20 @@ class ShenandoahBarrierSetC1 : public BarrierSetC1 { return _pre_barrier_c1_runtime_code_blob; } - CodeBlob* load_reference_barrier_rt_code_blob() { - assert(_load_reference_barrier_rt_code_blob != NULL, ""); - return _load_reference_barrier_rt_code_blob; + CodeBlob* load_reference_barrier_normal_rt_code_blob() { + assert(_load_reference_barrier_normal_rt_code_blob != NULL, ""); + return _load_reference_barrier_normal_rt_code_blob; } CodeBlob* load_reference_barrier_native_rt_code_blob() { assert(_load_reference_barrier_native_rt_code_blob != NULL, ""); return _load_reference_barrier_native_rt_code_blob; } + + CodeBlob* load_reference_barrier_weak_rt_code_blob() { + assert(_load_reference_barrier_weak_rt_code_blob != NULL, ""); + return _load_reference_barrier_weak_rt_code_blob; + } protected: virtual void store_at_resolved(LIRAccess& access, LIR_Opr value); diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index 67d05ec25c1..e13065e56c2 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "classfile/javaClasses.hpp" #include "gc/shared/barrierSet.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahForwarding.hpp" @@ -304,7 +305,8 @@ bool ShenandoahBarrierSetC2::is_shenandoah_lrb_call(Node* call) { address entry_point = call->as_CallLeaf()->entry_point(); return (entry_point == CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier)) || (entry_point == CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_narrow)) || - (entry_point == CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_native)); + (entry_point == CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak)) || + (entry_point == CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow)); } bool ShenandoahBarrierSetC2::is_shenandoah_marking_if(PhaseTransform *phase, Node* n) { @@ -544,9 +546,8 @@ Node* ShenandoahBarrierSetC2::load_at_resolved(C2Access& access, const Type* val // 2: apply LRB if needed if (ShenandoahBarrierSet::need_load_reference_barrier(decorators, type)) { - load = new ShenandoahLoadReferenceBarrierNode(NULL, - load, - ShenandoahBarrierSet::use_load_reference_barrier_native(decorators, type)); + ShenandoahBarrierSet::AccessKind kind = ShenandoahBarrierSet::access_kind(decorators, type); + load = new ShenandoahLoadReferenceBarrierNode(NULL, load, kind); if (access.is_parse_access()) { load = static_cast(access).kit()->gvn().transform(load); } else { @@ -643,7 +644,8 @@ Node* ShenandoahBarrierSetC2::atomic_cmpxchg_val_at_resolved(C2AtomicParseAccess load_store = kit->gvn().transform(new DecodeNNode(load_store, load_store->get_ptr_type())); } #endif - load_store = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(NULL, load_store, false)); + ShenandoahBarrierSet::AccessKind kind = ShenandoahBarrierSet::access_kind(access.decorators(), access.type()); + load_store = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(NULL, load_store, kind)); return load_store; } return BarrierSetC2::atomic_cmpxchg_val_at_resolved(access, expected_val, new_val, value_type); @@ -711,7 +713,8 @@ Node* ShenandoahBarrierSetC2::atomic_xchg_at_resolved(C2AtomicParseAccess& acces } Node* result = BarrierSetC2::atomic_xchg_at_resolved(access, val, value_type); if (access.is_oop()) { - result = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(NULL, result, false)); + ShenandoahBarrierSet::AccessKind kind = ShenandoahBarrierSet::access_kind(access.decorators(), access.type()); + result = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(NULL, result, kind)); shenandoah_write_barrier_pre(kit, false /* do_load */, NULL, NULL, max_juint, NULL, NULL, result /* pre_val */, T_OBJECT); @@ -1058,12 +1061,19 @@ Node* ShenandoahBarrierSetC2::ideal_node(PhaseGVN* phase, Node* n, bool can_resh if (n->Opcode() == Op_CmpP) { Node* in1 = n->in(1); Node* in2 = n->in(2); - if (in1->bottom_type() == TypePtr::NULL_PTR) { + + // If one input is NULL, then step over the barriers normal LRB barriers on the other input + if (in1->bottom_type() == TypePtr::NULL_PTR && + !((in2->Opcode() == Op_ShenandoahLoadReferenceBarrier) && + ((ShenandoahLoadReferenceBarrierNode*)in2)->kind() != ShenandoahBarrierSet::AccessKind::NORMAL)) { in2 = step_over_gc_barrier(in2); } - if (in2->bottom_type() == TypePtr::NULL_PTR) { + if (in2->bottom_type() == TypePtr::NULL_PTR && + !((in1->Opcode() == Op_ShenandoahLoadReferenceBarrier) && + ((ShenandoahLoadReferenceBarrierNode*)in1)->kind() != ShenandoahBarrierSet::AccessKind::NORMAL)) { in1 = step_over_gc_barrier(in1); } + PhaseIterGVN* igvn = phase->is_IterGVN(); if (in1 != n->in(1)) { if (igvn != NULL) { diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index a753ca61ac8..ed9c9b545cb 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -59,11 +59,6 @@ bool ShenandoahBarrierC2Support::expand(Compile* C, PhaseIterGVN& igvn) { return false; } C->clear_major_progress(); - if (C->range_check_cast_count() > 0) { - // No more loop optimizations. Remove all range check dependent CastIINodes. - C->remove_range_check_casts(igvn); - igvn.optimize(); - } } } return true; @@ -961,7 +956,8 @@ void ShenandoahBarrierC2Support::test_in_cset(Node*& ctrl, Node*& not_cset_ctrl, phase->register_new_node(cset_bool, old_ctrl); } -void ShenandoahBarrierC2Support::call_lrb_stub(Node*& ctrl, Node*& val, Node* load_addr, Node*& result_mem, Node* raw_mem, bool is_native, PhaseIdealLoop* phase) { +void ShenandoahBarrierC2Support::call_lrb_stub(Node*& ctrl, Node*& val, Node* load_addr, Node*& result_mem, Node* raw_mem, + ShenandoahBarrierSet::AccessKind kind, PhaseIdealLoop* phase) { IdealLoopTree*loop = phase->get_loop(ctrl); const TypePtr* obj_type = phase->igvn().type(val)->is_oopptr(); @@ -972,13 +968,28 @@ void ShenandoahBarrierC2Support::call_lrb_stub(Node*& ctrl, Node*& val, Node* lo mm->set_memory_at(Compile::AliasIdxRaw, raw_mem); phase->register_new_node(mm, ctrl); - address target = LP64_ONLY(UseCompressedOops) NOT_LP64(false) ? - CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_narrow) : - CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier); - - address calladdr = is_native ? CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_native) - : target; - const char* name = is_native ? "load_reference_barrier_native" : "load_reference_barrier"; + address calladdr = NULL; + const char* name = NULL; + switch (kind) { + case ShenandoahBarrierSet::AccessKind::NATIVE: + calladdr = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak); + name = "load_reference_barrier_native"; + break; + case ShenandoahBarrierSet::AccessKind::WEAK: + calladdr = LP64_ONLY(UseCompressedOops) NOT_LP64(false) ? + CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow) : + CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak); + name = "load_reference_barrier_weak"; + break; + case ShenandoahBarrierSet::AccessKind::NORMAL: + calladdr = LP64_ONLY(UseCompressedOops) NOT_LP64(false) ? + CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_narrow) : + CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier); + name = "load_reference_barrier"; + break; + default: + ShouldNotReachHere(); + } Node* call = new CallLeafNode(ShenandoahBarrierSetC2::shenandoah_load_reference_barrier_Type(), calladdr, name, TypeRawPtr::BOTTOM); call->init_req(TypeFunc::Control, ctrl); @@ -1325,7 +1336,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) { assert(val->bottom_type()->make_oopptr(), "need oop"); assert(val->bottom_type()->make_oopptr()->const_oop() == NULL, "expect non-constant"); - enum { _heap_stable = 1, _not_cset, _evac_path, PATH_LIMIT }; + enum { _heap_stable = 1, _evac_path, _not_cset, PATH_LIMIT }; Node* region = new RegionNode(PATH_LIMIT); Node* val_phi = new PhiNode(region, val->bottom_type()->is_oopptr()); Node* raw_mem_phi = PhiNode::make(region, raw_mem, Type::MEMORY, TypeRawPtr::BOTTOM); @@ -1339,14 +1350,21 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) { val_phi->init_req(_heap_stable, val); raw_mem_phi->init_req(_heap_stable, raw_mem); - // Test for in-cset. + // Test for in-cset, unless it's a native-LRB. Native LRBs need to return NULL + // even for non-cset objects to prevent ressurrection of such objects. // Wires !in_cset(obj) to slot 2 of region and phis Node* not_cset_ctrl = NULL; - test_in_cset(ctrl, not_cset_ctrl, val, raw_mem, phase); + if (lrb->kind() == ShenandoahBarrierSet::AccessKind::NORMAL) { + test_in_cset(ctrl, not_cset_ctrl, val, raw_mem, phase); + } if (not_cset_ctrl != NULL) { region->init_req(_not_cset, not_cset_ctrl); val_phi->init_req(_not_cset, val); raw_mem_phi->init_req(_not_cset, raw_mem); + } else { + region->del_req(_not_cset); + val_phi->del_req(_not_cset); + raw_mem_phi->del_req(_not_cset); } // Resolve object when orig-value is in cset. @@ -1387,7 +1405,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) { } } } - call_lrb_stub(ctrl, val, addr, result_mem, raw_mem, lrb->is_native(), phase); + call_lrb_stub(ctrl, val, addr, result_mem, raw_mem, lrb->kind(), phase); region->init_req(_evac_path, ctrl); val_phi->init_req(_evac_path, val); raw_mem_phi->init_req(_evac_path, result_mem); @@ -2312,12 +2330,12 @@ void MemoryGraphFixer::collect_memory_nodes() { Node* m = _memory_nodes[c->in(k)->_idx]; assert(m != NULL, "expect memory state"); if (u->in(k) != m) { - phi = NULL; + phi = NodeSentinel; } } } } - if (phi == NULL) { + if (phi == NodeSentinel) { phi = new PhiNode(c, Type::MEMORY, _phase->C->get_adr_type(_alias)); for (uint k = 1; k < c->req(); k++) { Node* m = _memory_nodes[c->in(k)->_idx]; @@ -2326,8 +2344,11 @@ void MemoryGraphFixer::collect_memory_nodes() { } } } - assert(phi != NULL, ""); - regions.map(c->_idx, phi); + if (phi != NULL) { + regions.map(c->_idx, phi); + } else { + assert(c->unique_ctrl_out()->Opcode() == Op_Halt, "expected memory state"); + } } Node* current_region = regions[c->_idx]; if (current_region != prev_region) { @@ -2338,7 +2359,7 @@ void MemoryGraphFixer::collect_memory_nodes() { } } else if (prev_mem == NULL || prev_mem->is_Phi() || ctrl_or_self(prev_mem) != c) { Node* m = _memory_nodes[_phase->idom(c)->_idx]; - assert(m != NULL, "expect memory state"); + assert(m != NULL || c->Opcode() == Op_Halt, "expect memory state"); if (m != prev_mem) { _memory_nodes.map(c->_idx, m); progress = true; @@ -2356,13 +2377,14 @@ void MemoryGraphFixer::collect_memory_nodes() { while (dead_phis.size() > 0) { Node* n = dead_phis.pop(); n->replace_by(_phase->C->top()); - n->destruct(); + n->destruct(&_phase->igvn()); } for (int i = rpo_list.size() - 1; i >= 0; i--) { Node* c = rpo_list.at(i); if (c->is_Region() && (_include_lsm || !c->is_OuterStripMinedLoop())) { Node* n = regions[c->_idx]; - if (n->is_Phi() && n->_idx >= last && n->in(0) == c) { + assert(n != NULL || c->unique_ctrl_out()->Opcode() == Op_Halt, "expected memory state"); + if (n != NULL && n->is_Phi() && n->_idx >= last && n->in(0) == c) { _phase->register_new_node(n, c); } } @@ -2371,10 +2393,12 @@ void MemoryGraphFixer::collect_memory_nodes() { Node* c = rpo_list.at(i); if (c->is_Region() && (_include_lsm || !c->is_OuterStripMinedLoop())) { Node* n = regions[c->_idx]; + assert(n != NULL || c->unique_ctrl_out()->Opcode() == Op_Halt, "expected memory state"); for (DUIterator_Fast imax, i = c->fast_outs(imax); i < imax; i++) { Node* u = c->fast_out(i); if (u->is_Phi() && u->bottom_type() == Type::MEMORY && u != n) { + assert(c->unique_ctrl_out()->Opcode() != Op_Halt, "expected memory state"); if (u->adr_type() == TypePtr::BOTTOM) { fix_memory_uses(u, n, n, c); } else if (_phase->C->get_alias_index(u->adr_type()) == _alias) { @@ -2877,13 +2901,13 @@ void MemoryGraphFixer::fix_memory_uses(Node* mem, Node* replacement, Node* rep_p } } -ShenandoahLoadReferenceBarrierNode::ShenandoahLoadReferenceBarrierNode(Node* ctrl, Node* obj, bool native) -: Node(ctrl, obj), _native(native) { +ShenandoahLoadReferenceBarrierNode::ShenandoahLoadReferenceBarrierNode(Node* ctrl, Node* obj, ShenandoahBarrierSet::AccessKind kind) +: Node(ctrl, obj), _kind(kind) { ShenandoahBarrierSetC2::bsc2()->state()->add_load_reference_barrier(this); } -bool ShenandoahLoadReferenceBarrierNode::is_native() const { - return _native; +ShenandoahBarrierSet::AccessKind ShenandoahLoadReferenceBarrierNode::kind() const { + return _kind; } uint ShenandoahLoadReferenceBarrierNode::size_of() const { @@ -2891,12 +2915,26 @@ uint ShenandoahLoadReferenceBarrierNode::size_of() const { } uint ShenandoahLoadReferenceBarrierNode::hash() const { - return Node::hash() + (_native ? 1 : 0); + uint hash = Node::hash(); + switch (_kind) { + case ShenandoahBarrierSet::AccessKind::NORMAL: + hash += 0; + break; + case ShenandoahBarrierSet::AccessKind::WEAK: + hash += 1; + break; + case ShenandoahBarrierSet::AccessKind::NATIVE: + hash += 2; + break; + default: + ShouldNotReachHere(); + } + return hash; } bool ShenandoahLoadReferenceBarrierNode::cmp( const Node &n ) const { return Node::cmp(n) && n.Opcode() == Op_ShenandoahLoadReferenceBarrier && - _native == ((const ShenandoahLoadReferenceBarrierNode&)n)._native; + _kind == ((const ShenandoahLoadReferenceBarrierNode&)n)._kind; } const Type* ShenandoahLoadReferenceBarrierNode::bottom_type() const { @@ -2907,7 +2945,12 @@ const Type* ShenandoahLoadReferenceBarrierNode::bottom_type() const { if (t == TypePtr::NULL_PTR) { return t; } - return t->is_oopptr(); + + if (kind() == ShenandoahBarrierSet::AccessKind::NORMAL) { + return t; + } + + return t->meet(TypePtr::NULL_PTR); } const Type* ShenandoahLoadReferenceBarrierNode::Value(PhaseGVN* phase) const { @@ -2919,8 +2962,11 @@ const Type* ShenandoahLoadReferenceBarrierNode::Value(PhaseGVN* phase) const { return t2; } - const Type* type = t2->is_oopptr(); - return type; + if (kind() == ShenandoahBarrierSet::AccessKind::NORMAL) { + return t2; + } + + return t2->meet(TypePtr::NULL_PTR); } Node* ShenandoahLoadReferenceBarrierNode::Identity(PhaseGVN* phase) { diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp index 91c8d167abf..c07b4a1ed72 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_GC_SHENANDOAH_C2_SHENANDOAHSUPPORT_HPP #define SHARE_GC_SHENANDOAH_C2_SHENANDOAHSUPPORT_HPP +#include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "memory/allocation.hpp" #include "opto/addnode.hpp" #include "opto/graphKit.hpp" @@ -60,7 +61,8 @@ class ShenandoahBarrierC2Support : public AllStatic { static void test_null(Node*& ctrl, Node* val, Node*& null_ctrl, PhaseIdealLoop* phase); static void test_gc_state(Node*& ctrl, Node* raw_mem, Node*& heap_stable_ctrl, PhaseIdealLoop* phase, int flags); - static void call_lrb_stub(Node*& ctrl, Node*& val, Node* load_addr, Node*& result_mem, Node* raw_mem, bool is_native, PhaseIdealLoop* phase); + static void call_lrb_stub(Node*& ctrl, Node*& val, Node* load_addr, Node*& result_mem, Node* raw_mem, + ShenandoahBarrierSet::AccessKind kind, PhaseIdealLoop* phase); static void test_in_cset(Node*& ctrl, Node*& not_cset_ctrl, Node* val, Node* raw_mem, PhaseIdealLoop* phase); static void move_gc_state_test_out_of_loop(IfNode* iff, PhaseIdealLoop* phase); static void merge_back_to_back_tests(Node* n, PhaseIdealLoop* phase); @@ -229,12 +231,12 @@ class ShenandoahLoadReferenceBarrierNode : public Node { }; private: - bool _native; + ShenandoahBarrierSet::AccessKind _kind; public: - ShenandoahLoadReferenceBarrierNode(Node* ctrl, Node* val, bool native); + ShenandoahLoadReferenceBarrierNode(Node* ctrl, Node* val, ShenandoahBarrierSet::AccessKind kind); - bool is_native() const; + ShenandoahBarrierSet::AccessKind kind() const; virtual int Opcode() const; virtual const Type* bottom_type() const; virtual const Type* Value(PhaseGVN* phase) const; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp index 72eba58baa3..dccc0e9e6b5 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp @@ -61,12 +61,6 @@ bool ShenandoahAggressiveHeuristics::should_start_gc() const { return true; } -bool ShenandoahAggressiveHeuristics::should_process_references() { - if (!can_process_references()) return false; - // Randomly process refs with 50% chance. - return (os::random() & 1) == 1; -} - bool ShenandoahAggressiveHeuristics::should_unload_classes() { if (!can_unload_classes_normal()) return false; if (has_metaspace_oom()) return true; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp index 071b3be8ff6..46956e40ca9 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp @@ -37,8 +37,6 @@ class ShenandoahAggressiveHeuristics : public ShenandoahHeuristics { virtual bool should_start_gc() const; - virtual bool should_process_references(); - virtual bool should_unload_classes(); virtual const char* name() { return "Aggressive"; } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp index 4ce2366fe0c..7027082cbc1 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp @@ -257,18 +257,6 @@ void ShenandoahHeuristics::record_requested_gc() { _gc_times_learned = 0; } -bool ShenandoahHeuristics::can_process_references() { - if (ShenandoahRefProcFrequency == 0) return false; - return true; -} - -bool ShenandoahHeuristics::should_process_references() { - if (!can_process_references()) return false; - size_t cycle = ShenandoahHeap::heap()->shenandoah_policy()->cycle_counter(); - // Process references every Nth GC cycle. - return cycle % ShenandoahRefProcFrequency == 0; -} - bool ShenandoahHeuristics::can_unload_classes() { if (!ClassUnloading) return false; return true; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp index 573b2585ce0..fed9296d667 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp @@ -120,9 +120,6 @@ class ShenandoahHeuristics : public CHeapObj { virtual void choose_collection_set(ShenandoahCollectionSet* collection_set); - virtual bool can_process_references(); - virtual bool should_process_references(); - virtual bool can_unload_classes(); virtual bool can_unload_classes_normal(); virtual bool should_unload_classes(); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp index d8027af7105..e13f9321898 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp @@ -36,11 +36,6 @@ bool ShenandoahPassiveHeuristics::should_start_gc() const { return false; } -bool ShenandoahPassiveHeuristics::should_process_references() { - // Always process references, if we can. - return can_process_references(); -} - bool ShenandoahPassiveHeuristics::should_unload_classes() { // Always unload classes, if we can. return can_unload_classes(); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp index 752b0be4dcd..1f259549fdc 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp @@ -31,8 +31,6 @@ class ShenandoahPassiveHeuristics : public ShenandoahHeuristics { public: virtual bool should_start_gc() const; - virtual bool should_process_references(); - virtual bool should_unload_classes(); virtual bool should_degenerate_cycle(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp index d5cc677786e..27c1f988833 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp @@ -68,7 +68,8 @@ void ShenandoahAsserts::print_obj(ShenandoahMessageBuffer& msg, oop obj) { msg.append(" " PTR_FORMAT " - klass " PTR_FORMAT " %s\n", p2i(obj), p2i(obj->klass()), obj->klass()->external_name()); msg.append(" %3s allocated after mark start\n", ctx->allocated_after_mark_start(obj) ? "" : "not"); msg.append(" %3s after update watermark\n", cast_from_oop(obj) >= r->get_update_watermark() ? "" : "not"); - msg.append(" %3s marked \n", ctx->is_marked(obj) ? "" : "not"); + msg.append(" %3s marked strong\n", ctx->is_marked_strong(obj) ? "" : "not"); + msg.append(" %3s marked weak\n", ctx->is_marked_weak(obj) ? "" : "not"); msg.append(" %3s in collection set\n", heap->in_collection_set(obj) ? "" : "not"); msg.append(" mark:%s\n", mw_ss.as_string()); msg.append(" region: %s", ss.as_string()); @@ -175,6 +176,16 @@ void ShenandoahAsserts::assert_in_heap(void* interior_loc, oop obj, const char * } } +void ShenandoahAsserts::assert_in_heap_or_null(void* interior_loc, oop obj, const char *file, int line) { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + + if (obj != NULL && !heap->is_in(obj)) { + print_failure(_safe_unknown, obj, interior_loc, NULL, "Shenandoah assert_in_heap_or_null failed", + "oop must point to a heap address", + file, line); + } +} + void ShenandoahAsserts::assert_correct(void* interior_loc, oop obj, const char* file, int line) { ShenandoahHeap* heap = ShenandoahHeap::heap(); @@ -343,24 +354,6 @@ void ShenandoahAsserts::print_rp_failure(const char *label, BoolObjectClosure* a report_vm_error(file, line, msg.buffer()); } -void ShenandoahAsserts::assert_rp_isalive_not_installed(const char *file, int line) { - ShenandoahHeap* heap = ShenandoahHeap::heap(); - ReferenceProcessor* rp = heap->ref_processor(); - if (rp->is_alive_non_header() != NULL) { - print_rp_failure("Shenandoah assert_rp_isalive_not_installed failed", rp->is_alive_non_header(), - file, line); - } -} - -void ShenandoahAsserts::assert_rp_isalive_installed(const char *file, int line) { - ShenandoahHeap* heap = ShenandoahHeap::heap(); - ReferenceProcessor* rp = heap->ref_processor(); - if (rp->is_alive_non_header() == NULL) { - print_rp_failure("Shenandoah assert_rp_isalive_installed failed", rp->is_alive_non_header(), - file, line); - } -} - void ShenandoahAsserts::assert_locked_or_shenandoah_safepoint(Mutex* lock, const char* file, int line) { if (ShenandoahSafepoint::is_at_shenandoah_safepoint()) { return; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp index 42d0e60bba3..61779b447d6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp @@ -54,6 +54,7 @@ class ShenandoahAsserts { const char *file, int line); static void assert_in_heap(void* interior_loc, oop obj, const char* file, int line); + static void assert_in_heap_or_null(void* interior_loc, oop obj, const char* file, int line); static void assert_in_correct_region(void* interior_loc, oop obj, const char* file, int line); static void assert_correct(void* interior_loc, oop obj, const char* file, int line); @@ -64,9 +65,6 @@ class ShenandoahAsserts { static void assert_not_in_cset(void* interior_loc, oop obj, const char* file, int line); static void assert_not_in_cset_loc(void* interior_loc, const char* file, int line); - static void assert_rp_isalive_not_installed(const char *file, int line); - static void assert_rp_isalive_installed(const char *file, int line); - static void assert_locked_or_shenandoah_safepoint(Mutex* lock, const char* file, int line); static void assert_heaplocked(const char* file, int line); @@ -76,6 +74,8 @@ class ShenandoahAsserts { #ifdef ASSERT #define shenandoah_assert_in_heap(interior_loc, obj) \ ShenandoahAsserts::assert_in_heap(interior_loc, obj, __FILE__, __LINE__) +#define shenandoah_assert_in_heap_or_null(interior_loc, obj) \ + ShenandoahAsserts::assert_in_heap_or_null(interior_loc, obj, __FILE__, __LINE__) #define shenandoah_assert_in_correct_region(interior_loc, obj) \ ShenandoahAsserts::assert_in_correct_region(interior_loc, obj, __FILE__, __LINE__) @@ -149,6 +149,7 @@ class ShenandoahAsserts { ShenandoahAsserts::assert_heaplocked_or_safepoint(__FILE__, __LINE__) #else #define shenandoah_assert_in_heap(interior_loc, obj) +#define shenandoah_assert_in_heap_or_null(interior_loc, obj) #define shenandoah_assert_in_correct_region(interior_loc, obj) #define shenandoah_assert_correct_if(interior_loc, obj, condition) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp index 4b5f1ebe76a..adf349d3b24 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp @@ -87,17 +87,6 @@ bool ShenandoahBarrierSet::need_load_reference_barrier(DecoratorSet decorators, return is_reference_type(type); } -bool ShenandoahBarrierSet::use_load_reference_barrier_native(DecoratorSet decorators, BasicType type) { - assert(need_load_reference_barrier(decorators, type), "Should be subset of LRB"); - assert(is_reference_type(type), "Why we here?"); - // Native load reference barrier is only needed for concurrent root processing - if (!ShenandoahConcurrentRoots::can_do_concurrent_roots()) { - return false; - } - - return (decorators & IN_NATIVE) != 0; -} - bool ShenandoahBarrierSet::need_keep_alive_barrier(DecoratorSet decorators,BasicType type) { if (!ShenandoahSATBBarrier) return false; // Only needed for references @@ -109,38 +98,13 @@ bool ShenandoahBarrierSet::need_keep_alive_barrier(DecoratorSet decorators,Basic return (on_weak_ref || unknown) && keep_alive; } -oop ShenandoahBarrierSet::load_reference_barrier_not_null(oop obj) { - if (ShenandoahLoadRefBarrier && _heap->has_forwarded_objects()) { - return load_reference_barrier_impl(obj); - } else { - return obj; - } -} - -oop ShenandoahBarrierSet::load_reference_barrier(oop obj) { - if (obj != NULL) { - return load_reference_barrier_not_null(obj); +ShenandoahBarrierSet::AccessKind ShenandoahBarrierSet::access_kind(DecoratorSet decorators, BasicType type) { + if ((decorators & IN_NATIVE) != 0) { + return AccessKind::NATIVE; + } else if ((decorators & (ON_WEAK_OOP_REF | ON_PHANTOM_OOP_REF | ON_UNKNOWN_OOP_REF)) != 0) { + return AccessKind::WEAK; } else { - return obj; - } -} - -oop ShenandoahBarrierSet::load_reference_barrier_impl(oop obj) { - assert(ShenandoahLoadRefBarrier, "should be enabled"); - if (!CompressedOops::is_null(obj)) { - bool evac_in_progress = _heap->is_evacuation_in_progress(); - oop fwd = resolve_forwarded_not_null(obj); - if (evac_in_progress && - _heap->in_collection_set(obj) && - obj == fwd) { - Thread *t = Thread::current(); - ShenandoahEvacOOMScope oom_evac_scope(t); - return _heap->evacuate_object(obj, t); - } else { - return fwd; - } - } else { - return obj; + return AccessKind::NORMAL; } } @@ -179,39 +143,6 @@ void ShenandoahBarrierSet::on_thread_detach(Thread *thread) { } } -oop ShenandoahBarrierSet::load_reference_barrier_native(oop obj, oop* load_addr) { - return load_reference_barrier_native_impl(obj, load_addr); -} - -oop ShenandoahBarrierSet::load_reference_barrier_native(oop obj, narrowOop* load_addr) { - return load_reference_barrier_native_impl(obj, load_addr); -} - -template -oop ShenandoahBarrierSet::load_reference_barrier_native_impl(oop obj, T* load_addr) { - if (CompressedOops::is_null(obj)) { - return NULL; - } - - ShenandoahMarkingContext* const marking_context = _heap->marking_context(); - if (_heap->is_concurrent_weak_root_in_progress() && !marking_context->is_marked(obj)) { - Thread* thr = Thread::current(); - if (thr->is_Java_thread()) { - return NULL; - } else { - return obj; - } - } - - oop fwd = load_reference_barrier_not_null(obj); - if (ShenandoahSelfFixing && load_addr != NULL && fwd != obj) { - // Since we are here and we know the load address, update the reference. - ShenandoahHeap::cas_oop(fwd, load_addr, obj); - } - - return fwd; -} - void ShenandoahBarrierSet::clone_barrier_runtime(oop src) { if (_heap->has_forwarded_objects() || (ShenandoahStoreValEnqueueBarrier && _heap->is_concurrent_mark_in_progress())) { clone_barrier(src); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp index 742c7fda196..eee66c0c05d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp @@ -33,8 +33,19 @@ class ShenandoahBarrierSetAssembler; class ShenandoahBarrierSet: public BarrierSet { -private: +public: + enum class AccessKind { + // Regular in-heap access on reference fields + NORMAL, + + // Off-heap reference access + NATIVE, + + // In-heap reference access on referent fields of j.l.r.Reference objects + WEAK + }; +private: ShenandoahHeap* _heap; BufferNode::Allocator _satb_mark_queue_buffer_allocator; ShenandoahSATBMarkQueueSet _satb_mark_queue_set; @@ -53,8 +64,8 @@ class ShenandoahBarrierSet: public BarrierSet { } static bool need_load_reference_barrier(DecoratorSet decorators, BasicType type); - static bool use_load_reference_barrier_native(DecoratorSet decorators, BasicType type); static bool need_keep_alive_barrier(DecoratorSet decorators, BasicType type); + static AccessKind access_kind(DecoratorSet decorators, BasicType type); void print_on(outputStream* st) const; @@ -87,14 +98,13 @@ class ShenandoahBarrierSet: public BarrierSet { inline void enqueue(oop obj); - oop load_reference_barrier(oop obj); - oop load_reference_barrier_not_null(oop obj); + inline oop load_reference_barrier(oop obj); template inline oop load_reference_barrier_mutator(oop obj, T* load_addr); - oop load_reference_barrier_native(oop obj, oop* load_addr); - oop load_reference_barrier_native(oop obj, narrowOop* load_addr); + template + inline oop load_reference_barrier(oop obj, T* load_addr); private: template @@ -111,11 +121,6 @@ class ShenandoahBarrierSet: public BarrierSet { template inline void arraycopy_work(T* src, size_t count); - oop load_reference_barrier_impl(oop obj); - - template - oop load_reference_barrier_native_impl(oop obj, T* load_addr); - inline bool need_bulk_update(HeapWord* dst); public: // Callbacks for runtime accesses. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp index 41cfd4cba46..1388465328d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp @@ -76,6 +76,56 @@ inline oop ShenandoahBarrierSet::load_reference_barrier_mutator(oop obj, T* load return fwd; } +inline oop ShenandoahBarrierSet::load_reference_barrier(oop obj) { + if (!ShenandoahLoadRefBarrier) { + return obj; + } + if (_heap->has_forwarded_objects() && + _heap->in_collection_set(obj)) { // Subsumes NULL-check + assert(obj != NULL, "cset check must have subsumed NULL-check"); + oop fwd = resolve_forwarded_not_null(obj); + // TODO: It should not be necessary to check evac-in-progress here. + // We do it for mark-compact, which may have forwarded objects, + // and objects in cset and gets here via runtime barriers. + // We can probably fix this as soon as mark-compact has its own + // marking phase. + if (obj == fwd && _heap->is_evacuation_in_progress()) { + Thread* t = Thread::current(); + ShenandoahEvacOOMScope oom_evac_scope(t); + return _heap->evacuate_object(obj, t); + } + return fwd; + } + return obj; +} + +template +inline oop ShenandoahBarrierSet::load_reference_barrier(oop obj, T* load_addr) { + + // Prevent resurrection of unreachable non-strorg references. + if (!HasDecorator::value && obj != NULL && + _heap->is_concurrent_weak_root_in_progress() && + !_heap->marking_context()->is_marked(obj)) { + return NULL; + } + + // Prevent resurrection of unreachable objects that are visited during + // concurrent class-unloading. + if (HasDecorator::value && obj != NULL && + _heap->is_evacuation_in_progress() && + !_heap->marking_context()->is_marked(obj)) { + return obj; + } + + oop fwd = load_reference_barrier(obj); + if (ShenandoahSelfFixing && load_addr != NULL && fwd != obj) { + // Since we are here and we know the load address, update the reference. + ShenandoahHeap::cas_oop(fwd, load_addr, obj); + } + + return fwd; +} + inline void ShenandoahBarrierSet::enqueue(oop obj) { assert(obj != NULL, "checked by caller"); assert(_satb_mark_queue_set.is_active(), "only get here when SATB active"); @@ -103,8 +153,7 @@ inline void ShenandoahBarrierSet::satb_barrier(T *field) { } inline void ShenandoahBarrierSet::satb_enqueue(oop value) { - assert(value != NULL, "checked before"); - if (ShenandoahSATBBarrier && _heap->is_concurrent_mark_in_progress()) { + if (value != NULL && ShenandoahSATBBarrier && _heap->is_concurrent_mark_in_progress()) { enqueue(value); } } @@ -117,7 +166,6 @@ inline void ShenandoahBarrierSet::storeval_barrier(oop obj) { inline void ShenandoahBarrierSet::keep_alive_if_weak(DecoratorSet decorators, oop value) { assert((decorators & ON_UNKNOWN_OOP_REF) == 0, "Reference strength must be known"); - assert(value != NULL, "checked by caller"); const bool on_strong_oop_ref = (decorators & ON_STRONG_OOP_REF) != 0; const bool peek = (decorators & AS_NO_KEEPALIVE) != 0; if (!peek && !on_strong_oop_ref) { @@ -127,7 +175,6 @@ inline void ShenandoahBarrierSet::keep_alive_if_weak(DecoratorSet decorators, oo template inline void ShenandoahBarrierSet::keep_alive_if_weak(oop value) { - assert(value != NULL, "checked by caller"); assert((decorators & ON_UNKNOWN_OOP_REF) == 0, "Reference strength must be known"); if (!HasDecorator::value && !HasDecorator::value) { @@ -141,10 +188,8 @@ inline oop ShenandoahBarrierSet::AccessBarrier::oop_loa oop value = Raw::oop_load_not_in_heap(addr); if (value != NULL) { ShenandoahBarrierSet *const bs = ShenandoahBarrierSet::barrier_set(); - value = bs->load_reference_barrier_native(value, addr); - if (value != NULL) { - bs->keep_alive_if_weak(value); - } + value = bs->load_reference_barrier(value, addr); + bs->keep_alive_if_weak(value); } return value; } @@ -153,23 +198,19 @@ template template inline oop ShenandoahBarrierSet::AccessBarrier::oop_load_in_heap(T* addr) { oop value = Raw::oop_load_in_heap(addr); - if (value != NULL) { - ShenandoahBarrierSet *const bs = ShenandoahBarrierSet::barrier_set(); - value = bs->load_reference_barrier_not_null(value); - bs->keep_alive_if_weak(value); - } + ShenandoahBarrierSet *const bs = ShenandoahBarrierSet::barrier_set(); + value = bs->load_reference_barrier(value, addr); + bs->keep_alive_if_weak(value); return value; } template inline oop ShenandoahBarrierSet::AccessBarrier::oop_load_in_heap_at(oop base, ptrdiff_t offset) { oop value = Raw::oop_load_in_heap_at(base, offset); - if (value != NULL) { - ShenandoahBarrierSet *const bs = ShenandoahBarrierSet::barrier_set(); - value = bs->load_reference_barrier_not_null(value); - bs->keep_alive_if_weak(AccessBarrierSupport::resolve_possibly_unknown_oop_ref_strength(base, offset), - value); - } + ShenandoahBarrierSet *const bs = ShenandoahBarrierSet::barrier_set(); + DecoratorSet resolved_decorators = AccessBarrierSupport::resolve_possibly_unknown_oop_ref_strength(base, offset); + value = bs->load_reference_barrier(value, AccessInternal::oop_field_addr(base, offset)); + bs->keep_alive_if_weak(resolved_decorators, value); return value; } @@ -177,6 +218,7 @@ template template inline void ShenandoahBarrierSet::AccessBarrier::oop_store_not_in_heap(T* addr, oop value) { shenandoah_assert_marked_if(NULL, value, !CompressedOops::is_null(value) && ShenandoahHeap::heap()->is_evacuation_in_progress()); + shenandoah_assert_not_in_cset_if(addr, value, value != NULL && !ShenandoahHeap::heap()->cancelled_gc()); ShenandoahBarrierSet* const bs = ShenandoahBarrierSet::barrier_set(); bs->storeval_barrier(value); bs->satb_barrier(addr); @@ -214,10 +256,8 @@ inline oop ShenandoahBarrierSet::AccessBarrier::oop_ato // Note: We don't need a keep-alive-barrier here. We already enqueue any loaded reference for SATB anyway, // because it must be the previous value. - if (res != NULL) { - res = ShenandoahBarrierSet::barrier_set()->load_reference_barrier_not_null(res); - bs->satb_enqueue(res); - } + res = ShenandoahBarrierSet::barrier_set()->load_reference_barrier(res, NULL); + bs->satb_enqueue(res); return res; } @@ -242,10 +282,8 @@ inline oop ShenandoahBarrierSet::AccessBarrier::oop_ato // Note: We don't need a keep-alive-barrier here. We already enqueue any loaded reference for SATB anyway, // because it must be the previous value. - if (previous != NULL) { - previous = ShenandoahBarrierSet::barrier_set()->load_reference_barrier_not_null(previous); - bs->satb_enqueue(previous); - } + previous = ShenandoahBarrierSet::barrier_set()->load_reference_barrier(previous, NULL); + bs->satb_enqueue(previous); return previous; } @@ -303,7 +341,7 @@ void ShenandoahBarrierSet::arraycopy_work(T* src, size_t count) { oop witness = ShenandoahHeap::cas_oop(fwd, elem_ptr, o); obj = fwd; } - if (ENQUEUE && !ctx->is_marked(obj)) { + if (ENQUEUE && !ctx->is_marked_strong(obj)) { queue.enqueue_known_active(obj); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.inline.hpp index fbb34b808c5..a327524a72d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.inline.hpp @@ -40,12 +40,12 @@ bool ShenandoahCollectionSet::is_in(ShenandoahHeapRegion* r) const { } bool ShenandoahCollectionSet::is_in(oop p) const { - shenandoah_assert_in_heap(NULL, p); + shenandoah_assert_in_heap_or_null(NULL, p); return is_in_loc(cast_from_oop(p)); } bool ShenandoahCollectionSet::is_in_loc(void* p) const { - assert(_heap->is_in(p), "Must be in the heap"); + assert(p == NULL || _heap->is_in(p), "Must be in the heap"); uintx index = ((uintx) p) >> _region_size_bytes_shift; // no need to subtract the bottom of the heap from p, // _biased_cset_map is biased diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp index 815fde581b4..5331ca26d48 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp @@ -31,8 +31,6 @@ #include "gc/shared/weakProcessor.inline.hpp" #include "gc/shared/gcTimer.hpp" #include "gc/shared/gcTrace.hpp" -#include "gc/shared/referenceProcessor.hpp" -#include "gc/shared/referenceProcessorPhaseTimes.hpp" #include "gc/shared/strongRootsScope.hpp" #include "gc/shenandoah/shenandoahBarrierSet.inline.hpp" @@ -40,6 +38,7 @@ #include "gc/shenandoah/shenandoahConcurrentMark.inline.hpp" #include "gc/shenandoah/shenandoahMarkCompact.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" #include "gc/shenandoah/shenandoahOopClosures.inline.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" @@ -61,7 +60,7 @@ class ShenandoahInitMarkRootsClosure : public OopClosure { template inline void do_oop_work(T* p) { - ShenandoahConcurrentMark::mark_through_ref(p, _heap, _queue, _mark_context); + ShenandoahConcurrentMark::mark_through_ref(p, _heap, _queue, _mark_context, false); } public: @@ -74,11 +73,12 @@ class ShenandoahInitMarkRootsClosure : public OopClosure { void do_oop(oop* p) { do_oop_work(p); } }; -ShenandoahMarkRefsSuperClosure::ShenandoahMarkRefsSuperClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : +ShenandoahMarkRefsSuperClosure::ShenandoahMarkRefsSuperClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : MetadataVisitingOopIterateClosure(rp), _queue(q), _heap(ShenandoahHeap::heap()), - _mark_context(_heap->marking_context()) + _mark_context(_heap->marking_context()), + _weak(false) { } template @@ -153,14 +153,8 @@ class ShenandoahConcurrentMarkingTask : public AbstractGangTask { ShenandoahConcurrentWorkerSession worker_session(worker_id); ShenandoahSuspendibleThreadSetJoiner stsj(ShenandoahSuspendibleWorkers); ShenandoahObjToScanQueue* q = _cm->get_queue(worker_id); - ReferenceProcessor* rp; - if (heap->process_references()) { - rp = heap->ref_processor(); - shenandoah_assert_rp_isalive_installed(); - } else { - rp = NULL; - } - + ShenandoahReferenceProcessor* rp = heap->ref_processor(); + assert(rp != NULL, "need reference processor"); _cm->mark_loop(worker_id, _terminator, rp, true, // cancellable ShenandoahStringDedup::is_enabled()); // perform string dedup @@ -206,7 +200,7 @@ class ShenandoahProcessConcurrentRootsTask : public AbstractGangTask { private: ShenandoahConcurrentRootScanner _rs; ShenandoahConcurrentMark* const _cm; - ReferenceProcessor* _rp; + ShenandoahReferenceProcessor* _rp; public: ShenandoahProcessConcurrentRootsTask(ShenandoahConcurrentMark* cm, @@ -222,12 +216,7 @@ ShenandoahProcessConcurrentRootsTask::ShenandoahProcessConcurrentRootsTask(Sh AbstractGangTask("Shenandoah Process Concurrent Roots"), _rs(nworkers, phase), _cm(cm), - _rp(NULL) { - ShenandoahHeap* heap = ShenandoahHeap::heap(); - if (heap->process_references()) { - _rp = heap->ref_processor(); - shenandoah_assert_rp_isalive_installed(); - } + _rp(ShenandoahHeap::heap()->ref_processor()) { } template @@ -253,13 +242,7 @@ class ShenandoahFinalMarkingTask : public AbstractGangTask { ShenandoahHeap* heap = ShenandoahHeap::heap(); ShenandoahParallelWorkerSession worker_session(worker_id); - ReferenceProcessor* rp; - if (heap->process_references()) { - rp = heap->ref_processor(); - shenandoah_assert_rp_isalive_installed(); - } else { - rp = NULL; - } + ShenandoahReferenceProcessor* rp = heap->ref_processor(); // First drain remaining SATB buffers. // Notice that this is not strictly necessary for mark-compact. But since @@ -303,9 +286,12 @@ void ShenandoahConcurrentMark::mark_roots(ShenandoahPhaseTimings::Phase root_pha assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at a safepoint"); ShenandoahHeap* heap = ShenandoahHeap::heap(); - ShenandoahGCPhase phase(root_phase); + ShenandoahReferenceProcessor* ref_processor = heap->ref_processor(); + ref_processor->reset_thread_locals(); + ref_processor->set_soft_reference_policy(_heap->soft_ref_policy()->should_clear_all_soft_refs()); + WorkGang* workers = heap->workers(); uint nworkers = workers->active_workers(); @@ -407,21 +393,21 @@ void ShenandoahConcurrentMark::initialize(uint workers) { // Mark concurrent roots during concurrent phases class ShenandoahMarkConcurrentRootsTask : public AbstractGangTask { private: - SuspendibleThreadSetJoiner _sts_joiner; + SuspendibleThreadSetJoiner _sts_joiner; ShenandoahConcurrentRootScanner _rs; - ShenandoahObjToScanQueueSet* const _queue_set; - ReferenceProcessor* const _rp; + ShenandoahObjToScanQueueSet* const _queue_set; + ShenandoahReferenceProcessor* const _rp; public: ShenandoahMarkConcurrentRootsTask(ShenandoahObjToScanQueueSet* qs, - ReferenceProcessor* rp, + ShenandoahReferenceProcessor* rp, ShenandoahPhaseTimings::Phase phase, uint nworkers); void work(uint worker_id); }; ShenandoahMarkConcurrentRootsTask::ShenandoahMarkConcurrentRootsTask(ShenandoahObjToScanQueueSet* qs, - ReferenceProcessor* rp, + ShenandoahReferenceProcessor* rp, ShenandoahPhaseTimings::Phase phase, uint nworkers) : AbstractGangTask("Shenandoah Concurrent Mark Roots"), @@ -442,19 +428,7 @@ void ShenandoahConcurrentMark::mark_from_roots() { WorkGang* workers = _heap->workers(); uint nworkers = workers->active_workers(); - ReferenceProcessor* rp = NULL; - if (_heap->process_references()) { - rp = _heap->ref_processor(); - rp->set_active_mt_degree(nworkers); - - // enable ("weak") refs discovery - rp->enable_discovery(true /*verify_no_refs*/); - rp->setup_policy(_heap->soft_ref_policy()->should_clear_all_soft_refs()); - } - - shenandoah_assert_rp_isalive_not_installed(); - ShenandoahIsAliveSelector is_alive; - ReferenceProcessorIsAliveMutator fix_isalive(_heap->ref_processor(), is_alive.is_alive_closure()); + ShenandoahReferenceProcessor* rp = _heap->ref_processor(); task_queues()->reserve(nworkers); @@ -480,10 +454,6 @@ void ShenandoahConcurrentMark::finish_mark_from_roots(bool full_gc) { uint nworkers = _heap->workers()->active_workers(); { - shenandoah_assert_rp_isalive_not_installed(); - ShenandoahIsAliveSelector is_alive; - ReferenceProcessorIsAliveMutator fix_isalive(_heap->ref_processor(), is_alive.is_alive_closure()); - // Full GC does not execute concurrent cycle. Degenerated cycle may bypass concurrent cycle. // In those cases, concurrent roots might not be scanned, scan them here. Ideally, this // should piggyback to ShenandoahFinalMarkingTask, but it makes time tracking very hard. @@ -524,343 +494,11 @@ void ShenandoahConcurrentMark::finish_mark_from_roots(bool full_gc) { assert(task_queues()->is_empty(), "Should be empty"); } - // When we're done marking everything, we process weak references. - if (_heap->process_references()) { - weak_refs_work(full_gc); - } - assert(task_queues()->is_empty(), "Should be empty"); TASKQUEUE_STATS_ONLY(task_queues()->print_taskqueue_stats()); TASKQUEUE_STATS_ONLY(task_queues()->reset_taskqueue_stats()); } -// Weak Reference Closures -class ShenandoahCMDrainMarkingStackClosure: public VoidClosure { - uint _worker_id; - TaskTerminator* _terminator; - bool _reset_terminator; - -public: - ShenandoahCMDrainMarkingStackClosure(uint worker_id, TaskTerminator* t, bool reset_terminator = false): - _worker_id(worker_id), - _terminator(t), - _reset_terminator(reset_terminator) { - } - - void do_void() { - assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at a safepoint"); - - ShenandoahHeap* sh = ShenandoahHeap::heap(); - ShenandoahConcurrentMark* scm = sh->concurrent_mark(); - assert(sh->process_references(), "why else would we be here?"); - ReferenceProcessor* rp = sh->ref_processor(); - - shenandoah_assert_rp_isalive_installed(); - - scm->mark_loop(_worker_id, _terminator, rp, - false, // not cancellable - false); // do not do strdedup - - if (_reset_terminator) { - _terminator->reset_for_reuse(); - } - } -}; - -class ShenandoahCMKeepAliveClosure : public OopClosure { -private: - ShenandoahObjToScanQueue* _queue; - ShenandoahHeap* _heap; - ShenandoahMarkingContext* const _mark_context; - - template - inline void do_oop_work(T* p) { - ShenandoahConcurrentMark::mark_through_ref(p, _heap, _queue, _mark_context); - } - -public: - ShenandoahCMKeepAliveClosure(ShenandoahObjToScanQueue* q) : - _queue(q), - _heap(ShenandoahHeap::heap()), - _mark_context(_heap->marking_context()) {} - - void do_oop(narrowOop* p) { do_oop_work(p); } - void do_oop(oop* p) { do_oop_work(p); } -}; - -class ShenandoahCMKeepAliveUpdateClosure : public OopClosure { -private: - ShenandoahObjToScanQueue* _queue; - ShenandoahHeap* _heap; - ShenandoahMarkingContext* const _mark_context; - - template - inline void do_oop_work(T* p) { - ShenandoahConcurrentMark::mark_through_ref(p, _heap, _queue, _mark_context); - } - -public: - ShenandoahCMKeepAliveUpdateClosure(ShenandoahObjToScanQueue* q) : - _queue(q), - _heap(ShenandoahHeap::heap()), - _mark_context(_heap->marking_context()) {} - - void do_oop(narrowOop* p) { do_oop_work(p); } - void do_oop(oop* p) { do_oop_work(p); } -}; - -class ShenandoahWeakUpdateClosure : public OopClosure { -private: - ShenandoahHeap* const _heap; - - template - inline void do_oop_work(T* p) { - oop o = _heap->maybe_update_with_forwarded(p); - shenandoah_assert_marked_except(p, o, o == NULL); - } - -public: - ShenandoahWeakUpdateClosure() : _heap(ShenandoahHeap::heap()) {} - - void do_oop(narrowOop* p) { do_oop_work(p); } - void do_oop(oop* p) { do_oop_work(p); } -}; - -class ShenandoahRefProcTaskProxy : public AbstractGangTask { -private: - AbstractRefProcTaskExecutor::ProcessTask& _proc_task; - TaskTerminator* _terminator; - -public: - ShenandoahRefProcTaskProxy(AbstractRefProcTaskExecutor::ProcessTask& proc_task, - TaskTerminator* t) : - AbstractGangTask("Shenandoah Process Weak References"), - _proc_task(proc_task), - _terminator(t) { - } - - void work(uint worker_id) { - Thread* current_thread = Thread::current(); - ResourceMark rm(current_thread); - HandleMark hm(current_thread); - assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at a safepoint"); - ShenandoahHeap* heap = ShenandoahHeap::heap(); - ShenandoahParallelWorkerSession worker_session(worker_id); - ShenandoahCMDrainMarkingStackClosure complete_gc(worker_id, _terminator); - if (heap->has_forwarded_objects()) { - ShenandoahForwardedIsAliveClosure is_alive; - ShenandoahCMKeepAliveUpdateClosure keep_alive(heap->concurrent_mark()->get_queue(worker_id)); - _proc_task.work(worker_id, is_alive, keep_alive, complete_gc); - } else { - ShenandoahIsAliveClosure is_alive; - ShenandoahCMKeepAliveClosure keep_alive(heap->concurrent_mark()->get_queue(worker_id)); - _proc_task.work(worker_id, is_alive, keep_alive, complete_gc); - } - } -}; - -class ShenandoahRefProcTaskExecutor : public AbstractRefProcTaskExecutor { -private: - WorkGang* _workers; - -public: - ShenandoahRefProcTaskExecutor(WorkGang* workers) : - _workers(workers) { - } - - // Executes a task using worker threads. - void execute(ProcessTask& task, uint ergo_workers) { - assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at a safepoint"); - - ShenandoahHeap* heap = ShenandoahHeap::heap(); - ShenandoahConcurrentMark* cm = heap->concurrent_mark(); - ShenandoahPushWorkerQueuesScope scope(_workers, cm->task_queues(), - ergo_workers, - /* do_check = */ false); - uint nworkers = _workers->active_workers(); - cm->task_queues()->reserve(nworkers); - TaskTerminator terminator(nworkers, cm->task_queues()); - ShenandoahRefProcTaskProxy proc_task_proxy(task, &terminator); - _workers->run_task(&proc_task_proxy); - } -}; - -void ShenandoahConcurrentMark::weak_refs_work(bool full_gc) { - assert(_heap->process_references(), "sanity"); - - ShenandoahPhaseTimings::Phase phase_root = - full_gc ? - ShenandoahPhaseTimings::full_gc_weakrefs : - ShenandoahPhaseTimings::weakrefs; - - ShenandoahGCPhase phase(phase_root); - - ReferenceProcessor* rp = _heap->ref_processor(); - - // NOTE: We cannot shortcut on has_discovered_references() here, because - // we will miss marking JNI Weak refs then, see implementation in - // ReferenceProcessor::process_discovered_references. - weak_refs_work_doit(full_gc); - - rp->verify_no_references_recorded(); - assert(!rp->discovery_enabled(), "Post condition"); - -} - -void ShenandoahConcurrentMark::weak_refs_work_doit(bool full_gc) { - ReferenceProcessor* rp = _heap->ref_processor(); - - ShenandoahPhaseTimings::Phase phase_process = - full_gc ? - ShenandoahPhaseTimings::full_gc_weakrefs_process : - ShenandoahPhaseTimings::weakrefs_process; - - shenandoah_assert_rp_isalive_not_installed(); - ShenandoahIsAliveSelector is_alive; - ReferenceProcessorIsAliveMutator fix_isalive(rp, is_alive.is_alive_closure()); - - WorkGang* workers = _heap->workers(); - uint nworkers = workers->active_workers(); - - rp->setup_policy(_heap->soft_ref_policy()->should_clear_all_soft_refs()); - rp->set_active_mt_degree(nworkers); - - assert(task_queues()->is_empty(), "Should be empty"); - - // complete_gc and keep_alive closures instantiated here are only needed for - // single-threaded path in RP. They share the queue 0 for tracking work, which - // simplifies implementation. Since RP may decide to call complete_gc several - // times, we need to be able to reuse the terminator. - uint serial_worker_id = 0; - TaskTerminator terminator(1, task_queues()); - ShenandoahCMDrainMarkingStackClosure complete_gc(serial_worker_id, &terminator, /* reset_terminator = */ true); - - ShenandoahRefProcTaskExecutor executor(workers); - - ReferenceProcessorPhaseTimes pt(_heap->gc_timer(), rp->num_queues()); - - { - // Note: Don't emit JFR event for this phase, to avoid overflow nesting phase level. - // Reference Processor emits 2 levels JFR event, that can get us over the JFR - // event nesting level limits, in case of degenerated GC gets upgraded to - // full GC. - ShenandoahTimingsTracker phase_timing(phase_process); - - if (_heap->has_forwarded_objects()) { - ShenandoahCMKeepAliveUpdateClosure keep_alive(get_queue(serial_worker_id)); - const ReferenceProcessorStats& stats = - rp->process_discovered_references(is_alive.is_alive_closure(), &keep_alive, - &complete_gc, &executor, - &pt); - _heap->tracer()->report_gc_reference_stats(stats); - } else { - ShenandoahCMKeepAliveClosure keep_alive(get_queue(serial_worker_id)); - const ReferenceProcessorStats& stats = - rp->process_discovered_references(is_alive.is_alive_closure(), &keep_alive, - &complete_gc, &executor, - &pt); - _heap->tracer()->report_gc_reference_stats(stats); - } - - pt.print_all_references(); - - assert(task_queues()->is_empty(), "Should be empty"); - } -} - -class ShenandoahCancelledGCYieldClosure : public YieldClosure { -private: - ShenandoahHeap* const _heap; -public: - ShenandoahCancelledGCYieldClosure() : _heap(ShenandoahHeap::heap()) {}; - virtual bool should_return() { return _heap->cancelled_gc(); } -}; - -class ShenandoahPrecleanCompleteGCClosure : public VoidClosure { -public: - void do_void() { - ShenandoahHeap* sh = ShenandoahHeap::heap(); - ShenandoahConcurrentMark* scm = sh->concurrent_mark(); - assert(sh->process_references(), "why else would we be here?"); - TaskTerminator terminator(1, scm->task_queues()); - - ReferenceProcessor* rp = sh->ref_processor(); - shenandoah_assert_rp_isalive_installed(); - - scm->mark_loop(0, &terminator, rp, - false, // not cancellable - false); // do not do strdedup - } -}; - -class ShenandoahPrecleanTask : public AbstractGangTask { -private: - ReferenceProcessor* _rp; - -public: - ShenandoahPrecleanTask(ReferenceProcessor* rp) : - AbstractGangTask("Shenandoah Precleaning"), - _rp(rp) {} - - void work(uint worker_id) { - assert(worker_id == 0, "The code below is single-threaded, only one worker is expected"); - ShenandoahParallelWorkerSession worker_session(worker_id); - - ShenandoahHeap* sh = ShenandoahHeap::heap(); - assert(!sh->has_forwarded_objects(), "No forwarded objects expected here"); - - ShenandoahObjToScanQueue* q = sh->concurrent_mark()->get_queue(worker_id); - - ShenandoahCancelledGCYieldClosure yield; - ShenandoahPrecleanCompleteGCClosure complete_gc; - - ShenandoahIsAliveClosure is_alive; - ShenandoahCMKeepAliveClosure keep_alive(q); - ResourceMark rm; - _rp->preclean_discovered_references(&is_alive, &keep_alive, - &complete_gc, &yield, - NULL); - } -}; - -void ShenandoahConcurrentMark::preclean_weak_refs() { - // Pre-cleaning weak references before diving into STW makes sense at the - // end of concurrent mark. This will filter out the references which referents - // are alive. Note that ReferenceProcessor already filters out these on reference - // discovery, and the bulk of work is done here. This phase processes leftovers - // that missed the initial filtering, i.e. when referent was marked alive after - // reference was discovered by RP. - - assert(_heap->process_references(), "sanity"); - - // Shortcut if no references were discovered to avoid winding up threads. - ReferenceProcessor* rp = _heap->ref_processor(); - if (!rp->has_discovered_references()) { - return; - } - - assert(task_queues()->is_empty(), "Should be empty"); - - ReferenceProcessorMTDiscoveryMutator fix_mt_discovery(rp, false); - - shenandoah_assert_rp_isalive_not_installed(); - ShenandoahIsAliveSelector is_alive; - ReferenceProcessorIsAliveMutator fix_isalive(rp, is_alive.is_alive_closure()); - - // Execute precleaning in the worker thread: it will give us GCLABs, String dedup - // queues and other goodies. When upstream ReferenceProcessor starts supporting - // parallel precleans, we can extend this to more threads. - WorkGang* workers = _heap->workers(); - uint nworkers = workers->active_workers(); - assert(nworkers == 1, "This code uses only a single worker"); - task_queues()->reserve(nworkers); - - ShenandoahPrecleanTask task(rp); - workers->run_task(&task); - - assert(task_queues()->is_empty(), "Should be empty"); -} - void ShenandoahConcurrentMark::cancel() { // Clean up marking stacks. ShenandoahObjToScanQueueSet* queues = task_queues(); @@ -876,7 +514,7 @@ ShenandoahObjToScanQueue* ShenandoahConcurrentMark::get_queue(uint worker_id) { } template -void ShenandoahConcurrentMark::mark_loop_prework(uint w, TaskTerminator *t, ReferenceProcessor *rp, +void ShenandoahConcurrentMark::mark_loop_prework(uint w, TaskTerminator *t, ShenandoahReferenceProcessor* rp, bool strdedup) { ShenandoahObjToScanQueue* q = get_queue(w); @@ -934,6 +572,8 @@ void ShenandoahConcurrentMark::mark_loop_work(T* cl, ShenandoahLiveData* live_da ShenandoahObjToScanQueue* q; ShenandoahMarkTask t; + _heap->ref_processor()->set_mark_closure(worker_id, cl); + /* * Process outstanding queues, if any. * diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp index 554e45c818e..2e8538234ee 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp @@ -32,6 +32,7 @@ #include "gc/shenandoah/shenandoahTaskqueue.hpp" class ShenandoahStrDedupQueue; +class ShenandoahReferenceProcessor; class ShenandoahConcurrentMark: public CHeapObj { private: @@ -49,10 +50,10 @@ class ShenandoahConcurrentMark: public CHeapObj { inline void do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveData* live_data, ShenandoahMarkTask* task); template - inline void do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop array); + inline void do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop array, bool weak); template - inline void do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop array, int chunk, int pow); + inline void do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop array, int chunk, int pow, bool weak); inline void count_liveness(ShenandoahLiveData* live_data, oop obj); @@ -60,10 +61,10 @@ class ShenandoahConcurrentMark: public CHeapObj { void mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint worker_id, TaskTerminator *t); template - void mark_loop_prework(uint worker_id, TaskTerminator *terminator, ReferenceProcessor *rp, bool strdedup); + void mark_loop_prework(uint worker_id, TaskTerminator *terminator, ShenandoahReferenceProcessor* rp, bool strdedup); public: - void mark_loop(uint worker_id, TaskTerminator* terminator, ReferenceProcessor *rp, + void mark_loop(uint worker_id, TaskTerminator* terminator, ShenandoahReferenceProcessor* rp, bool cancellable, bool strdedup) { if (cancellable) { mark_loop_prework(worker_id, terminator, rp, strdedup); @@ -73,7 +74,7 @@ class ShenandoahConcurrentMark: public CHeapObj { } template - static inline void mark_through_ref(T* p, ShenandoahHeap* heap, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context); + static inline void mark_through_ref(T* p, ShenandoahHeap* heap, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak); void mark_from_roots(); void finish_mark_from_roots(bool full_gc); @@ -82,15 +83,6 @@ class ShenandoahConcurrentMark: public CHeapObj { void update_roots(ShenandoahPhaseTimings::Phase root_phase); void update_thread_roots(ShenandoahPhaseTimings::Phase root_phase); -// ---------- Weak references -// -private: - void weak_refs_work(bool full_gc); - void weak_refs_work_doit(bool full_gc); - -public: - void preclean_weak_refs(); - // ---------- Helpers // Used from closures, need to be public // diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.inline.hpp index f1504b1d46d..837c00de2b0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Red Hat, Inc. All rights reserved. + * Copyright (c) 2015, 2020, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,6 +45,10 @@ void ShenandoahConcurrentMark::do_task(ShenandoahObjToScanQueue* q, T* cl, Shena shenandoah_assert_marked(NULL, obj); shenandoah_assert_not_in_cset_except(NULL, obj, _heap->cancelled_gc()); + // Are we in weak subgraph scan? + bool weak = task->is_weak(); + cl->set_weak(weak); + if (task->is_not_chunked()) { if (obj->is_instance()) { // Case 1: Normal oop, process as usual. @@ -52,7 +56,7 @@ void ShenandoahConcurrentMark::do_task(ShenandoahObjToScanQueue* q, T* cl, Shena } else if (obj->is_objArray()) { // Case 2: Object array instance and no chunk is set. Must be the first // time we visit it, start the chunked processing. - do_chunked_array_start(q, cl, obj); + do_chunked_array_start(q, cl, obj, weak); } else { // Case 3: Primitive array. Do nothing, no oops there. We use the same // performance tweak TypeArrayKlass::oop_oop_iterate_impl is using: @@ -61,10 +65,14 @@ void ShenandoahConcurrentMark::do_task(ShenandoahObjToScanQueue* q, T* cl, Shena assert (obj->is_typeArray(), "should be type array"); } // Count liveness the last: push the outstanding work to the queues first - count_liveness(live_data, obj); + // Avoid double-counting objects that are visited twice due to upgrade + // from final- to strong mark. + if (task->count_liveness()) { + count_liveness(live_data, obj); + } } else { // Case 4: Array chunk, has sensible chunk id. Process it. - do_chunked_array(q, cl, obj, task->chunk(), task->pow()); + do_chunked_array(q, cl, obj, task->chunk(), task->pow(), weak); } } @@ -98,7 +106,7 @@ inline void ShenandoahConcurrentMark::count_liveness(ShenandoahLiveData* live_da } template -inline void ShenandoahConcurrentMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop obj) { +inline void ShenandoahConcurrentMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop obj, bool weak) { assert(obj->is_objArray(), "expect object array"); objArrayOop array = objArrayOop(obj); int len = array->length(); @@ -129,11 +137,11 @@ inline void ShenandoahConcurrentMark::do_chunked_array_start(ShenandoahObjToScan pow--; chunk = 2; last_idx = (1 << pow); - bool pushed = q->push(ShenandoahMarkTask(array, 1, pow)); + bool pushed = q->push(ShenandoahMarkTask(array, true, weak, 1, pow)); assert(pushed, "overflow queue should always succeed pushing"); } - // Split out tasks, as suggested in ObjArrayChunkedTask docs. Record the last + // Split out tasks, as suggested in ShenandoahMarkTask docs. Record the last // successful right boundary to figure out the irregular tail. while ((1 << pow) > (int)ObjArrayMarkingStride && (chunk*2 < ShenandoahMarkTask::chunk_size())) { @@ -142,7 +150,7 @@ inline void ShenandoahConcurrentMark::do_chunked_array_start(ShenandoahObjToScan int right_chunk = chunk*2; int left_chunk_end = left_chunk * (1 << pow); if (left_chunk_end < len) { - bool pushed = q->push(ShenandoahMarkTask(array, left_chunk, pow)); + bool pushed = q->push(ShenandoahMarkTask(array, true, weak, left_chunk, pow)); assert(pushed, "overflow queue should always succeed pushing"); chunk = right_chunk; last_idx = left_chunk_end; @@ -160,18 +168,18 @@ inline void ShenandoahConcurrentMark::do_chunked_array_start(ShenandoahObjToScan } template -inline void ShenandoahConcurrentMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop obj, int chunk, int pow) { +inline void ShenandoahConcurrentMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop obj, int chunk, int pow, bool weak) { assert(obj->is_objArray(), "expect object array"); objArrayOop array = objArrayOop(obj); assert (ObjArrayMarkingStride > 0, "sanity"); - // Split out tasks, as suggested in ObjArrayChunkedTask docs. Avoid pushing tasks that + // Split out tasks, as suggested in ShenandoahMarkTask docs. Avoid pushing tasks that // are known to start beyond the array. while ((1 << pow) > (int)ObjArrayMarkingStride && (chunk*2 < ShenandoahMarkTask::chunk_size())) { pow--; chunk *= 2; - bool pushed = q->push(ShenandoahMarkTask(array, chunk - 1, pow)); + bool pushed = q->push(ShenandoahMarkTask(array, true, weak, chunk - 1, pow)); assert(pushed, "overflow queue should always succeed pushing"); } @@ -215,13 +223,13 @@ class ShenandoahSATBBufferClosure : public SATBBufferClosure { void do_buffer_impl(void **buffer, size_t size) { for (size_t i = 0; i < size; ++i) { oop *p = (oop *) &buffer[i]; - ShenandoahConcurrentMark::mark_through_ref(p, _heap, _queue, _mark_context); + ShenandoahConcurrentMark::mark_through_ref(p, _heap, _queue, _mark_context, false); } } }; template -inline void ShenandoahConcurrentMark::mark_through_ref(T *p, ShenandoahHeap* heap, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context) { +inline void ShenandoahConcurrentMark::mark_through_ref(T *p, ShenandoahHeap* heap, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak) { T o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { oop obj = CompressedOops::decode_not_null(o); @@ -252,8 +260,15 @@ inline void ShenandoahConcurrentMark::mark_through_ref(T *p, ShenandoahHeap* hea shenandoah_assert_not_forwarded(p, obj); shenandoah_assert_not_in_cset_except(p, obj, heap->cancelled_gc()); - if (mark_context->mark(obj)) { - bool pushed = q->push(ShenandoahMarkTask(obj)); + bool skip_live = false; + bool marked; + if (weak) { + marked = mark_context->mark_weak(obj); + } else { + marked = mark_context->mark_strong(obj, /* was_upgraded = */ skip_live); + } + if (marked) { + bool pushed = q->push(ShenandoahMarkTask(obj, skip_live, weak)); assert(pushed, "overflow queue should always succeed pushing"); if ((STRING_DEDUP == ENQUEUE_DEDUP) && ShenandoahStringDedup::is_candidate(obj)) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index 07a99143949..b698da1f38b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -141,7 +141,6 @@ void ShenandoahControlThread::run_service() { policy->record_explicit_to_concurrent(); mode = default_mode; // Unload and clean up everything - heap->set_process_references(heuristics->can_process_references()); heap->set_unload_classes(heuristics->can_unload_classes()); } else { policy->record_explicit_to_full(); @@ -158,7 +157,6 @@ void ShenandoahControlThread::run_service() { mode = default_mode; // Unload and clean up everything - heap->set_process_references(heuristics->can_process_references()); heap->set_unload_classes(heuristics->can_unload_classes()); } else { policy->record_implicit_to_full(); @@ -172,7 +170,6 @@ void ShenandoahControlThread::run_service() { } // Ask policy if this cycle wants to process references or unload classes - heap->set_process_references(heuristics->should_process_references()); heap->set_unload_classes(heuristics->should_unload_classes()); } @@ -404,14 +401,12 @@ void ShenandoahControlThread::service_concurrent_normal_cycle(GCCause::Cause cau heap->entry_mark(); if (check_cancellation_or_degen(ShenandoahHeap::_degenerated_mark)) return; - // If not cancelled, can try to concurrently pre-clean - heap->entry_preclean(); - // Complete marking under STW, and start evacuation heap->vmop_entry_final_mark(); // Process weak roots that might still point to regions that would be broken by cleanup if (heap->is_concurrent_weak_root_in_progress()) { + heap->entry_weak_refs(); heap->entry_weak_roots(); } @@ -449,6 +444,10 @@ void ShenandoahControlThread::service_concurrent_normal_cycle(GCCause::Cause cau heap->entry_updaterefs(); if (check_cancellation_or_degen(ShenandoahHeap::_degenerated_updaterefs)) return; + // Concurrent update thread roots + heap->entry_update_thread_roots(); + if (check_cancellation_or_degen(ShenandoahHeap::_degenerated_updaterefs)) return; + heap->vmop_entry_final_updaterefs(); // Update references freed up collection set, kick the cleanup to reclaim the space. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahEvacOOMHandler.cpp b/src/hotspot/share/gc/shenandoah/shenandoahEvacOOMHandler.cpp index 5cd9c346bf3..bed372be170 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahEvacOOMHandler.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahEvacOOMHandler.cpp @@ -50,18 +50,18 @@ void ShenandoahEvacOOMHandler::register_thread(Thread* thr) { assert(!ShenandoahThreadLocalData::is_oom_during_evac(Thread::current()), "TL oom-during-evac must not be set"); while (true) { + // Check for OOM. + // If offender has OOM_MARKER_MASK, then loop until no more threads in evac + if ((threads_in_evac & OOM_MARKER_MASK) != 0) { + wait_for_no_evac_threads(); + return; + } + jint other = Atomic::cmpxchg(&_threads_in_evac, threads_in_evac, threads_in_evac + 1); if (other == threads_in_evac) { // Success: caller may safely enter evacuation return; } else { - // Failure: - // - if offender has OOM_MARKER_MASK, then loop until no more threads in evac - // - otherwise re-try CAS - if ((other & OOM_MARKER_MASK) != 0) { - wait_for_no_evac_threads(); - return; - } threads_in_evac = other; } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahEvacOOMHandler.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahEvacOOMHandler.inline.hpp index 9f05581fe94..f5cd459cf76 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahEvacOOMHandler.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahEvacOOMHandler.inline.hpp @@ -34,17 +34,18 @@ void ShenandoahEvacOOMHandler::enter_evacuation(Thread* thr) { jint threads_in_evac = Atomic::load_acquire(&_threads_in_evac); uint8_t level = ShenandoahThreadLocalData::push_evac_oom_scope(thr); - if ((threads_in_evac & OOM_MARKER_MASK) != 0) { - wait_for_no_evac_threads(); - return; - } - - // Nesting case, this thread already registered - if (level != 0) { - return; - } - // Entering top level scope, register this thread. - register_thread(thr); + if (level == 0) { + // Entering top level scope, register this thread. + register_thread(thr); + } else if (!ShenandoahThreadLocalData::is_oom_during_evac(thr)) { + jint threads_in_evac = Atomic::load_acquire(&_threads_in_evac); + // If OOM is in progress, handle it. + if ((threads_in_evac & OOM_MARKER_MASK) != 0) { + assert((threads_in_evac & ~OOM_MARKER_MASK) > 0, "sanity"); + Atomic::dec(&_threads_in_evac); + wait_for_no_evac_threads(); + } + } } void ShenandoahEvacOOMHandler::leave_evacuation(Thread* thr) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index eff5c50ea53..7c388284665 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -55,6 +55,7 @@ #include "gc/shenandoah/shenandoahPacer.inline.hpp" #include "gc/shenandoah/shenandoahPadding.hpp" #include "gc/shenandoah/shenandoahParallelCleaning.inline.hpp" +#include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" #include "gc/shenandoah/shenandoahStringDedup.hpp" #include "gc/shenandoah/shenandoahTaskqueue.hpp" @@ -71,7 +72,7 @@ #include "gc/shenandoah/shenandoahJfrSupport.hpp" #endif -#include "memory/metaspace.hpp" +#include "memory/classLoaderMetaspace.hpp" #include "oops/compressedOops.inline.hpp" #include "runtime/atomic.hpp" #include "runtime/globals.hpp" @@ -184,14 +185,14 @@ jint ShenandoahHeap::initialize() { assert((((size_t) base()) & ShenandoahHeapRegion::region_size_bytes_mask()) == 0, "Misaligned heap: " PTR_FORMAT, p2i(base())); -#if SHENANDOAH_OPTIMIZED_OBJTASK - // The optimized ObjArrayChunkedTask takes some bits away from the full object bits. +#if SHENANDOAH_OPTIMIZED_MARKTASK + // The optimized ShenandoahMarkTask takes some bits away from the full object bits. // Fail if we ever attempt to address more than we can. - if ((uintptr_t)heap_rs.end() >= ObjArrayChunkedTask::max_addressable()) { + if ((uintptr_t)heap_rs.end() >= ShenandoahMarkTask::max_addressable()) { FormatBuffer<512> buf("Shenandoah reserved [" PTR_FORMAT ", " PTR_FORMAT") for the heap, \n" "but max object address is " PTR_FORMAT ". Try to reduce heap size, or try other \n" "VM options that allocate heap at lower addresses (HeapBaseMinAddress, AllocateHeapAt, etc).", - p2i(heap_rs.base()), p2i(heap_rs.end()), ObjArrayChunkedTask::max_addressable()); + p2i(heap_rs.base()), p2i(heap_rs.end()), ShenandoahMarkTask::max_addressable()); vm_exit_during_initialization("Fatal Error", buf); } #endif @@ -206,10 +207,10 @@ jint ShenandoahHeap::initialize() { // Reserve and commit memory for bitmap(s) // - _bitmap_size = MarkBitMap::compute_size(heap_rs.size()); + _bitmap_size = ShenandoahMarkBitMap::compute_size(heap_rs.size()); _bitmap_size = align_up(_bitmap_size, bitmap_page_size); - size_t bitmap_bytes_per_region = reg_size_bytes / MarkBitMap::heap_map_factor(); + size_t bitmap_bytes_per_region = reg_size_bytes / ShenandoahMarkBitMap::heap_map_factor(); guarantee(bitmap_bytes_per_region != 0, "Bitmap bytes per region should not be zero"); @@ -393,9 +394,6 @@ jint ShenandoahHeap::initialize() { _control_thread = new ShenandoahControlThread(); - _ref_proc_mt_processing = ParallelRefProcEnabled && (ParallelGCThreads > 1); - _ref_proc_mt_discovery = _max_workers > 1; - ShenandoahInitLogger::print(); return JNI_OK; @@ -475,7 +473,7 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) : _gc_timer(new (ResourceObj::C_HEAP, mtGC) ConcurrentGCTimer()), _soft_ref_policy(), _log_min_obj_alignment_in_bytes(LogMinObjAlignmentInBytes), - _ref_processor(NULL), + _ref_processor(new ShenandoahReferenceProcessor(MAX2(_max_workers, 1U))), _marking_context(NULL), _bitmap_size(0), _bitmap_regions_per_slice(0), @@ -615,8 +613,6 @@ void ShenandoahHeap::post_initialize() { _scm->initialize(_max_workers); _full_gc->initialize(_gc_timer); - ref_processing_init(); - _heuristics->initialize(); JFR_ONLY(ShenandoahJFRSupport::register_jfr_type_serializers()); @@ -1791,13 +1787,9 @@ void ShenandoahHeap::op_final_mark() { concurrent_mark()->cancel(); set_concurrent_mark_in_progress(false); - if (process_references()) { - // Abandon reference processing right away: pre-cleaning must have failed. - ReferenceProcessor *rp = ref_processor(); - rp->disable_discovery(); - rp->abandon_partial_discovery(); - rp->verify_no_references_recorded(); - } + // Abandon reference processing right away: pre-cleaning must have failed. + ShenandoahReferenceProcessor* rp = ref_processor(); + rp->abandon_partial_discovery(); } } @@ -1806,6 +1798,31 @@ void ShenandoahHeap::op_conc_evac() { workers()->run_task(&task); } +class ShenandoahUpdateThreadClosure : public HandshakeClosure { +private: + ShenandoahUpdateRefsClosure _cl; +public: + ShenandoahUpdateThreadClosure(); + void do_thread(Thread* thread); +}; + +ShenandoahUpdateThreadClosure::ShenandoahUpdateThreadClosure() : + HandshakeClosure("Shenandoah Update Thread Roots") { +} + +void ShenandoahUpdateThreadClosure::do_thread(Thread* thread) { + if (thread->is_Java_thread()) { + JavaThread* jt = thread->as_Java_thread(); + ResourceMark rm; + jt->oops_do(&_cl, NULL); + } +} + +void ShenandoahHeap::op_update_thread_roots() { + ShenandoahUpdateThreadClosure cl; + Handshake::execute(&cl); +} + void ShenandoahHeap::op_stw_evac() { ShenandoahEvacuationTask task(this, _collection_set, false); workers()->run_task(&task); @@ -1916,7 +1933,7 @@ class ShenandoahConcurrentWeakRootsEvacUpdateTask : public AbstractGangTask { ShenandoahVMWeakRoots _vm_roots; // Roots related to concurrent class unloading - ShenandoahClassLoaderDataRoots + ShenandoahClassLoaderDataRoots _cld_roots; ShenandoahConcurrentNMethodIterator _nmethod_itr; ShenandoahConcurrentStringDedupRoots _dedup_roots; @@ -1979,6 +1996,15 @@ class ShenandoahConcurrentWeakRootsEvacUpdateTask : public AbstractGangTask { } }; +void ShenandoahHeap::op_weak_refs() { + // Concurrent weak refs processing + { + ShenandoahTimingsTracker t(ShenandoahPhaseTimings::conc_weak_refs_work); + ShenandoahGCWorkerPhase worker_phase(ShenandoahPhaseTimings::conc_weak_refs_work); + ref_processor()->process_references(workers(), true /* concurrent */); + } +} + void ShenandoahHeap::op_weak_roots() { if (is_concurrent_weak_root_in_progress()) { // Concurrent weak root processing @@ -2052,13 +2078,6 @@ void ShenandoahHeap::op_reset() { parallel_heap_region_iterate(&cl); } -void ShenandoahHeap::op_preclean() { - if (ShenandoahPacing) { - pacer()->setup_for_preclean(); - } - concurrent_mark()->preclean_weak_refs(); -} - void ShenandoahHeap::op_full(GCCause::Cause cause) { ShenandoahMetricsSnapshot metrics; metrics.snap_before(); @@ -2100,7 +2119,6 @@ void ShenandoahHeap::op_degenerated(ShenandoahDegenPoint point) { // // Note that we can only do this for "outside-cycle" degens, otherwise we would risk // changing the cycle parameters mid-cycle during concurrent -> degenerated handover. - set_process_references(heuristics()->can_process_references()); set_unload_classes(heuristics()->can_unload_classes()); op_reset(); @@ -2125,6 +2143,12 @@ void ShenandoahHeap::op_degenerated(ShenandoahDegenPoint point) { ShenandoahCodeRoots::disarm_nmethods(); } + { + ShenandoahTimingsTracker t(ShenandoahPhaseTimings::conc_weak_refs_work); + ShenandoahGCWorkerPhase worker_phase(ShenandoahPhaseTimings::conc_weak_refs_work); + ref_processor()->process_references(workers(), false /* concurrent */); + } + op_cleanup_early(); case _degenerated_evac: @@ -2285,22 +2309,6 @@ void ShenandoahHeap::set_concurrent_weak_root_in_progress(bool in_progress) { } } -void ShenandoahHeap::ref_processing_init() { - assert(_max_workers > 0, "Sanity"); - - _ref_processor = - new ReferenceProcessor(&_subject_to_discovery, // is_subject_to_discovery - _ref_proc_mt_processing, // MT processing - _max_workers, // Degree of MT processing - _ref_proc_mt_discovery, // MT discovery - _max_workers, // Degree of MT discovery - false, // Reference discovery is not atomic - NULL, // No closure, should be installed before use - true); // Scale worker threads - - shenandoah_assert_rp_isalive_not_installed(); -} - GCTracer* ShenandoahHeap::tracer() { return shenandoah_policy()->tracer(); } @@ -2385,7 +2393,7 @@ void ShenandoahHeap::stw_unload_classes(bool full_gc) { } // Resize and verify metaspace MetaspaceGC::compute_new_size(); - MetaspaceUtils::verify_metrics(); + DEBUG_ONLY(MetaspaceUtils::verify();) } // Weak roots are either pre-evacuated (final mark) or updated (final updaterefs), @@ -2408,17 +2416,17 @@ void ShenandoahHeap::stw_process_weak_roots(bool full_gc) { ShenandoahForwardedIsAliveClosure is_alive; ShenandoahUpdateRefsClosure keep_alive; ShenandoahParallelWeakRootsCleaningTask - cleaning_task(timing_phase, &is_alive, &keep_alive, num_workers, !ShenandoahConcurrentRoots::should_do_concurrent_class_unloading()); + cleaning_task(timing_phase, &is_alive, &keep_alive, num_workers, is_stw_gc_in_progress()); _workers->run_task(&cleaning_task); } else { ShenandoahIsAliveClosure is_alive; #ifdef ASSERT ShenandoahAssertNotForwardedClosure verify_cl; ShenandoahParallelWeakRootsCleaningTask - cleaning_task(timing_phase, &is_alive, &verify_cl, num_workers, !ShenandoahConcurrentRoots::should_do_concurrent_class_unloading()); + cleaning_task(timing_phase, &is_alive, &verify_cl, num_workers, is_stw_gc_in_progress()); #else ShenandoahParallelWeakRootsCleaningTask - cleaning_task(timing_phase, &is_alive, &do_nothing_cl, num_workers, !ShenandoahConcurrentRoots::should_do_concurrent_class_unloading()); + cleaning_task(timing_phase, &is_alive, &do_nothing_cl, num_workers, is_stw_gc_in_progress()); #endif _workers->run_task(&cleaning_task); } @@ -2436,18 +2444,10 @@ void ShenandoahHeap::set_has_forwarded_objects(bool cond) { set_gc_state_mask(HAS_FORWARDED, cond); } -void ShenandoahHeap::set_process_references(bool pr) { - _process_references.set_cond(pr); -} - void ShenandoahHeap::set_unload_classes(bool uc) { _unload_classes.set_cond(uc); } -bool ShenandoahHeap::process_references() const { - return _process_references.is_set(); -} - bool ShenandoahHeap::unload_classes() const { return _unload_classes.is_set(); } @@ -2735,8 +2735,6 @@ void ShenandoahHeap::op_final_updaterefs() { if (is_degenerated_gc_in_progress()) { concurrent_mark()->update_roots(ShenandoahPhaseTimings::degen_gc_update_roots); - } else { - concurrent_mark()->update_thread_roots(ShenandoahPhaseTimings::final_update_refs_roots); } // Has to be done before cset is clear @@ -3018,6 +3016,19 @@ void ShenandoahHeap::entry_evac() { op_conc_evac(); } +void ShenandoahHeap::entry_update_thread_roots() { + TraceCollectorStats tcs(monitoring_support()->concurrent_collection_counters()); + + static const char* msg = "Concurrent update thread roots"; + ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_thread_roots); + EventMark em("%s", msg); + + // No workers used in this phase, no setup required + try_inject_alloc_failure(); + op_update_thread_roots(); +} + + void ShenandoahHeap::entry_updaterefs() { static const char* msg = "Concurrent update references"; ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_refs); @@ -3031,6 +3042,19 @@ void ShenandoahHeap::entry_updaterefs() { op_updaterefs(); } +void ShenandoahHeap::entry_weak_refs() { + static const char* msg = "Concurrent weak references"; + ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_weak_refs); + EventMark em("%s", msg); + + ShenandoahWorkerScope scope(workers(), + ShenandoahWorkerPolicy::calc_workers_for_conc_refs_processing(), + "concurrent weak references"); + + try_inject_alloc_failure(); + op_weak_refs(); +} + void ShenandoahHeap::entry_weak_roots() { static const char* msg = "Concurrent weak roots"; ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_weak_roots); @@ -3117,22 +3141,6 @@ void ShenandoahHeap::entry_reset() { op_reset(); } -void ShenandoahHeap::entry_preclean() { - if (ShenandoahPreclean && process_references()) { - static const char* msg = "Concurrent precleaning"; - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_preclean); - EventMark em("%s", msg); - - ShenandoahWorkerScope scope(workers(), - ShenandoahWorkerPolicy::calc_workers_for_conc_preclean(), - "concurrent preclean", - /* check_workers = */ false); - - try_inject_alloc_failure(); - op_preclean(); - } -} - void ShenandoahHeap::entry_uncommit(double shrink_before, size_t shrink_until) { static const char *msg = "Concurrent uncommit"; ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_uncommit, true /* log_heap_usage */); @@ -3209,14 +3217,9 @@ void ShenandoahHeap::deduplicate_string(oop str) { const char* ShenandoahHeap::init_mark_event_message() const { assert(!has_forwarded_objects(), "Should not have forwarded objects here"); - bool proc_refs = process_references(); bool unload_cls = unload_classes(); - if (proc_refs && unload_cls) { - return "Pause Init Mark (process weakrefs) (unload classes)"; - } else if (proc_refs) { - return "Pause Init Mark (process weakrefs)"; - } else if (unload_cls) { + if (unload_cls) { return "Pause Init Mark (unload classes)"; } else { return "Pause Init Mark"; @@ -3226,14 +3229,9 @@ const char* ShenandoahHeap::init_mark_event_message() const { const char* ShenandoahHeap::final_mark_event_message() const { assert(!has_forwarded_objects(), "Should not have forwarded objects here"); - bool proc_refs = process_references(); bool unload_cls = unload_classes(); - if (proc_refs && unload_cls) { - return "Pause Final Mark (process weakrefs) (unload classes)"; - } else if (proc_refs) { - return "Pause Final Mark (process weakrefs)"; - } else if (unload_cls) { + if (unload_cls) { return "Pause Final Mark (unload classes)"; } else { return "Pause Final Mark"; @@ -3243,14 +3241,9 @@ const char* ShenandoahHeap::final_mark_event_message() const { const char* ShenandoahHeap::conc_mark_event_message() const { assert(!has_forwarded_objects(), "Should not have forwarded objects here"); - bool proc_refs = process_references(); bool unload_cls = unload_classes(); - if (proc_refs && unload_cls) { - return "Concurrent marking (process weakrefs) (unload classes)"; - } else if (proc_refs) { - return "Concurrent marking (process weakrefs)"; - } else if (unload_cls) { + if (unload_cls) { return "Concurrent marking (unload classes)"; } else { return "Concurrent marking"; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index c20aa63fb2b..3a109a22673 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -35,13 +35,13 @@ #include "gc/shenandoah/shenandoahPadding.hpp" #include "gc/shenandoah/shenandoahSharedVariables.hpp" #include "gc/shenandoah/shenandoahUnload.hpp" +#include "memory/metaspace.hpp" #include "services/memoryManager.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/stack.hpp" class ConcurrentGCTimer; class ObjectIterateScanRootClosure; -class ReferenceProcessor; class ShenandoahCollectorPolicy; class ShenandoahControlThread; class ShenandoahGCSession; @@ -59,7 +59,7 @@ class ShenandoahFreeSet; class ShenandoahConcurrentMark; class ShenandoahMarkCompact; class ShenandoahMonitoringSupport; -class ShenandoahObjToScanQueueSet; +class ShenandoahReferenceProcessor; class ShenandoahPacer; class ShenandoahVerifier; class ShenandoahWorkGang; @@ -389,13 +389,14 @@ class ShenandoahHeap : public CollectedHeap { // for concurrent operation. void entry_reset(); void entry_mark(); - void entry_preclean(); + void entry_weak_refs(); void entry_weak_roots(); void entry_class_unloading(); void entry_strong_roots(); void entry_cleanup_early(); void entry_rendezvous_roots(); void entry_evac(); + void entry_update_thread_roots(); void entry_updaterefs(); void entry_cleanup_complete(); void entry_uncommit(double shrink_before, size_t shrink_until); @@ -413,7 +414,7 @@ class ShenandoahHeap : public CollectedHeap { void op_reset(); void op_mark(); - void op_preclean(); + void op_weak_refs(); void op_weak_roots(); void op_class_unloading(); void op_strong_roots(); @@ -421,6 +422,7 @@ class ShenandoahHeap : public CollectedHeap { void op_rendezvous_roots(); void op_conc_evac(); void op_stw_evac(); + void op_update_thread_roots(); void op_updaterefs(); void op_cleanup_complete(); void op_uncommit(double shrink_before, size_t shrink_until); @@ -491,20 +493,10 @@ class ShenandoahHeap : public CollectedHeap { // ---------- Reference processing // private: - AlwaysTrueClosure _subject_to_discovery; - ReferenceProcessor* _ref_processor; - ShenandoahSharedFlag _process_references; - bool _ref_proc_mt_discovery; - bool _ref_proc_mt_processing; - - void ref_processing_init(); + ShenandoahReferenceProcessor* const _ref_processor; public: - ReferenceProcessor* ref_processor() { return _ref_processor; } - bool ref_processor_mt_discovery() { return _ref_proc_mt_discovery; } - bool ref_processor_mt_processing() { return _ref_proc_mt_processing; } - void set_process_references(bool pr); - bool process_references() const; + ShenandoahReferenceProcessor* ref_processor() { return _ref_processor; } // ---------- Class Unloading // diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index 3bb97e01f59..6c49253e2c9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -401,7 +401,6 @@ inline void ShenandoahHeap::marked_object_iterate(ShenandoahHeapRegion* region, ShenandoahMarkingContext* const ctx = complete_marking_context(); assert(ctx->is_complete(), "sanity"); - MarkBitMap* mark_bit_map = ctx->mark_bit_map(); HeapWord* tams = ctx->top_at_mark_start(region); size_t skip_bitmap_delta = 1; @@ -413,7 +412,7 @@ inline void ShenandoahHeap::marked_object_iterate(ShenandoahHeapRegion* region, // Try to scan the initial candidate. If the candidate is above the TAMS, it would // fail the subsequent "< limit_bitmap" checks, and fall through to Step 2. - HeapWord* cb = mark_bit_map->get_next_marked_addr(start, end); + HeapWord* cb = ctx->get_next_marked_addr(start, end); intx dist = ShenandoahMarkScanPrefetch; if (dist > 0) { @@ -440,7 +439,7 @@ inline void ShenandoahHeap::marked_object_iterate(ShenandoahHeapRegion* region, slots[avail++] = cb; cb += skip_bitmap_delta; if (cb < limit_bitmap) { - cb = mark_bit_map->get_next_marked_addr(cb, limit_bitmap); + cb = ctx->get_next_marked_addr(cb, limit_bitmap); } } @@ -463,7 +462,7 @@ inline void ShenandoahHeap::marked_object_iterate(ShenandoahHeapRegion* region, cl->do_object(obj); cb += skip_bitmap_delta; if (cb < limit_bitmap) { - cb = mark_bit_map->get_next_marked_addr(cb, limit_bitmap); + cb = ctx->get_next_marked_addr(cb, limit_bitmap); } } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahInitLogger.cpp b/src/hotspot/share/gc/shenandoah/shenandoahInitLogger.cpp index 2c2bc91cfff..9f0233dd08c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahInitLogger.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahInitLogger.cpp @@ -57,10 +57,6 @@ void ShenandoahInitLogger::print_heap() { log_info(gc, init)("Humongous Object Threshold: " SIZE_FORMAT "%s", byte_size_in_exact_unit(ShenandoahHeapRegion::humongous_threshold_bytes()), exact_unit_for_byte_size(ShenandoahHeapRegion::humongous_threshold_bytes())); - - log_info(gc, init)("Reference Processing: %s discovery, %s processing", - heap->ref_processor_mt_discovery() ? "Parallel" : "Serial", - heap->ref_processor_mt_processing() ? "Parallel" : "Serial"); } void ShenandoahInitLogger::print() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp new file mode 100644 index 00000000000..75af01c0cc7 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat, Inc. and/or its affiliates. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "gc/shenandoah/shenandoahMarkBitMap.inline.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "utilities/globalDefinitions.hpp" + +ShenandoahMarkBitMap::ShenandoahMarkBitMap(MemRegion heap, MemRegion storage) : + _shift(LogMinObjAlignment), + _covered(heap), + _map((BitMap::bm_word_t*) storage.start()), + _size((heap.word_size() * 2) >> _shift) { +} + +size_t ShenandoahMarkBitMap::compute_size(size_t heap_size) { + return ReservedSpace::allocation_align_size_up(heap_size / mark_distance()); +} + +size_t ShenandoahMarkBitMap::mark_distance() { + return MinObjAlignmentInBytes * BitsPerByte / 2; +} + +HeapWord* ShenandoahMarkBitMap::get_next_marked_addr(const HeapWord* addr, + const HeapWord* limit) const { + assert(limit != NULL, "limit must not be NULL"); + // Round addr up to a possible object boundary to be safe. + size_t const addr_offset = address_to_index(align_up(addr, HeapWordSize << LogMinObjAlignment)); + size_t const limit_offset = address_to_index(limit); + size_t const nextOffset = get_next_one_offset(addr_offset, limit_offset); + return index_to_address(nextOffset); +} + +void ShenandoahMarkBitMap::clear_range_within_word(idx_t beg, idx_t end) { + // With a valid range (beg <= end), this test ensures that end != 0, as + // required by inverted_bit_mask_for_range. Also avoids an unnecessary write. + if (beg != end) { + bm_word_t mask = inverted_bit_mask_for_range(beg, end); + *word_addr(beg) &= mask; + } +} + +void ShenandoahMarkBitMap::clear_range(idx_t beg, idx_t end) { + verify_range(beg, end); + + idx_t beg_full_word = to_words_align_up(beg); + idx_t end_full_word = to_words_align_down(end); + + if (beg_full_word < end_full_word) { + // The range includes at least one full word. + clear_range_within_word(beg, bit_index(beg_full_word)); + clear_range_of_words(beg_full_word, end_full_word); + clear_range_within_word(bit_index(end_full_word), end); + } else { + // The range spans at most 2 partial words. + idx_t boundary = MIN2(bit_index(beg_full_word), end); + clear_range_within_word(beg, boundary); + clear_range_within_word(boundary, end); + } +} + +bool ShenandoahMarkBitMap::is_small_range_of_words(idx_t beg_full_word, idx_t end_full_word) { + // There is little point to call large version on small ranges. + // Need to check carefully, keeping potential idx_t over/underflow in mind, + // because beg_full_word > end_full_word can occur when beg and end are in + // the same word. + // The threshold should be at least one word. + STATIC_ASSERT(small_range_words >= 1); + return beg_full_word + small_range_words >= end_full_word; +} + + +void ShenandoahMarkBitMap::clear_large_range(idx_t beg, idx_t end) { + verify_range(beg, end); + + idx_t beg_full_word = to_words_align_up(beg); + idx_t end_full_word = to_words_align_down(end); + + if (is_small_range_of_words(beg_full_word, end_full_word)) { + clear_range(beg, end); + return; + } + + // The range includes at least one full word. + clear_range_within_word(beg, bit_index(beg_full_word)); + clear_large_range_of_words(beg_full_word, end_full_word); + clear_range_within_word(bit_index(end_full_word), end); +} + +void ShenandoahMarkBitMap::clear_range_large(MemRegion mr) { + MemRegion intersection = mr.intersection(_covered); + assert(!intersection.is_empty(), + "Given range from " PTR_FORMAT " to " PTR_FORMAT " is completely outside the heap", + p2i(mr.start()), p2i(mr.end())); + // convert address range into offset range + size_t beg = address_to_index(intersection.start()); + size_t end = address_to_index(intersection.end()); + clear_large_range(beg, end); +} + +#ifdef ASSERT +void ShenandoahMarkBitMap::check_mark(HeapWord* addr) const { + assert(ShenandoahHeap::heap()->is_in(addr), + "Trying to access bitmap " PTR_FORMAT " for address " PTR_FORMAT " not in the heap.", + p2i(this), p2i(addr)); +} + +void ShenandoahMarkBitMap::verify_index(idx_t bit) const { + assert(bit < _size, + "BitMap index out of bounds: " SIZE_FORMAT " >= " SIZE_FORMAT, + bit, _size); +} + +void ShenandoahMarkBitMap::verify_limit(idx_t bit) const { + assert(bit <= _size, + "BitMap limit out of bounds: " SIZE_FORMAT " > " SIZE_FORMAT, + bit, _size); +} + +void ShenandoahMarkBitMap::verify_range(idx_t beg, idx_t end) const { + assert(beg <= end, + "BitMap range error: " SIZE_FORMAT " > " SIZE_FORMAT, beg, end); + verify_limit(end); +} +#endif diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp new file mode 100644 index 00000000000..a37523f8197 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat, Inc. and/or its affiliates. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_HPP +#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_HPP + +#include "memory/memRegion.hpp" +#include "runtime/atomic.hpp" +#include "utilities/globalDefinitions.hpp" + +class ShenandoahMarkBitMap { +public: + typedef size_t idx_t; // Type used for bit and word indices. + typedef uintptr_t bm_word_t; // Element type of array that represents the + // bitmap, with BitsPerWord bits per element. + +private: + // Values for get_next_bit_impl flip parameter. + static const bm_word_t find_ones_flip = 0; + static const bm_word_t find_zeros_flip = ~(bm_word_t)0; + + int const _shift; + MemRegion _covered; + + bm_word_t* _map; // First word in bitmap + idx_t _size; // Size of bitmap (in bits) + + // Threshold for performing small range operation, even when large range + // operation was requested. Measured in words. + static const size_t small_range_words = 32; + + static bool is_small_range_of_words(idx_t beg_full_word, idx_t end_full_word); + + inline size_t address_to_index(const HeapWord* addr) const; + inline HeapWord* index_to_address(size_t offset) const; + + void check_mark(HeapWord* addr) const NOT_DEBUG_RETURN; + + // Return a mask that will select the specified bit, when applied to the word + // containing the bit. + static bm_word_t bit_mask(idx_t bit) { return (bm_word_t)1 << bit_in_word(bit); } + + // Return the bit number of the first bit in the specified word. + static idx_t bit_index(idx_t word) { return word << LogBitsPerWord; } + + // Return the position of bit within the word that contains it (e.g., if + // bitmap words are 32 bits, return a number 0 <= n <= 31). + static idx_t bit_in_word(idx_t bit) { return bit & (BitsPerWord - 1); } + + bm_word_t* map() { return _map; } + const bm_word_t* map() const { return _map; } + bm_word_t map(idx_t word) const { return _map[word]; } + + // Return a pointer to the word containing the specified bit. + bm_word_t* word_addr(idx_t bit) { + return map() + to_words_align_down(bit); + } + + const bm_word_t* word_addr(idx_t bit) const { + return map() + to_words_align_down(bit); + } + + static inline const bm_word_t load_word_ordered(const volatile bm_word_t* const addr, atomic_memory_order memory_order); + + bool at(idx_t index) const { + verify_index(index); + return (*word_addr(index) & bit_mask(index)) != 0; + } + + // Assumes relevant validity checking for bit has already been done. + static idx_t raw_to_words_align_up(idx_t bit) { + return raw_to_words_align_down(bit + (BitsPerWord - 1)); + } + + // Assumes relevant validity checking for bit has already been done. + static idx_t raw_to_words_align_down(idx_t bit) { + return bit >> LogBitsPerWord; + } + + // Word-aligns bit and converts it to a word offset. + // precondition: bit <= size() + idx_t to_words_align_up(idx_t bit) const { + verify_limit(bit); + return raw_to_words_align_up(bit); + } + + // Word-aligns bit and converts it to a word offset. + // precondition: bit <= size() + inline idx_t to_words_align_down(idx_t bit) const { + verify_limit(bit); + return raw_to_words_align_down(bit); + } + + // Helper for get_next_{zero,one}_bit variants. + // - flip designates whether searching for 1s or 0s. Must be one of + // find_{zeros,ones}_flip. + // - aligned_right is true if r_index is a priori on a bm_word_t boundary. + template + inline idx_t get_next_bit_impl(idx_t l_index, idx_t r_index) const; + + inline idx_t get_next_one_offset (idx_t l_index, idx_t r_index) const; + + void clear_large_range (idx_t beg, idx_t end); + + // Verify bit is less than size(). + void verify_index(idx_t bit) const NOT_DEBUG_RETURN; + // Verify bit is not greater than size(). + void verify_limit(idx_t bit) const NOT_DEBUG_RETURN; + // Verify [beg,end) is a valid range, e.g. beg <= end <= size(). + void verify_range(idx_t beg, idx_t end) const NOT_DEBUG_RETURN; + +public: + static size_t compute_size(size_t heap_size); + // Returns the amount of bytes on the heap between two marks in the bitmap. + static size_t mark_distance(); + // Returns how many bytes (or bits) of the heap a single byte (or bit) of the + // mark bitmap corresponds to. This is the same as the mark distance above. + static size_t heap_map_factor() { + return mark_distance(); + } + + ShenandoahMarkBitMap(MemRegion heap, MemRegion storage); + + // Mark word as 'strong' if it hasn't been marked strong yet. + // Return true if the word has been marked strong, false if it has already been + // marked strong or if another thread has beat us by marking it + // strong. + // Words that have been marked final before or by a concurrent thread will be + // upgraded to strong. In this case, this method also returns true. + inline bool mark_strong(HeapWord* w, bool& was_upgraded); + + // Mark word as 'weak' if it hasn't been marked weak or strong yet. + // Return true if the word has been marked weak, false if it has already been + // marked strong or weak or if another thread has beat us by marking it + // strong or weak. + inline bool mark_weak(HeapWord* heap_addr); + + inline bool is_marked(HeapWord* addr) const; + inline bool is_marked_strong(HeapWord* w) const; + inline bool is_marked_weak(HeapWord* addr) const; + + // Return the address corresponding to the next marked bit at or after + // "addr", and before "limit", if "limit" is non-NULL. If there is no + // such bit, returns "limit" if that is non-NULL, or else "endWord()". + HeapWord* get_next_marked_addr(const HeapWord* addr, + const HeapWord* limit) const; + + bm_word_t inverted_bit_mask_for_range(idx_t beg, idx_t end) const; + void clear_range_within_word (idx_t beg, idx_t end); + void clear_range (idx_t beg, idx_t end); + void clear_range_large(MemRegion mr); + + void clear_range_of_words(idx_t beg, idx_t end); + void clear_large_range_of_words(idx_t beg, idx_t end); + static void clear_range_of_words(bm_word_t* map, idx_t beg, idx_t end); + +}; + +#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp new file mode 100644 index 00000000000..b9e0bb61f54 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat, Inc. and/or its affiliates. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_INLINE_HPP +#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_INLINE_HPP + +#include "gc/shenandoah/shenandoahMarkBitMap.hpp" +#include "runtime/atomic.hpp" +#include "utilities/count_trailing_zeros.hpp" + +inline size_t ShenandoahMarkBitMap::address_to_index(const HeapWord* addr) const { + return (pointer_delta(addr, _covered.start()) << 1) >> _shift; +} + +inline HeapWord* ShenandoahMarkBitMap::index_to_address(size_t offset) const { + return _covered.start() + ((offset >> 1) << _shift); +} + +inline bool ShenandoahMarkBitMap::mark_strong(HeapWord* heap_addr, bool& was_upgraded) { + check_mark(heap_addr); + + idx_t bit = address_to_index(heap_addr); + verify_index(bit); + volatile bm_word_t* const addr = word_addr(bit); + const bm_word_t mask = bit_mask(bit); + const bm_word_t mask_weak = (bm_word_t)1 << (bit_in_word(bit) + 1); + bm_word_t old_val = load_word_ordered(addr, memory_order_conservative); + + do { + const bm_word_t new_val = old_val | mask; + if (new_val == old_val) { + assert(!was_upgraded, "Should be false already"); + return false; // Someone else beat us to it. + } + const bm_word_t cur_val = Atomic::cmpxchg(addr, old_val, new_val, memory_order_conservative); + if (cur_val == old_val) { + was_upgraded = (cur_val & mask_weak) != 0; + return true; // Success. + } + old_val = cur_val; // The value changed, try again. + } while (true); +} + +inline bool ShenandoahMarkBitMap::mark_weak(HeapWord* heap_addr) { + check_mark(heap_addr); + + idx_t bit = address_to_index(heap_addr); + verify_index(bit); + volatile bm_word_t* const addr = word_addr(bit); + const bm_word_t mask_weak = (bm_word_t)1 << (bit_in_word(bit) + 1); + const bm_word_t mask_strong = (bm_word_t)1 << bit_in_word(bit); + bm_word_t old_val = load_word_ordered(addr, memory_order_conservative); + + do { + if ((old_val & mask_strong) != 0) { + return false; // Already marked strong + } + const bm_word_t new_val = old_val | mask_weak; + if (new_val == old_val) { + return false; // Someone else beat us to it. + } + const bm_word_t cur_val = Atomic::cmpxchg(addr, old_val, new_val, memory_order_conservative); + if (cur_val == old_val) { + return true; // Success. + } + old_val = cur_val; // The value changed, try again. + } while (true); +} + +inline bool ShenandoahMarkBitMap::is_marked_strong(HeapWord* addr) const { + check_mark(addr); + return at(address_to_index(addr)); +} + +inline bool ShenandoahMarkBitMap::is_marked_weak(HeapWord* addr) const { + check_mark(addr); + return at(address_to_index(addr) + 1); +} + +inline bool ShenandoahMarkBitMap::is_marked(HeapWord* addr) const { + check_mark(addr); + idx_t index = address_to_index(addr); + verify_index(index); + bm_word_t mask = (bm_word_t)3 << bit_in_word(index); + return (*word_addr(index) & mask) != 0; +} + +inline const ShenandoahMarkBitMap::bm_word_t ShenandoahMarkBitMap::load_word_ordered(const volatile bm_word_t* const addr, atomic_memory_order memory_order) { + if (memory_order == memory_order_relaxed || memory_order == memory_order_release) { + return Atomic::load(addr); + } else { + assert(memory_order == memory_order_acq_rel || + memory_order == memory_order_acquire || + memory_order == memory_order_conservative, + "unexpected memory ordering"); + return Atomic::load_acquire(addr); + } +} + +template +inline ShenandoahMarkBitMap::idx_t ShenandoahMarkBitMap::get_next_bit_impl(idx_t l_index, idx_t r_index) const { + STATIC_ASSERT(flip == find_ones_flip || flip == find_zeros_flip); + verify_range(l_index, r_index); + assert(!aligned_right || is_aligned(r_index, BitsPerWord), "r_index not aligned"); + + // The first word often contains an interesting bit, either due to + // density or because of features of the calling algorithm. So it's + // important to examine that first word with a minimum of fuss, + // minimizing setup time for later words that will be wasted if the + // first word is indeed interesting. + + // The benefit from aligned_right being true is relatively small. + // It saves an operation in the setup for the word search loop. + // It also eliminates the range check on the final result. + // However, callers often have a comparison with r_index, and + // inlining often allows the two comparisons to be combined; it is + // important when !aligned_right that return paths either return + // r_index or a value dominated by a comparison with r_index. + // aligned_right is still helpful when the caller doesn't have a + // range check because features of the calling algorithm guarantee + // an interesting bit will be present. + + if (l_index < r_index) { + // Get the word containing l_index, and shift out low bits. + idx_t index = to_words_align_down(l_index); + bm_word_t cword = (map(index) ^ flip) >> bit_in_word(l_index); + if ((cword & 1) != 0) { + // The first bit is similarly often interesting. When it matters + // (density or features of the calling algorithm make it likely + // the first bit is set), going straight to the next clause compares + // poorly with doing this check first; count_trailing_zeros can be + // relatively expensive, plus there is the additional range check. + // But when the first bit isn't set, the cost of having tested for + // it is relatively small compared to the rest of the search. + return l_index; + } else if (cword != 0) { + // Flipped and shifted first word is non-zero. + idx_t result = l_index + count_trailing_zeros(cword); + if (aligned_right || (result < r_index)) return result; + // Result is beyond range bound; return r_index. + } else { + // Flipped and shifted first word is zero. Word search through + // aligned up r_index for a non-zero flipped word. + idx_t limit = aligned_right + ? to_words_align_down(r_index) // Miniscule savings when aligned. + : to_words_align_up(r_index); + while (++index < limit) { + cword = map(index) ^ flip; + if (cword != 0) { + idx_t result = bit_index(index) + count_trailing_zeros(cword); + if (aligned_right || (result < r_index)) return result; + // Result is beyond range bound; return r_index. + assert((index + 1) == limit, "invariant"); + break; + } + } + // No bits in range; return r_index. + } + } + return r_index; +} + +inline ShenandoahMarkBitMap::idx_t ShenandoahMarkBitMap::get_next_one_offset(idx_t l_offset, idx_t r_offset) const { + return get_next_bit_impl(l_offset, r_offset); +} + +// Returns a bit mask for a range of bits [beg, end) within a single word. Each +// bit in the mask is 0 if the bit is in the range, 1 if not in the range. The +// returned mask can be used directly to clear the range, or inverted to set the +// range. Note: end must not be 0. +inline ShenandoahMarkBitMap::bm_word_t +ShenandoahMarkBitMap::inverted_bit_mask_for_range(idx_t beg, idx_t end) const { + assert(end != 0, "does not work when end == 0"); + assert(beg == end || to_words_align_down(beg) == to_words_align_down(end - 1), + "must be a single-word range"); + bm_word_t mask = bit_mask(beg) - 1; // low (right) bits + if (bit_in_word(end) != 0) { + mask |= ~(bit_mask(end) - 1); // high (left) bits + } + return mask; +} + +inline void ShenandoahMarkBitMap::clear_range_of_words(bm_word_t* map, idx_t beg, idx_t end) { + for (idx_t i = beg; i < end; ++i) map[i] = 0; +} + +inline void ShenandoahMarkBitMap::clear_large_range_of_words(idx_t beg, idx_t end) { + assert(beg <= end, "underflow"); + memset(_map + beg, 0, (end - beg) * sizeof(bm_word_t)); +} + +inline void ShenandoahMarkBitMap::clear_range_of_words(idx_t beg, idx_t end) { + clear_range_of_words(_map, beg, end); +} + + +#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_INLINE_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkCompact.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkCompact.cpp index 9993a9c612b..846b42c73ad 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkCompact.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkCompact.cpp @@ -38,6 +38,7 @@ #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" +#include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" #include "gc/shenandoah/shenandoahTaskqueue.inline.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" @@ -129,10 +130,8 @@ void ShenandoahMarkCompact::do_it(GCCause::Cause gc_cause) { assert(!heap->marking_context()->is_complete(), "sanity"); // e. Abandon reference discovery and clear all discovered references. - ReferenceProcessor* rp = heap->ref_processor(); - rp->disable_discovery(); + ShenandoahReferenceProcessor* rp = heap->ref_processor(); rp->abandon_partial_discovery(); - rp->verify_no_references_recorded(); // f. Set back forwarded objects bit back, in case some steps above dropped it. heap->set_has_forwarded_objects(has_forwarded_objects); @@ -241,18 +240,16 @@ void ShenandoahMarkCompact::phase1_mark_heap() { ShenandoahConcurrentMark* cm = heap->concurrent_mark(); - heap->set_process_references(heap->heuristics()->can_process_references()); heap->set_unload_classes(heap->heuristics()->can_unload_classes()); - ReferenceProcessor* rp = heap->ref_processor(); + ShenandoahReferenceProcessor* rp = heap->ref_processor(); // enable ("weak") refs discovery - rp->enable_discovery(true /*verify_no_refs*/); - rp->setup_policy(true); // forcefully purge all soft references - rp->set_active_mt_degree(heap->workers()->active_workers()); + rp->set_soft_reference_policy(true); // forcefully purge all soft references cm->mark_roots(ShenandoahPhaseTimings::full_gc_scan_roots); cm->finish_mark_from_roots(/* full_gc = */ true); heap->mark_complete_marking_context(); + rp->process_references(heap->workers(), false /* concurrent */); heap->parallel_cleaning(true /* full_gc */); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp index eea57c3dd5d..df5c2f114db 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp @@ -29,11 +29,11 @@ #include "gc/shenandoah/shenandoahMarkingContext.hpp" ShenandoahMarkingContext::ShenandoahMarkingContext(MemRegion heap_region, MemRegion bitmap_region, size_t num_regions) : + _mark_bit_map(heap_region, bitmap_region), _top_bitmaps(NEW_C_HEAP_ARRAY(HeapWord*, num_regions, mtGC)), _top_at_mark_starts_base(NEW_C_HEAP_ARRAY(HeapWord*, num_regions, mtGC)), _top_at_mark_starts(_top_at_mark_starts_base - ((uintx) heap_region.start() >> ShenandoahHeapRegion::region_size_bytes_shift())) { - _mark_bit_map.initialize(heap_region, bitmap_region); } bool ShenandoahMarkingContext::is_bitmap_clear() const { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp index 8274afa2c19..a17f9680032 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp @@ -25,7 +25,8 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHMARKINGCONTEXT_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHMARKINGCONTEXT_HPP -#include "gc/shared/markBitMap.hpp" +#include "gc/shenandoah/shenandoahMarkBitMap.hpp" +#include "gc/shenandoah/shenandoahSharedVariables.hpp" #include "memory/allocation.hpp" #include "memory/memRegion.hpp" #include "oops/oopsHierarchy.hpp" @@ -35,7 +36,7 @@ */ class ShenandoahMarkingContext : public CHeapObj { private: - MarkBitMap _mark_bit_map; + ShenandoahMarkBitMap _mark_bit_map; HeapWord** const _top_bitmaps; HeapWord** const _top_at_mark_starts_base; @@ -51,15 +52,19 @@ class ShenandoahMarkingContext : public CHeapObj { * been marked by this thread. Returns false if the object has already been marked, * or if a competing thread succeeded in marking this object. */ - inline bool mark(oop obj); + inline bool mark_strong(oop obj, bool& was_upgraded); + inline bool mark_weak(oop obj); - inline bool is_marked(oop obj) const; + // Simple versions of marking accessors, to be used outside of marking (e.g. no possible concurrent updates) + inline bool is_marked(oop) const; + inline bool is_marked_strong(oop obj) const; + inline bool is_marked_weak(oop obj) const; + + inline HeapWord* get_next_marked_addr(HeapWord* addr, HeapWord* limit) const; inline bool allocated_after_mark_start(oop obj) const; inline bool allocated_after_mark_start(HeapWord* addr) const; - inline MarkBitMap* mark_bit_map(); - inline HeapWord* top_at_mark_start(ShenandoahHeapRegion* r) const; inline void capture_top_at_mark_start(ShenandoahHeapRegion* r); inline void reset_top_at_mark_start(ShenandoahHeapRegion* r); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp index 338f30f86b1..5cb3bd5c353 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp @@ -25,19 +25,33 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHMARKINGCONTEXT_INLINE_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHMARKINGCONTEXT_INLINE_HPP +#include "gc/shenandoah/shenandoahMarkBitMap.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.hpp" -inline MarkBitMap* ShenandoahMarkingContext::mark_bit_map() { - return &_mark_bit_map; +inline bool ShenandoahMarkingContext::mark_strong(oop obj, bool& was_upgraded) { + shenandoah_assert_not_forwarded(NULL, obj); + return (! allocated_after_mark_start(obj)) && _mark_bit_map.mark_strong(cast_from_oop(obj), was_upgraded); } -inline bool ShenandoahMarkingContext::mark(oop obj) { +inline bool ShenandoahMarkingContext::mark_weak(oop obj) { shenandoah_assert_not_forwarded(NULL, obj); - return (! allocated_after_mark_start(obj)) && _mark_bit_map.par_mark(obj); + return (! allocated_after_mark_start(obj)) && _mark_bit_map.mark_weak(cast_from_oop(obj)); } inline bool ShenandoahMarkingContext::is_marked(oop obj) const { - return allocated_after_mark_start(obj) || _mark_bit_map.is_marked(obj); + return allocated_after_mark_start(obj) || _mark_bit_map.is_marked(cast_from_oop(obj)); +} + +inline bool ShenandoahMarkingContext::is_marked_strong(oop obj) const { + return allocated_after_mark_start(obj) || _mark_bit_map.is_marked_strong(cast_from_oop(obj)); +} + +inline bool ShenandoahMarkingContext::is_marked_weak(oop obj) const { + return allocated_after_mark_start(obj) || _mark_bit_map.is_marked_weak(cast_from_oop(obj)); +} + +inline HeapWord* ShenandoahMarkingContext::get_next_marked_addr(HeapWord* start, HeapWord* limit) const { + return _mark_bit_map.get_next_marked_addr(start, limit); } inline bool ShenandoahMarkingContext::allocated_after_mark_start(oop obj) const { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.hpp index a175fc1743f..e9dceaad071 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.hpp @@ -49,13 +49,22 @@ class ShenandoahMarkRefsSuperClosure : public MetadataVisitingOopIterateClosure ShenandoahObjToScanQueue* _queue; ShenandoahHeap* _heap; ShenandoahMarkingContext* const _mark_context; + bool _weak; protected: template void work(T *p); public: - ShenandoahMarkRefsSuperClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp); + ShenandoahMarkRefsSuperClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp); + + bool is_weak() const { + return _weak; + } + + void set_weak(bool weak) { + _weak = weak; + } }; class ShenandoahMarkUpdateRefsClosure : public ShenandoahMarkRefsSuperClosure { @@ -64,7 +73,7 @@ class ShenandoahMarkUpdateRefsClosure : public ShenandoahMarkRefsSuperClosure { inline void do_oop_work(T* p) { work(p); } public: - ShenandoahMarkUpdateRefsClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : + ShenandoahMarkUpdateRefsClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : ShenandoahMarkRefsSuperClosure(q, rp) {}; virtual void do_oop(narrowOop* p) { do_oop_work(p); } @@ -78,7 +87,7 @@ class ShenandoahMarkUpdateRefsDedupClosure : public ShenandoahMarkRefsSuperClosu inline void do_oop_work(T* p) { work(p); } public: - ShenandoahMarkUpdateRefsDedupClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : + ShenandoahMarkUpdateRefsDedupClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : ShenandoahMarkRefsSuperClosure(q, rp) {}; virtual void do_oop(narrowOop* p) { do_oop_work(p); } @@ -92,7 +101,7 @@ class ShenandoahMarkUpdateRefsMetadataClosure : public ShenandoahMarkRefsSuperCl inline void do_oop_work(T* p) { work(p); } public: - ShenandoahMarkUpdateRefsMetadataClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : + ShenandoahMarkUpdateRefsMetadataClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : ShenandoahMarkRefsSuperClosure(q, rp) {}; virtual void do_oop(narrowOop* p) { do_oop_work(p); } @@ -106,7 +115,7 @@ class ShenandoahMarkUpdateRefsMetadataDedupClosure : public ShenandoahMarkRefsSu inline void do_oop_work(T* p) { work(p); } public: - ShenandoahMarkUpdateRefsMetadataDedupClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : + ShenandoahMarkUpdateRefsMetadataDedupClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : ShenandoahMarkRefsSuperClosure(q, rp) {}; virtual void do_oop(narrowOop* p) { do_oop_work(p); } @@ -120,7 +129,7 @@ class ShenandoahMarkRefsClosure : public ShenandoahMarkRefsSuperClosure { inline void do_oop_work(T* p) { work(p); } public: - ShenandoahMarkRefsClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : + ShenandoahMarkRefsClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : ShenandoahMarkRefsSuperClosure(q, rp) {}; virtual void do_oop(narrowOop* p) { do_oop_work(p); } @@ -134,7 +143,7 @@ class ShenandoahMarkRefsDedupClosure : public ShenandoahMarkRefsSuperClosure { inline void do_oop_work(T* p) { work(p); } public: - ShenandoahMarkRefsDedupClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : + ShenandoahMarkRefsDedupClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : ShenandoahMarkRefsSuperClosure(q, rp) {}; virtual void do_oop(narrowOop* p) { do_oop_work(p); } @@ -148,7 +157,7 @@ class ShenandoahMarkResolveRefsClosure : public ShenandoahMarkRefsSuperClosure { inline void do_oop_work(T* p) { work(p); } public: - ShenandoahMarkResolveRefsClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : + ShenandoahMarkResolveRefsClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : ShenandoahMarkRefsSuperClosure(q, rp) {}; virtual void do_oop(narrowOop* p) { do_oop_work(p); } @@ -162,7 +171,7 @@ class ShenandoahMarkRefsMetadataClosure : public ShenandoahMarkRefsSuperClosure inline void do_oop_work(T* p) { work(p); } public: - ShenandoahMarkRefsMetadataClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : + ShenandoahMarkRefsMetadataClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : ShenandoahMarkRefsSuperClosure(q, rp) {}; virtual void do_oop(narrowOop* p) { do_oop_work(p); } @@ -176,7 +185,7 @@ class ShenandoahMarkRefsMetadataDedupClosure : public ShenandoahMarkRefsSuperClo inline void do_oop_work(T* p) { work(p); } public: - ShenandoahMarkRefsMetadataDedupClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : + ShenandoahMarkRefsMetadataDedupClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : ShenandoahMarkRefsSuperClosure(q, rp) {}; virtual void do_oop(narrowOop* p) { do_oop_work(p); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.inline.hpp index 1adebeaac0e..3476f65a7fe 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.inline.hpp @@ -30,7 +30,7 @@ template inline void ShenandoahMarkRefsSuperClosure::work(T *p) { - ShenandoahConcurrentMark::mark_through_ref(p, _heap, _queue, _mark_context); + ShenandoahConcurrentMark::mark_through_ref(p, _heap, _queue, _mark_context, _weak); } template diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPacer.cpp b/src/hotspot/share/gc/shenandoah/shenandoahPacer.cpp index 30c875551f8..d8866ebd6dd 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPacer.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPacer.cpp @@ -153,16 +153,6 @@ void ShenandoahPacer::setup_for_idle() { * the allocators unnecessarily, allow them to run unimpeded. */ -void ShenandoahPacer::setup_for_preclean() { - assert(ShenandoahPacing, "Only be here when pacing is enabled"); - - size_t initial = _heap->max_capacity(); - restart_with(initial, 1.0); - - log_info(gc, ergo)("Pacer for Precleaning. Non-Taxable: " SIZE_FORMAT "%s", - byte_size_in_proper_unit(initial), proper_unit_for_byte_size(initial)); -} - void ShenandoahPacer::setup_for_reset() { assert(ShenandoahPacing, "Only be here when pacing is enabled"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPacer.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPacer.hpp index d177581ac78..6b2dd53ff76 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPacer.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPacer.hpp @@ -79,7 +79,6 @@ class ShenandoahPacer : public CHeapObj { void setup_for_updaterefs(); void setup_for_reset(); - void setup_for_preclean(); inline void report_mark(size_t words); inline void report_evac(size_t words); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.inline.hpp index 09f0e92d6fb..61e5b4b7705 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.inline.hpp @@ -53,7 +53,9 @@ ShenandoahParallelWeakRootsCleaningTask::~ShenandoahParallel if (StringDedup::is_enabled()) { StringDedup::gc_epilogue(); } - _weak_processing_task.report_num_dead(); + if (_include_concurrent_roots) { + _weak_processing_task.report_num_dead(); + } } template diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.cpp b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.cpp index 797fcb84789..b65746c3791 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.cpp @@ -113,6 +113,7 @@ bool ShenandoahPhaseTimings::is_worker_phase(Phase phase) { case heap_iteration_roots: case conc_mark_roots: case conc_weak_roots_work: + case conc_weak_refs_work: case conc_strong_roots: return true; default: diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp index c4f150af690..9452cb63777 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp @@ -60,8 +60,6 @@ class outputStream; f(conc_mark_roots, " Roots ") \ SHENANDOAH_PAR_PHASE_DO(conc_mark_roots, " CM: ", f) \ \ - f(conc_preclean, "Concurrent Precleaning") \ - \ f(final_mark_gross, "Pause Final Mark (G)") \ f(final_mark, "Pause Final Mark (N)") \ f(update_roots, " Update Roots") \ @@ -82,6 +80,9 @@ class outputStream; f(init_evac, " Initial Evacuation") \ SHENANDOAH_PAR_PHASE_DO(evac_, " E: ", f) \ \ + f(conc_weak_refs, "Concurrent Weak References") \ + f(conc_weak_refs_work, " Process") \ + SHENANDOAH_PAR_PHASE_DO(conc_weak_refs_work_, " CWRF: ", f) \ f(conc_weak_roots, "Concurrent Weak Roots") \ f(conc_weak_roots_work, " Roots") \ SHENANDOAH_PAR_PHASE_DO(conc_weak_roots_work_, " CWR: ", f) \ @@ -98,14 +99,15 @@ class outputStream; f(conc_class_unload_purge_cldg, " CLDG") \ f(conc_class_unload_purge_ec, " Exception Caches") \ f(conc_strong_roots, "Concurrent Strong Roots") \ - f(conc_rendezvous_roots, "Rendezvous") \ SHENANDOAH_PAR_PHASE_DO(conc_strong_roots_, " CSR: ", f) \ + f(conc_rendezvous_roots, "Rendezvous") \ f(conc_evac, "Concurrent Evacuation") \ \ f(init_update_refs_gross, "Pause Init Update Refs (G)") \ f(init_update_refs, "Pause Init Update Refs (N)") \ f(init_update_refs_manage_gclabs, " Manage GCLABs") \ \ + f(conc_update_thread_roots, "Concurrent Update Thread Roots") \ f(conc_update_refs, "Concurrent Update Refs") \ \ f(final_update_refs_gross, "Pause Final Update Refs (G)") \ diff --git a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp new file mode 100644 index 00000000000..8bbecdc0e2c --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp @@ -0,0 +1,593 @@ +/* + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat, Inc. and/or its affiliates. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "classfile/javaClasses.hpp" +#include "gc/shenandoah/shenandoahOopClosures.hpp" +#include "gc/shenandoah/shenandoahReferenceProcessor.hpp" +#include "gc/shenandoah/shenandoahThreadLocalData.hpp" +#include "gc/shenandoah/shenandoahUtils.hpp" +#include "runtime/atomic.hpp" +#include "logging/log.hpp" + +static ReferenceType reference_type(oop reference) { + return InstanceKlass::cast(reference->klass())->reference_type(); +} + +static const char* reference_type_name(ReferenceType type) { + switch (type) { + case REF_SOFT: + return "Soft"; + + case REF_WEAK: + return "Weak"; + + case REF_FINAL: + return "Final"; + + case REF_PHANTOM: + return "Phantom"; + + default: + ShouldNotReachHere(); + return NULL; + } +} + +template +static void set_oop_field(T* field, oop value); + +template <> +void set_oop_field(oop* field, oop value) { + *field = value; +} + +template <> +void set_oop_field(narrowOop* field, oop value) { + *field = CompressedOops::encode(value); +} + +static oop lrb(oop obj) { + if (obj != NULL && ShenandoahHeap::heap()->marking_context()->is_marked(obj)) { + return ShenandoahBarrierSet::barrier_set()->load_reference_barrier(obj); + } else { + return obj; + } +} + +template +static volatile T* reference_referent_addr(oop reference) { + return (volatile T*)java_lang_ref_Reference::referent_addr_raw(reference); +} + +template +static oop reference_referent(oop reference) { + T heap_oop = Atomic::load(reference_referent_addr(reference)); + return CompressedOops::decode(heap_oop); +} + +static void reference_set_referent(oop reference, oop referent) { + java_lang_ref_Reference::set_referent_raw(reference, referent); +} + +template +static T* reference_discovered_addr(oop reference) { + return reinterpret_cast(java_lang_ref_Reference::discovered_addr_raw(reference)); +} + +template +static oop reference_discovered(oop reference) { + T heap_oop = *reference_discovered_addr(reference); + return lrb(CompressedOops::decode(heap_oop)); +} + +template +static void reference_set_discovered(oop reference, oop discovered); + +template <> +void reference_set_discovered(oop reference, oop discovered) { + *reference_discovered_addr(reference) = discovered; +} + +template <> +void reference_set_discovered(oop reference, oop discovered) { + *reference_discovered_addr(reference) = CompressedOops::encode(discovered); +} + +template +static bool reference_cas_discovered(oop reference, oop discovered); + +template<> +bool reference_cas_discovered(oop reference, oop discovered) { + volatile narrowOop* addr = reinterpret_cast(java_lang_ref_Reference::discovered_addr_raw(reference)); + narrowOop compare = CompressedOops::encode(NULL); + narrowOop exchange = CompressedOops::encode(discovered); + return Atomic::cmpxchg(addr, compare, exchange) == compare; +} + +template<> +bool reference_cas_discovered(oop reference, oop discovered) { + volatile oop* addr = reinterpret_cast(java_lang_ref_Reference::discovered_addr_raw(reference)); + return Atomic::cmpxchg(addr, oop(NULL), discovered) == NULL; +} + +template +static T* reference_next_addr(oop reference) { + return reinterpret_cast(java_lang_ref_Reference::next_addr_raw(reference)); +} + +template +static oop reference_next(oop reference) { + T heap_oop = RawAccess<>::oop_load(reference_next_addr(reference)); + return lrb(CompressedOops::decode(heap_oop)); +} + +static void reference_set_next(oop reference, oop next) { + java_lang_ref_Reference::set_next_raw(reference, next); +} + +static void soft_reference_update_clock() { + const jlong now = os::javaTimeNanos() / NANOSECS_PER_MILLISEC; + java_lang_ref_SoftReference::set_clock(now); +} + +ShenandoahRefProcThreadLocal::ShenandoahRefProcThreadLocal() : + _discovered_list(NULL), + _encountered_count(), + _discovered_count(), + _enqueued_count() { +} + +void ShenandoahRefProcThreadLocal::reset() { + _discovered_list = NULL; + _mark_closure = NULL; + for (uint i = 0; i < reference_type_count; i++) { + _encountered_count[i] = 0; + _discovered_count[i] = 0; + _enqueued_count[i] = 0; + } +} + +template +T* ShenandoahRefProcThreadLocal::discovered_list_addr() { + return reinterpret_cast(&_discovered_list); +} + +template <> +oop ShenandoahRefProcThreadLocal::discovered_list_head() const { + return *reinterpret_cast(&_discovered_list); +} + +template <> +oop ShenandoahRefProcThreadLocal::discovered_list_head() const { + return CompressedOops::decode(*reinterpret_cast(&_discovered_list)); +} + +template <> +void ShenandoahRefProcThreadLocal::set_discovered_list_head(oop head) { + *discovered_list_addr() = CompressedOops::encode(head); +} + +template <> +void ShenandoahRefProcThreadLocal::set_discovered_list_head(oop head) { + *discovered_list_addr() = head; +} + +ShenandoahReferenceProcessor::ShenandoahReferenceProcessor(uint max_workers) : + _soft_reference_policy(NULL), + _ref_proc_thread_locals(NEW_C_HEAP_ARRAY(ShenandoahRefProcThreadLocal, max_workers, mtGC)), + _pending_list(NULL), + _pending_list_tail(&_pending_list), + _iterate_discovered_list_id(0U) { + for (size_t i = 0; i < max_workers; i++) { + _ref_proc_thread_locals[i].reset(); + } +} + +void ShenandoahReferenceProcessor::reset_thread_locals() { + uint max_workers = ShenandoahHeap::heap()->max_workers(); + for (uint i = 0; i < max_workers; i++) { + _ref_proc_thread_locals[i].reset(); + } +} + +void ShenandoahReferenceProcessor::set_mark_closure(uint worker_id, ShenandoahMarkRefsSuperClosure* mark_closure) { + _ref_proc_thread_locals[worker_id].set_mark_closure(mark_closure); +} + +void ShenandoahReferenceProcessor::set_soft_reference_policy(bool clear) { + static AlwaysClearPolicy always_clear_policy; + static LRUMaxHeapPolicy lru_max_heap_policy; + + if (clear) { + log_info(gc, ref)("Clearing All SoftReferences"); + _soft_reference_policy = &always_clear_policy; + } else { + _soft_reference_policy = &lru_max_heap_policy; + } + + _soft_reference_policy->setup(); +} + +template +bool ShenandoahReferenceProcessor::is_inactive(oop reference, oop referent, ReferenceType type) const { + if (type == REF_FINAL) { + // A FinalReference is inactive if its next field is non-null. An application can't + // call enqueue() or clear() on a FinalReference. + return reference_next(reference) != NULL; + } else { + // A non-FinalReference is inactive if the referent is null. The referent can only + // be null if the application called Reference.enqueue() or Reference.clear(). + return referent == NULL; + } +} + +bool ShenandoahReferenceProcessor::is_strongly_live(oop referent) const { + return ShenandoahHeap::heap()->marking_context()->is_marked_strong(referent); +} + +bool ShenandoahReferenceProcessor::is_softly_live(oop reference, ReferenceType type) const { + if (type != REF_SOFT) { + // Not a SoftReference + return false; + } + + // Ask SoftReference policy + const jlong clock = java_lang_ref_SoftReference::clock(); + assert(clock != 0, "Clock not initialized"); + assert(_soft_reference_policy != NULL, "Policy not initialized"); + return !_soft_reference_policy->should_clear_reference(reference, clock); +} + +template +bool ShenandoahReferenceProcessor::should_discover(oop reference, ReferenceType type) const { + T* referent_addr = (T*) java_lang_ref_Reference::referent_addr_raw(reference); + T heap_oop = RawAccess<>::oop_load(referent_addr); + oop referent = CompressedOops::decode(heap_oop); + + if (is_inactive(reference, referent, type)) { + log_trace(gc,ref)("Reference inactive: " PTR_FORMAT, p2i(reference)); + return false; + } + + if (is_strongly_live(referent)) { + log_trace(gc,ref)("Reference strongly live: " PTR_FORMAT, p2i(reference)); + return false; + } + + if (is_softly_live(reference, type)) { + log_trace(gc,ref)("Reference softly live: " PTR_FORMAT, p2i(reference)); + return false; + } + + return true; +} + +template +bool ShenandoahReferenceProcessor::should_drop(oop reference, ReferenceType type) const { + const oop referent = reference_referent(reference); + if (referent == NULL) { + // Reference has been cleared, by a call to Reference.enqueue() + // or Reference.clear() from the application, which means we + // should drop the reference. + return true; + } + + // Check if the referent is still alive, in which case we should + // drop the reference. + if (type == REF_PHANTOM) { + return ShenandoahHeap::heap()->complete_marking_context()->is_marked(referent); + } else { + return ShenandoahHeap::heap()->complete_marking_context()->is_marked_strong(referent); + } +} + +template +void ShenandoahReferenceProcessor::make_inactive(oop reference, ReferenceType type) const { + if (type == REF_FINAL) { + // Don't clear referent. It is needed by the Finalizer thread to make the call + // to finalize(). A FinalReference is instead made inactive by self-looping the + // next field. An application can't call FinalReference.enqueue(), so there is + // no race to worry about when setting the next field. + assert(reference_next(reference) == NULL, "Already inactive"); + assert(ShenandoahHeap::heap()->marking_context()->is_marked(reference_referent(reference)), "only make inactive final refs with alive referents"); + reference_set_next(reference, reference); + } else { + // Clear referent + reference_set_referent(reference, NULL); + } +} + +template +bool ShenandoahReferenceProcessor::discover(oop reference, ReferenceType type, uint worker_id) { + if (!should_discover(reference, type)) { + // Not discovered + return false; + } + + if (reference_discovered(reference) != NULL) { + // Already discovered. This can happen if the reference is marked finalizable first, and then strong, + // in which case it will be seen 2x by marking. + log_trace(gc,ref)("Reference already discovered: " PTR_FORMAT, p2i(reference)); + return true; + } + + if (type == REF_FINAL) { + ShenandoahMarkRefsSuperClosure* cl = _ref_proc_thread_locals[worker_id].mark_closure(); + bool weak = cl->is_weak(); + cl->set_weak(true); + if (UseCompressedOops) { + cl->do_oop(reinterpret_cast(java_lang_ref_Reference::referent_addr_raw(reference))); + } else { + cl->do_oop(reinterpret_cast(java_lang_ref_Reference::referent_addr_raw(reference))); + } + cl->set_weak(weak); + } + + // Add reference to discovered list + assert(worker_id != ShenandoahThreadLocalData::INVALID_WORKER_ID, "need valid worker ID"); + ShenandoahRefProcThreadLocal& refproc_data = _ref_proc_thread_locals[worker_id]; + oop discovered_head = refproc_data.discovered_list_head(); + if (discovered_head == NULL) { + // Self-loop tail of list. We distinguish discovered from not-discovered references by looking at their + // discovered field: if it is NULL, then it is not-yet discovered, otherwise it is discovered + discovered_head = reference; + } + if (reference_cas_discovered(reference, discovered_head)) { + refproc_data.set_discovered_list_head(reference); + assert(refproc_data.discovered_list_head() == reference, "reference must be new discovered head"); + log_trace(gc, ref)("Discovered Reference: " PTR_FORMAT " (%s)", p2i(reference), reference_type_name(type)); + _ref_proc_thread_locals[worker_id].inc_discovered(type); + } + return true; +} + +bool ShenandoahReferenceProcessor::discover_reference(oop reference, ReferenceType type) { + if (!RegisterReferences) { + // Reference processing disabled + return false; + } + + log_trace(gc, ref)("Encountered Reference: " PTR_FORMAT " (%s)", p2i(reference), reference_type_name(type)); + uint worker_id = ShenandoahThreadLocalData::worker_id(Thread::current()); + _ref_proc_thread_locals->inc_encountered(type); + + if (UseCompressedOops) { + return discover(reference, type, worker_id); + } else { + return discover(reference, type, worker_id); + } +} + +template +oop ShenandoahReferenceProcessor::drop(oop reference, ReferenceType type) { + log_trace(gc, ref)("Dropped Reference: " PTR_FORMAT " (%s)", p2i(reference), reference_type_name(type)); + + assert(reference_referent(reference) == NULL || + ShenandoahHeap::heap()->marking_context()->is_marked(reference_referent(reference)), "only drop references with alive referents"); + + // Unlink and return next in list + oop next = reference_discovered(reference); + reference_set_discovered(reference, NULL); + return next; +} + +template +T* ShenandoahReferenceProcessor::keep(oop reference, ReferenceType type, uint worker_id) { + log_trace(gc, ref)("Enqueued Reference: " PTR_FORMAT " (%s)", p2i(reference), reference_type_name(type)); + + // Update statistics + _ref_proc_thread_locals[worker_id].inc_enqueued(type); + + // Make reference inactive + make_inactive(reference, type); + + // Return next in list + return reference_discovered_addr(reference); +} + +template +void ShenandoahReferenceProcessor::process_references(ShenandoahRefProcThreadLocal& refproc_data, uint worker_id) {; + log_trace(gc, ref)("Processing discovered list #%u : " PTR_FORMAT, worker_id, p2i(refproc_data.discovered_list_head())); + T* list = refproc_data.discovered_list_addr(); + // The list head is basically a GC root, we need to resolve and update it, + // otherwise we will later swap a from-space ref into Universe::pending_list(). + if (!CompressedOops::is_null(*list)) { + oop first_resolved = lrb(CompressedOops::decode_not_null(*list)); + set_oop_field(list, first_resolved); + } + T* p = list; + while (true) { + const oop reference = lrb(CompressedOops::decode(*p)); + if (reference == NULL) { + break; + } + log_trace(gc, ref)("Processing reference: " PTR_FORMAT, p2i(reference)); + const ReferenceType type = reference_type(reference); + + if (should_drop(reference, type)) { + set_oop_field(p, drop(reference, type)); + } else { + p = keep(reference, type, worker_id); + } + + const oop discovered = lrb(reference_discovered(reference)); + if (reference == discovered) { + // Reset terminating self-loop to NULL + reference_set_discovered(reference, oop(NULL)); + break; + } + } + + // Prepend discovered references to internal pending list + if (!CompressedOops::is_null(*list)) { + oop head = lrb(CompressedOops::decode_not_null(*list)); + shenandoah_assert_not_in_cset_except(&head, head, ShenandoahHeap::heap()->cancelled_gc() || !ShenandoahLoadRefBarrier); + oop prev = Atomic::xchg(&_pending_list, head); + RawAccess<>::oop_store(p, prev); + if (prev == NULL) { + // First to prepend to list, record tail + _pending_list_tail = reinterpret_cast(p); + } + + // Clear discovered list + set_oop_field(list, oop(NULL)); + } +} + +void ShenandoahReferenceProcessor::work() { + // Process discovered references + uint max_workers = ShenandoahHeap::heap()->max_workers(); + uint worker_id = Atomic::add(&_iterate_discovered_list_id, 1U) - 1; + while (worker_id < max_workers) { + if (UseCompressedOops) { + process_references(_ref_proc_thread_locals[worker_id], worker_id); + } else { + process_references(_ref_proc_thread_locals[worker_id], worker_id); + } + worker_id = Atomic::add(&_iterate_discovered_list_id, 1U) - 1; + } +} + +class ShenandoahReferenceProcessorTask : public AbstractGangTask { +private: + ShenandoahReferenceProcessor* const _reference_processor; + +public: + ShenandoahReferenceProcessorTask(ShenandoahReferenceProcessor* reference_processor) : + AbstractGangTask("ShenandoahReferenceProcessorTask"), + _reference_processor(reference_processor) { + } + + virtual void work(uint worker_id) { + ShenandoahConcurrentWorkerSession worker_session(worker_id); + _reference_processor->work(); + } +}; + +void ShenandoahReferenceProcessor::process_references(WorkGang* workers, bool concurrent) { + + Atomic::release_store_fence(&_iterate_discovered_list_id, 0U); + + // Process discovered lists + ShenandoahReferenceProcessorTask task(this); + workers->run_task(&task); + + // Update SoftReference clock + soft_reference_update_clock(); + + // Collect, log and trace statistics + collect_statistics(); + + enqueue_references(concurrent); +} + +void ShenandoahReferenceProcessor::enqueue_references_locked() { + // Prepend internal pending list to external pending list + shenandoah_assert_not_in_cset_except(&_pending_list, _pending_list, ShenandoahHeap::heap()->cancelled_gc() || !ShenandoahLoadRefBarrier); + if (UseCompressedOops) { + *reinterpret_cast(_pending_list_tail) = CompressedOops::encode(Universe::swap_reference_pending_list(_pending_list)); + } else { + *reinterpret_cast(_pending_list_tail) = Universe::swap_reference_pending_list(_pending_list); + } +} + +void ShenandoahReferenceProcessor::enqueue_references(bool concurrent) { + if (_pending_list == NULL) { + // Nothing to enqueue + return; + } + + if (!concurrent) { + // When called from mark-compact or degen-GC, the locking is done by the VMOperation, + enqueue_references_locked(); + } else { + // Heap_lock protects external pending list + MonitorLocker ml(Heap_lock, Mutex::_no_safepoint_check_flag); + + enqueue_references_locked(); + + // Notify ReferenceHandler thread + ml.notify_all(); + } + + // Reset internal pending list + _pending_list = NULL; + _pending_list_tail = &_pending_list; +} + +template +void ShenandoahReferenceProcessor::clean_discovered_list(T* list) { + T discovered = *list; + while (!CompressedOops::is_null(discovered)) { + oop discovered_ref = CompressedOops::decode_not_null(discovered); + set_oop_field(list, oop(NULL)); + list = reference_discovered_addr(discovered_ref); + discovered = *list; + } +} + +void ShenandoahReferenceProcessor::abandon_partial_discovery() { + uint max_workers = ShenandoahHeap::heap()->max_workers(); + for (uint index = 0; index < max_workers; index++) { + if (UseCompressedOops) { + clean_discovered_list(_ref_proc_thread_locals[index].discovered_list_addr()); + } else { + clean_discovered_list(_ref_proc_thread_locals[index].discovered_list_addr()); + } + } + if (_pending_list != NULL) { + oop pending = _pending_list; + _pending_list = NULL; + if (UseCompressedOops) { + narrowOop* list = reference_discovered_addr(pending); + clean_discovered_list(list); + } else { + oop* list = reference_discovered_addr(pending); + clean_discovered_list(list); + } + } + _pending_list_tail = &_pending_list; +} + +void ShenandoahReferenceProcessor::collect_statistics() { + Counters encountered = {}; + Counters discovered = {}; + Counters enqueued = {}; + uint max_workers = ShenandoahHeap::heap()->max_workers(); + for (uint i = 0; i < max_workers; i++) { + for (size_t type = 0; type < reference_type_count; type++) { + encountered[type] += _ref_proc_thread_locals[i].encountered((ReferenceType)type); + discovered[type] += _ref_proc_thread_locals[i].discovered((ReferenceType)type); + enqueued[type] += _ref_proc_thread_locals[i].enqueued((ReferenceType)type); + } + } + log_info(gc,ref)("Encountered references: Soft: " SIZE_FORMAT ", Weak: " SIZE_FORMAT ", Final: " SIZE_FORMAT ", Phantom: " SIZE_FORMAT, + encountered[REF_SOFT], encountered[REF_WEAK], encountered[REF_FINAL], encountered[REF_PHANTOM]); + log_info(gc,ref)("Discovered references: Soft: " SIZE_FORMAT ", Weak: " SIZE_FORMAT ", Final: " SIZE_FORMAT ", Phantom: " SIZE_FORMAT, + discovered[REF_SOFT], discovered[REF_WEAK], discovered[REF_FINAL], discovered[REF_PHANTOM]); + log_info(gc,ref)("Enqueued references: Soft: " SIZE_FORMAT ", Weak: " SIZE_FORMAT ", Final: " SIZE_FORMAT ", Phantom: " SIZE_FORMAT, + enqueued[REF_SOFT], enqueued[REF_WEAK], enqueued[REF_FINAL], enqueued[REF_PHANTOM]); +} \ No newline at end of file diff --git a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp new file mode 100644 index 00000000000..40dbbc37f6e --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat, Inc. and/or its affiliates. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHREFERENCEPROCESSOR_HPP +#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHREFERENCEPROCESSOR_HPP + +#include "gc/shared/referenceDiscoverer.hpp" +#include "memory/allocation.hpp" + +class ShenandoahMarkRefsSuperClosure; +class WorkGang; + +static const size_t reference_type_count = REF_PHANTOM + 1; +typedef size_t Counters[reference_type_count]; + +/* + * Shenandoah concurrent reference processing + * + * Concurrent reference processing is made up of two main phases: + * 1. Concurrent reference marking: Discover all j.l.r.Reference objects and determine reachability of all live objects. + * 2. Concurrent reference processing: For all discoved j.l.r.References, determine whether to keep them alive or clean + * them. Also, clean and enqueue relevant references concurrently. + * + * Concurrent reference marking: + * The goal here is to establish the kind of reachability for all objects on the heap. We distinguish two kinds of + * reachability: + * - An object is 'strongly reachable' if it can be found by searching transitively from GC roots. + * - An object is 'finalizably reachable' if it is not strongly reachable, but can be found by searching + * from the referents of FinalReferences. + * + * These reachabilities are implemented in shenandoahMarkBitMap.* + * Conceptually, marking starts with a strong wavefront at the GC roots. Whenever a Reference object is encountered, + * it may be discovered by the ShenandoahReferenceProcessor. If it is discovered, it + * gets added to the discovered list, and that wavefront stops there, except when it's a FinalReference, in which + * case the wavefront switches to finalizable marking and marks through the referent. When a Reference is not + * discovered, e.g. if it's a SoftReference that is not eligible for discovery, then marking continues as if the + * Reference was a regular object. Whenever a strong wavefront encounters an object that is already marked + * finalizable, then the object's reachability is upgraded to strong. + * + * Concurrent reference processing: + * This happens after the concurrent marking phase and the final marking pause, when reachability for all objects + * has been established. + * The discovered list is scanned and for each reference is decided what to do: + * - If the referent is reachable (finalizable for PhantomReference, strong for all others), then the Reference + * is dropped from the discovered list and otherwise ignored + * - Otherwise its referent becomes cleared and the Reference added to the pending list, from which it will later + * be processed (e.g. enqueued in its ReferenceQueue) by the Java ReferenceHandler thread. + * + * In order to prevent resurrection by Java threads calling Reference.get() concurrently while we are clearing + * referents, we employ a special barrier, the native LRB, which returns NULL when the referent is unreachable. + */ + +class ShenandoahRefProcThreadLocal : public CHeapObj { +private: + void* _discovered_list; + ShenandoahMarkRefsSuperClosure* _mark_closure; + Counters _encountered_count; + Counters _discovered_count; + Counters _enqueued_count; + +public: + ShenandoahRefProcThreadLocal(); + + ShenandoahRefProcThreadLocal(const ShenandoahRefProcThreadLocal&) = delete; // non construction-copyable + ShenandoahRefProcThreadLocal& operator=(const ShenandoahRefProcThreadLocal&) = delete; // non copyable + + void reset(); + + ShenandoahMarkRefsSuperClosure* mark_closure() const { + return _mark_closure; + } + + void set_mark_closure(ShenandoahMarkRefsSuperClosure* mark_closure) { + _mark_closure = mark_closure; + } + + template + T* discovered_list_addr(); + template + oop discovered_list_head() const; + template + void set_discovered_list_head(oop head); + + size_t encountered(ReferenceType type) const { + return _encountered_count[type]; + } + size_t discovered(ReferenceType type) const { + return _discovered_count[type]; + } + size_t enqueued(ReferenceType type) const { + return _enqueued_count[type]; + } + + void inc_encountered(ReferenceType type) { + _encountered_count[type]++; + } + void inc_discovered(ReferenceType type) { + _discovered_count[type]++; + } + void inc_enqueued(ReferenceType type) { + _enqueued_count[type]++; + } +}; + +class ShenandoahReferenceProcessor : public ReferenceDiscoverer { +private: + ReferencePolicy* _soft_reference_policy; + + ShenandoahRefProcThreadLocal* _ref_proc_thread_locals; + + oop _pending_list; + void* _pending_list_tail; // T* + + volatile uint _iterate_discovered_list_id; + + template + bool is_inactive(oop reference, oop referent, ReferenceType type) const; + bool is_strongly_live(oop referent) const; + bool is_softly_live(oop reference, ReferenceType type) const; + + template + bool should_discover(oop reference, ReferenceType type) const; + template + bool should_drop(oop reference, ReferenceType type) const; + + template + void make_inactive(oop reference, ReferenceType type) const; + + template + bool discover(oop reference, ReferenceType type, uint worker_id); + + template + oop drop(oop reference, ReferenceType type); + template + T* keep(oop reference, ReferenceType type, uint worker_id); + + template + void process_references(ShenandoahRefProcThreadLocal& refproc_data, uint worker_id); + void enqueue_references_locked(); + void enqueue_references(bool concurrent); + + void collect_statistics(); + + template + void clean_discovered_list(T* list); + +public: + ShenandoahReferenceProcessor(uint max_workers); + + void reset_thread_locals(); + void set_mark_closure(uint worker_id, ShenandoahMarkRefsSuperClosure* mark_closure); + + void set_soft_reference_policy(bool clear); + + bool discover_reference(oop obj, ReferenceType type) override; + + void process_references(WorkGang* workers, bool concurrent); + + void work(); + + void abandon_partial_discovery(); +}; + +#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHREFERENCEPROCESSOR_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp index f901c6ac360..4bb361776e5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp @@ -149,6 +149,8 @@ class ShenandoahClassLoaderDataRoots { ShenandoahPhaseTimings::Phase _phase; static uint worker_count(uint n_workers) { + if (SINGLE_THREADED) return 1u; + // Limit concurrency a bit, otherwise it wastes resources when workers are tripping // over each other. This also leaves free workers to process other parts of the root // set, while admitted workers are busy with doing the CLDG walk. @@ -161,6 +163,10 @@ class ShenandoahClassLoaderDataRoots { void always_strong_cld_do(CLDClosure* clds, uint worker_id); void cld_do(CLDClosure* clds, uint worker_id); + +private: + typedef void (*CldDo)(CLDClosure*); + void cld_do_impl(CldDo f, CLDClosure* clds, uint worker_id); }; class ShenandoahRootProcessor : public StackObj { @@ -194,7 +200,6 @@ class ShenandoahConcurrentRootScanner { ShenandoahVMRoots _vm_roots; ShenandoahClassLoaderDataRoots _cld_roots; - ShenandoahConcurrentStringDedupRoots _dedup_roots; ShenandoahNMethodTableSnapshot* _codecache_snapshot; ShenandoahPhaseTimings::Phase _phase; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp index 87e2cd1bdac..84a95370a01 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp @@ -35,6 +35,7 @@ #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "memory/resourceArea.hpp" +#include "runtime/mutexLocker.hpp" #include "runtime/safepoint.hpp" template @@ -81,43 +82,49 @@ ShenandoahClassLoaderDataRoots::ShenandoahClassLoad if (!SINGLE_THREADED) { ClassLoaderDataGraph::clear_claimed_marks(); } - if (CONCURRENT) { + if (CONCURRENT && !SINGLE_THREADED) { ClassLoaderDataGraph_lock->lock(); } + + // Non-concurrent mode only runs at safepoints by VM thread + assert(CONCURRENT || SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); + assert(CONCURRENT || Thread::current()->is_VM_thread(), "Can only be done by VM thread"); } template ShenandoahClassLoaderDataRoots::~ShenandoahClassLoaderDataRoots() { - if (CONCURRENT) { + if (CONCURRENT && !SINGLE_THREADED) { ClassLoaderDataGraph_lock->unlock(); } } +template +void ShenandoahClassLoaderDataRoots::cld_do_impl(CldDo f, CLDClosure* clds, uint worker_id) { + if (CONCURRENT) { + if (_semaphore.try_acquire()) { + ShenandoahWorkerTimingsTracker timer(_phase, ShenandoahPhaseTimings::CLDGRoots, worker_id); + if (SINGLE_THREADED){ + MutexLocker ml(ClassLoaderDataGraph_lock, Mutex::_no_safepoint_check_flag); + f(clds); + } else { + f(clds); + } + _semaphore.claim_all(); + } + } else { + f(clds); + } +} + template void ShenandoahClassLoaderDataRoots::always_strong_cld_do(CLDClosure* clds, uint worker_id) { - if (SINGLE_THREADED) { - assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); - assert(Thread::current()->is_VM_thread(), "Single threaded CLDG iteration can only be done by VM thread"); - ClassLoaderDataGraph::always_strong_cld_do(clds); - } else if (_semaphore.try_acquire()) { - ShenandoahWorkerTimingsTracker timer(_phase, ShenandoahPhaseTimings::CLDGRoots, worker_id); - ClassLoaderDataGraph::always_strong_cld_do(clds); - _semaphore.claim_all(); - } + cld_do_impl(&ClassLoaderDataGraph::always_strong_cld_do, clds, worker_id); } template void ShenandoahClassLoaderDataRoots::cld_do(CLDClosure* clds, uint worker_id) { - if (SINGLE_THREADED) { - assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); - assert(Thread::current()->is_VM_thread(), "Single threaded CLDG iteration can only be done by VM thread"); - ClassLoaderDataGraph::cld_do(clds); - } else if (_semaphore.try_acquire()) { - ShenandoahWorkerTimingsTracker timer(_phase, ShenandoahPhaseTimings::CLDGRoots, worker_id); - ClassLoaderDataGraph::cld_do(clds); - _semaphore.claim_all(); - } + cld_do_impl(&ClassLoaderDataGraph::cld_do, clds, worker_id); } class ShenandoahParallelOopsDoThreadClosure : public ThreadClosure { @@ -142,7 +149,6 @@ ShenandoahConcurrentRootScanner::ShenandoahConcurrentRootScanner(uin ShenandoahPhaseTimings::Phase phase) : _vm_roots(phase), _cld_roots(phase, n_workers), - _dedup_roots(phase), _codecache_snapshot(NULL), _phase(phase) { if (!ShenandoahHeap::heap()->unload_classes()) { @@ -173,9 +179,7 @@ void ShenandoahConcurrentRootScanner::oops_do(OopClosure* oops, uint _vm_roots.oops_do(oops, worker_id); if (!heap->unload_classes()) { - AlwaysTrueClosure always_true; _cld_roots.cld_do(&clds_cl, worker_id); - _dedup_roots.oops_do(&always_true, oops, worker_id); ShenandoahWorkerTimingsTracker timer(_phase, ShenandoahPhaseTimings::CodeCacheRoots, worker_id); CodeBlobToOopClosure blobs(oops, !CodeBlobToOopClosure::FixRelocations); _codecache_snapshot->parallel_blobs_do(&blobs); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp index dcc8e115fd1..b1f6d175cd9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp @@ -29,7 +29,7 @@ #include "classfile/classLoaderDataGraph.hpp" #include "code/codeCache.hpp" #include "gc/shenandoah/shenandoahAsserts.hpp" -#include "gc/shenandoah/shenandoahHeap.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahRootVerifier.hpp" #include "gc/shenandoah/shenandoahStringDedup.hpp" @@ -40,6 +40,20 @@ #include "runtime/thread.hpp" #include "utilities/debug.hpp" +ShenandoahGCStateResetter::ShenandoahGCStateResetter() : + _heap(ShenandoahHeap::heap()), + _gc_state(_heap->gc_state()), + _concurrent_weak_root_in_progress(ShenandoahHeap::heap()->is_concurrent_weak_root_in_progress()) { + _heap->_gc_state.clear(); + _heap->set_concurrent_weak_root_in_progress(false); +} + +ShenandoahGCStateResetter::~ShenandoahGCStateResetter() { + _heap->_gc_state.set(_gc_state); + assert(_heap->gc_state() == _gc_state, "Should be restored"); + _heap->set_concurrent_weak_root_in_progress(_concurrent_weak_root_in_progress); +} + // Check for overflow of number of root types. STATIC_ASSERT((static_cast(ShenandoahRootVerifier::AllRoots) + 1) > static_cast(ShenandoahRootVerifier::AllRoots)); @@ -60,6 +74,8 @@ ShenandoahRootVerifier::RootTypes ShenandoahRootVerifier::combine(RootTypes t1, } void ShenandoahRootVerifier::oops_do(OopClosure* oops) { + ShenandoahGCStateResetter resetter; + CodeBlobToOopClosure blobs(oops, !CodeBlobToOopClosure::FixRelocations); if (verify(CodeRoots)) { shenandoah_assert_locked_or_safepoint(CodeCache_lock); @@ -108,6 +124,7 @@ void ShenandoahRootVerifier::oops_do(OopClosure* oops) { } void ShenandoahRootVerifier::roots_do(OopClosure* oops) { + ShenandoahGCStateResetter resetter; shenandoah_assert_safepoint(); CodeBlobToOopClosure blobs(oops, !CodeBlobToOopClosure::FixRelocations); @@ -119,13 +136,6 @@ void ShenandoahRootVerifier::roots_do(OopClosure* oops) { JNIHandles::oops_do(oops); Universe::vm_global()->oops_do(oops); - AlwaysTrueClosure always_true; - WeakProcessor::weak_oops_do(&always_true, oops); - - if (ShenandoahStringDedup::is_enabled()) { - ShenandoahStringDedup::oops_do_slow(oops); - } - // Do thread roots the last. This allows verification code to find // any broken objects from those special roots first, not the accidental // dangling reference from the thread root. @@ -133,6 +143,7 @@ void ShenandoahRootVerifier::roots_do(OopClosure* oops) { } void ShenandoahRootVerifier::strong_roots_do(OopClosure* oops) { + ShenandoahGCStateResetter resetter; shenandoah_assert_safepoint(); CodeBlobToOopClosure blobs(oops, !CodeBlobToOopClosure::FixRelocations); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp index dc7bd55a843..3668c240190 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp @@ -28,6 +28,17 @@ #include "memory/allocation.hpp" #include "memory/iterator.hpp" +class ShenandoahGCStateResetter : public StackObj { +private: + ShenandoahHeap* const _heap; + const char _gc_state; + const bool _concurrent_weak_root_in_progress; + +public: + ShenandoahGCStateResetter(); + ~ShenandoahGCStateResetter(); +}; + class ShenandoahRootVerifier : public StackObj { public: enum RootTypes { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp index 38135a5e33d..5fb69eb9b0f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp @@ -66,6 +66,10 @@ JRT_LEAF(void, ShenandoahRuntime::shenandoah_clone_barrier(oopDesc* src)) ShenandoahBarrierSet::barrier_set()->clone_barrier(s); JRT_END -JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_native(oopDesc * src, oop* load_addr)) - return (oopDesc*) ShenandoahBarrierSet::barrier_set()->load_reference_barrier_native(oop(src), load_addr); +JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_weak(oopDesc * src, oop* load_addr)) + return (oopDesc*) ShenandoahBarrierSet::barrier_set()->load_reference_barrier(oop(src), load_addr); +JRT_END + +JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_weak_narrow(oopDesc * src, narrowOop* load_addr)) + return (oopDesc*) ShenandoahBarrierSet::barrier_set()->load_reference_barrier(oop(src), load_addr); JRT_END diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp index a0b2582d4e7..c0446d32168 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp @@ -41,7 +41,8 @@ class ShenandoahRuntime : public AllStatic { static oopDesc* load_reference_barrier(oopDesc* src, oop* load_addr); static oopDesc* load_reference_barrier_narrow(oopDesc* src, narrowOop* load_addr); - static oopDesc* load_reference_barrier_native(oopDesc* src, oop* load_addr); + static oopDesc* load_reference_barrier_weak(oopDesc* src, oop* load_addr); + static oopDesc* load_reference_barrier_weak_narrow(oopDesc* src, narrowOop* load_addr); static void shenandoah_clone_barrier(oopDesc* src); }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp index 0881779c9d9..bc38a0936eb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp @@ -32,6 +32,7 @@ #include "runtime/atomic.hpp" #include "runtime/mutex.hpp" #include "runtime/thread.hpp" +#include "utilities/debug.hpp" template class BufferedOverflowTaskQueue: public OverflowTaskQueue @@ -60,7 +61,7 @@ class BufferedOverflowTaskQueue: public OverflowTaskQueue E _elem; }; -// ObjArrayChunkedTask +// ShenandoahMarkTask // // Encodes both regular oops, and the array oops plus chunking data for parallel array processing. // The design goal is to make the regular oop ops very fast, because that would be the prevailing @@ -73,11 +74,15 @@ class BufferedOverflowTaskQueue: public OverflowTaskQueue // that the block has the size of 2^pow. This requires for pow to have only 5 bits (2^32) to encode // all possible arrays. // -// |---------oop---------|-pow-|--chunk---| +// |xx-------oop---------|-pow-|--chunk---| // 0 49 54 64 // // By definition, chunk == 0 means "no chunk", i.e. chunking starts from 1. // +// Lower bits of oop are reserved to handle "skip_live" and "strong" properties. Since this encoding +// stores uncompressed oops, those bits are always available. These bits default to zero for "skip_live" +// and "weak". This aligns with their frequent values: strong/counted-live references. +// // This encoding gives a few interesting benefits: // // a) Encoding/decoding regular oops is very simple, because the upper bits are zero in that task: @@ -123,54 +128,71 @@ class BufferedOverflowTaskQueue: public OverflowTaskQueue #endif #ifdef _LP64 -#define SHENANDOAH_OPTIMIZED_OBJTASK 1 +#define SHENANDOAH_OPTIMIZED_MARKTASK 1 #else -#define SHENANDOAH_OPTIMIZED_OBJTASK 0 +#define SHENANDOAH_OPTIMIZED_MARKTASK 0 #endif -#if SHENANDOAH_OPTIMIZED_OBJTASK -class ObjArrayChunkedTask +#if SHENANDOAH_OPTIMIZED_MARKTASK +class ShenandoahMarkTask { -public: - enum { - chunk_bits = 10, - pow_bits = 5, - oop_bits = sizeof(uintptr_t)*8 - chunk_bits - pow_bits - }; - enum { - oop_shift = 0, - pow_shift = oop_shift + oop_bits, - chunk_shift = pow_shift + pow_bits - }; +private: + // Everything is encoded into this field... + uintptr_t _obj; -public: - ObjArrayChunkedTask(oop o = NULL) { - assert(decode_oop(encode_oop(o)) == o, "oop can be encoded: " PTR_FORMAT, p2i(o)); - _obj = encode_oop(o); - } - ObjArrayChunkedTask(oop o, int chunk, int pow) { - assert(decode_oop(encode_oop(o)) == o, "oop can be encoded: " PTR_FORMAT, p2i(o)); - assert(decode_chunk(encode_chunk(chunk)) == chunk, "chunk can be encoded: %d", chunk); - assert(decode_pow(encode_pow(pow)) == pow, "pow can be encoded: %d", pow); - _obj = encode_oop(o) | encode_chunk(chunk) | encode_pow(pow); - } + // ...with these: + static const uint8_t chunk_bits = 10; + static const uint8_t pow_bits = 5; + static const uint8_t oop_bits = sizeof(uintptr_t)*8 - chunk_bits - pow_bits; - // Trivially copyable. + static const uint8_t oop_shift = 0; + static const uint8_t pow_shift = oop_bits; + static const uint8_t chunk_shift = oop_bits + pow_bits; + + static const uintptr_t oop_extract_mask = right_n_bits(oop_bits) - 3; + static const uintptr_t skip_live_extract_mask = 1 << 0; + static const uintptr_t weak_extract_mask = 1 << 1; + static const uintptr_t chunk_pow_extract_mask = ~right_n_bits(oop_bits); + + static const int chunk_range_mask = right_n_bits(chunk_bits); + static const int pow_range_mask = right_n_bits(pow_bits); inline oop decode_oop(uintptr_t val) const { - return (oop) reinterpret_cast((val >> oop_shift) & right_n_bits(oop_bits)); + STATIC_ASSERT(oop_shift == 0); + return cast_to_oop(val & oop_extract_mask); + } + + inline bool decode_not_chunked(uintptr_t val) const { + // No need to shift for a comparison to zero + return (val & chunk_pow_extract_mask) == 0; } inline int decode_chunk(uintptr_t val) const { - return (int) ((val >> chunk_shift) & right_n_bits(chunk_bits)); + return (int) ((val >> chunk_shift) & chunk_range_mask); } inline int decode_pow(uintptr_t val) const { - return (int) ((val >> pow_shift) & right_n_bits(pow_bits)); + return (int) ((val >> pow_shift) & pow_range_mask); + } + + inline bool decode_weak(uintptr_t val) const { + return (val & weak_extract_mask) != 0; + } + + inline bool decode_cnt_live(uintptr_t val) const { + return (val & skip_live_extract_mask) == 0; } - inline uintptr_t encode_oop(oop obj) const { - return ((uintptr_t)(void*) obj) << oop_shift; + inline uintptr_t encode_oop(oop obj, bool skip_live, bool weak) const { + STATIC_ASSERT(oop_shift == 0); + uintptr_t encoded = cast_from_oop(obj); + if (skip_live) { + encoded |= skip_live_extract_mask; + } + if (weak) { + encoded |= weak_extract_mask; + } + return encoded; } inline uintptr_t encode_chunk(int chunk) const { @@ -181,12 +203,42 @@ class ObjArrayChunkedTask return ((uintptr_t) pow) << pow_shift; } - inline oop obj() const { return decode_oop(_obj); } - inline int chunk() const { return decode_chunk(_obj); } - inline int pow() const { return decode_pow(_obj); } - inline bool is_not_chunked() const { return (_obj & ~right_n_bits(oop_bits + pow_bits)) == 0; } +public: + ShenandoahMarkTask(oop o = NULL, bool skip_live = false, bool weak = false) { + uintptr_t enc = encode_oop(o, skip_live, weak); + assert(decode_oop(enc) == o, "oop encoding should work: " PTR_FORMAT, p2i(o)); + assert(decode_cnt_live(enc) == !skip_live, "skip_live encoding should work"); + assert(decode_weak(enc) == weak, "weak encoding should work"); + assert(decode_not_chunked(enc), "task should not be chunked"); + _obj = enc; + } + + ShenandoahMarkTask(oop o, bool skip_live, bool weak, int chunk, int pow) { + uintptr_t enc_oop = encode_oop(o, skip_live, weak); + uintptr_t enc_chunk = encode_chunk(chunk); + uintptr_t enc_pow = encode_pow(pow); + uintptr_t enc = enc_oop | enc_chunk | enc_pow; + assert(decode_oop(enc) == o, "oop encoding should work: " PTR_FORMAT, p2i(o)); + assert(decode_cnt_live(enc) == !skip_live, "skip_live should be true for chunked tasks"); + assert(decode_weak(enc) == weak, "weak encoding should work"); + assert(decode_chunk(enc) == chunk, "chunk encoding should work: %d", chunk); + assert(decode_pow(enc) == pow, "pow encoding should work: %d", pow); + assert(!decode_not_chunked(enc), "task should be chunked"); + _obj = enc; + } + + // Trivially copyable. + +public: + inline oop obj() const { return decode_oop(_obj); } + inline int chunk() const { return decode_chunk(_obj); } + inline int pow() const { return decode_pow(_obj); } - DEBUG_ONLY(bool is_valid() const); // Tasks to be pushed/popped must be valid. + inline bool is_not_chunked() const { return decode_not_chunked(_obj); } + inline bool is_weak() const { return decode_weak(_obj); } + inline bool count_liveness() const { return decode_cnt_live(_obj); } + + DEBUG_ONLY(bool is_valid() const;) // Tasks to be pushed/popped must be valid. static uintptr_t max_addressable() { return nth_bit(oop_bits); @@ -195,35 +247,40 @@ class ObjArrayChunkedTask static int chunk_size() { return nth_bit(chunk_bits); } - -private: - uintptr_t _obj; }; #else -class ObjArrayChunkedTask +class ShenandoahMarkTask { +private: + static const uint8_t chunk_bits = 10; + static const uint8_t pow_bits = 5; + + static const int chunk_max = nth_bit(chunk_bits) - 1; + static const int pow_max = nth_bit(pow_bits) - 1; + + oop _obj; + bool _skip_live; + bool _weak; + int _chunk; + int _pow; + public: - enum { - chunk_bits = 10, - pow_bits = 5, - }; -public: - ObjArrayChunkedTask(oop o = NULL, int chunk = 0, int pow = 0): _obj(o) { - assert(0 <= chunk && chunk < nth_bit(chunk_bits), "chunk is sane: %d", chunk); - assert(0 <= pow && pow < nth_bit(pow_bits), "pow is sane: %d", pow); - _chunk = chunk; - _pow = pow; + ShenandoahMarkTask(oop o = NULL, bool skip_live = false, bool weak = false, int chunk = 0, int pow = 0): + _obj(o), _skip_live(skip_live), _weak(weak), _chunk(chunk), _pow(pow) { + assert(0 <= chunk && chunk <= chunk_max, "chunk is in range: %d", chunk); + assert(0 <= pow && pow <= pow_max, "pow is in range: %d", pow); } // Trivially copyable. - inline oop obj() const { return _obj; } - inline int chunk() const { return _chunk; } - inline int pow() const { return _pow; } - + inline oop obj() const { return _obj; } + inline int chunk() const { return _chunk; } + inline int pow() const { return _pow; } inline bool is_not_chunked() const { return _chunk == 0; } + inline bool is_weak() const { return _weak; } + inline bool count_liveness() const { return !_skip_live; } - DEBUG_ONLY(bool is_valid() const); // Tasks to be pushed/popped must be valid. + DEBUG_ONLY(bool is_valid() const;) // Tasks to be pushed/popped must be valid. static size_t max_addressable() { return sizeof(oop); @@ -232,19 +289,13 @@ class ObjArrayChunkedTask static int chunk_size() { return nth_bit(chunk_bits); } - -private: - oop _obj; - int _chunk; - int _pow; }; -#endif // SHENANDOAH_OPTIMIZED_OBJTASK +#endif // SHENANDOAH_OPTIMIZED_MARKTASK #ifdef _MSC_VER #pragma warning(pop) #endif -typedef ObjArrayChunkedTask ShenandoahMarkTask; typedef BufferedOverflowTaskQueue ShenandoahBufferedOverflowTaskQueue; typedef Padded ShenandoahObjToScanQueue; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp b/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp index 5ef533a67b2..d24403f1a83 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp @@ -197,5 +197,5 @@ void ShenandoahUnload::unload() { void ShenandoahUnload::finish() { MetaspaceGC::compute_new_size(); - MetaspaceUtils::verify_metrics(); + DEBUG_ONLY(MetaspaceUtils::verify();) } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp index 9624d4c5e8a..b3a4aef6426 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp @@ -59,9 +59,9 @@ class VM_ShenandoahInitMark: public VM_ShenandoahOperation { virtual void doit(); }; -class VM_ShenandoahFinalMarkStartEvac: public VM_ShenandoahReferenceOperation { +class VM_ShenandoahFinalMarkStartEvac: public VM_ShenandoahOperation { public: - VM_ShenandoahFinalMarkStartEvac() : VM_ShenandoahReferenceOperation() {}; + VM_ShenandoahFinalMarkStartEvac() : VM_ShenandoahOperation() {}; VM_Operation::VMOp_Type type() const { return VMOp_ShenandoahFinalMarkStartEvac; } const char* name() const { return "Shenandoah Final Mark and Start Evacuation"; } virtual void doit(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp index 026a811d571..fa464496ec0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp @@ -47,6 +47,17 @@ #undef verify_oop #endif +static bool is_instance_ref_klass(Klass* k) { + return k->is_instance_klass() && InstanceKlass::cast(k)->reference_type() != REF_NONE; +} + +class ShenandoahIgnoreReferenceDiscoverer : public ReferenceDiscoverer { +public: + virtual bool discover_reference(oop obj, ReferenceType type) { + return true; + } +}; + class ShenandoahVerifyOopClosure : public BasicOopIterateClosure { private: const char* _phase; @@ -68,7 +79,12 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure { _map(map), _ld(ld), _interior_loc(NULL), - _loc(NULL) { } + _loc(NULL) { + if (options._verify_marked == ShenandoahVerifier::_verify_marked_complete_except_references || + options._verify_marked == ShenandoahVerifier::_verify_marked_disable) { + set_ref_discoverer_internal(new ShenandoahIgnoreReferenceDiscoverer()); + } + } private: void check(ShenandoahAsserts::SafeLevel level, oop obj, bool test, const char* label) { @@ -82,7 +98,9 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure { T o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { oop obj = CompressedOops::decode_not_null(o); - + if (is_instance_ref_klass(obj->klass())) { + obj = ShenandoahForwarding::get_forwardee(obj); + } // Single threaded verification can use faster non-atomic stack and bitmap // methods. // @@ -208,6 +226,10 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure { check(ShenandoahAsserts::_safe_all, obj, _heap->complete_marking_context()->is_marked(obj), "Must be marked in complete bitmap"); break; + case ShenandoahVerifier::_verify_marked_complete_except_references: + check(ShenandoahAsserts::_safe_all, obj, _heap->complete_marking_context()->is_marked(obj), + "Must be marked in complete bitmap, except j.l.r.Reference referents"); + break; default: assert(false, "Unhandled mark verification"); } @@ -526,19 +548,19 @@ class ShenandoahVerifierMarkedRegionTask : public AbstractGangTask { virtual void work_regular(ShenandoahHeapRegion *r, ShenandoahVerifierStack &stack, ShenandoahVerifyOopClosure &cl) { size_t processed = 0; - MarkBitMap* mark_bit_map = _heap->complete_marking_context()->mark_bit_map(); - HeapWord* tams = _heap->complete_marking_context()->top_at_mark_start(r); + ShenandoahMarkingContext* ctx = _heap->complete_marking_context(); + HeapWord* tams = ctx->top_at_mark_start(r); // Bitmaps, before TAMS if (tams > r->bottom()) { HeapWord* start = r->bottom(); - HeapWord* addr = mark_bit_map->get_next_marked_addr(start, tams); + HeapWord* addr = ctx->get_next_marked_addr(start, tams); while (addr < tams) { verify_and_follow(addr, stack, cl, &processed); addr += 1; if (addr < tams) { - addr = mark_bit_map->get_next_marked_addr(addr, tams); + addr = ctx->get_next_marked_addr(addr, tams); } } } @@ -566,9 +588,10 @@ class ShenandoahVerifierMarkedRegionTask : public AbstractGangTask { // Verify everything reachable from that object too, hopefully realizing // everything was already marked, and never touching further: - cl.verify_oops_from(obj); - (*processed)++; - + if (!is_instance_ref_klass(obj->klass())) { + cl.verify_oops_from(obj); + (*processed)++; + } while (!stack.is_empty()) { ShenandoahVerifierTask task = stack.pop(); cl.verify_oops_from(task.obj()); @@ -592,23 +615,6 @@ class VerifyThreadGCState : public ThreadClosure { } }; -class ShenandoahGCStateResetter : public StackObj { -private: - ShenandoahHeap* const _heap; - char _gc_state; - -public: - ShenandoahGCStateResetter() : _heap(ShenandoahHeap::heap()) { - _gc_state = _heap->gc_state(); - _heap->_gc_state.clear(); - } - - ~ShenandoahGCStateResetter() { - _heap->_gc_state.set(_gc_state); - assert(_heap->gc_state() == _gc_state, "Should be restored"); - } -}; - void ShenandoahVerifier::verify_at_safepoint(const char *label, VerifyForwarded forwarded, VerifyMarked marked, VerifyCollectionSet cset, @@ -735,7 +741,7 @@ void ShenandoahVerifier::verify_at_safepoint(const char *label, // version size_t count_marked = 0; - if (ShenandoahVerifyLevel >= 4 && marked == _verify_marked_complete) { + if (ShenandoahVerifyLevel >= 4 && (marked == _verify_marked_complete || marked == _verify_marked_complete_except_references)) { guarantee(_heap->marking_context()->is_complete(), "Marking context should be complete"); ShenandoahVerifierMarkedRegionTask task(_verification_bit_map, ld, label, options); _heap->workers()->run_task(&task); @@ -810,11 +816,11 @@ void ShenandoahVerifier::verify_after_concmark() { verify_at_safepoint( "After Mark", _verify_forwarded_none, // no forwarded references - _verify_marked_complete, // bitmaps as precise as we can get + _verify_marked_complete_except_references, // bitmaps as precise as we can get, except dangling j.l.r.Refs _verify_cset_none, // no references to cset anymore _verify_liveness_complete, // liveness data must be complete here _verify_regions_disable, // trash regions not yet recycled - _verify_gcstate_stable, // mark should have stabilized the heap + _verify_gcstate_stable, // mark should have stabilized the heap _verify_all_weak_roots ); } @@ -827,12 +833,12 @@ void ShenandoahVerifier::verify_before_evacuation() { verify_at_safepoint( "Before Evacuation", - _verify_forwarded_none, // no forwarded references - _verify_marked_complete, // walk over marked objects too - _verify_cset_disable, // non-forwarded references to cset expected - _verify_liveness_complete, // liveness data must be complete here - _verify_regions_disable, // trash regions not yet recycled - _verify_gcstate_stable, // mark should have stabilized the heap + _verify_forwarded_none, // no forwarded references + _verify_marked_complete_except_references, // walk over marked objects too + _verify_cset_disable, // non-forwarded references to cset expected + _verify_liveness_complete, // liveness data must be complete here + _verify_regions_disable, // trash regions not yet recycled + _verify_gcstate_stable, // mark should have stabilized the heap verify_weak_roots ); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp index bb5d8ff9708..b9844715197 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp @@ -65,7 +65,11 @@ class ShenandoahVerifier : public CHeapObj { _verify_marked_incomplete, // Objects should be marked in "complete" bitmap. - _verify_marked_complete + _verify_marked_complete, + + // Objects should be marked in "complete" bitmap, except j.l.r.Reference referents, which + // may be dangling after marking but before conc-weakrefs-processing. + _verify_marked_complete_except_references } VerifyMarked; typedef enum { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahWorkerPolicy.cpp b/src/hotspot/share/gc/shenandoah/shenandoahWorkerPolicy.cpp index 54cd8cfcc00..867dbc6aa8c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahWorkerPolicy.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahWorkerPolicy.cpp @@ -32,6 +32,7 @@ uint ShenandoahWorkerPolicy::_prev_par_marking = 0; uint ShenandoahWorkerPolicy::_prev_conc_marking = 0; uint ShenandoahWorkerPolicy::_prev_conc_evac = 0; uint ShenandoahWorkerPolicy::_prev_conc_root_proc = 0; +uint ShenandoahWorkerPolicy::_prev_conc_refs_proc = 0; uint ShenandoahWorkerPolicy::_prev_fullgc = 0; uint ShenandoahWorkerPolicy::_prev_degengc = 0; uint ShenandoahWorkerPolicy::_prev_conc_update_ref = 0; @@ -63,13 +64,23 @@ uint ShenandoahWorkerPolicy::calc_workers_for_final_marking() { return _prev_par_marking; } +// Calculate workers for concurrent refs processing +uint ShenandoahWorkerPolicy::calc_workers_for_conc_refs_processing() { + uint active_workers = (_prev_conc_refs_proc == 0) ? ConcGCThreads : _prev_conc_refs_proc; + _prev_conc_refs_proc = + WorkerPolicy::calc_active_conc_workers(ConcGCThreads, + active_workers, + Threads::number_of_non_daemon_threads()); + return _prev_conc_refs_proc; +} + // Calculate workers for concurrent root processing uint ShenandoahWorkerPolicy::calc_workers_for_conc_root_processing() { uint active_workers = (_prev_conc_root_proc == 0) ? ConcGCThreads : _prev_conc_root_proc; _prev_conc_root_proc = - WorkerPolicy::calc_active_conc_workers(ConcGCThreads, - active_workers, - Threads::number_of_non_daemon_threads()); + WorkerPolicy::calc_active_conc_workers(ConcGCThreads, + active_workers, + Threads::number_of_non_daemon_threads()); return _prev_conc_root_proc; } @@ -123,11 +134,6 @@ uint ShenandoahWorkerPolicy::calc_workers_for_final_update_ref() { return _prev_par_update_ref; } -uint ShenandoahWorkerPolicy::calc_workers_for_conc_preclean() { - // Precleaning is single-threaded - return 1; -} - uint ShenandoahWorkerPolicy::calc_workers_for_conc_cleanup() { uint active_workers = (_prev_conc_cleanup == 0) ? ConcGCThreads : _prev_conc_cleanup; _prev_conc_cleanup = diff --git a/src/hotspot/share/gc/shenandoah/shenandoahWorkerPolicy.hpp b/src/hotspot/share/gc/shenandoah/shenandoahWorkerPolicy.hpp index 6a7abbd8fef..10a6fec6535 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahWorkerPolicy.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahWorkerPolicy.hpp @@ -32,6 +32,7 @@ class ShenandoahWorkerPolicy : AllStatic { static uint _prev_par_marking; static uint _prev_conc_marking; static uint _prev_conc_root_proc; + static uint _prev_conc_refs_proc; static uint _prev_conc_evac; static uint _prev_fullgc; static uint _prev_degengc; @@ -53,6 +54,9 @@ class ShenandoahWorkerPolicy : AllStatic { // Calculate workers for concurrent root processing static uint calc_workers_for_conc_root_processing(); + // Calculate workers for concurrent refs processing + static uint calc_workers_for_conc_refs_processing(); + // Calculate workers for concurrent evacuation (concurrent GC) static uint calc_workers_for_conc_evac(); @@ -68,9 +72,6 @@ class ShenandoahWorkerPolicy : AllStatic { // Calculate workers for parallel/final reference update static uint calc_workers_for_final_update_ref(); - // Calculate workers for concurrent precleaning - static uint calc_workers_for_conc_preclean(); - // Calculate workers for concurrent cleanup static uint calc_workers_for_conc_cleanup(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp index 5417cb5a9fc..113e90cb0f3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp @@ -76,13 +76,6 @@ " compact - run GC more frequently and with deeper targets to " \ "free up more memory.") \ \ - product(uintx, ShenandoahRefProcFrequency, 5, EXPERIMENTAL, \ - "Process process weak (soft, phantom, finalizers) references " \ - "every Nth cycle. Normally affects concurrent GC cycles only, " \ - "as degenerated and full GCs would try to process references " \ - "regardless. Set to zero to disable reference processing " \ - "completely.") \ - \ product(uintx, ShenandoahUnloadClassesFrequency, 1, EXPERIMENTAL, \ "Unload the classes every Nth cycle. Normally affects concurrent "\ "GC cycles, as degenerated and full GCs would try to unload " \ @@ -313,11 +306,6 @@ "Forcefully flush non-empty SATB buffers at this interval. " \ "Time is in milliseconds.") \ \ - product(bool, ShenandoahPreclean, true, DIAGNOSTIC, \ - "Do concurrent preclean phase before final mark: process " \ - "definitely alive references to avoid dealing with them during " \ - "pause.") \ - \ product(bool, ShenandoahSuspendibleWorkers, false, EXPERIMENTAL, \ "Suspend concurrent GC worker threads at safepoints") \ \ diff --git a/src/hotspot/share/gc/z/zArray.hpp b/src/hotspot/share/gc/z/zArray.hpp index 1ee7f6cda96..f7bd3967d79 100644 --- a/src/hotspot/share/gc/z/zArray.hpp +++ b/src/hotspot/share/gc/z/zArray.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,34 +25,27 @@ #define SHARE_GC_Z_ZARRAY_HPP #include "memory/allocation.hpp" -#include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" -template -class ZArray : public GrowableArrayCHeap { -public: - ZArray(); - - void transfer(ZArray* from); -}; +template using ZArray = GrowableArrayCHeap; -template +template class ZArrayIteratorImpl : public StackObj { private: - ZArray* const _array; - int _next; + const T* _next; + const T* const _end; + + bool next_serial(T* elem); + bool next_parallel(T* elem); public: - ZArrayIteratorImpl(ZArray* array); + ZArrayIteratorImpl(const T* array, size_t length); + ZArrayIteratorImpl(const ZArray* array); bool next(T* elem); }; -// Iterator types -#define ZARRAY_SERIAL false -#define ZARRAY_PARALLEL true - -template using ZArrayIterator = ZArrayIteratorImpl; -template using ZArrayParallelIterator = ZArrayIteratorImpl; +template using ZArrayIterator = ZArrayIteratorImpl; +template using ZArrayParallelIterator = ZArrayIteratorImpl; #endif // SHARE_GC_Z_ZARRAY_HPP diff --git a/src/hotspot/share/gc/z/zArray.inline.hpp b/src/hotspot/share/gc/z/zArray.inline.hpp index 15a8f45ccaf..03ea1d18754 100644 --- a/src/hotspot/share/gc/z/zArray.inline.hpp +++ b/src/hotspot/share/gc/z/zArray.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,46 +25,56 @@ #define SHARE_GC_Z_ZARRAY_INLINE_HPP #include "gc/z/zArray.hpp" -#include "memory/allocation.inline.hpp" #include "runtime/atomic.hpp" -template -inline ZArray::ZArray() : - GrowableArrayCHeap(0) {} +template +inline bool ZArrayIteratorImpl::next_serial(T* elem) { + if (_next == _end) { + return false; + } + + *elem = *_next; + _next++; -template -inline void ZArray::transfer(ZArray* from) { - assert(this->_data == NULL, "Should be empty"); - this->_data = from->_data; - this->_len = from->_len; - this->_max = from->_max; - from->_data = NULL; - from->_len = 0; - from->_max = 0; + return true; } -template -inline ZArrayIteratorImpl::ZArrayIteratorImpl(ZArray* array) : - _array(array), - _next(0) {} +template +inline bool ZArrayIteratorImpl::next_parallel(T* elem) { + const T* old_next = Atomic::load(&_next); -template -inline bool ZArrayIteratorImpl::next(T* elem) { - if (parallel) { - const int next = Atomic::fetch_and_add(&_next, 1); - if (next < _array->length()) { - *elem = _array->at(next); - return true; + for (;;) { + if (old_next == _end) { + return false; } - } else { - if (_next < _array->length()) { - *elem = _array->at(_next++); + + const T* const new_next = old_next + 1; + const T* const prev_next = Atomic::cmpxchg(&_next, old_next, new_next); + if (prev_next == old_next) { + *elem = *old_next; return true; } + + old_next = prev_next; } +} + +template +inline ZArrayIteratorImpl::ZArrayIteratorImpl(const T* array, size_t length) : + _next(array), + _end(array + length) {} + +template +inline ZArrayIteratorImpl::ZArrayIteratorImpl(const ZArray* array) : + ZArrayIteratorImpl(array->is_empty() ? NULL : array->adr_at(0), array->length()) {} - // No more elements - return false; +template +inline bool ZArrayIteratorImpl::next(T* elem) { + if (Parallel) { + return next_parallel(elem); + } else { + return next_serial(elem); + } } #endif // SHARE_GC_Z_ZARRAY_INLINE_HPP diff --git a/src/hotspot/share/gc/z/zAttachedArray.hpp b/src/hotspot/share/gc/z/zAttachedArray.hpp index 319a9084e12..edbdeafadef 100644 --- a/src/hotspot/share/gc/z/zAttachedArray.hpp +++ b/src/hotspot/share/gc/z/zAttachedArray.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,8 +32,12 @@ class ZAttachedArray { const size_t _length; static size_t object_size(); + static size_t array_size(size_t length); public: + template + static void* alloc(Allocator* allocator, size_t length); + static void* alloc(size_t length); static void free(ObjectT* obj); diff --git a/src/hotspot/share/gc/z/zAttachedArray.inline.hpp b/src/hotspot/share/gc/z/zAttachedArray.inline.hpp index 81015e44b94..f52f83186e4 100644 --- a/src/hotspot/share/gc/z/zAttachedArray.inline.hpp +++ b/src/hotspot/share/gc/z/zAttachedArray.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,13 +34,35 @@ inline size_t ZAttachedArray::object_size() { } template -inline void* ZAttachedArray::alloc(size_t length) { - const size_t array_size = sizeof(ArrayT) * length; - char* const addr = AllocateHeap(object_size() + array_size, mtGC); - ::new (addr + object_size()) ArrayT[length]; +inline size_t ZAttachedArray::array_size(size_t length) { + return sizeof(ArrayT) * length; +} + +template +template +inline void* ZAttachedArray::alloc(Allocator* allocator, size_t length) { + // Allocate memory for object and array + const size_t size = object_size() + array_size(length); + void* const addr = allocator->alloc(size); + + // Placement new array + void* const array_addr = reinterpret_cast(addr) + object_size(); + ::new (array_addr) ArrayT[length]; + + // Return pointer to object return addr; } +template +inline void* ZAttachedArray::alloc(size_t length) { + struct Allocator { + void* alloc(size_t size) const { + return AllocateHeap(size, mtGC); + } + } allocator; + return alloc(&allocator, length); +} + template inline void ZAttachedArray::free(ObjectT* obj) { FreeHeap(obj); diff --git a/src/hotspot/share/gc/z/zBarrier.cpp b/src/hotspot/share/gc/z/zBarrier.cpp index 9123c890f55..3200b2ab0c3 100644 --- a/src/hotspot/share/gc/z/zBarrier.cpp +++ b/src/hotspot/share/gc/z/zBarrier.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -114,6 +114,10 @@ uintptr_t ZBarrier::relocate_or_mark(uintptr_t addr) { return during_relocate() ? relocate(addr) : mark(addr); } +uintptr_t ZBarrier::relocate_or_mark_no_follow(uintptr_t addr) { + return during_relocate() ? relocate(addr) : mark(addr); +} + uintptr_t ZBarrier::relocate_or_remap(uintptr_t addr) { return during_relocate() ? relocate(addr) : remap(addr); } @@ -125,6 +129,10 @@ uintptr_t ZBarrier::load_barrier_on_oop_slow_path(uintptr_t addr) { return relocate_or_mark(addr); } +uintptr_t ZBarrier::load_barrier_on_invisible_root_oop_slow_path(uintptr_t addr) { + return relocate_or_mark_no_follow(addr); +} + void ZBarrier::load_barrier_on_oop_fields(oop o) { assert(ZAddress::is_good(ZOop::to_address(o)), "Should be good"); ZLoadBarrierOopClosure cl; @@ -198,14 +206,6 @@ uintptr_t ZBarrier::mark_barrier_on_root_oop_slow_path(uintptr_t addr) { return mark(addr); } -uintptr_t ZBarrier::mark_barrier_on_invisible_root_oop_slow_path(uintptr_t addr) { - assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint"); - assert(during_mark(), "Invalid phase"); - - // Mark - return mark(addr); -} - // // Relocate barrier // diff --git a/src/hotspot/share/gc/z/zBarrier.hpp b/src/hotspot/share/gc/z/zBarrier.hpp index 3df89f27695..c3ab96dc5d9 100644 --- a/src/hotspot/share/gc/z/zBarrier.hpp +++ b/src/hotspot/share/gc/z/zBarrier.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,9 +58,11 @@ class ZBarrier : public AllStatic { static uintptr_t remap(uintptr_t addr); static uintptr_t relocate(uintptr_t addr); static uintptr_t relocate_or_mark(uintptr_t addr); + static uintptr_t relocate_or_mark_no_follow(uintptr_t addr); static uintptr_t relocate_or_remap(uintptr_t addr); static uintptr_t load_barrier_on_oop_slow_path(uintptr_t addr); + static uintptr_t load_barrier_on_invisible_root_oop_slow_path(uintptr_t addr); static uintptr_t weak_load_barrier_on_oop_slow_path(uintptr_t addr); static uintptr_t weak_load_barrier_on_weak_oop_slow_path(uintptr_t addr); @@ -72,7 +74,6 @@ class ZBarrier : public AllStatic { static uintptr_t mark_barrier_on_oop_slow_path(uintptr_t addr); static uintptr_t mark_barrier_on_finalizable_oop_slow_path(uintptr_t addr); static uintptr_t mark_barrier_on_root_oop_slow_path(uintptr_t addr); - static uintptr_t mark_barrier_on_invisible_root_oop_slow_path(uintptr_t addr); static uintptr_t relocate_barrier_on_root_oop_slow_path(uintptr_t addr); @@ -86,6 +87,7 @@ class ZBarrier : public AllStatic { static oop load_barrier_on_weak_oop_field_preloaded(volatile oop* p, oop o); static oop load_barrier_on_phantom_oop_field_preloaded(volatile oop* p, oop o); static void load_barrier_on_root_oop_field(oop* p); + static void load_barrier_on_invisible_root_oop_field(oop* p); // Weak load barrier static oop weak_load_barrier_on_oop_field(volatile oop* p); diff --git a/src/hotspot/share/gc/z/zBarrier.inline.hpp b/src/hotspot/share/gc/z/zBarrier.inline.hpp index f278eba0095..ed12ed44c0e 100644 --- a/src/hotspot/share/gc/z/zBarrier.inline.hpp +++ b/src/hotspot/share/gc/z/zBarrier.inline.hpp @@ -277,6 +277,11 @@ inline void ZBarrier::load_barrier_on_root_oop_field(oop* p) { root_barrier(p, o); } +inline void ZBarrier::load_barrier_on_invisible_root_oop_field(oop* p) { + const oop o = *p; + root_barrier(p, o); +} + // // Weak load barrier // @@ -407,11 +412,6 @@ inline void ZBarrier::mark_barrier_on_root_oop_field(oop* p) { root_barrier(p, o); } -inline void ZBarrier::mark_barrier_on_invisible_root_oop_field(oop* p) { - const oop o = *p; - root_barrier(p, o); -} - // // Relocate barrier // diff --git a/src/hotspot/share/gc/z/zBarrierSet.cpp b/src/hotspot/share/gc/z/zBarrierSet.cpp index 94b9554b54b..67b85a897ee 100644 --- a/src/hotspot/share/gc/z/zBarrierSet.cpp +++ b/src/hotspot/share/gc/z/zBarrierSet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ #include "gc/z/zBarrierSetNMethod.hpp" #include "gc/z/zGlobals.hpp" #include "gc/z/zHeap.inline.hpp" +#include "gc/z/zStackWatermark.hpp" #include "gc/z/zThreadLocalData.hpp" #include "runtime/thread.hpp" #include "utilities/macros.hpp" @@ -40,20 +41,11 @@ class ZBarrierSetC1; class ZBarrierSetC2; -static BarrierSetNMethod* make_barrier_set_nmethod() { - // NMethod barriers are only used when class unloading is enabled - if (!ClassUnloading) { - return NULL; - } - - return new ZBarrierSetNMethod(); -} - ZBarrierSet::ZBarrierSet() : BarrierSet(make_barrier_set_assembler(), make_barrier_set_c1(), make_barrier_set_c2(), - make_barrier_set_nmethod(), + new ZBarrierSetNMethod(), BarrierSet::FakeRtti(BarrierSet::ZBarrierSet)) {} ZBarrierSetAssembler* ZBarrierSet::assembler() { @@ -89,6 +81,11 @@ void ZBarrierSet::on_thread_destroy(Thread* thread) { void ZBarrierSet::on_thread_attach(Thread* thread) { // Set thread local address bad mask ZThreadLocalData::set_address_bad_mask(thread, ZAddressBadMask); + if (thread->is_Java_thread()) { + JavaThread* const jt = thread->as_Java_thread(); + StackWatermark* const watermark = new ZStackWatermark(jt); + StackWatermarkSet::add_watermark(jt, watermark); + } } void ZBarrierSet::on_thread_detach(Thread* thread) { diff --git a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp index 8779e0a4929..bfb8a17e872 100644 --- a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp +++ b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,16 +55,14 @@ bool ZBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { // Heal oops and disarm ZNMethodOopClosure cl; - ZNMethod::nmethod_oops_do(nm, &cl); + ZNMethod::nmethod_oops_do_inner(nm, &cl); disarm(nm); return true; } int* ZBarrierSetNMethod::disarmed_value_address() const { - const uintptr_t mask_addr = reinterpret_cast(&ZAddressBadMask); - const uintptr_t disarmed_addr = mask_addr + ZNMethodDisarmedOffset; - return reinterpret_cast(disarmed_addr); + return (int*)ZAddressBadMaskHighOrderBitsAddr; } ByteSize ZBarrierSetNMethod::thread_disarmed_offset() const { diff --git a/src/hotspot/share/gc/z/zBitField.hpp b/src/hotspot/share/gc/z/zBitField.hpp index 7114e4c097c..4d7171c41ac 100644 --- a/src/hotspot/share/gc/z/zBitField.hpp +++ b/src/hotspot/share/gc/z/zBitField.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,9 +60,9 @@ class ZBitField : public AllStatic { private: static const int ContainerBits = sizeof(ContainerType) * BitsPerByte; - STATIC_ASSERT(FieldBits < ContainerBits); - STATIC_ASSERT(FieldShift + FieldBits <= ContainerBits); - STATIC_ASSERT(ValueShift + FieldBits <= ContainerBits); + static_assert(FieldBits < ContainerBits, "Field too large"); + static_assert(FieldShift + FieldBits <= ContainerBits, "Field too large"); + static_assert(ValueShift + FieldBits <= ContainerBits, "Field too large"); static const ContainerType FieldMask = (((ContainerType)1 << FieldBits) - 1); diff --git a/src/hotspot/share/gc/z/zCollectedHeap.cpp b/src/hotspot/share/gc/z/zCollectedHeap.cpp index 59d6bf66da8..f07d4b4c7aa 100644 --- a/src/hotspot/share/gc/z/zCollectedHeap.cpp +++ b/src/hotspot/share/gc/z/zCollectedHeap.cpp @@ -35,9 +35,9 @@ #include "gc/z/zServiceability.hpp" #include "gc/z/zStat.hpp" #include "gc/z/zUtils.inline.hpp" +#include "memory/classLoaderMetaspace.hpp" #include "memory/iterator.hpp" #include "memory/universe.hpp" -#include "runtime/mutexLocker.hpp" #include "utilities/align.hpp" ZCollectedHeap* ZCollectedHeap::heap() { @@ -221,6 +221,10 @@ size_t ZCollectedHeap::unsafe_max_tlab_alloc(Thread* ignored) const { return _heap.unsafe_max_tlab_alloc(); } +bool ZCollectedHeap::uses_stack_watermark_barrier() const { + return true; +} + GrowableArray ZCollectedHeap::memory_managers() { return GrowableArray(1, 1, _heap.serviceability_memory_manager()); } @@ -233,6 +237,10 @@ void ZCollectedHeap::object_iterate(ObjectClosure* cl) { _heap.object_iterate(cl, true /* visit_weaks */); } +ParallelObjectIterator* ZCollectedHeap::parallel_object_iterator(uint nworkers) { + return _heap.parallel_object_iterator(nworkers, true /* visit_weaks */); +} + void ZCollectedHeap::keep_alive(oop obj) { _heap.keep_alive(obj); } diff --git a/src/hotspot/share/gc/z/zCollectedHeap.hpp b/src/hotspot/share/gc/z/zCollectedHeap.hpp index 0b00c51d3ea..c6f26a2e518 100644 --- a/src/hotspot/share/gc/z/zCollectedHeap.hpp +++ b/src/hotspot/share/gc/z/zCollectedHeap.hpp @@ -30,6 +30,7 @@ #include "gc/z/zHeap.hpp" #include "gc/z/zInitialize.hpp" #include "gc/z/zRuntimeWorkers.hpp" +#include "memory/metaspace.hpp" class ZDirector; class ZDriver; @@ -88,10 +89,13 @@ class ZCollectedHeap : public CollectedHeap { virtual size_t max_tlab_size() const; virtual size_t unsafe_max_tlab_alloc(Thread* thr) const; + virtual bool uses_stack_watermark_barrier() const; + virtual GrowableArray memory_managers(); virtual GrowableArray memory_pools(); virtual void object_iterate(ObjectClosure* cl); + virtual ParallelObjectIterator* parallel_object_iterator(uint nworkers); virtual void keep_alive(oop obj); diff --git a/src/hotspot/share/gc/z/zDirector.cpp b/src/hotspot/share/gc/z/zDirector.cpp index 345d202e063..728b4d9ffe3 100644 --- a/src/hotspot/share/gc/z/zDirector.cpp +++ b/src/hotspot/share/gc/z/zDirector.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,6 @@ #include "gc/z/zDirector.hpp" #include "gc/z/zHeap.inline.hpp" #include "gc/z/zStat.hpp" -#include "gc/z/zUtils.hpp" #include "logging/log.hpp" const double ZDirector::one_in_1000 = 3.290527; @@ -49,7 +48,7 @@ void ZDirector::sample_allocation_rate() const { } bool ZDirector::rule_timer() const { - if (ZCollectionInterval == 0) { + if (ZCollectionInterval <= 0) { // Rule disabled return false; } @@ -58,7 +57,7 @@ bool ZDirector::rule_timer() const { const double time_since_last_gc = ZStatCycle::time_since_last(); const double time_until_gc = ZCollectionInterval - time_since_last_gc; - log_debug(gc, director)("Rule: Timer, Interval: %us, TimeUntilGC: %.3fs", + log_debug(gc, director)("Rule: Timer, Interval: %.3fs, TimeUntilGC: %.3fs", ZCollectionInterval, time_until_gc); return time_until_gc <= 0; diff --git a/src/hotspot/share/gc/z/zDriver.cpp b/src/hotspot/share/gc/z/zDriver.cpp index d0470e052bb..eceaf768cf1 100644 --- a/src/hotspot/share/gc/z/zDriver.cpp +++ b/src/hotspot/share/gc/z/zDriver.cpp @@ -70,6 +70,10 @@ class VM_ZOperation : public VM_Operation { return false; } + virtual bool skip_thread_oop_barriers() const { + return true; + } + virtual bool do_operation() = 0; virtual bool doit_prologue() { @@ -218,6 +222,10 @@ class VM_ZVerify : public VM_Operation { return VMOp_ZVerify; } + virtual bool skip_thread_oop_barriers() const { + return true; + } + virtual void doit() { ZVerify::after_weak_processing(); } diff --git a/src/hotspot/share/gc/z/zForwarding.cpp b/src/hotspot/share/gc/z/zForwarding.cpp index f664233975a..139ff6443d8 100644 --- a/src/hotspot/share/gc/z/zForwarding.cpp +++ b/src/hotspot/share/gc/z/zForwarding.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,32 +23,7 @@ #include "precompiled.hpp" #include "gc/z/zForwarding.inline.hpp" -#include "gc/z/zPage.inline.hpp" -#include "memory/allocation.hpp" #include "utilities/debug.hpp" -#include "utilities/powerOfTwo.hpp" - -ZForwarding* ZForwarding::create(ZPage* page) { - // Allocate table for linear probing. The size of the table must be - // a power of two to allow for quick and inexpensive indexing/masking. - // The table is sized to have a load factor of 50%, i.e. sized to have - // double the number of entries actually inserted. - assert(page->live_objects() > 0, "Invalid value"); - const size_t nentries = round_up_power_of_2(page->live_objects() * 2); - return ::new (AttachedArray::alloc(nentries)) ZForwarding(page, nentries); -} - -void ZForwarding::destroy(ZForwarding* forwarding) { - AttachedArray::free(forwarding); -} - -ZForwarding::ZForwarding(ZPage* page, size_t nentries) : - _virtual(page->virtual_memory()), - _object_alignment_shift(page->object_alignment_shift()), - _entries(nentries), - _page(page), - _refcount(1), - _pinned(false) {} void ZForwarding::verify() const { guarantee(_refcount > 0, "Invalid refcount"); diff --git a/src/hotspot/share/gc/z/zForwarding.hpp b/src/hotspot/share/gc/z/zForwarding.hpp index b91e6f702c8..6a5bde71908 100644 --- a/src/hotspot/share/gc/z/zForwarding.hpp +++ b/src/hotspot/share/gc/z/zForwarding.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ #include "gc/z/zForwardingEntry.hpp" #include "gc/z/zVirtualMemory.hpp" +class ZForwardingAllocator; class ZPage; typedef size_t ZForwardingCursor; @@ -57,8 +58,8 @@ class ZForwarding { ZForwarding(ZPage* page, size_t nentries); public: - static ZForwarding* create(ZPage* page); - static void destroy(ZForwarding* forwarding); + static uint32_t nentries(const ZPage* page); + static ZForwarding* alloc(ZForwardingAllocator* allocator, ZPage* page); uintptr_t start() const; size_t size() const; diff --git a/src/hotspot/share/gc/z/zForwarding.inline.hpp b/src/hotspot/share/gc/z/zForwarding.inline.hpp index 5001f1ca930..80bb72002e1 100644 --- a/src/hotspot/share/gc/z/zForwarding.inline.hpp +++ b/src/hotspot/share/gc/z/zForwarding.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,12 +26,38 @@ #include "gc/z/zAttachedArray.inline.hpp" #include "gc/z/zForwarding.hpp" -#include "gc/z/zGlobals.hpp" +#include "gc/z/zForwardingAllocator.inline.hpp" #include "gc/z/zHash.inline.hpp" #include "gc/z/zHeap.hpp" +#include "gc/z/zPage.inline.hpp" #include "gc/z/zVirtualMemory.inline.hpp" #include "runtime/atomic.hpp" #include "utilities/debug.hpp" +#include "utilities/powerOfTwo.hpp" + +inline uint32_t ZForwarding::nentries(const ZPage* page) { + // The number returned by the function is used to size the hash table of + // forwarding entries for this page. This hash table uses linear probing. + // The size of the table must be a power of two to allow for quick and + // inexpensive indexing/masking. The table is also sized to have a load + // factor of 50%, i.e. sized to have double the number of entries actually + // inserted, to allow for good lookup/insert performance. + return round_up_power_of_2(page->live_objects() * 2); +} + +inline ZForwarding* ZForwarding::alloc(ZForwardingAllocator* allocator, ZPage* page) { + const size_t nentries = ZForwarding::nentries(page); + void* const addr = AttachedArray::alloc(allocator, nentries); + return ::new (addr) ZForwarding(page, nentries); +} + +inline ZForwarding::ZForwarding(ZPage* page, size_t nentries) : + _virtual(page->virtual_memory()), + _object_alignment_shift(page->object_alignment_shift()), + _entries(nentries), + _page(page), + _refcount(1), + _pinned(false) {} inline uintptr_t ZForwarding::start() const { return _virtual.start(); diff --git a/src/hotspot/share/gc/z/zForwardingAllocator.cpp b/src/hotspot/share/gc/z/zForwardingAllocator.cpp new file mode 100644 index 00000000000..6550259966b --- /dev/null +++ b/src/hotspot/share/gc/z/zForwardingAllocator.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "precompiled.hpp" +#include "gc/z/zForwardingAllocator.hpp" +#include "memory/allocation.inline.hpp" + +ZForwardingAllocator::ZForwardingAllocator() : + _start(NULL), + _end(NULL), + _top(NULL) {} + +ZForwardingAllocator::~ZForwardingAllocator() { + FREE_C_HEAP_ARRAY(char, _start); +} + +void ZForwardingAllocator::reset(size_t size) { + _start = _top = REALLOC_C_HEAP_ARRAY(char, _start, size, mtGC); + _end = _start + size; +} diff --git a/src/hotspot/share/gc/z/zForwardingAllocator.hpp b/src/hotspot/share/gc/z/zForwardingAllocator.hpp new file mode 100644 index 00000000000..8424fe6cd70 --- /dev/null +++ b/src/hotspot/share/gc/z/zForwardingAllocator.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef SHARE_GC_Z_ZFORWARDINGALLOCATOR_HPP +#define SHARE_GC_Z_ZFORWARDINGALLOCATOR_HPP + +#include "utilities/globalDefinitions.hpp" + +class ZForwardingAllocator { +private: + char* _start; + char* _end; + char* _top; + +public: + ZForwardingAllocator(); + ~ZForwardingAllocator(); + + void reset(size_t size); + size_t size() const; + bool is_full() const; + + void* alloc(size_t size); +}; + +#endif // SHARE_GC_Z_ZFORWARDINGALLOCATOR_HPP diff --git a/src/hotspot/share/gc/z/zForwardingAllocator.inline.hpp b/src/hotspot/share/gc/z/zForwardingAllocator.inline.hpp new file mode 100644 index 00000000000..eea8110b1a5 --- /dev/null +++ b/src/hotspot/share/gc/z/zForwardingAllocator.inline.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef SHARE_GC_Z_ZFORWARDINGALLOCATOR_INLINE_HPP +#define SHARE_GC_Z_ZFORWARDINGALLOCATOR_INLINE_HPP + +#include "gc/z/zForwardingAllocator.hpp" +#include "runtime/atomic.hpp" +#include "utilities/debug.hpp" + +inline size_t ZForwardingAllocator::size() const { + return _end - _start; +} + +inline bool ZForwardingAllocator::is_full() const { + return _top == _end; +} + +inline void* ZForwardingAllocator::alloc(size_t size) { + char* const addr = Atomic::fetch_and_add(&_top, size); + assert(addr + size <= _end, "Allocation should never fail"); + return addr; +} + +#endif // SHARE_GC_Z_ZFORWARDINGALLOCATOR_INLINE_HPP diff --git a/src/hotspot/share/gc/z/zForwardingTable.inline.hpp b/src/hotspot/share/gc/z/zForwardingTable.inline.hpp index 3561711c43b..be2fbe3ee3b 100644 --- a/src/hotspot/share/gc/z/zForwardingTable.inline.hpp +++ b/src/hotspot/share/gc/z/zForwardingTable.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,12 +25,34 @@ #define SHARE_GC_Z_ZFORWARDINGTABLE_INLINE_HPP #include "gc/z/zAddress.inline.hpp" +#include "gc/z/zForwarding.inline.hpp" #include "gc/z/zForwardingTable.hpp" +#include "gc/z/zGlobals.hpp" #include "gc/z/zGranuleMap.inline.hpp" +#include "utilities/debug.hpp" + +inline ZForwardingTable::ZForwardingTable() : + _map(ZAddressOffsetMax) {} inline ZForwarding* ZForwardingTable::get(uintptr_t addr) const { assert(!ZAddress::is_null(addr), "Invalid address"); return _map.get(ZAddress::offset(addr)); } +inline void ZForwardingTable::insert(ZForwarding* forwarding) { + const uintptr_t offset = forwarding->start(); + const size_t size = forwarding->size(); + + assert(_map.get(offset) == NULL, "Invalid entry"); + _map.put(offset, size, forwarding); +} + +inline void ZForwardingTable::remove(ZForwarding* forwarding) { + const uintptr_t offset = forwarding->start(); + const size_t size = forwarding->size(); + + assert(_map.get(offset) == forwarding, "Invalid entry"); + _map.put(offset, size, NULL); +} + #endif // SHARE_GC_Z_ZFORWARDINGTABLE_INLINE_HPP diff --git a/src/hotspot/share/gc/z/zGlobals.cpp b/src/hotspot/share/gc/z/zGlobals.cpp index 0cb9c598423..28200e23b6e 100644 --- a/src/hotspot/share/gc/z/zGlobals.cpp +++ b/src/hotspot/share/gc/z/zGlobals.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,6 +42,13 @@ uintptr_t ZAddressGoodMask; uintptr_t ZAddressBadMask; uintptr_t ZAddressWeakBadMask; +static uint32_t* ZAddressCalculateBadMaskHighOrderBitsAddr() { + const uintptr_t addr = reinterpret_cast(&ZAddressBadMask); + return reinterpret_cast(addr + ZAddressBadMaskHighOrderBitsOffset); +} + +uint32_t* ZAddressBadMaskHighOrderBitsAddr = ZAddressCalculateBadMaskHighOrderBitsAddr(); + size_t ZAddressOffsetBits; uintptr_t ZAddressOffsetMask; size_t ZAddressOffsetMax; diff --git a/src/hotspot/share/gc/z/zGlobals.hpp b/src/hotspot/share/gc/z/zGlobals.hpp index 690227e3a8d..2721e4ec3e8 100644 --- a/src/hotspot/share/gc/z/zGlobals.hpp +++ b/src/hotspot/share/gc/z/zGlobals.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -94,6 +94,13 @@ extern uintptr_t ZAddressGoodMask; extern uintptr_t ZAddressBadMask; extern uintptr_t ZAddressWeakBadMask; +// The bad mask is 64 bit. Its high order 32 bits contain all possible value combinations +// that this mask will have. Therefore, the memory where the 32 high order bits are stored, +// can be used as a 32 bit GC epoch counter, that has a different bit pattern every time +// the bad mask is flipped. This provides a pointer to said 32 bits. +extern uint32_t* ZAddressBadMaskHighOrderBitsAddr; +const int ZAddressBadMaskHighOrderBitsOffset = LITTLE_ENDIAN_ONLY(4) BIG_ENDIAN_ONLY(0); + // Pointer part of address extern size_t ZAddressOffsetBits; const size_t ZAddressOffsetShift = 0; @@ -112,9 +119,6 @@ extern uintptr_t ZAddressMetadataMarked1; extern uintptr_t ZAddressMetadataRemapped; extern uintptr_t ZAddressMetadataFinalizable; -// NMethod entry barrier -const size_t ZNMethodDisarmedOffset = ZPlatformNMethodDisarmedOffset; - // Cache line size const size_t ZCacheLineSize = ZPlatformCacheLineSize; #define ZCACHE_ALIGNED ATTRIBUTE_ALIGNED(ZCacheLineSize) diff --git a/src/hotspot/share/gc/z/zGranuleMap.hpp b/src/hotspot/share/gc/z/zGranuleMap.hpp index dc21c7bae5b..f2cb317c8de 100644 --- a/src/hotspot/share/gc/z/zGranuleMap.hpp +++ b/src/hotspot/share/gc/z/zGranuleMap.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,15 +24,13 @@ #ifndef SHARE_GC_Z_ZGRANULEMAP_HPP #define SHARE_GC_Z_ZGRANULEMAP_HPP +#include "gc/z/zArray.hpp" #include "memory/allocation.hpp" -template -class ZGranuleMapIterator; - template class ZGranuleMap { friend class VMStructs; - friend class ZGranuleMapIterator; + template friend class ZGranuleMapIterator; private: const size_t _size; @@ -47,19 +45,15 @@ class ZGranuleMap { T get(uintptr_t offset) const; void put(uintptr_t offset, T value); void put(uintptr_t offset, size_t size, T value); + + T get_acquire(uintptr_t offset) const; + void release_put(uintptr_t offset, T value); }; template -class ZGranuleMapIterator : public StackObj { +class ZGranuleMapIterator : public ZArrayIteratorImpl { public: - const ZGranuleMap* const _map; - size_t _next; - -public: - ZGranuleMapIterator(const ZGranuleMap* map); - - bool next(T* value); - bool next(T** value); + ZGranuleMapIterator(const ZGranuleMap* granule_map); }; #endif // SHARE_GC_Z_ZGRANULEMAP_HPP diff --git a/src/hotspot/share/gc/z/zGranuleMap.inline.hpp b/src/hotspot/share/gc/z/zGranuleMap.inline.hpp index 802378bc1d8..33712fce37f 100644 --- a/src/hotspot/share/gc/z/zGranuleMap.inline.hpp +++ b/src/hotspot/share/gc/z/zGranuleMap.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,9 +24,11 @@ #ifndef SHARE_GC_Z_ZGRANULEMAP_INLINE_HPP #define SHARE_GC_Z_ZGRANULEMAP_INLINE_HPP +#include "gc/z/zArray.inline.hpp" #include "gc/z/zGlobals.hpp" #include "gc/z/zGranuleMap.hpp" #include "memory/allocation.inline.hpp" +#include "runtime/atomic.hpp" #include "utilities/align.hpp" #include "utilities/debug.hpp" @@ -46,7 +48,6 @@ template inline size_t ZGranuleMap::index_for_offset(uintptr_t offset) const { const size_t index = offset >> ZGranuleSizeShift; assert(index < _size, "Invalid index"); - return index; } @@ -74,30 +75,19 @@ inline void ZGranuleMap::put(uintptr_t offset, size_t size, T value) { } template -inline ZGranuleMapIterator::ZGranuleMapIterator(const ZGranuleMap* map) : - _map(map), - _next(0) {} +inline T ZGranuleMap::get_acquire(uintptr_t offset) const { + const size_t index = index_for_offset(offset); + return Atomic::load_acquire(_map + index); +} template -inline bool ZGranuleMapIterator::next(T* value) { - if (_next < _map->_size) { - *value = _map->_map[_next++]; - return true; - } - - // End of map - return false; +inline void ZGranuleMap::release_put(uintptr_t offset, T value) { + const size_t index = index_for_offset(offset); + Atomic::release_store(_map + index, value); } template -inline bool ZGranuleMapIterator::next(T** value) { - if (_next < _map->_size) { - *value = _map->_map + _next++; - return true; - } - - // End of map - return false; -} +inline ZGranuleMapIterator::ZGranuleMapIterator(const ZGranuleMap* granule_map) : + ZArrayIteratorImpl(granule_map->_map, granule_map->_size) {} #endif // SHARE_GC_Z_ZGRANULEMAP_INLINE_HPP diff --git a/src/hotspot/share/gc/z/zHeap.cpp b/src/hotspot/share/gc/z/zHeap.cpp index 3f6812aaa34..86778981947 100644 --- a/src/hotspot/share/gc/z/zHeap.cpp +++ b/src/hotspot/share/gc/z/zHeap.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "gc/shared/locationPrinter.hpp" #include "gc/z/zAddress.inline.hpp" +#include "gc/z/zArray.inline.hpp" #include "gc/z/zGlobals.hpp" #include "gc/z/zHeap.inline.hpp" #include "gc/z/zHeapIterator.hpp" @@ -35,7 +36,6 @@ #include "gc/z/zRelocationSetSelector.inline.hpp" #include "gc/z/zResurrection.hpp" #include "gc/z/zStat.hpp" -#include "gc/z/zTask.hpp" #include "gc/z/zThread.inline.hpp" #include "gc/z/zVerify.hpp" #include "gc/z/zWorkers.inline.hpp" @@ -66,7 +66,7 @@ ZHeap::ZHeap() : _reference_processor(&_workers), _weak_roots_processor(&_workers), _relocate(&_workers), - _relocation_set(), + _relocation_set(&_workers), _unload(&_workers), _serviceability(min_capacity(), max_capacity()) { // Install global heap instance @@ -221,6 +221,17 @@ void ZHeap::free_page(ZPage* page, bool reclaimed) { _page_allocator.free_page(page, reclaimed); } +void ZHeap::free_pages(const ZArray* pages, bool reclaimed) { + // Remove page table entries + ZArrayIterator iter(pages); + for (ZPage* page; iter.next(&page);) { + _page_table.remove(page); + } + + // Free pages + _page_allocator.free_pages(pages, reclaimed); +} + void ZHeap::flip_to_marked() { ZVerifyViewsFlip flip(&_page_allocator); ZAddress::flip_to_marked(); @@ -350,6 +361,16 @@ void ZHeap::process_non_strong_references() { _reference_processor.enqueue_references(); } +void ZHeap::free_garbage_pages(ZRelocationSetSelector* selector, int bulk) { + // Freeing garbage pages in bulk is an optimization to avoid grabbing + // the page allocator lock, and trying to satisfy stalled allocations + // too frequently. + if (selector->should_free_garbage_pages(bulk)) { + free_pages(selector->garbage_pages(), true /* reclaimed */); + selector->clear_garbage_pages(); + } +} + void ZHeap::select_relocation_set() { // Do not allow pages to be deleted _page_allocator.enable_deferred_delete(); @@ -370,16 +391,22 @@ void ZHeap::select_relocation_set() { // Register garbage page selector.register_garbage_page(page); - // Reclaim page immediately - free_page(page, true /* reclaimed */); + // Reclaim garbage pages in bulk + free_garbage_pages(&selector, 64 /* bulk */); } } + // Reclaim remaining garbage pages + free_garbage_pages(&selector, 0 /* bulk */); + // Allow pages to be deleted _page_allocator.disable_deferred_delete(); - // Select pages to relocate - selector.select(&_relocation_set); + // Select relocation set + selector.select(); + + // Install relocation set + _relocation_set.install(&selector); // Setup forwarding table ZRelocationSetIterator rs_iter(&_relocation_set); @@ -436,9 +463,13 @@ void ZHeap::relocate() { void ZHeap::object_iterate(ObjectClosure* cl, bool visit_weaks) { assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint"); + ZHeapIterator iter(1 /* nworkers */, visit_weaks); + iter.object_iterate(cl, 0 /* worker_id */); +} - ZHeapIterator iter; - iter.objects_do(cl, visit_weaks); +ParallelObjectIterator* ZHeap::parallel_object_iterator(uint nworkers, bool visit_weaks) { + assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint"); + return new ZHeapIterator(nworkers, visit_weaks); } void ZHeap::pages_do(ZPageClosure* cl) { diff --git a/src/hotspot/share/gc/z/zHeap.hpp b/src/hotspot/share/gc/z/zHeap.hpp index f8505274ebb..47bebc745f3 100644 --- a/src/hotspot/share/gc/z/zHeap.hpp +++ b/src/hotspot/share/gc/z/zHeap.hpp @@ -25,10 +25,10 @@ #define SHARE_GC_Z_ZHEAP_HPP #include "gc/z/zAllocationFlags.hpp" +#include "gc/z/zArray.hpp" #include "gc/z/zForwardingTable.hpp" #include "gc/z/zMark.hpp" #include "gc/z/zObjectAllocator.hpp" -#include "gc/z/zPage.hpp" #include "gc/z/zPageAllocator.hpp" #include "gc/z/zPageTable.hpp" #include "gc/z/zReferenceProcessor.hpp" @@ -40,6 +40,8 @@ #include "gc/z/zWorkers.hpp" class ThreadClosure; +class ZPage; +class ZRelocationSetSelector; class ZHeap { friend class VMStructs; @@ -63,6 +65,8 @@ class ZHeap { void flip_to_marked(); void flip_to_remapped(); + void free_garbage_pages(ZRelocationSetSelector* selector, int bulk); + void out_of_memory(); public: @@ -110,6 +114,7 @@ class ZHeap { ZPage* alloc_page(uint8_t type, size_t size, ZAllocationFlags flags); void undo_alloc_page(ZPage* page); void free_page(ZPage* page, bool reclaimed); + void free_pages(const ZArray* pages, bool reclaimed); // Object allocation uintptr_t alloc_tlab(size_t size); @@ -141,6 +146,7 @@ class ZHeap { // Iteration void object_iterate(ObjectClosure* cl, bool visit_weaks); + ParallelObjectIterator* parallel_object_iterator(uint nworkers, bool visit_weaks); void pages_do(ZPageClosure* cl); // Serviceability diff --git a/src/hotspot/share/gc/z/zHeap.inline.hpp b/src/hotspot/share/gc/z/zHeap.inline.hpp index e506f29ce0b..b1cc66cfec8 100644 --- a/src/hotspot/share/gc/z/zHeap.inline.hpp +++ b/src/hotspot/share/gc/z/zHeap.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ #include "gc/z/zHash.inline.hpp" #include "gc/z/zHeap.hpp" #include "gc/z/zMark.inline.hpp" -#include "gc/z/zOop.inline.hpp" #include "gc/z/zPage.inline.hpp" #include "gc/z/zPageTable.inline.hpp" #include "utilities/debug.hpp" diff --git a/src/hotspot/share/gc/z/zHeapIterator.cpp b/src/hotspot/share/gc/z/zHeapIterator.cpp index 2b7fa0fdbde..4434e53fbb1 100644 --- a/src/hotspot/share/gc/z/zHeapIterator.cpp +++ b/src/hotspot/share/gc/z/zHeapIterator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,61 +23,97 @@ #include "precompiled.hpp" #include "classfile/classLoaderData.hpp" -#include "classfile/classLoaderDataGraph.hpp" +#include "gc/shared/barrierSetNMethod.hpp" +#include "gc/shared/taskqueue.inline.hpp" #include "gc/z/zAddress.inline.hpp" -#include "gc/z/zBarrier.inline.hpp" #include "gc/z/zGlobals.hpp" #include "gc/z/zGranuleMap.inline.hpp" #include "gc/z/zHeapIterator.hpp" +#include "gc/z/zLock.inline.hpp" +#include "gc/z/zNMethod.hpp" #include "gc/z/zOop.inline.hpp" -#include "gc/z/zRootsIterator.hpp" -#include "gc/z/zStat.hpp" #include "memory/iterator.inline.hpp" #include "utilities/bitMap.inline.hpp" -#include "utilities/stack.inline.hpp" class ZHeapIteratorBitMap : public CHeapObj { private: - CHeapBitMap _map; + CHeapBitMap _bitmap; public: ZHeapIteratorBitMap(size_t size_in_bits) : - _map(size_in_bits) {} + _bitmap(size_in_bits, mtGC) {} bool try_set_bit(size_t index) { - if (_map.at(index)) { - return false; + return _bitmap.par_set_bit(index); + } +}; + +class ZHeapIteratorContext { +private: + ZHeapIterator* const _iter; + ZHeapIteratorQueue* const _queue; + ZHeapIteratorArrayQueue* const _array_queue; + const uint _worker_id; + ZStatTimerDisable _timer_disable; + +public: + ZHeapIteratorContext(ZHeapIterator* iter, uint worker_id) : + _iter(iter), + _queue(_iter->_queues.queue(worker_id)), + _array_queue(_iter->_array_queues.queue(worker_id)), + _worker_id(worker_id) {} + + void mark_and_push(oop obj) const { + if (_iter->mark_object(obj)) { + _queue->push(obj); } + } + + void push_array(const ObjArrayTask& array) const { + _array_queue->push(array); + } + + bool pop(oop& obj) const { + return _queue->pop_overflow(obj) || _queue->pop_local(obj); + } + + bool pop_array(ObjArrayTask& array) const { + return _array_queue->pop_overflow(array) || _array_queue->pop_local(array); + } - _map.set_bit(index); - return true; + bool steal(oop& obj) const { + return _iter->_queues.steal(_worker_id, obj); + } + + bool steal_array(ObjArrayTask& array) const { + return _iter->_array_queues.steal(_worker_id, array); + } + + bool is_drained() const { + return _queue->is_empty() && _array_queue->is_empty(); } }; -template -class ZHeapIteratorRootOopClosure : public ZRootsIteratorClosure { +template +class ZHeapIteratorRootOopClosure : public OopClosure { private: - ZHeapIterator* const _iter; + const ZHeapIteratorContext& _context; oop load_oop(oop* p) { if (Weak) { return NativeAccess::oop_load(p); } - if (Concurrent) { - return NativeAccess::oop_load(p); - } - - return RawAccess<>::oop_load(p); + return NativeAccess::oop_load(p); } public: - ZHeapIteratorRootOopClosure(ZHeapIterator* iter) : - _iter(iter) {} + ZHeapIteratorRootOopClosure(const ZHeapIteratorContext& context) : + _context(context) {} virtual void do_oop(oop* p) { const oop obj = load_oop(p); - _iter->push(obj); + _context.mark_and_push(obj); } virtual void do_oop(narrowOop* p) { @@ -88,8 +124,8 @@ class ZHeapIteratorRootOopClosure : public ZRootsIteratorClosure { template class ZHeapIteratorOopClosure : public ClaimMetadataVisitingOopIterateClosure { private: - ZHeapIterator* const _iter; - const oop _base; + const ZHeapIteratorContext& _context; + const oop _base; oop load_oop(oop* p) { if (VisitReferents) { @@ -100,9 +136,9 @@ class ZHeapIteratorOopClosure : public ClaimMetadataVisitingOopIterateClosure { } public: - ZHeapIteratorOopClosure(ZHeapIterator* iter, oop base) : + ZHeapIteratorOopClosure(const ZHeapIteratorContext& context, oop base) : ClaimMetadataVisitingOopIterateClosure(ClassLoaderData::_claim_other), - _iter(iter), + _context(context), _base(base) {} virtual ReferenceIterationMode reference_iteration_mode() { @@ -111,30 +147,57 @@ class ZHeapIteratorOopClosure : public ClaimMetadataVisitingOopIterateClosure { virtual void do_oop(oop* p) { const oop obj = load_oop(p); - _iter->push(obj); + _context.mark_and_push(obj); } virtual void do_oop(narrowOop* p) { ShouldNotReachHere(); } +}; -#ifdef ASSERT - virtual bool should_verify_oops() { - return false; +ZHeapIterator::ZHeapIterator(uint nworkers, bool visit_weaks) : + _visit_weaks(visit_weaks), + _timer_disable(), + _bitmaps(ZAddressOffsetMax), + _bitmaps_lock(), + _queues(nworkers), + _array_queues(nworkers), + _concurrent_roots(ClassLoaderData::_claim_other), + _weak_roots(), + _concurrent_weak_roots(), + _terminator(nworkers, &_queues) { + + // Create queues + for (uint i = 0; i < _queues.size(); i++) { + ZHeapIteratorQueue* const queue = new ZHeapIteratorQueue(); + queue->initialize(); + _queues.register_queue(i, queue); } -#endif -}; -ZHeapIterator::ZHeapIterator() : - _visit_stack(), - _visit_map(ZAddressOffsetMax) {} + // Create array queues + for (uint i = 0; i < _array_queues.size(); i++) { + ZHeapIteratorArrayQueue* const array_queue = new ZHeapIteratorArrayQueue(); + array_queue->initialize(); + _array_queues.register_queue(i, array_queue); + } +} ZHeapIterator::~ZHeapIterator() { - ZVisitMapIterator iter(&_visit_map); - for (ZHeapIteratorBitMap* map; iter.next(&map);) { - delete map; + // Destroy bitmaps + ZHeapIteratorBitMapsIterator iter(&_bitmaps); + for (ZHeapIteratorBitMap* bitmap; iter.next(&bitmap);) { + delete bitmap; + } + + // Destroy array queues + for (uint i = 0; i < _array_queues.size(); i++) { + delete _array_queues.queue(i); + } + + // Destroy queues + for (uint i = 0; i < _queues.size(); i++) { + delete _queues.queue(i); } - ClassLoaderDataGraph::clear_claimed_marks(ClassLoaderData::_claim_other); } static size_t object_index_max() { @@ -148,75 +211,191 @@ static size_t object_index(oop obj) { return (offset & mask) >> ZObjectAlignmentSmallShift; } -ZHeapIteratorBitMap* ZHeapIterator::object_map(oop obj) { +ZHeapIteratorBitMap* ZHeapIterator::object_bitmap(oop obj) { const uintptr_t offset = ZAddress::offset(ZOop::to_address(obj)); - ZHeapIteratorBitMap* map = _visit_map.get(offset); - if (map == NULL) { - map = new ZHeapIteratorBitMap(object_index_max()); - _visit_map.put(offset, map); + ZHeapIteratorBitMap* bitmap = _bitmaps.get_acquire(offset); + if (bitmap == NULL) { + ZLocker locker(&_bitmaps_lock); + bitmap = _bitmaps.get(offset); + if (bitmap == NULL) { + // Install new bitmap + bitmap = new ZHeapIteratorBitMap(object_index_max()); + _bitmaps.release_put(offset, bitmap); + } } - return map; + return bitmap; } -void ZHeapIterator::push(oop obj) { +bool ZHeapIterator::mark_object(oop obj) { if (obj == NULL) { - // Ignore - return; + return false; } - ZHeapIteratorBitMap* const map = object_map(obj); + ZHeapIteratorBitMap* const bitmap = object_bitmap(obj); const size_t index = object_index(obj); - if (!map->try_set_bit(index)) { - // Already pushed - return; + return bitmap->try_set_bit(index); +} + +typedef ClaimingCLDToOopClosure ZHeapIteratorCLDCLosure; + +class ZHeapIteratorNMethodClosure : public NMethodClosure { +private: + OopClosure* const _cl; + BarrierSetNMethod* const _bs_nm; + +public: + ZHeapIteratorNMethodClosure(OopClosure* cl) : + _cl(cl), + _bs_nm(BarrierSet::barrier_set()->barrier_set_nmethod()) {} + + virtual void do_nmethod(nmethod* nm) { + // If ClassUnloading is turned off, all nmethods are considered strong, + // not only those on the call stacks. The heap iteration might happen + // before the concurrent processign of the code cache, make sure that + // all nmethods have been processed before visiting the oops. + _bs_nm->nmethod_entry_barrier(nm); + + ZNMethod::nmethod_oops_do(nm, _cl); } +}; + +class ZHeapIteratorThreadClosure : public ThreadClosure { +private: + OopClosure* const _cl; + CodeBlobToNMethodClosure _cb_cl; + +public: + ZHeapIteratorThreadClosure(OopClosure* cl, NMethodClosure* nm_cl) : + _cl(cl), + _cb_cl(nm_cl) {} + + void do_thread(Thread* thread) { + thread->oops_do(_cl, &_cb_cl); + } +}; + +void ZHeapIterator::push_strong_roots(const ZHeapIteratorContext& context) { + ZHeapIteratorRootOopClosure cl(context); + ZHeapIteratorCLDCLosure cld_cl(&cl); + ZHeapIteratorNMethodClosure nm_cl(&cl); + ZHeapIteratorThreadClosure thread_cl(&cl, &nm_cl); + + _concurrent_roots.apply(&cl, + &cld_cl, + &thread_cl, + &nm_cl); +} + +void ZHeapIterator::push_weak_roots(const ZHeapIteratorContext& context) { + ZHeapIteratorRootOopClosure cl(context); + _concurrent_weak_roots.apply(&cl); - // Push - _visit_stack.push(obj); + AlwaysTrueClosure is_alive; + _weak_roots.apply(&is_alive, &cl); } -template -void ZHeapIterator::push_roots() { - ZHeapIteratorRootOopClosure cl(this); - RootsIterator roots; - roots.oops_do(&cl); +template +void ZHeapIterator::push_roots(const ZHeapIteratorContext& context) { + push_strong_roots(context); + if (VisitWeaks) { + push_weak_roots(context); + } } template -void ZHeapIterator::push_fields(oop obj) { - ZHeapIteratorOopClosure cl(this, obj); +void ZHeapIterator::follow_object(const ZHeapIteratorContext& context, oop obj) { + ZHeapIteratorOopClosure cl(context, obj); obj->oop_iterate(&cl); } +void ZHeapIterator::follow_array(const ZHeapIteratorContext& context, oop obj) { + // Follow klass + ZHeapIteratorOopClosure cl(context, obj); + cl.do_klass(obj->klass()); + + // Push array chunk + context.push_array(ObjArrayTask(obj, 0 /* index */)); +} + +void ZHeapIterator::follow_array_chunk(const ZHeapIteratorContext& context, const ObjArrayTask& array) { + const objArrayOop obj = objArrayOop(array.obj()); + const int length = obj->length(); + const int start = array.index(); + const int stride = MIN2(length - start, ObjArrayMarkingStride); + const int end = start + stride; + + // Push remaining array chunk first + if (end < length) { + context.push_array(ObjArrayTask(obj, end)); + } + + // Follow array chunk + ZHeapIteratorOopClosure cl(context, obj); + obj->oop_iterate_range(&cl, start, end); +} + template -void ZHeapIterator::objects_do(ObjectClosure* cl) { - ZStatTimerDisable disable; +void ZHeapIterator::visit_and_follow(const ZHeapIteratorContext& context, ObjectClosure* cl, oop obj) { + // Visit + cl->do_object(obj); - // Push roots to visit - push_roots(); - push_roots(); - if (VisitWeaks) { - push_roots(); - push_roots(); + // Follow + if (obj->is_objArray()) { + follow_array(context, obj); + } else { + follow_object(context, obj); } +} - // Drain stack - while (!_visit_stack.is_empty()) { - const oop obj = _visit_stack.pop(); +template +void ZHeapIterator::drain(const ZHeapIteratorContext& context, ObjectClosure* cl) { + ObjArrayTask array; + oop obj; - // Visit object - cl->do_object(obj); + do { + while (context.pop(obj)) { + visit_and_follow(context, cl, obj); + } - // Push fields to visit - push_fields(obj); + if (context.pop_array(array)) { + follow_array_chunk(context, array); + } + } while (!context.is_drained()); +} + +template +void ZHeapIterator::steal(const ZHeapIteratorContext& context, ObjectClosure* cl) { + ObjArrayTask array; + oop obj; + + if (context.steal_array(array)) { + follow_array_chunk(context, array); + } else if (context.steal(obj)) { + visit_and_follow(context, cl, obj); } } -void ZHeapIterator::objects_do(ObjectClosure* cl, bool visit_weaks) { - if (visit_weaks) { - objects_do(cl); +template +void ZHeapIterator::drain_and_steal(const ZHeapIteratorContext& context, ObjectClosure* cl) { + do { + drain(context, cl); + steal(context, cl); + } while (!context.is_drained() || !_terminator.offer_termination()); +} + +template +void ZHeapIterator::object_iterate_inner(const ZHeapIteratorContext& context, ObjectClosure* object_cl) { + push_roots(context); + drain_and_steal(context, object_cl); +} + +void ZHeapIterator::object_iterate(ObjectClosure* cl, uint worker_id) { + ZHeapIteratorContext context(this, worker_id); + + if (_visit_weaks) { + object_iterate_inner(context, cl); } else { - objects_do(cl); + object_iterate_inner(context, cl); } } diff --git a/src/hotspot/share/gc/z/zHeapIterator.hpp b/src/hotspot/share/gc/z/zHeapIterator.hpp index e6ab68fdc44..002e9ab949b 100644 --- a/src/hotspot/share/gc/z/zHeapIterator.hpp +++ b/src/hotspot/share/gc/z/zHeapIterator.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,37 +24,75 @@ #ifndef SHARE_GC_Z_ZHEAPITERATOR_HPP #define SHARE_GC_Z_ZHEAPITERATOR_HPP +#include "gc/shared/collectedHeap.hpp" +#include "gc/shared/taskTerminator.hpp" +#include "gc/shared/taskqueue.hpp" #include "gc/z/zGranuleMap.hpp" -#include "memory/allocation.hpp" -#include "utilities/stack.hpp" +#include "gc/z/zLock.hpp" +#include "gc/z/zRootsIterator.hpp" +#include "gc/z/zStat.hpp" -class ObjectClosure; class ZHeapIteratorBitMap; +class ZHeapIteratorContext; -class ZHeapIterator : public StackObj { - template friend class ZHeapIteratorRootOopClosure; - template friend class ZHeapIteratorOopClosure; +using ZHeapIteratorBitMaps = ZGranuleMap; +using ZHeapIteratorBitMapsIterator = ZGranuleMapIterator; +using ZHeapIteratorQueue = OverflowTaskQueue; +using ZHeapIteratorQueues = GenericTaskQueueSet; +using ZHeapIteratorArrayQueue = OverflowTaskQueue; +using ZHeapIteratorArrayQueues = GenericTaskQueueSet; + +class ZHeapIterator : public ParallelObjectIterator { + friend class ZHeapIteratorContext; private: - typedef ZGranuleMap ZVisitMap; - typedef ZGranuleMapIterator ZVisitMapIterator; - typedef Stack ZVisitStack; + const bool _visit_weaks; + ZStatTimerDisable _timer_disable; + ZHeapIteratorBitMaps _bitmaps; + ZLock _bitmaps_lock; + ZHeapIteratorQueues _queues; + ZHeapIteratorArrayQueues _array_queues; + ZConcurrentRootsIterator _concurrent_roots; + ZWeakRootsIterator _weak_roots; + ZConcurrentWeakRootsIterator _concurrent_weak_roots; + TaskTerminator _terminator; + + ZHeapIteratorBitMap* object_bitmap(oop obj); + + bool mark_object(oop obj); + + void push_strong_roots(const ZHeapIteratorContext& context); + void push_weak_roots(const ZHeapIteratorContext& context); + + template + void push_roots(const ZHeapIteratorContext& context); + + template + void follow_object(const ZHeapIteratorContext& context, oop obj); + + void follow_array(const ZHeapIteratorContext& context, oop obj); + void follow_array_chunk(const ZHeapIteratorContext& context, const ObjArrayTask& array); + + template + void visit_and_follow(const ZHeapIteratorContext& context, ObjectClosure* cl, oop obj); + + template + void drain(const ZHeapIteratorContext& context, ObjectClosure* cl); - ZVisitStack _visit_stack; - ZVisitMap _visit_map; + template + void steal(const ZHeapIteratorContext& context, ObjectClosure* cl); - ZHeapIteratorBitMap* object_map(oop obj); - void push(oop obj); + template + void drain_and_steal(const ZHeapIteratorContext& context, ObjectClosure* cl); - template void push_roots(); - template void push_fields(oop obj); - template void objects_do(ObjectClosure* cl); + template + void object_iterate_inner(const ZHeapIteratorContext& context, ObjectClosure* cl); public: - ZHeapIterator(); - ~ZHeapIterator(); + ZHeapIterator(uint nworkers, bool visit_weaks); + virtual ~ZHeapIterator(); - void objects_do(ObjectClosure* cl, bool visit_weaks); + virtual void object_iterate(ObjectClosure* cl, uint worker_id); }; #endif // SHARE_GC_Z_ZHEAPITERATOR_HPP diff --git a/src/hotspot/share/gc/z/zList.hpp b/src/hotspot/share/gc/z/zList.hpp index dcb20f97cbd..1cbc0d8e3eb 100644 --- a/src/hotspot/share/gc/z/zList.hpp +++ b/src/hotspot/share/gc/z/zList.hpp @@ -111,26 +111,8 @@ class ZListRemoveIteratorImpl : public StackObj { bool next(T** elem); }; -// Iterator types -#define ZLIST_FORWARD true -#define ZLIST_REVERSE false - -template -class ZListIterator : public ZListIteratorImpl { -public: - ZListIterator(const ZList* list); -}; - -template -class ZListReverseIterator : public ZListIteratorImpl { -public: - ZListReverseIterator(const ZList* list); -}; - -template -class ZListRemoveIterator : public ZListRemoveIteratorImpl { -public: - ZListRemoveIterator(ZList* list); -}; +template using ZListIterator = ZListIteratorImpl; +template using ZListReverseIterator = ZListIteratorImpl; +template using ZListRemoveIterator = ZListRemoveIteratorImpl; #endif // SHARE_GC_Z_ZLIST_HPP diff --git a/src/hotspot/share/gc/z/zList.inline.hpp b/src/hotspot/share/gc/z/zList.inline.hpp index 660d868f5a6..a69711e4a4a 100644 --- a/src/hotspot/share/gc/z/zList.inline.hpp +++ b/src/hotspot/share/gc/z/zList.inline.hpp @@ -232,16 +232,4 @@ inline bool ZListRemoveIteratorImpl::next(T** elem) { return *elem != NULL; } -template -inline ZListIterator::ZListIterator(const ZList* list) : - ZListIteratorImpl(list) {} - -template -inline ZListReverseIterator::ZListReverseIterator(const ZList* list) : - ZListIteratorImpl(list) {} - -template -inline ZListRemoveIterator::ZListRemoveIterator(ZList* list) : - ZListRemoveIteratorImpl(list) {} - #endif // SHARE_GC_Z_ZLIST_INLINE_HPP diff --git a/src/hotspot/share/gc/z/zMark.cpp b/src/hotspot/share/gc/z/zMark.cpp index a08cc54d5fb..d4c4bbdfde5 100644 --- a/src/hotspot/share/gc/z/zMark.cpp +++ b/src/hotspot/share/gc/z/zMark.cpp @@ -23,15 +23,20 @@ #include "precompiled.hpp" #include "classfile/classLoaderDataGraph.hpp" +#include "code/nmethod.hpp" +#include "gc/shared/suspendibleThreadSet.hpp" #include "gc/z/zBarrier.inline.hpp" +#include "gc/z/zLock.inline.hpp" #include "gc/z/zMark.inline.hpp" #include "gc/z/zMarkCache.inline.hpp" #include "gc/z/zMarkStack.inline.hpp" #include "gc/z/zMarkTerminate.inline.hpp" +#include "gc/z/zNMethod.hpp" #include "gc/z/zOopClosures.inline.hpp" #include "gc/z/zPage.hpp" #include "gc/z/zPageTable.inline.hpp" #include "gc/z/zRootsIterator.hpp" +#include "gc/z/zStackWatermark.hpp" #include "gc/z/zStat.hpp" #include "gc/z/zTask.hpp" #include "gc/z/zThread.inline.hpp" @@ -46,6 +51,8 @@ #include "runtime/handshake.hpp" #include "runtime/prefetch.inline.hpp" #include "runtime/safepointMechanism.hpp" +#include "runtime/stackWatermark.hpp" +#include "runtime/stackWatermarkSet.inline.hpp" #include "runtime/thread.hpp" #include "utilities/align.hpp" #include "utilities/globalDefinitions.hpp" @@ -120,63 +127,6 @@ void ZMark::prepare_mark() { } } -class ZMarkRootsIteratorClosure : public ZRootsIteratorClosure { -public: - ZMarkRootsIteratorClosure() { - ZThreadLocalAllocBuffer::reset_statistics(); - } - - ~ZMarkRootsIteratorClosure() { - ZThreadLocalAllocBuffer::publish_statistics(); - } - - virtual void do_thread(Thread* thread) { - // Update thread local address bad mask - ZThreadLocalData::set_address_bad_mask(thread, ZAddressBadMask); - - // Mark invisible root - ZThreadLocalData::do_invisible_root(thread, ZBarrier::mark_barrier_on_invisible_root_oop_field); - - // Retire TLAB - ZThreadLocalAllocBuffer::retire(thread); - } - - virtual bool should_disarm_nmethods() const { - return true; - } - - virtual void do_oop(oop* p) { - ZBarrier::mark_barrier_on_root_oop_field(p); - } - - virtual void do_oop(narrowOop* p) { - ShouldNotReachHere(); - } -}; - -class ZMarkRootsTask : public ZTask { -private: - ZMark* const _mark; - ZRootsIterator _roots; - ZMarkRootsIteratorClosure _cl; - -public: - ZMarkRootsTask(ZMark* mark) : - ZTask("ZMarkRootsTask"), - _mark(mark), - _roots(false /* visit_jvmti_weak_export */) {} - - virtual void work() { - _roots.oops_do(&_cl); - - // Flush and free worker stacks. Needed here since - // the set of workers executing during root scanning - // can be different from the set of workers executing - // during mark. - _mark->flush_and_free(); - } -}; - void ZMark::start() { // Verification if (ZVerifyMarking) { @@ -185,10 +135,6 @@ void ZMark::start() { // Prepare for concurrent mark prepare_mark(); - - // Mark roots - ZMarkRootsTask task(this); - _workers->run_parallel(&task); } void ZMark::prepare_work() { @@ -629,8 +575,7 @@ void ZMark::work(uint64_t timeout_in_micros) { stacks->free(&_allocator); } -class ZMarkConcurrentRootsIteratorClosure : public ZRootsIteratorClosure { -public: +class ZMarkOopClosure : public OopClosure { virtual void do_oop(oop* p) { ZBarrier::mark_barrier_on_oop_field(p, false /* finalizable */); } @@ -640,21 +585,69 @@ class ZMarkConcurrentRootsIteratorClosure : public ZRootsIteratorClosure { } }; +class ZMarkThreadClosure : public ThreadClosure { +private: + OopClosure* const _cl; + +public: + ZMarkThreadClosure(OopClosure* cl) : + _cl(cl) { + ZThreadLocalAllocBuffer::reset_statistics(); + } + ~ZMarkThreadClosure() { + ZThreadLocalAllocBuffer::publish_statistics(); + } + virtual void do_thread(Thread* thread) { + JavaThread* const jt = thread->as_Java_thread(); + StackWatermarkSet::finish_processing(jt, _cl, StackWatermarkKind::gc); + ZThreadLocalAllocBuffer::update_stats(jt); + } +}; + +class ZMarkNMethodClosure : public NMethodClosure { +private: + OopClosure* const _cl; + +public: + ZMarkNMethodClosure(OopClosure* cl) : + _cl(cl) {} + + virtual void do_nmethod(nmethod* nm) { + ZLocker locker(ZNMethod::lock_for_nmethod(nm)); + if (!nm->is_alive()) { + return; + } + + if (ZNMethod::is_armed(nm)) { + ZNMethod::nmethod_oops_do_inner(nm, _cl); + ZNMethod::disarm(nm); + } + } +}; + +typedef ClaimingCLDToOopClosure ZMarkCLDClosure; class ZMarkConcurrentRootsTask : public ZTask { private: - ZMark* const _mark; - SuspendibleThreadSetJoiner _sts_joiner; - ZConcurrentRootsIteratorClaimStrong _roots; - ZMarkConcurrentRootsIteratorClosure _cl; + ZMark* const _mark; + SuspendibleThreadSetJoiner _sts_joiner; + ZConcurrentRootsIterator _roots; + + ZMarkOopClosure _cl; + ZMarkCLDClosure _cld_cl; + ZMarkThreadClosure _thread_cl; + ZMarkNMethodClosure _nm_cl; public: ZMarkConcurrentRootsTask(ZMark* mark) : ZTask("ZMarkConcurrentRootsTask"), _mark(mark), _sts_joiner(), - _roots(), - _cl() { + _roots(ClassLoaderData::_claim_strong), + _cl(), + _cld_cl(&_cl), + _thread_cl(&_cl), + _nm_cl(&_cl) { ClassLoaderDataGraph_lock->lock(); } @@ -663,7 +656,10 @@ class ZMarkConcurrentRootsTask : public ZTask { } virtual void work() { - _roots.oops_do(&_cl); + _roots.apply(&_cl, + &_cld_cl, + &_thread_cl, + &_nm_cl); // Flush and free worker stacks. Needed here since // the set of workers executing during root scanning diff --git a/src/hotspot/share/gc/z/zMark.hpp b/src/hotspot/share/gc/z/zMark.hpp index 74f0fea1be8..4d25a905046 100644 --- a/src/hotspot/share/gc/z/zMark.hpp +++ b/src/hotspot/share/gc/z/zMark.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ #include "gc/z/zMarkStack.hpp" #include "gc/z/zMarkStackAllocator.hpp" +#include "gc/z/zMarkStackEntry.hpp" #include "gc/z/zMarkTerminate.hpp" #include "oops/oopsHierarchy.hpp" #include "utilities/globalDefinitions.hpp" @@ -36,9 +37,7 @@ class ZPageTable; class ZWorkers; class ZMark { - friend class ZMarkRootsTask; friend class ZMarkTask; - friend class ZMarkTryCompleteTask; private: ZWorkers* const _workers; diff --git a/src/hotspot/share/gc/z/zMarkStack.hpp b/src/hotspot/share/gc/z/zMarkStack.hpp index adcc7307f07..d61ae5bfdae 100644 --- a/src/hotspot/share/gc/z/zMarkStack.hpp +++ b/src/hotspot/share/gc/z/zMarkStack.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -66,10 +66,13 @@ class ZStackList { T* pop(); }; -typedef ZStack ZMarkStack; -typedef ZStackList ZMarkStackList; -typedef ZStack ZMarkStackMagazine; -typedef ZStackList ZMarkStackMagazineList; +using ZMarkStack = ZStack; +using ZMarkStackList = ZStackList; +using ZMarkStackMagazine = ZStack; +using ZMarkStackMagazineList = ZStackList; + +static_assert(sizeof(ZMarkStack) == ZMarkStackSize, "ZMarkStack size mismatch"); +static_assert(sizeof(ZMarkStackMagazine) <= ZMarkStackSize, "ZMarkStackMagazine size too large"); class ZMarkStripe { private: diff --git a/src/hotspot/share/gc/z/zMarkStackAllocator.cpp b/src/hotspot/share/gc/z/zMarkStackAllocator.cpp index fc6618aebf3..7673cca7a2d 100644 --- a/src/hotspot/share/gc/z/zMarkStackAllocator.cpp +++ b/src/hotspot/share/gc/z/zMarkStackAllocator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -128,9 +128,6 @@ uintptr_t ZMarkStackSpace::alloc(size_t size) { ZMarkStackAllocator::ZMarkStackAllocator() : _freelist(), _space() { - guarantee(sizeof(ZMarkStack) == ZMarkStackSize, "Size mismatch"); - guarantee(sizeof(ZMarkStackMagazine) <= ZMarkStackSize, "Size mismatch"); - // Prime free list to avoid an immediate space // expansion when marking starts. if (_space.is_initialized()) { diff --git a/src/hotspot/share/gc/z/zMemory.cpp b/src/hotspot/share/gc/z/zMemory.cpp index 616d31f7fa4..e4c826b9b1a 100644 --- a/src/hotspot/share/gc/z/zMemory.cpp +++ b/src/hotspot/share/gc/z/zMemory.cpp @@ -25,7 +25,6 @@ #include "gc/z/zList.inline.hpp" #include "gc/z/zLock.inline.hpp" #include "gc/z/zMemory.inline.hpp" -#include "memory/allocation.inline.hpp" ZMemory* ZMemoryManager::create(uintptr_t start, size_t size) { ZMemory* const area = new ZMemory(start, size); diff --git a/src/hotspot/share/gc/z/zMessagePort.hpp b/src/hotspot/share/gc/z/zMessagePort.hpp index c48f10d554c..2a9fdfa8868 100644 --- a/src/hotspot/share/gc/z/zMessagePort.hpp +++ b/src/hotspot/share/gc/z/zMessagePort.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,6 @@ #include "gc/z/zFuture.hpp" #include "gc/z/zList.hpp" -#include "memory/allocation.hpp" #include "runtime/mutex.hpp" template class ZMessageRequest; diff --git a/src/hotspot/share/gc/z/zNMethod.cpp b/src/hotspot/share/gc/z/zNMethod.cpp index 8ddead49387..ec3040e0f49 100644 --- a/src/hotspot/share/gc/z/zNMethod.cpp +++ b/src/hotspot/share/gc/z/zNMethod.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ #include "code/icBuffer.hpp" #include "gc/shared/barrierSet.hpp" #include "gc/shared/barrierSetNMethod.hpp" +#include "gc/shared/suspendibleThreadSet.hpp" #include "gc/z/zGlobals.hpp" #include "gc/z/zLock.inline.hpp" #include "gc/z/zNMethod.hpp" @@ -55,7 +56,7 @@ void ZNMethod::attach_gc_data(nmethod* nm) { GrowableArray immediate_oops; bool non_immediate_oops = false; - // Find all oops relocations + // Find all oop relocations RelocIterator iter(nm); while (iter.next()) { if (iter.type() != relocInfo::oop_type) { @@ -189,30 +190,29 @@ void ZNMethod::flush_nmethod(nmethod* nm) { bool ZNMethod::supports_entry_barrier(nmethod* nm) { BarrierSetNMethod* const bs = BarrierSet::barrier_set()->barrier_set_nmethod(); - if (bs != NULL) { - return bs->supports_entry_barrier(nm); - } - - return false; + return bs->supports_entry_barrier(nm); } bool ZNMethod::is_armed(nmethod* nm) { BarrierSetNMethod* const bs = BarrierSet::barrier_set()->barrier_set_nmethod(); - if (bs != NULL) { - return bs->is_armed(nm); - } - - return false; + return bs->is_armed(nm); } void ZNMethod::disarm(nmethod* nm) { BarrierSetNMethod* const bs = BarrierSet::barrier_set()->barrier_set_nmethod(); - if (bs != NULL) { - bs->disarm(nm); - } + bs->disarm(nm); } void ZNMethod::nmethod_oops_do(nmethod* nm, OopClosure* cl) { + ZLocker locker(ZNMethod::lock_for_nmethod(nm)); + if (!nm->is_alive()) { + return; + } + + ZNMethod::nmethod_oops_do_inner(nm, cl); +} + +void ZNMethod::nmethod_oops_do_inner(nmethod* nm, OopClosure* cl) { // Process oops table { oop* const begin = nm->oops_begin(); @@ -243,30 +243,16 @@ void ZNMethod::nmethod_oops_do(nmethod* nm, OopClosure* cl) { } } -class ZNMethodToOopsDoClosure : public NMethodClosure { -private: - OopClosure* _cl; - -public: - ZNMethodToOopsDoClosure(OopClosure* cl) : - _cl(cl) {} - - virtual void do_nmethod(nmethod* nm) { - ZNMethod::nmethod_oops_do(nm, _cl); - } -}; - -void ZNMethod::oops_do_begin() { +void ZNMethod::nmethods_do_begin() { ZNMethodTable::nmethods_do_begin(); } -void ZNMethod::oops_do_end() { +void ZNMethod::nmethods_do_end() { ZNMethodTable::nmethods_do_end(); } -void ZNMethod::oops_do(OopClosure* cl) { - ZNMethodToOopsDoClosure nmethod_cl(cl); - ZNMethodTable::nmethods_do(&nmethod_cl); +void ZNMethod::nmethods_do(NMethodClosure* cl) { + ZNMethodTable::nmethods_do(cl); } class ZNMethodUnlinkClosure : public NMethodClosure { diff --git a/src/hotspot/share/gc/z/zNMethod.hpp b/src/hotspot/share/gc/z/zNMethod.hpp index 19823588846..ec05389b7f4 100644 --- a/src/hotspot/share/gc/z/zNMethod.hpp +++ b/src/hotspot/share/gc/z/zNMethod.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,7 @@ #include "memory/allocation.hpp" class nmethod; -class OopClosure; +class NMethodClosure; class ZReentrantLock; class ZWorkers; @@ -49,10 +49,11 @@ class ZNMethod : public AllStatic { static void disarm(nmethod* nm); static void nmethod_oops_do(nmethod* nm, OopClosure* cl); + static void nmethod_oops_do_inner(nmethod* nm, OopClosure* cl); - static void oops_do_begin(); - static void oops_do_end(); - static void oops_do(OopClosure* cl); + static void nmethods_do_begin(); + static void nmethods_do_end(); + static void nmethods_do(NMethodClosure* cl); static ZReentrantLock* lock_for_nmethod(nmethod* nm); diff --git a/src/hotspot/share/gc/z/zObjectAllocator.cpp b/src/hotspot/share/gc/z/zObjectAllocator.cpp index 9adca8dbf70..fe523a83c2a 100644 --- a/src/hotspot/share/gc/z/zObjectAllocator.cpp +++ b/src/hotspot/share/gc/z/zObjectAllocator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,6 @@ */ #include "precompiled.hpp" -#include "gc/z/zCollectedHeap.hpp" #include "gc/z/zGlobals.hpp" #include "gc/z/zHeap.inline.hpp" #include "gc/z/zHeuristics.hpp" @@ -30,13 +29,10 @@ #include "gc/z/zPage.inline.hpp" #include "gc/z/zStat.hpp" #include "gc/z/zThread.inline.hpp" -#include "gc/z/zUtils.inline.hpp" #include "gc/z/zValue.inline.hpp" #include "logging/log.hpp" #include "runtime/atomic.hpp" #include "runtime/safepoint.hpp" -#include "runtime/thread.hpp" -#include "runtime/threadSMR.hpp" #include "utilities/align.hpp" #include "utilities/debug.hpp" diff --git a/src/hotspot/share/gc/z/zObjectAllocator.hpp b/src/hotspot/share/gc/z/zObjectAllocator.hpp index fe46f35b4d3..d4346f64939 100644 --- a/src/hotspot/share/gc/z/zObjectAllocator.hpp +++ b/src/hotspot/share/gc/z/zObjectAllocator.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,9 +25,9 @@ #define SHARE_GC_Z_ZOBJECTALLOCATOR_HPP #include "gc/z/zAllocationFlags.hpp" -#include "gc/z/zPage.hpp" #include "gc/z/zValue.hpp" -#include "memory/allocation.hpp" + +class ZPage; class ZObjectAllocator { private: diff --git a/src/hotspot/share/gc/z/zOopClosures.hpp b/src/hotspot/share/gc/z/zOopClosures.hpp index 2823f92f263..73201c339f5 100644 --- a/src/hotspot/share/gc/z/zOopClosures.hpp +++ b/src/hotspot/share/gc/z/zOopClosures.hpp @@ -31,12 +31,6 @@ class ZLoadBarrierOopClosure : public BasicOopIterateClosure { public: virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); - -#ifdef ASSERT - virtual bool should_verify_oops() { - return false; - } -#endif }; class ZNMethodOopClosure : public OopClosure { @@ -52,12 +46,6 @@ class ZMarkBarrierOopClosure : public ClaimMetadataVisitingOopIterateClosure { virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); - -#ifdef ASSERT - virtual bool should_verify_oops() { - return false; - } -#endif }; class ZPhantomIsAliveObjectClosure : public BoolObjectClosure { @@ -65,13 +53,12 @@ class ZPhantomIsAliveObjectClosure : public BoolObjectClosure { virtual bool do_object_b(oop o); }; -class ZPhantomKeepAliveOopClosure : public ZRootsIteratorClosure { +class ZPhantomKeepAliveOopClosure : public OopClosure { public: virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); }; - -class ZPhantomCleanOopClosure : public ZRootsIteratorClosure { +class ZPhantomCleanOopClosure : public OopClosure { public: virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); diff --git a/src/hotspot/share/gc/z/zPage.inline.hpp b/src/hotspot/share/gc/z/zPage.inline.hpp index 92170d0525c..835557c239a 100644 --- a/src/hotspot/share/gc/z/zPage.inline.hpp +++ b/src/hotspot/share/gc/z/zPage.inline.hpp @@ -27,12 +27,10 @@ #include "gc/z/zAddress.inline.hpp" #include "gc/z/zGlobals.hpp" #include "gc/z/zLiveMap.inline.hpp" -#include "gc/z/zMark.hpp" #include "gc/z/zNUMA.hpp" #include "gc/z/zPage.hpp" #include "gc/z/zPhysicalMemory.inline.hpp" #include "gc/z/zVirtualMemory.inline.hpp" -#include "oops/oop.inline.hpp" #include "runtime/atomic.hpp" #include "runtime/os.hpp" #include "utilities/align.hpp" diff --git a/src/hotspot/share/gc/z/zPageAllocator.cpp b/src/hotspot/share/gc/z/zPageAllocator.cpp index 8b82367dd2c..371ee2ff75a 100644 --- a/src/hotspot/share/gc/z/zPageAllocator.cpp +++ b/src/hotspot/share/gc/z/zPageAllocator.cpp @@ -24,7 +24,7 @@ #include "precompiled.hpp" #include "gc/shared/gcLogPrecious.hpp" #include "gc/shared/suspendibleThreadSet.hpp" -#include "gc/z/zAddress.inline.hpp" +#include "gc/z/zArray.inline.hpp" #include "gc/z/zCollectedHeap.hpp" #include "gc/z/zFuture.inline.hpp" #include "gc/z/zGlobals.hpp" @@ -35,7 +35,6 @@ #include "gc/z/zSafeDelete.inline.hpp" #include "gc/z/zStat.hpp" #include "gc/z/zTask.hpp" -#include "gc/z/zTracer.inline.hpp" #include "gc/z/zUncommitter.hpp" #include "gc/z/zUnmapper.hpp" #include "gc/z/zWorkers.hpp" @@ -750,6 +749,19 @@ void ZPageAllocator::free_page(ZPage* page, bool reclaimed) { satisfy_stalled(); } +void ZPageAllocator::free_pages(const ZArray* pages, bool reclaimed) { + ZLocker locker(&_lock); + + // Free pages + ZArrayIterator iter(pages); + for (ZPage* page; iter.next(&page);) { + free_page_inner(page, reclaimed); + } + + // Try satisfy stalled allocations + satisfy_stalled(); +} + size_t ZPageAllocator::uncommit(uint64_t* timeout) { // We need to join the suspendible thread set while manipulating capacity and // used, to make sure GC safepoints will have a consistent view. However, when diff --git a/src/hotspot/share/gc/z/zPageAllocator.hpp b/src/hotspot/share/gc/z/zPageAllocator.hpp index 800ef4d9875..9c970350732 100644 --- a/src/hotspot/share/gc/z/zPageAllocator.hpp +++ b/src/hotspot/share/gc/z/zPageAllocator.hpp @@ -25,6 +25,7 @@ #define SHARE_GC_Z_ZPAGEALLOCATOR_HPP #include "gc/z/zAllocationFlags.hpp" +#include "gc/z/zArray.hpp" #include "gc/z/zList.hpp" #include "gc/z/zLock.hpp" #include "gc/z/zPageCache.hpp" @@ -124,6 +125,7 @@ class ZPageAllocator { ZPage* alloc_page(uint8_t type, size_t size, ZAllocationFlags flags); void free_page(ZPage* page, bool reclaimed); + void free_pages(const ZArray* pages, bool reclaimed); void enable_deferred_delete() const; void disable_deferred_delete() const; diff --git a/src/hotspot/share/gc/z/zPageCache.cpp b/src/hotspot/share/gc/z/zPageCache.cpp index 5abf272cd55..ae05b257244 100644 --- a/src/hotspot/share/gc/z/zPageCache.cpp +++ b/src/hotspot/share/gc/z/zPageCache.cpp @@ -29,7 +29,6 @@ #include "gc/z/zPageCache.hpp" #include "gc/z/zStat.hpp" #include "gc/z/zValue.inline.hpp" -#include "logging/log.hpp" #include "memory/allocation.hpp" #include "runtime/globals.hpp" #include "runtime/os.hpp" diff --git a/src/hotspot/share/gc/z/zReferenceProcessor.cpp b/src/hotspot/share/gc/z/zReferenceProcessor.cpp index 1d59fee3408..113ee36d789 100644 --- a/src/hotspot/share/gc/z/zReferenceProcessor.cpp +++ b/src/hotspot/share/gc/z/zReferenceProcessor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,6 @@ #include "gc/z/zStat.hpp" #include "gc/z/zTask.hpp" #include "gc/z/zTracer.inline.hpp" -#include "gc/z/zUtils.inline.hpp" #include "gc/z/zValue.inline.hpp" #include "memory/universe.hpp" #include "runtime/atomic.hpp" diff --git a/src/hotspot/share/gc/z/zRelocate.cpp b/src/hotspot/share/gc/z/zRelocate.cpp index 2828e5859a6..95c07cb2bc4 100644 --- a/src/hotspot/share/gc/z/zRelocate.cpp +++ b/src/hotspot/share/gc/z/zRelocate.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,39 +27,25 @@ #include "gc/z/zForwarding.inline.hpp" #include "gc/z/zHeap.hpp" #include "gc/z/zOopClosures.inline.hpp" -#include "gc/z/zPage.hpp" +#include "gc/z/zPage.inline.hpp" #include "gc/z/zRelocate.hpp" #include "gc/z/zRelocationSet.inline.hpp" #include "gc/z/zRootsIterator.hpp" #include "gc/z/zStat.hpp" #include "gc/z/zTask.hpp" #include "gc/z/zThread.inline.hpp" -#include "gc/z/zThreadLocalAllocBuffer.hpp" #include "gc/z/zWorkers.hpp" #include "logging/log.hpp" +#include "prims/jvmtiTagMap.hpp" static const ZStatCounter ZCounterRelocationContention("Contention", "Relocation Contention", ZStatUnitOpsPerSecond); +static const ZStatSubPhase ZSubPhasePauseRootsJVMTITagMap("Pause Roots JVMTITagMap"); ZRelocate::ZRelocate(ZWorkers* workers) : _workers(workers) {} -class ZRelocateRootsIteratorClosure : public ZRootsIteratorClosure { +class ZRelocateRootsIteratorClosure : public OopClosure { public: - virtual void do_thread(Thread* thread) { - // Update thread local address bad mask - ZThreadLocalData::set_address_bad_mask(thread, ZAddressBadMask); - - // Relocate invisible root - ZThreadLocalData::do_invisible_root(thread, ZBarrier::relocate_barrier_on_root_oop_field); - - // Remap TLAB - ZThreadLocalAllocBuffer::remap(thread); - } - - virtual bool should_disarm_nmethods() const { - return true; - } - virtual void do_oop(oop* p) { ZBarrier::relocate_barrier_on_root_oop_field(p); } @@ -71,24 +57,28 @@ class ZRelocateRootsIteratorClosure : public ZRootsIteratorClosure { class ZRelocateRootsTask : public ZTask { private: - ZRootsIterator _roots; ZRelocateRootsIteratorClosure _cl; public: ZRelocateRootsTask() : - ZTask("ZRelocateRootsTask"), - _roots(true /* visit_jvmti_weak_export */) {} + ZTask("ZRelocateRootsTask") {} virtual void work() { + // Allocation path assumes that relocating GC threads are ZWorkers + assert(ZThread::is_worker(), "Relocation code needs to be run as a worker"); + assert(ZThread::worker_id() == 0, "No multi-thread support"); + // During relocation we need to visit the JVMTI - // export weak roots to rehash the JVMTI tag map - _roots.oops_do(&_cl); + // tag map to rehash the entries with the new oop addresses. + ZStatTimer timer(ZSubPhasePauseRootsJVMTITagMap); + AlwaysTrueClosure always_alive; + JvmtiTagMap::weak_oops_do(&always_alive, &_cl); } }; void ZRelocate::start() { ZRelocateRootsTask task; - _workers->run_parallel(&task); + _workers->run_serial(&task); } uintptr_t ZRelocate::relocate_object_inner(ZForwarding* forwarding, uintptr_t from_index, uintptr_t from_offset) const { diff --git a/src/hotspot/share/gc/z/zRelocate.hpp b/src/hotspot/share/gc/z/zRelocate.hpp index 546ddd22829..e10edf347b2 100644 --- a/src/hotspot/share/gc/z/zRelocate.hpp +++ b/src/hotspot/share/gc/z/zRelocate.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,10 +25,9 @@ #define SHARE_GC_Z_ZRELOCATE_HPP #include "gc/z/zRelocationSet.hpp" -#include "gc/z/zWorkers.hpp" -#include "memory/allocation.hpp" class ZForwarding; +class ZWorkers; class ZRelocate { friend class ZRelocateTask; diff --git a/src/hotspot/share/gc/z/zRelocationSet.cpp b/src/hotspot/share/gc/z/zRelocationSet.cpp index b7f566397ae..0591c2a711a 100644 --- a/src/hotspot/share/gc/z/zRelocationSet.cpp +++ b/src/hotspot/share/gc/z/zRelocationSet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,35 +22,108 @@ */ #include "precompiled.hpp" -#include "gc/z/zForwarding.hpp" +#include "gc/z/zArray.inline.hpp" +#include "gc/z/zForwarding.inline.hpp" +#include "gc/z/zForwardingAllocator.inline.hpp" #include "gc/z/zRelocationSet.hpp" -#include "memory/allocation.hpp" +#include "gc/z/zRelocationSetSelector.inline.hpp" +#include "gc/z/zStat.hpp" +#include "gc/z/zTask.hpp" +#include "gc/z/zWorkers.hpp" +#include "runtime/atomic.hpp" +#include "utilities/debug.hpp" -ZRelocationSet::ZRelocationSet() : - _forwardings(NULL), - _nforwardings(0) {} +class ZRelocationSetInstallTask : public ZTask { +private: + ZForwardingAllocator* const _allocator; + ZForwarding** _forwardings; + const size_t _nforwardings; + ZArrayParallelIterator _small_iter; + ZArrayParallelIterator _medium_iter; + volatile size_t _small_next; + volatile size_t _medium_next; + + void install(ZForwarding* forwarding, volatile size_t* next) { + const size_t index = Atomic::fetch_and_add(next, 1u); + assert(index < _nforwardings, "Invalid index"); + _forwardings[index] = forwarding; + } + + void install_small(ZForwarding* forwarding) { + install(forwarding, &_small_next); + } + + void install_medium(ZForwarding* forwarding) { + install(forwarding, &_medium_next); + } + +public: + ZRelocationSetInstallTask(ZForwardingAllocator* allocator, const ZRelocationSetSelector* selector) : + ZTask("ZRelocationSetInstallTask"), + _allocator(allocator), + _forwardings(NULL), + _nforwardings(selector->small()->length() + selector->medium()->length()), + _small_iter(selector->small()), + _medium_iter(selector->medium()), + _small_next(selector->medium()->length()), + _medium_next(0) { + + // Reset the allocator to have room for the relocation + // set, all forwardings, and all forwarding entries. + const size_t relocation_set_size = _nforwardings * sizeof(ZForwarding*); + const size_t forwardings_size = _nforwardings * sizeof(ZForwarding); + const size_t forwarding_entries_size = selector->forwarding_entries() * sizeof(ZForwardingEntry); + _allocator->reset(relocation_set_size + forwardings_size + forwarding_entries_size); + + // Allocate relocation set + _forwardings = new (_allocator->alloc(relocation_set_size)) ZForwarding*[_nforwardings]; + } + + ~ZRelocationSetInstallTask() { + assert(_allocator->is_full(), "Should be full"); + } -void ZRelocationSet::populate(ZPage* const* group0, size_t ngroup0, - ZPage* const* group1, size_t ngroup1) { - _nforwardings = ngroup0 + ngroup1; - _forwardings = REALLOC_C_HEAP_ARRAY(ZForwarding*, _forwardings, _nforwardings, mtGC); + virtual void work() { + // Allocate and install forwardings for small pages + for (ZPage* page; _small_iter.next(&page);) { + ZForwarding* const forwarding = ZForwarding::alloc(_allocator, page); + install_small(forwarding); + } - size_t j = 0; + // Allocate and install forwardings for medium pages + for (ZPage* page; _medium_iter.next(&page);) { + ZForwarding* const forwarding = ZForwarding::alloc(_allocator, page); + install_medium(forwarding); + } + } - // Populate group 0 - for (size_t i = 0; i < ngroup0; i++) { - _forwardings[j++] = ZForwarding::create(group0[i]); + ZForwarding** forwardings() const { + return _forwardings; } - // Populate group 1 - for (size_t i = 0; i < ngroup1; i++) { - _forwardings[j++] = ZForwarding::create(group1[i]); + size_t nforwardings() const { + return _nforwardings; } +}; + +ZRelocationSet::ZRelocationSet(ZWorkers* workers) : + _workers(workers), + _allocator(), + _forwardings(NULL), + _nforwardings(0) {} + +void ZRelocationSet::install(const ZRelocationSetSelector* selector) { + // Install relocation set + ZRelocationSetInstallTask task(&_allocator, selector); + _workers->run_concurrent(&task); + + _forwardings = task.forwardings(); + _nforwardings = task.nforwardings(); + + // Update statistics + ZStatRelocation::set_at_install_relocation_set(_allocator.size()); } void ZRelocationSet::reset() { - for (size_t i = 0; i < _nforwardings; i++) { - ZForwarding::destroy(_forwardings[i]); - _forwardings[i] = NULL; - } + _nforwardings = 0; } diff --git a/src/hotspot/share/gc/z/zRelocationSet.hpp b/src/hotspot/share/gc/z/zRelocationSet.hpp index 353edc566a5..5881055db99 100644 --- a/src/hotspot/share/gc/z/zRelocationSet.hpp +++ b/src/hotspot/share/gc/z/zRelocationSet.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,52 +24,36 @@ #ifndef SHARE_GC_Z_ZRELOCATIONSET_HPP #define SHARE_GC_Z_ZRELOCATIONSET_HPP -#include "memory/allocation.hpp" +#include "gc/z/zArray.hpp" +#include "gc/z/zForwardingAllocator.hpp" class ZForwarding; -class ZPage; +class ZRelocationSetSelector; +class ZWorkers; class ZRelocationSet { template friend class ZRelocationSetIteratorImpl; private: - ZForwarding** _forwardings; - size_t _nforwardings; + ZWorkers* _workers; + ZForwardingAllocator _allocator; + ZForwarding** _forwardings; + size_t _nforwardings; public: - ZRelocationSet(); + ZRelocationSet(ZWorkers* workers); - void populate(ZPage* const* group0, size_t ngroup0, - ZPage* const* group1, size_t ngroup1); + void install(const ZRelocationSetSelector* selector); void reset(); }; -template -class ZRelocationSetIteratorImpl : public StackObj { -private: - ZRelocationSet* const _relocation_set; - size_t _next; - +template +class ZRelocationSetIteratorImpl : public ZArrayIteratorImpl { public: ZRelocationSetIteratorImpl(ZRelocationSet* relocation_set); - - bool next(ZForwarding** forwarding); }; -// Iterator types -#define ZRELOCATIONSET_SERIAL false -#define ZRELOCATIONSET_PARALLEL true - -class ZRelocationSetIterator : public ZRelocationSetIteratorImpl { -public: - ZRelocationSetIterator(ZRelocationSet* relocation_set) : - ZRelocationSetIteratorImpl(relocation_set) {} -}; - -class ZRelocationSetParallelIterator : public ZRelocationSetIteratorImpl { -public: - ZRelocationSetParallelIterator(ZRelocationSet* relocation_set) : - ZRelocationSetIteratorImpl(relocation_set) {} -}; +using ZRelocationSetIterator = ZRelocationSetIteratorImpl; +using ZRelocationSetParallelIterator = ZRelocationSetIteratorImpl; #endif // SHARE_GC_Z_ZRELOCATIONSET_HPP diff --git a/src/hotspot/share/gc/z/zRelocationSet.inline.hpp b/src/hotspot/share/gc/z/zRelocationSet.inline.hpp index 52f3a281707..bdd5330f4a0 100644 --- a/src/hotspot/share/gc/z/zRelocationSet.inline.hpp +++ b/src/hotspot/share/gc/z/zRelocationSet.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,34 +24,11 @@ #ifndef SHARE_GC_Z_ZRELOCATIONSET_INLINE_HPP #define SHARE_GC_Z_ZRELOCATIONSET_INLINE_HPP +#include "gc/z/zArray.inline.hpp" #include "gc/z/zRelocationSet.hpp" -#include "runtime/atomic.hpp" -template -inline ZRelocationSetIteratorImpl::ZRelocationSetIteratorImpl(ZRelocationSet* relocation_set) : - _relocation_set(relocation_set), - _next(0) {} - -template -inline bool ZRelocationSetIteratorImpl::next(ZForwarding** forwarding) { - const size_t nforwardings = _relocation_set->_nforwardings; - - if (parallel) { - if (_next < nforwardings) { - const size_t next = Atomic::fetch_and_add(&_next, 1u); - if (next < nforwardings) { - *forwarding = _relocation_set->_forwardings[next]; - return true; - } - } - } else { - if (_next < nforwardings) { - *forwarding = _relocation_set->_forwardings[_next++]; - return true; - } - } - - return false; -} +template +inline ZRelocationSetIteratorImpl::ZRelocationSetIteratorImpl(ZRelocationSet* relocation_set) : + ZArrayIteratorImpl(relocation_set->_forwardings, relocation_set->_nforwardings) {} #endif // SHARE_GC_Z_ZRELOCATIONSET_INLINE_HPP diff --git a/src/hotspot/share/gc/z/zRelocationSetSelector.cpp b/src/hotspot/share/gc/z/zRelocationSetSelector.cpp index ac33451735e..3d80286a894 100644 --- a/src/hotspot/share/gc/z/zRelocationSetSelector.cpp +++ b/src/hotspot/share/gc/z/zRelocationSetSelector.cpp @@ -23,8 +23,8 @@ #include "precompiled.hpp" #include "gc/z/zArray.inline.hpp" +#include "gc/z/zForwarding.inline.hpp" #include "gc/z/zPage.inline.hpp" -#include "gc/z/zRelocationSet.hpp" #include "gc/z/zRelocationSetSelector.inline.hpp" #include "jfr/jfrEvents.hpp" #include "logging/log.hpp" @@ -51,39 +51,9 @@ ZRelocationSetSelectorGroup::ZRelocationSetSelectorGroup(const char* name, _object_size_limit(object_size_limit), _fragmentation_limit(page_size * (ZFragmentationLimit / 100)), _registered_pages(), - _sorted_pages(NULL), - _nselected(0), + _forwarding_entries(0), _stats() {} -ZRelocationSetSelectorGroup::~ZRelocationSetSelectorGroup() { - FREE_C_HEAP_ARRAY(ZPage*, _sorted_pages); -} - -void ZRelocationSetSelectorGroup::register_live_page(ZPage* page) { - const uint8_t type = page->type(); - const size_t size = page->size(); - const size_t live = page->live_bytes(); - const size_t garbage = size - live; - - if (garbage > _fragmentation_limit) { - _registered_pages.append(page); - } - - _stats._npages++; - _stats._total += size; - _stats._live += live; - _stats._garbage += garbage; -} - -void ZRelocationSetSelectorGroup::register_garbage_page(ZPage* page) { - const size_t size = page->size(); - - _stats._npages++; - _stats._total += size; - _stats._garbage += size; - _stats._empty += size; -} - bool ZRelocationSetSelectorGroup::is_disabled() { // Medium pages are disabled when their page size is zero return _page_type == ZPageTypeMedium && _page_size == 0; @@ -100,18 +70,11 @@ void ZRelocationSetSelectorGroup::semi_sort() { const size_t npartitions = (size_t)1 << npartitions_shift; const size_t partition_size = _page_size >> npartitions_shift; const size_t partition_size_shift = exact_log2(partition_size); - const size_t npages = _registered_pages.length(); // Partition slots/fingers - size_t partitions[npartitions]; - - // Allocate destination array - assert(_sorted_pages == NULL, "Already initialized"); - _sorted_pages = NEW_C_HEAP_ARRAY(ZPage*, npages, mtGC); - debug_only(memset(_sorted_pages, 0, npages * sizeof(ZPage*))); + int partitions[npartitions] = { /* zero initialize */ }; // Calculate partition slots - memset(partitions, 0, sizeof(partitions)); ZArrayIterator iter1(&_registered_pages); for (ZPage* page; iter1.next(&page);) { const size_t index = page->live_bytes() >> partition_size_shift; @@ -119,70 +82,83 @@ void ZRelocationSetSelectorGroup::semi_sort() { } // Calculate partition fingers - size_t finger = 0; + int finger = 0; for (size_t i = 0; i < npartitions; i++) { - const size_t slots = partitions[i]; + const int slots = partitions[i]; partitions[i] = finger; finger += slots; } + // Allocate destination array + const int npages = _registered_pages.length(); + ZArray sorted_pages(npages, npages, NULL); + // Sort pages into partitions ZArrayIterator iter2(&_registered_pages); for (ZPage* page; iter2.next(&page);) { const size_t index = page->live_bytes() >> partition_size_shift; - const size_t finger = partitions[index]++; - assert(_sorted_pages[finger] == NULL, "Invalid finger"); - _sorted_pages[finger] = page; + const int finger = partitions[index]++; + assert(sorted_pages.at(finger) == NULL, "Invalid finger"); + sorted_pages.at_put(finger, page); } + + _registered_pages.swap(&sorted_pages); } void ZRelocationSetSelectorGroup::select_inner() { // Calculate the number of pages to relocate by successively including pages in // a candidate relocation set and calculate the maximum space requirement for // their live objects. - const size_t npages = _registered_pages.length(); - size_t selected_from = 0; - size_t selected_to = 0; - size_t from_size = 0; + const int npages = _registered_pages.length(); + int selected_from = 0; + int selected_to = 0; + size_t selected_forwarding_entries = 0; + size_t from_live_bytes = 0; + size_t from_forwarding_entries = 0; semi_sort(); - for (size_t from = 1; from <= npages; from++) { + for (int from = 1; from <= npages; from++) { // Add page to the candidate relocation set - from_size += _sorted_pages[from - 1]->live_bytes(); + ZPage* const page = _registered_pages.at(from - 1); + from_live_bytes += page->live_bytes(); + from_forwarding_entries += ZForwarding::nentries(page); // Calculate the maximum number of pages needed by the candidate relocation set. // By subtracting the object size limit from the pages size we get the maximum // number of pages that the relocation set is guaranteed to fit in, regardless // of in which order the objects are relocated. - const size_t to = ceil((double)(from_size) / (double)(_page_size - _object_size_limit)); + const int to = ceil((double)(from_live_bytes) / (double)(_page_size - _object_size_limit)); // Calculate the relative difference in reclaimable space compared to our // currently selected final relocation set. If this number is larger than the // acceptable fragmentation limit, then the current candidate relocation set // becomes our new final relocation set. - const size_t diff_from = from - selected_from; - const size_t diff_to = to - selected_to; + const int diff_from = from - selected_from; + const int diff_to = to - selected_to; const double diff_reclaimable = 100 - percent_of(diff_to, diff_from); if (diff_reclaimable > ZFragmentationLimit) { selected_from = from; selected_to = to; + selected_forwarding_entries = from_forwarding_entries; } - log_trace(gc, reloc)("Candidate Relocation Set (%s Pages): " - SIZE_FORMAT "->" SIZE_FORMAT ", %.1f%% relative defragmentation, %s", - _name, from, to, diff_reclaimable, (selected_from == from) ? "Selected" : "Rejected"); + log_trace(gc, reloc)("Candidate Relocation Set (%s Pages): %d->%d, " + "%.1f%% relative defragmentation, " SIZE_FORMAT " forwarding entries, %s", + _name, from, to, diff_reclaimable, from_forwarding_entries, + (selected_from == from) ? "Selected" : "Rejected"); } // Finalize selection - _nselected = selected_from; + _registered_pages.trunc_to(selected_from); + _forwarding_entries = selected_forwarding_entries; // Update statistics _stats._compacting_from = selected_from * _page_size; _stats._compacting_to = selected_to * _page_size; - log_trace(gc, reloc)("Relocation Set (%s Pages): " SIZE_FORMAT "->" SIZE_FORMAT ", " SIZE_FORMAT " skipped", - _name, selected_from, selected_to, npages - _nselected); + log_trace(gc, reloc)("Relocation Set (%s Pages): %d->%d, %d skipped, " SIZE_FORMAT " forwarding entries", + _name, selected_from, selected_to, npages - selected_from, selected_forwarding_entries); } void ZRelocationSetSelectorGroup::select() { @@ -204,33 +180,10 @@ void ZRelocationSetSelectorGroup::select() { ZRelocationSetSelector::ZRelocationSetSelector() : _small("Small", ZPageTypeSmall, ZPageSizeSmall, ZObjectSizeLimitSmall), _medium("Medium", ZPageTypeMedium, ZPageSizeMedium, ZObjectSizeLimitMedium), - _large("Large", ZPageTypeLarge, 0 /* page_size */, 0 /* object_size_limit */) {} - -void ZRelocationSetSelector::register_live_page(ZPage* page) { - const uint8_t type = page->type(); - - if (type == ZPageTypeSmall) { - _small.register_live_page(page); - } else if (type == ZPageTypeMedium) { - _medium.register_live_page(page); - } else { - _large.register_live_page(page); - } -} - -void ZRelocationSetSelector::register_garbage_page(ZPage* page) { - const uint8_t type = page->type(); - - if (type == ZPageTypeSmall) { - _small.register_garbage_page(page); - } else if (type == ZPageTypeMedium) { - _medium.register_garbage_page(page); - } else { - _large.register_garbage_page(page); - } -} + _large("Large", ZPageTypeLarge, 0 /* page_size */, 0 /* object_size_limit */), + _garbage_pages() {} -void ZRelocationSetSelector::select(ZRelocationSet* relocation_set) { +void ZRelocationSetSelector::select() { // Select pages to relocate. The resulting relocation set will be // sorted such that medium pages comes first, followed by small // pages. Pages within each page group will be semi-sorted by live @@ -244,10 +197,6 @@ void ZRelocationSetSelector::select(ZRelocationSet* relocation_set) { _medium.select(); _small.select(); - // Populate relocation set - relocation_set->populate(_medium.selected(), _medium.nselected(), - _small.selected(), _small.nselected()); - // Send event event.commit(total(), empty(), compacting_from(), compacting_to()); } diff --git a/src/hotspot/share/gc/z/zRelocationSetSelector.hpp b/src/hotspot/share/gc/z/zRelocationSetSelector.hpp index 70900f814d8..164fcd628a0 100644 --- a/src/hotspot/share/gc/z/zRelocationSetSelector.hpp +++ b/src/hotspot/share/gc/z/zRelocationSetSelector.hpp @@ -28,7 +28,6 @@ #include "memory/allocation.hpp" class ZPage; -class ZRelocationSet; class ZRelocationSetSelectorGroupStats { friend class ZRelocationSetSelectorGroup; @@ -75,10 +74,8 @@ class ZRelocationSetSelectorGroup { const size_t _page_size; const size_t _object_size_limit; const size_t _fragmentation_limit; - ZArray _registered_pages; - ZPage** _sorted_pages; - size_t _nselected; + size_t _forwarding_entries; ZRelocationSetSelectorGroupStats _stats; bool is_disabled(); @@ -91,14 +88,13 @@ class ZRelocationSetSelectorGroup { uint8_t page_type, size_t page_size, size_t object_size_limit); - ~ZRelocationSetSelectorGroup(); void register_live_page(ZPage* page); void register_garbage_page(ZPage* page); void select(); - ZPage* const* selected() const; - size_t nselected() const; + const ZArray* selected() const; + size_t forwarding_entries() const; const ZRelocationSetSelectorGroupStats& stats() const; }; @@ -108,6 +104,7 @@ class ZRelocationSetSelector : public StackObj { ZRelocationSetSelectorGroup _small; ZRelocationSetSelectorGroup _medium; ZRelocationSetSelectorGroup _large; + ZArray _garbage_pages; size_t total() const; size_t empty() const; @@ -119,7 +116,16 @@ class ZRelocationSetSelector : public StackObj { void register_live_page(ZPage* page); void register_garbage_page(ZPage* page); - void select(ZRelocationSet* relocation_set); + + bool should_free_garbage_pages(int bulk) const; + const ZArray* garbage_pages() const; + void clear_garbage_pages(); + + void select(); + + const ZArray* small() const; + const ZArray* medium() const; + size_t forwarding_entries() const; ZRelocationSetSelectorStats stats() const; }; diff --git a/src/hotspot/share/gc/z/zRelocationSetSelector.inline.hpp b/src/hotspot/share/gc/z/zRelocationSetSelector.inline.hpp index 37b7839511d..11c48394fc9 100644 --- a/src/hotspot/share/gc/z/zRelocationSetSelector.inline.hpp +++ b/src/hotspot/share/gc/z/zRelocationSetSelector.inline.hpp @@ -24,6 +24,8 @@ #ifndef SHARE_GC_Z_ZRELOCATIONSETSELECTOR_INLINE_HPP #define SHARE_GC_Z_ZRELOCATIONSETSELECTOR_INLINE_HPP +#include "gc/z/zArray.inline.hpp" +#include "gc/z/zPage.inline.hpp" #include "gc/z/zRelocationSetSelector.hpp" inline size_t ZRelocationSetSelectorGroupStats::npages() const { @@ -66,18 +68,81 @@ inline const ZRelocationSetSelectorGroupStats& ZRelocationSetSelectorStats::larg return _large; } -inline ZPage* const* ZRelocationSetSelectorGroup::selected() const { - return _sorted_pages; +inline void ZRelocationSetSelectorGroup::register_live_page(ZPage* page) { + const uint8_t type = page->type(); + const size_t size = page->size(); + const size_t live = page->live_bytes(); + const size_t garbage = size - live; + + if (garbage > _fragmentation_limit) { + _registered_pages.append(page); + } + + _stats._npages++; + _stats._total += size; + _stats._live += live; + _stats._garbage += garbage; +} + +inline void ZRelocationSetSelectorGroup::register_garbage_page(ZPage* page) { + const size_t size = page->size(); + + _stats._npages++; + _stats._total += size; + _stats._garbage += size; + _stats._empty += size; +} + +inline const ZArray* ZRelocationSetSelectorGroup::selected() const { + return &_registered_pages; } -inline size_t ZRelocationSetSelectorGroup::nselected() const { - return _nselected; +inline size_t ZRelocationSetSelectorGroup::forwarding_entries() const { + return _forwarding_entries; } inline const ZRelocationSetSelectorGroupStats& ZRelocationSetSelectorGroup::stats() const { return _stats; } +inline void ZRelocationSetSelector::register_live_page(ZPage* page) { + const uint8_t type = page->type(); + + if (type == ZPageTypeSmall) { + _small.register_live_page(page); + } else if (type == ZPageTypeMedium) { + _medium.register_live_page(page); + } else { + _large.register_live_page(page); + } +} + +inline void ZRelocationSetSelector::register_garbage_page(ZPage* page) { + const uint8_t type = page->type(); + + if (type == ZPageTypeSmall) { + _small.register_garbage_page(page); + } else if (type == ZPageTypeMedium) { + _medium.register_garbage_page(page); + } else { + _large.register_garbage_page(page); + } + + _garbage_pages.append(page); +} + +inline bool ZRelocationSetSelector::should_free_garbage_pages(int bulk) const { + return _garbage_pages.length() >= bulk && _garbage_pages.is_nonempty(); +} + +inline const ZArray* ZRelocationSetSelector::garbage_pages() const { + return &_garbage_pages; +} + +inline void ZRelocationSetSelector::clear_garbage_pages() { + return _garbage_pages.clear(); +} + inline size_t ZRelocationSetSelector::total() const { return _small.stats().total() + _medium.stats().total() + _large.stats().total(); } @@ -94,4 +159,16 @@ inline size_t ZRelocationSetSelector::compacting_to() const { return _small.stats().compacting_to() + _medium.stats().compacting_to() + _large.stats().compacting_to(); } +inline const ZArray* ZRelocationSetSelector::small() const { + return _small.selected(); +} + +inline const ZArray* ZRelocationSetSelector::medium() const { + return _medium.selected(); +} + +inline size_t ZRelocationSetSelector::forwarding_entries() const { + return _small.forwarding_entries() + _medium.forwarding_entries(); +} + #endif // SHARE_GC_Z_ZRELOCATIONSETSELECTOR_INLINE_HPP diff --git a/src/hotspot/share/gc/z/zRootsIterator.cpp b/src/hotspot/share/gc/z/zRootsIterator.cpp index bc6fb2d5206..ec1887e2de2 100644 --- a/src/hotspot/share/gc/z/zRootsIterator.cpp +++ b/src/hotspot/share/gc/z/zRootsIterator.cpp @@ -23,148 +23,55 @@ #include "precompiled.hpp" #include "classfile/classLoaderDataGraph.hpp" -#include "classfile/stringTable.hpp" -#include "code/codeCache.hpp" -#include "compiler/oopMap.hpp" -#include "gc/shared/barrierSet.hpp" -#include "gc/shared/barrierSetNMethod.hpp" -#include "gc/shared/oopStorageSet.hpp" -#include "gc/shared/oopStorageParState.inline.hpp" #include "gc/shared/oopStorageSetParState.inline.hpp" -#include "gc/shared/suspendibleThreadSet.hpp" -#include "gc/z/zBarrierSetNMethod.hpp" -#include "gc/z/zGlobals.hpp" #include "gc/z/zNMethod.hpp" -#include "gc/z/zOopClosures.inline.hpp" +#include "gc/z/zNMethodTable.hpp" #include "gc/z/zRootsIterator.hpp" #include "gc/z/zStat.hpp" -#include "gc/z/zThreadLocalData.hpp" -#include "memory/iterator.hpp" #include "memory/resourceArea.hpp" -#include "memory/universe.hpp" -#include "prims/jvmtiExport.hpp" -#include "prims/resolvedMethodTable.hpp" +#include "prims/jvmtiTagMap.hpp" #include "runtime/atomic.hpp" +#include "runtime/globals.hpp" #include "runtime/safepoint.hpp" -#include "runtime/synchronizer.hpp" -#include "runtime/thread.hpp" -#include "runtime/vmThread.hpp" #include "utilities/debug.hpp" -static const ZStatSubPhase ZSubPhasePauseRootsSetup("Pause Roots Setup"); -static const ZStatSubPhase ZSubPhasePauseRoots("Pause Roots"); -static const ZStatSubPhase ZSubPhasePauseRootsTeardown("Pause Roots Teardown"); -static const ZStatSubPhase ZSubPhasePauseRootsJVMTIWeakExport("Pause Roots JVMTIWeakExport"); -static const ZStatSubPhase ZSubPhasePauseRootsVMThread("Pause Roots VM Thread"); -static const ZStatSubPhase ZSubPhasePauseRootsJavaThreads("Pause Roots Java Threads"); -static const ZStatSubPhase ZSubPhasePauseRootsCodeCache("Pause Roots CodeCache"); - -static const ZStatSubPhase ZSubPhaseConcurrentRootsSetup("Concurrent Roots Setup"); -static const ZStatSubPhase ZSubPhaseConcurrentRoots("Concurrent Roots"); -static const ZStatSubPhase ZSubPhaseConcurrentRootsTeardown("Concurrent Roots Teardown"); static const ZStatSubPhase ZSubPhaseConcurrentRootsOopStorageSet("Concurrent Roots OopStorageSet"); static const ZStatSubPhase ZSubPhaseConcurrentRootsClassLoaderDataGraph("Concurrent Roots ClassLoaderDataGraph"); - -static const ZStatSubPhase ZSubPhasePauseWeakRootsSetup("Pause Weak Roots Setup"); -static const ZStatSubPhase ZSubPhasePauseWeakRoots("Pause Weak Roots"); -static const ZStatSubPhase ZSubPhasePauseWeakRootsTeardown("Pause Weak Roots Teardown"); -static const ZStatSubPhase ZSubPhasePauseWeakRootsJVMTIWeakExport("Pause Weak Roots JVMTIWeakExport"); - -static const ZStatSubPhase ZSubPhaseConcurrentWeakRoots("Concurrent Weak Roots"); +static const ZStatSubPhase ZSubPhaseConcurrentRootsJavaThreads("Concurrent Roots JavaThreads"); +static const ZStatSubPhase ZSubPhaseConcurrentRootsCodeCache("Concurrent Roots CodeCache"); +static const ZStatSubPhase ZSubPhasePauseWeakRootsJVMTITagMap("Pause Weak Roots JVMTITagMap"); static const ZStatSubPhase ZSubPhaseConcurrentWeakRootsOopStorageSet("Concurrent Weak Roots OopStorageSet"); -template -ZSerialOopsDo::ZSerialOopsDo(T* iter) : - _iter(iter), - _claimed(false) {} - -template -void ZSerialOopsDo::oops_do(ZRootsIteratorClosure* cl) { - if (!_claimed && Atomic::cmpxchg(&_claimed, false, true) == false) { - (_iter->*F)(cl); - } -} - -template -ZParallelOopsDo::ZParallelOopsDo(T* iter) : - _iter(iter), - _completed(false) {} - -template -void ZParallelOopsDo::oops_do(ZRootsIteratorClosure* cl) { - if (!_completed) { - (_iter->*F)(cl); - if (!_completed) { - _completed = true; +template +template +void ZParallelApply::apply(ClosureType* cl) { + if (!Atomic::load(&_completed)) { + _iter.apply(cl); + if (!Atomic::load(&_completed)) { + Atomic::store(&_completed, true); } } } -template -ZSerialWeakOopsDo::ZSerialWeakOopsDo(T* iter) : - _iter(iter), - _claimed(false) {} - -template -void ZSerialWeakOopsDo::weak_oops_do(BoolObjectClosure* is_alive, ZRootsIteratorClosure* cl) { - if (!_claimed && Atomic::cmpxchg(&_claimed, false, true) == false) { - (_iter->*F)(is_alive, cl); +template +void ZSerialWeakApply::apply(BoolObjectClosure* is_alive, OopClosure* cl) { + if (!Atomic::load(&_claimed) && Atomic::cmpxchg(&_claimed, false, true) == false) { + _iter.apply(is_alive, cl); } } -template -ZParallelWeakOopsDo::ZParallelWeakOopsDo(T* iter) : - _iter(iter), - _completed(false) {} +ZStrongOopStorageSetIterator::ZStrongOopStorageSetIterator() : + _iter() {} -template -void ZParallelWeakOopsDo::weak_oops_do(BoolObjectClosure* is_alive, ZRootsIteratorClosure* cl) { - if (!_completed) { - (_iter->*F)(is_alive, cl); - if (!_completed) { - _completed = true; - } - } +void ZStrongOopStorageSetIterator::apply(OopClosure* cl) { + ZStatTimer timer(ZSubPhaseConcurrentRootsOopStorageSet); + _iter.oops_do(cl); } -class ZRootsIteratorCodeBlobClosure : public CodeBlobClosure { -private: - ZRootsIteratorClosure* const _cl; - const bool _should_disarm_nmethods; - -public: - ZRootsIteratorCodeBlobClosure(ZRootsIteratorClosure* cl) : - _cl(cl), - _should_disarm_nmethods(cl->should_disarm_nmethods()) {} - - virtual void do_code_blob(CodeBlob* cb) { - nmethod* const nm = cb->as_nmethod_or_null(); - if (nm != NULL && nm->oops_do_try_claim()) { - ZNMethod::nmethod_oops_do(nm, _cl); - assert(!ZNMethod::supports_entry_barrier(nm) || - ZNMethod::is_armed(nm) == _should_disarm_nmethods, "Invalid state"); - if (_should_disarm_nmethods) { - ZNMethod::disarm(nm); - } - } - } -}; - -class ZRootsIteratorThreadClosure : public ThreadClosure { -private: - ZRootsIteratorClosure* const _cl; - ResourceMark _rm; - -public: - ZRootsIteratorThreadClosure(ZRootsIteratorClosure* cl) : - _cl(cl) {} - - virtual void do_thread(Thread* thread) { - ZRootsIteratorCodeBlobClosure code_cl(_cl); - thread->oops_do(_cl, ClassUnloading ? &code_cl : NULL); - _cl->do_thread(thread); - } -}; +void ZStrongCLDsIterator::apply(CLDClosure* cl) { + ZStatTimer timer(ZSubPhaseConcurrentRootsClassLoaderDataGraph); + ClassLoaderDataGraph::always_strong_cld_do(cl); +} ZJavaThreadsIterator::ZJavaThreadsIterator() : _threads(), @@ -174,146 +81,84 @@ uint ZJavaThreadsIterator::claim() { return Atomic::fetch_and_add(&_claimed, 1u); } -void ZJavaThreadsIterator::threads_do(ThreadClosure* cl) { +void ZJavaThreadsIterator::apply(ThreadClosure* cl) { + ZStatTimer timer(ZSubPhaseConcurrentRootsJavaThreads); + + // The resource mark is needed because interpreter oop maps are + // not reused in concurrent mode. Instead, they are temporary and + // resource allocated. + ResourceMark _rm; + for (uint i = claim(); i < _threads.length(); i = claim()) { cl->do_thread(_threads.thread_at(i)); } } -ZRootsIterator::ZRootsIterator(bool visit_jvmti_weak_export) : - _visit_jvmti_weak_export(visit_jvmti_weak_export), - _java_threads_iter(), - _jvmti_weak_export(this), - _vm_thread(this), - _java_threads(this), - _code_cache(this) { - assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint"); - ZStatTimer timer(ZSubPhasePauseRootsSetup); - COMPILER2_OR_JVMCI_PRESENT(DerivedPointerTable::clear()); - if (ClassUnloading) { - nmethod::oops_do_marking_prologue(); - } else { - ZNMethod::oops_do_begin(); +ZNMethodsIterator::ZNMethodsIterator() { + if (!ClassUnloading) { + ZNMethod::nmethods_do_begin(); } } -ZRootsIterator::~ZRootsIterator() { - ZStatTimer timer(ZSubPhasePauseRootsTeardown); - ResourceMark rm; - if (ClassUnloading) { - nmethod::oops_do_marking_epilogue(); - } else { - ZNMethod::oops_do_end(); +ZNMethodsIterator::~ZNMethodsIterator() { + if (!ClassUnloading) { + ZNMethod::nmethods_do_end(); } - - COMPILER2_OR_JVMCI_PRESENT(DerivedPointerTable::update_pointers()); -} - -void ZRootsIterator::do_jvmti_weak_export(ZRootsIteratorClosure* cl) { - ZStatTimer timer(ZSubPhasePauseRootsJVMTIWeakExport); - AlwaysTrueClosure always_alive; - JvmtiExport::weak_oops_do(&always_alive, cl); -} - -void ZRootsIterator::do_vm_thread(ZRootsIteratorClosure* cl) { - ZStatTimer timer(ZSubPhasePauseRootsVMThread); - ZRootsIteratorThreadClosure thread_cl(cl); - thread_cl.do_thread(VMThread::vm_thread()); } -void ZRootsIterator::do_java_threads(ZRootsIteratorClosure* cl) { - ZStatTimer timer(ZSubPhasePauseRootsJavaThreads); - ZRootsIteratorThreadClosure thread_cl(cl); - _java_threads_iter.threads_do(&thread_cl); +void ZNMethodsIterator::apply(NMethodClosure* cl) { + ZStatTimer timer(ZSubPhaseConcurrentRootsCodeCache); + ZNMethod::nmethods_do(cl); } -void ZRootsIterator::do_code_cache(ZRootsIteratorClosure* cl) { - ZStatTimer timer(ZSubPhasePauseRootsCodeCache); - ZNMethod::oops_do(cl); +ZConcurrentRootsIterator::ZConcurrentRootsIterator(int cld_claim) { + if (cld_claim != ClassLoaderData::_claim_none) { + ClassLoaderDataGraph::clear_claimed_marks(cld_claim); + } } -void ZRootsIterator::oops_do(ZRootsIteratorClosure* cl) { - ZStatTimer timer(ZSubPhasePauseRoots); - _vm_thread.oops_do(cl); - _java_threads.oops_do(cl); +void ZConcurrentRootsIterator::apply(OopClosure* cl, + CLDClosure* cld_cl, + ThreadClosure* thread_cl, + NMethodClosure* nm_cl) { + _oop_storage_set.apply(cl); + _class_loader_data_graph.apply(cld_cl); + _java_threads.apply(thread_cl); if (!ClassUnloading) { - _code_cache.oops_do(cl); - } - if (_visit_jvmti_weak_export) { - _jvmti_weak_export.oops_do(cl); + _nmethods.apply(nm_cl); } } -ZConcurrentRootsIterator::ZConcurrentRootsIterator(int cld_claim) : - _oop_storage_set_iter(), - _cld_claim(cld_claim), - _oop_storage_set(this), - _class_loader_data_graph(this) { - ZStatTimer timer(ZSubPhaseConcurrentRootsSetup); - ClassLoaderDataGraph::clear_claimed_marks(cld_claim); -} - -ZConcurrentRootsIterator::~ZConcurrentRootsIterator() { - ZStatTimer timer(ZSubPhaseConcurrentRootsTeardown); -} - -void ZConcurrentRootsIterator::do_oop_storage_set(ZRootsIteratorClosure* cl) { - ZStatTimer timer(ZSubPhaseConcurrentRootsOopStorageSet); - _oop_storage_set_iter.oops_do(cl); -} - -void ZConcurrentRootsIterator::do_class_loader_data_graph(ZRootsIteratorClosure* cl) { - ZStatTimer timer(ZSubPhaseConcurrentRootsClassLoaderDataGraph); - CLDToOopClosure cld_cl(cl, _cld_claim); - ClassLoaderDataGraph::always_strong_cld_do(&cld_cl); -} - -void ZConcurrentRootsIterator::oops_do(ZRootsIteratorClosure* cl) { - ZStatTimer timer(ZSubPhaseConcurrentRoots); - _oop_storage_set.oops_do(cl); - _class_loader_data_graph.oops_do(cl); -} - ZWeakRootsIterator::ZWeakRootsIterator() : - _jvmti_weak_export(this) { + _jvmti_tag_map() { assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint"); - ZStatTimer timer(ZSubPhasePauseWeakRootsSetup); } -ZWeakRootsIterator::~ZWeakRootsIterator() { - ZStatTimer timer(ZSubPhasePauseWeakRootsTeardown); +void ZWeakRootsIterator::apply(BoolObjectClosure* is_alive, OopClosure* cl) { + _jvmti_tag_map.apply(is_alive, cl); } -void ZWeakRootsIterator::do_jvmti_weak_export(BoolObjectClosure* is_alive, ZRootsIteratorClosure* cl) { - ZStatTimer timer(ZSubPhasePauseWeakRootsJVMTIWeakExport); - JvmtiExport::weak_oops_do(is_alive, cl); +void ZJVMTITagMapIterator::apply(BoolObjectClosure* is_alive, OopClosure* cl) { + ZStatTimer timer(ZSubPhasePauseWeakRootsJVMTITagMap); + JvmtiTagMap::weak_oops_do(is_alive, cl); } -void ZWeakRootsIterator::weak_oops_do(BoolObjectClosure* is_alive, ZRootsIteratorClosure* cl) { - ZStatTimer timer(ZSubPhasePauseWeakRoots); - _jvmti_weak_export.weak_oops_do(is_alive, cl); -} +ZWeakOopStorageSetIterator::ZWeakOopStorageSetIterator() : + _iter() {} -void ZWeakRootsIterator::oops_do(ZRootsIteratorClosure* cl) { - AlwaysTrueClosure always_alive; - weak_oops_do(&always_alive, cl); +void ZWeakOopStorageSetIterator::apply(OopClosure* cl) { + ZStatTimer timer(ZSubPhaseConcurrentWeakRootsOopStorageSet); + _iter.oops_do(cl); } -ZConcurrentWeakRootsIterator::ZConcurrentWeakRootsIterator() : - _oop_storage_set_iter(), - _oop_storage_set(this) { +void ZWeakOopStorageSetIterator::report_num_dead() { + _iter.report_num_dead(); } void ZConcurrentWeakRootsIterator::report_num_dead() { - _oop_storage_set_iter.report_num_dead(); -} - -void ZConcurrentWeakRootsIterator::do_oop_storage_set(ZRootsIteratorClosure* cl) { - ZStatTimer timer(ZSubPhaseConcurrentWeakRootsOopStorageSet); - _oop_storage_set_iter.oops_do(cl); + _oop_storage_set.iter().report_num_dead(); } -void ZConcurrentWeakRootsIterator::oops_do(ZRootsIteratorClosure* cl) { - ZStatTimer timer(ZSubPhaseConcurrentWeakRoots); - _oop_storage_set.oops_do(cl); +void ZConcurrentWeakRootsIterator::apply(OopClosure* cl) { + _oop_storage_set.apply(cl); } diff --git a/src/hotspot/share/gc/z/zRootsIterator.hpp b/src/hotspot/share/gc/z/zRootsIterator.hpp index becb39089f6..57b7ce6372d 100644 --- a/src/hotspot/share/gc/z/zRootsIterator.hpp +++ b/src/hotspot/share/gc/z/zRootsIterator.hpp @@ -24,72 +24,56 @@ #ifndef SHARE_GC_Z_ZROOTSITERATOR_HPP #define SHARE_GC_Z_ZROOTSITERATOR_HPP -#include "gc/shared/oopStorageParState.hpp" #include "gc/shared/oopStorageSetParState.hpp" -#include "gc/shared/suspendibleThreadSet.hpp" -#include "memory/allocation.hpp" +#include "logging/log.hpp" #include "memory/iterator.hpp" -#include "runtime/thread.hpp" #include "runtime/threadSMR.hpp" -#include "utilities/globalDefinitions.hpp" -class ZRootsIteratorClosure; - -typedef OopStorage::ParState ZOopStorageIterator; -typedef OopStorageSetStrongParState ZOopStorageSetStrongIterator; -typedef OopStorageSetWeakParState ZOopStorageSetWeakIterator; - -template -class ZSerialOopsDo { +template +class ZParallelApply { private: - T* const _iter; - volatile bool _claimed; + Iterator _iter; + volatile bool _completed; public: - ZSerialOopsDo(T* iter); - void oops_do(ZRootsIteratorClosure* cl); -}; + ZParallelApply() : + _iter(), + _completed(false) {} -template -class ZParallelOopsDo { -private: - T* const _iter; - volatile bool _completed; + template + void apply(ClosureType* cl); -public: - ZParallelOopsDo(T* iter); - void oops_do(ZRootsIteratorClosure* cl); + Iterator& iter() { + return _iter; + } }; -template -class ZSerialWeakOopsDo { +template +class ZSerialWeakApply { private: - T* const _iter; + Iterator _iter; volatile bool _claimed; public: - ZSerialWeakOopsDo(T* iter); - void weak_oops_do(BoolObjectClosure* is_alive, ZRootsIteratorClosure* cl); + ZSerialWeakApply() : + _iter(), + _claimed(false) {} + + void apply(BoolObjectClosure* is_alive, OopClosure* cl); }; -template -class ZParallelWeakOopsDo { -private: - T* const _iter; - volatile bool _completed; +class ZStrongOopStorageSetIterator { + OopStorageSetStrongParState _iter; public: - ZParallelWeakOopsDo(T* iter); - void weak_oops_do(BoolObjectClosure* is_alive, ZRootsIteratorClosure* cl); + ZStrongOopStorageSetIterator(); + + void apply(OopClosure* cl); }; -class ZRootsIteratorClosure : public OopClosure { +class ZStrongCLDsIterator { public: - virtual void do_thread(Thread* thread) {} - - virtual bool should_disarm_nmethods() const { - return false; - } + void apply(CLDClosure* cl); }; class ZJavaThreadsIterator { @@ -102,93 +86,66 @@ class ZJavaThreadsIterator { public: ZJavaThreadsIterator(); - void threads_do(ThreadClosure* cl); + void apply(ThreadClosure* cl); }; -class ZRootsIterator { -private: - const bool _visit_jvmti_weak_export; - ZJavaThreadsIterator _java_threads_iter; - - void do_jvmti_weak_export(ZRootsIteratorClosure* cl); - void do_vm_thread(ZRootsIteratorClosure* cl); - void do_java_threads(ZRootsIteratorClosure* cl); - void do_code_cache(ZRootsIteratorClosure* cl); - - ZSerialOopsDo _jvmti_weak_export; - ZSerialOopsDo _vm_thread; - ZParallelOopsDo _java_threads; - ZParallelOopsDo _code_cache; - +class ZNMethodsIterator { public: - ZRootsIterator(bool visit_jvmti_weak_export = false); - ~ZRootsIterator(); + ZNMethodsIterator(); + ~ZNMethodsIterator(); - void oops_do(ZRootsIteratorClosure* cl); + void apply(NMethodClosure* cl); }; class ZConcurrentRootsIterator { private: - ZOopStorageSetStrongIterator _oop_storage_set_iter; - const int _cld_claim; - - void do_oop_storage_set(ZRootsIteratorClosure* cl); - void do_class_loader_data_graph(ZRootsIteratorClosure* cl); - - ZParallelOopsDo _oop_storage_set; - ZParallelOopsDo _class_loader_data_graph; + ZParallelApply _oop_storage_set; + ZParallelApply _class_loader_data_graph; + ZParallelApply _java_threads; + ZParallelApply _nmethods; public: ZConcurrentRootsIterator(int cld_claim); - ~ZConcurrentRootsIterator(); - void oops_do(ZRootsIteratorClosure* cl); + void apply(OopClosure* cl, + CLDClosure* cld_cl, + ThreadClosure* thread_cl, + NMethodClosure* nm_cl); }; -class ZConcurrentRootsIteratorClaimStrong : public ZConcurrentRootsIterator { -public: - ZConcurrentRootsIteratorClaimStrong() : - ZConcurrentRootsIterator(ClassLoaderData::_claim_strong) {} -}; +class ZWeakOopStorageSetIterator { +private: + OopStorageSetWeakParState _iter; -class ZConcurrentRootsIteratorClaimOther : public ZConcurrentRootsIterator { public: - ZConcurrentRootsIteratorClaimOther() : - ZConcurrentRootsIterator(ClassLoaderData::_claim_other) {} + ZWeakOopStorageSetIterator(); + + void apply(OopClosure* cl); + + void report_num_dead(); }; -class ZConcurrentRootsIteratorClaimNone : public ZConcurrentRootsIterator { +class ZJVMTITagMapIterator { public: - ZConcurrentRootsIteratorClaimNone() : - ZConcurrentRootsIterator(ClassLoaderData::_claim_none) {} + void apply(BoolObjectClosure* is_alive, OopClosure* cl); }; class ZWeakRootsIterator { private: - void do_jvmti_weak_export(BoolObjectClosure* is_alive, ZRootsIteratorClosure* cl); - - ZSerialWeakOopsDo _jvmti_weak_export; + ZSerialWeakApply _jvmti_tag_map; public: ZWeakRootsIterator(); - ~ZWeakRootsIterator(); - void weak_oops_do(BoolObjectClosure* is_alive, ZRootsIteratorClosure* cl); - void oops_do(ZRootsIteratorClosure* cl); + void apply(BoolObjectClosure* is_alive, OopClosure* cl); }; class ZConcurrentWeakRootsIterator { private: - ZOopStorageSetWeakIterator _oop_storage_set_iter; - - void do_oop_storage_set(ZRootsIteratorClosure* cl); - - ZParallelOopsDo _oop_storage_set; + ZParallelApply _oop_storage_set; public: - ZConcurrentWeakRootsIterator(); - - void oops_do(ZRootsIteratorClosure* cl); + void apply(OopClosure* cl); void report_num_dead(); }; diff --git a/src/hotspot/share/gc/z/zRuntimeWorkers.cpp b/src/hotspot/share/gc/z/zRuntimeWorkers.cpp index e77fa66b9dc..16a4ee8d23a 100644 --- a/src/hotspot/share/gc/z/zRuntimeWorkers.cpp +++ b/src/hotspot/share/gc/z/zRuntimeWorkers.cpp @@ -23,10 +23,10 @@ #include "precompiled.hpp" #include "gc/shared/gcLogPrecious.hpp" -#include "gc/shared/workgroup.hpp" #include "gc/z/zRuntimeWorkers.hpp" #include "gc/z/zThread.hpp" #include "runtime/java.hpp" +#include "runtime/mutex.hpp" #include "runtime/mutexLocker.hpp" class ZRuntimeWorkersInitializeTask : public AbstractGangTask { diff --git a/src/hotspot/share/gc/z/zSafeDelete.inline.hpp b/src/hotspot/share/gc/z/zSafeDelete.inline.hpp index ebe102b014d..2a14eeb9387 100644 --- a/src/hotspot/share/gc/z/zSafeDelete.inline.hpp +++ b/src/hotspot/share/gc/z/zSafeDelete.inline.hpp @@ -69,7 +69,7 @@ void ZSafeDeleteImpl::disable_deferred_delete() { ZLocker locker(_lock); assert(_enabled > 0, "Invalid state"); if (--_enabled == 0) { - deferred.transfer(&_deferred); + deferred.swap(&_deferred); } } diff --git a/src/hotspot/share/gc/z/zStackWatermark.cpp b/src/hotspot/share/gc/z/zStackWatermark.cpp new file mode 100644 index 00000000000..eb154fe933c --- /dev/null +++ b/src/hotspot/share/gc/z/zStackWatermark.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "precompiled.hpp" +#include "gc/z/zAddress.hpp" +#include "gc/z/zBarrier.inline.hpp" +#include "gc/z/zOopClosures.inline.hpp" +#include "gc/z/zStackWatermark.hpp" +#include "gc/z/zThread.inline.hpp" +#include "gc/z/zThreadLocalAllocBuffer.hpp" +#include "gc/z/zThreadLocalData.hpp" +#include "gc/z/zVerify.hpp" +#include "memory/resourceArea.inline.hpp" +#include "runtime/frame.inline.hpp" +#include "utilities/preserveException.hpp" + +ZOnStackCodeBlobClosure::ZOnStackCodeBlobClosure() : + _bs_nm(BarrierSet::barrier_set()->barrier_set_nmethod()) {} + +void ZOnStackCodeBlobClosure::do_code_blob(CodeBlob* cb) { + nmethod* const nm = cb->as_nmethod_or_null(); + if (nm != NULL) { + const bool result = _bs_nm->nmethod_entry_barrier(nm); + assert(result, "NMethod on-stack must be alive"); + } +} + +ThreadLocalAllocStats& ZStackWatermark::stats() { + return _stats; +} + +uint32_t ZStackWatermark::epoch_id() const { + return *ZAddressBadMaskHighOrderBitsAddr; +} + +ZStackWatermark::ZStackWatermark(JavaThread* jt) : + StackWatermark(jt, StackWatermarkKind::gc, *ZAddressBadMaskHighOrderBitsAddr), + _jt_cl(), + _cb_cl(), + _stats() {} + +OopClosure* ZStackWatermark::closure_from_context(void* context) { + if (context != NULL) { + assert(ZThread::is_worker(), "Unexpected thread passing in context: " PTR_FORMAT, p2i(context)); + return reinterpret_cast(context); + } else { + return &_jt_cl; + } +} + +void ZStackWatermark::start_processing_impl(void* context) { + // Verify the head (no_frames) of the thread is bad before fixing it. + ZVerify::verify_thread_head_bad(_jt); + + // Process the non-frame part of the thread + _jt->oops_do_no_frames(closure_from_context(context), &_cb_cl); + ZThreadLocalData::do_invisible_root(_jt, ZBarrier::load_barrier_on_invisible_root_oop_field); + + // Verification of frames is done after processing of the "head" (no_frames). + // The reason is that the exception oop is fiddled with during frame processing. + ZVerify::verify_thread_frames_bad(_jt); + + // Update thread local address bad mask + ZThreadLocalData::set_address_bad_mask(_jt, ZAddressBadMask); + + // Retire TLAB + if (ZGlobalPhase == ZPhaseMark) { + ZThreadLocalAllocBuffer::retire(_jt, &_stats); + } else { + ZThreadLocalAllocBuffer::remap(_jt); + } + + // Publishes the processing start to concurrent threads + StackWatermark::start_processing_impl(context); +} + +void ZStackWatermark::process(const frame& fr, RegisterMap& register_map, void* context) { + ZVerify::verify_frame_bad(fr, register_map); + fr.oops_do(closure_from_context(context), &_cb_cl, ®ister_map, DerivedPointerIterationMode::_directly); +} diff --git a/src/hotspot/share/gc/z/zStackWatermark.hpp b/src/hotspot/share/gc/z/zStackWatermark.hpp new file mode 100644 index 00000000000..dc657aaa7cf --- /dev/null +++ b/src/hotspot/share/gc/z/zStackWatermark.hpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef SHARE_GC_Z_ZSTACKWATERMARK_HPP +#define SHARE_GC_Z_ZSTACKWATERMARK_HPP + +#include "gc/shared/barrierSet.hpp" +#include "gc/shared/barrierSetNMethod.hpp" +#include "gc/z/zOopClosures.hpp" +#include "memory/allocation.hpp" +#include "memory/iterator.hpp" +#include "oops/oopsHierarchy.hpp" +#include "runtime/stackWatermark.hpp" +#include "utilities/globalDefinitions.hpp" + +class frame; +class JavaThread; + +class ZOnStackCodeBlobClosure : public CodeBlobClosure { +private: + BarrierSetNMethod* _bs_nm; + + virtual void do_code_blob(CodeBlob* cb); + +public: + ZOnStackCodeBlobClosure(); +}; + +class ZStackWatermark : public StackWatermark { +private: + ZLoadBarrierOopClosure _jt_cl; + ZOnStackCodeBlobClosure _cb_cl; + ThreadLocalAllocStats _stats; + + OopClosure* closure_from_context(void* context); + + virtual uint32_t epoch_id() const; + virtual void start_processing_impl(void* context); + virtual void process(const frame& fr, RegisterMap& register_map, void* context); + +public: + ZStackWatermark(JavaThread* jt); + + ThreadLocalAllocStats& stats(); +}; + +#endif // SHARE_GC_Z_ZSTACKWATERMARK_HPP diff --git a/src/hotspot/share/gc/z/zStat.cpp b/src/hotspot/share/gc/z/zStat.cpp index 183e8d5bcc4..5d2933289d2 100644 --- a/src/hotspot/share/gc/z/zStat.cpp +++ b/src/hotspot/share/gc/z/zStat.cpp @@ -25,10 +25,7 @@ #include "gc/z/zCollectedHeap.hpp" #include "gc/z/zCPU.inline.hpp" #include "gc/z/zGlobals.hpp" -#include "gc/z/zHeap.inline.hpp" -#include "gc/z/zLargePages.inline.hpp" #include "gc/z/zNMethodTable.hpp" -#include "gc/z/zNUMA.hpp" #include "gc/z/zRelocationSetSelector.inline.hpp" #include "gc/z/zStat.hpp" #include "gc/z/zTracer.inline.hpp" @@ -38,7 +35,6 @@ #include "runtime/os.hpp" #include "runtime/timer.hpp" #include "utilities/align.hpp" -#include "utilities/compilerWarnings.hpp" #include "utilities/debug.hpp" #include "utilities/ticks.hpp" @@ -1144,12 +1140,17 @@ void ZStatMark::print() { // Stat relocation // ZRelocationSetSelectorStats ZStatRelocation::_stats; +size_t ZStatRelocation::_forwarding_usage; bool ZStatRelocation::_success; void ZStatRelocation::set_at_select_relocation_set(const ZRelocationSetSelectorStats& stats) { _stats = stats; } +void ZStatRelocation::set_at_install_relocation_set(size_t forwarding_usage) { + _forwarding_usage = forwarding_usage; +} + void ZStatRelocation::set_at_relocate_end(bool success) { _success = success; } @@ -1173,6 +1174,7 @@ void ZStatRelocation::print() { } print("Large", _stats.large()); + log_info(gc, reloc)("Forwarding Usage: " SIZE_FORMAT "M", _forwarding_usage / M); log_info(gc, reloc)("Relocation: %s", _success ? "Successful" : "Incomplete"); } @@ -1190,10 +1192,9 @@ void ZStatNMethods::print() { // void ZStatMetaspace::print() { log_info(gc, metaspace)("Metaspace: " - SIZE_FORMAT "M used, " SIZE_FORMAT "M capacity, " + SIZE_FORMAT "M used, " SIZE_FORMAT "M committed, " SIZE_FORMAT "M reserved", MetaspaceUtils::used_bytes() / M, - MetaspaceUtils::capacity_bytes() / M, MetaspaceUtils::committed_bytes() / M, MetaspaceUtils::reserved_bytes() / M); } diff --git a/src/hotspot/share/gc/z/zStat.hpp b/src/hotspot/share/gc/z/zStat.hpp index 3faac3b8ea5..6d44c3c99c5 100644 --- a/src/hotspot/share/gc/z/zStat.hpp +++ b/src/hotspot/share/gc/z/zStat.hpp @@ -423,12 +423,14 @@ class ZStatMark : public AllStatic { class ZStatRelocation : public AllStatic { private: static ZRelocationSetSelectorStats _stats; + static size_t _forwarding_usage; static bool _success; static void print(const char* name, const ZRelocationSetSelectorGroupStats& group); public: static void set_at_select_relocation_set(const ZRelocationSetSelectorStats& stats); + static void set_at_install_relocation_set(size_t forwarding_usage); static void set_at_relocate_end(bool success); static void print(); diff --git a/src/hotspot/share/gc/z/zThreadLocalAllocBuffer.cpp b/src/hotspot/share/gc/z/zThreadLocalAllocBuffer.cpp index fd1d25e38e1..5fd3a07c78a 100644 --- a/src/hotspot/share/gc/z/zThreadLocalAllocBuffer.cpp +++ b/src/hotspot/share/gc/z/zThreadLocalAllocBuffer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,9 +23,11 @@ #include "precompiled.hpp" #include "gc/z/zAddress.inline.hpp" +#include "gc/z/zStackWatermark.hpp" #include "gc/z/zThreadLocalAllocBuffer.hpp" #include "gc/z/zValue.inline.hpp" #include "runtime/globals.hpp" +#include "runtime/stackWatermarkSet.inline.hpp" #include "runtime/thread.hpp" ZPerWorker* ZThreadLocalAllocBuffer::_stats = NULL; @@ -64,9 +66,9 @@ static void fixup_address(HeapWord** p) { *p = (HeapWord*)ZAddress::good_or_null((uintptr_t)*p); } -void ZThreadLocalAllocBuffer::retire(Thread* thread) { - if (UseTLAB && thread->is_Java_thread()) { - ThreadLocalAllocStats* const stats = _stats->addr(); +void ZThreadLocalAllocBuffer::retire(JavaThread* thread, ThreadLocalAllocStats* stats) { + if (UseTLAB) { + stats->reset(); thread->tlab().addresses_do(fixup_address); thread->tlab().retire(stats); if (ResizeTLAB) { @@ -75,8 +77,15 @@ void ZThreadLocalAllocBuffer::retire(Thread* thread) { } } -void ZThreadLocalAllocBuffer::remap(Thread* thread) { - if (UseTLAB && thread->is_Java_thread()) { +void ZThreadLocalAllocBuffer::remap(JavaThread* thread) { + if (UseTLAB) { thread->tlab().addresses_do(fixup_address); } } + +void ZThreadLocalAllocBuffer::update_stats(JavaThread* thread) { + if (UseTLAB) { + ZStackWatermark* const watermark = StackWatermarkSet::get(thread, StackWatermarkKind::gc); + _stats->addr()->update(watermark->stats()); + } +} diff --git a/src/hotspot/share/gc/z/zThreadLocalAllocBuffer.hpp b/src/hotspot/share/gc/z/zThreadLocalAllocBuffer.hpp index cc708164822..d6693fceb00 100644 --- a/src/hotspot/share/gc/z/zThreadLocalAllocBuffer.hpp +++ b/src/hotspot/share/gc/z/zThreadLocalAllocBuffer.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,8 @@ #include "gc/z/zValue.hpp" #include "memory/allocation.hpp" +class JavaThread; + class ZThreadLocalAllocBuffer : public AllStatic { private: static ZPerWorker* _stats; @@ -38,8 +40,9 @@ class ZThreadLocalAllocBuffer : public AllStatic { static void reset_statistics(); static void publish_statistics(); - static void retire(Thread* thread); - static void remap(Thread* thread); + static void retire(JavaThread* thread, ThreadLocalAllocStats* stats); + static void remap(JavaThread* thread); + static void update_stats(JavaThread* thread); }; #endif // SHARE_GC_Z_ZTHREADLOCALALLOCBUFFER_HPP diff --git a/src/hotspot/share/gc/z/zThreadLocalData.hpp b/src/hotspot/share/gc/z/zThreadLocalData.hpp index ac308efe41e..e7e0b37af1d 100644 --- a/src/hotspot/share/gc/z/zThreadLocalData.hpp +++ b/src/hotspot/share/gc/z/zThreadLocalData.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -84,7 +84,7 @@ class ZThreadLocalData { } static ByteSize nmethod_disarmed_offset() { - return address_bad_mask_offset() + in_ByteSize(ZNMethodDisarmedOffset); + return address_bad_mask_offset() + in_ByteSize(ZAddressBadMaskHighOrderBitsOffset); } }; diff --git a/src/hotspot/share/gc/z/zTracer.inline.hpp b/src/hotspot/share/gc/z/zTracer.inline.hpp index 2289b0083d6..3e7f62cb16a 100644 --- a/src/hotspot/share/gc/z/zTracer.inline.hpp +++ b/src/hotspot/share/gc/z/zTracer.inline.hpp @@ -24,7 +24,6 @@ #ifndef SHARE_GC_Z_ZTRACER_INLINE_HPP #define SHARE_GC_Z_ZTRACER_INLINE_HPP -#include "gc/z/zStat.hpp" #include "gc/z/zTracer.hpp" #include "jfr/jfrEvents.hpp" diff --git a/src/hotspot/share/gc/z/zUncommitter.hpp b/src/hotspot/share/gc/z/zUncommitter.hpp index 0e8384012e2..6cb38d9db4c 100644 --- a/src/hotspot/share/gc/z/zUncommitter.hpp +++ b/src/hotspot/share/gc/z/zUncommitter.hpp @@ -24,8 +24,8 @@ #ifndef SHARE_GC_Z_ZUNCOMMITTER_HPP #define SHARE_GC_Z_ZUNCOMMITTER_HPP -#include "gc/z/zLock.hpp" #include "gc/shared/concurrentGCThread.hpp" +#include "gc/z/zLock.hpp" class ZPageAllocation; diff --git a/src/hotspot/share/gc/z/zUnload.cpp b/src/hotspot/share/gc/z/zUnload.cpp index d3fd8877763..e4d2cfc6eec 100644 --- a/src/hotspot/share/gc/z/zUnload.cpp +++ b/src/hotspot/share/gc/z/zUnload.cpp @@ -72,7 +72,7 @@ class ZIsUnloadingBehaviour : public IsUnloadingBehaviour { ZReentrantLock* const lock = ZNMethod::lock_for_nmethod(nm); ZLocker locker(lock); ZIsUnloadingOopClosure cl; - ZNMethod::nmethod_oops_do(nm, &cl); + ZNMethod::nmethod_oops_do_inner(nm, &cl); return cl.is_unloading(); } }; @@ -164,5 +164,5 @@ void ZUnload::purge() { void ZUnload::finish() { // Resize and verify metaspace MetaspaceGC::compute_new_size(); - MetaspaceUtils::verify_metrics(); + DEBUG_ONLY(MetaspaceUtils::verify();) } diff --git a/src/hotspot/share/gc/z/zUnmapper.hpp b/src/hotspot/share/gc/z/zUnmapper.hpp index 894926cb25e..fd263948759 100644 --- a/src/hotspot/share/gc/z/zUnmapper.hpp +++ b/src/hotspot/share/gc/z/zUnmapper.hpp @@ -24,9 +24,9 @@ #ifndef SHARE_GC_Z_ZUNMAPPER_HPP #define SHARE_GC_Z_ZUNMAPPER_HPP +#include "gc/shared/concurrentGCThread.hpp" #include "gc/z/zList.hpp" #include "gc/z/zLock.hpp" -#include "gc/shared/concurrentGCThread.hpp" class ZPage; class ZPageAllocator; diff --git a/src/hotspot/share/gc/z/zVerify.cpp b/src/hotspot/share/gc/z/zVerify.cpp index 51f48893caf..1481a8576d5 100644 --- a/src/hotspot/share/gc/z/zVerify.cpp +++ b/src/hotspot/share/gc/z/zVerify.cpp @@ -23,16 +23,29 @@ #include "precompiled.hpp" #include "classfile/classLoaderData.hpp" -#include "gc/z/zAddress.hpp" +#include "gc/z/zAddress.inline.hpp" #include "gc/z/zHeap.inline.hpp" +#include "gc/z/zNMethod.hpp" #include "gc/z/zOop.hpp" #include "gc/z/zPageAllocator.hpp" #include "gc/z/zResurrection.hpp" #include "gc/z/zRootsIterator.hpp" +#include "gc/z/zStackWatermark.hpp" #include "gc/z/zStat.hpp" #include "gc/z/zVerify.hpp" #include "memory/iterator.inline.hpp" +#include "memory/resourceArea.hpp" #include "oops/oop.hpp" +#include "runtime/frame.inline.hpp" +#include "runtime/globals.hpp" +#include "runtime/handles.hpp" +#include "runtime/safepoint.hpp" +#include "runtime/stackWatermark.inline.hpp" +#include "runtime/stackWatermarkSet.inline.hpp" +#include "runtime/thread.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/preserveException.hpp" #define BAD_OOP_ARG(o, p) "Bad oop " PTR_FORMAT " found at " PTR_FORMAT, p2i(o), p2i(p) @@ -54,18 +67,115 @@ static void z_verify_possibly_weak_oop(oop* p) { } } -class ZVerifyRootClosure : public ZRootsIteratorClosure { +class ZVerifyRootClosure : public OopClosure { +private: + const bool _verify_fixed; + public: + ZVerifyRootClosure(bool verify_fixed) : + _verify_fixed(verify_fixed) {} + virtual void do_oop(oop* p) { - z_verify_oop(p); + if (_verify_fixed) { + z_verify_oop(p); + } else { + // Don't know the state of the oop. + oop obj = *p; + obj = NativeAccess::oop_load(&obj); + z_verify_oop(&obj); + } } virtual void do_oop(narrowOop*) { ShouldNotReachHere(); } + + bool verify_fixed() const { + return _verify_fixed; + } }; -class ZVerifyOopClosure : public ClaimMetadataVisitingOopIterateClosure, public ZRootsIteratorClosure { +class ZVerifyCodeBlobClosure : public CodeBlobToOopClosure { +public: + ZVerifyCodeBlobClosure(ZVerifyRootClosure* _cl) : + CodeBlobToOopClosure(_cl, false /* fix_relocations */) {} + + virtual void do_code_blob(CodeBlob* cb) { + CodeBlobToOopClosure::do_code_blob(cb); + } +}; + +class ZVerifyStack : public OopClosure { +private: + ZVerifyRootClosure* const _cl; + JavaThread* const _jt; + uint64_t _last_good; + bool _verifying_bad_frames; + +public: + ZVerifyStack(ZVerifyRootClosure* cl, JavaThread* jt) : + _cl(cl), + _jt(jt), + _last_good(0), + _verifying_bad_frames(false) { + ZStackWatermark* const stack_watermark = StackWatermarkSet::get(jt, StackWatermarkKind::gc); + + if (_cl->verify_fixed()) { + assert(stack_watermark->processing_started(), "Should already have been fixed"); + assert(stack_watermark->processing_completed(), "Should already have been fixed"); + } else { + // We don't really know the state of the stack, verify watermark. + if (!stack_watermark->processing_started()) { + _verifying_bad_frames = true; + } else { + // Not time yet to verify bad frames + _last_good = stack_watermark->last_processed(); + } + } + } + + void do_oop(oop* p) { + if (_verifying_bad_frames) { + const oop obj = *p; + guarantee(!ZAddress::is_good(ZOop::to_address(obj)), BAD_OOP_ARG(obj, p)); + } + _cl->do_oop(p); + } + + void do_oop(narrowOop* p) { + ShouldNotReachHere(); + } + + void prepare_next_frame(frame& frame) { + if (_cl->verify_fixed()) { + // All frames need to be good + return; + } + + // The verification has two modes, depending on whether we have reached the + // last processed frame or not. Before it is reached, we expect everything to + // be good. After reaching it, we expect everything to be bad. + const uintptr_t sp = reinterpret_cast(frame.sp()); + + if (!_verifying_bad_frames && sp == _last_good) { + // Found the last good frame, now verify the bad ones + _verifying_bad_frames = true; + } + } + + void verify_frames() { + ZVerifyCodeBlobClosure cb_cl(_cl); + for (StackFrameStream frames(_jt, true /* update */, false /* process_frames */); + !frames.is_done(); + frames.next()) { + frame& frame = *frames.current(); + frame.oops_do(this, &cb_cl, frames.register_map(), DerivedPointerIterationMode::_ignore); + prepare_next_frame(frame); + } + } +}; + +class ZVerifyOopClosure : public ClaimMetadataVisitingOopIterateClosure { private: const bool _verify_weaks; @@ -91,49 +201,92 @@ class ZVerifyOopClosure : public ClaimMetadataVisitingOopIterateClosure, public virtual ReferenceIterationMode reference_iteration_mode() { return _verify_weaks ? DO_FIELDS : DO_FIELDS_EXCEPT_REFERENT; } +}; -#ifdef ASSERT - // Verification handled by the closure itself - virtual bool should_verify_oops() { - return false; +typedef ClaimingCLDToOopClosure ZVerifyCLDClosure; + +class ZVerifyThreadClosure : public ThreadClosure { +private: + ZVerifyRootClosure* const _cl; + +public: + ZVerifyThreadClosure(ZVerifyRootClosure* cl) : + _cl(cl) {} + + virtual void do_thread(Thread* thread) { + thread->oops_do_no_frames(_cl, NULL); + + JavaThread* const jt = thread->as_Java_thread(); + if (!jt->has_last_Java_frame()) { + return; + } + + ZVerifyStack verify_stack(_cl, jt); + verify_stack.verify_frames(); } -#endif }; -template -void ZVerify::roots() { - assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); - assert(!ZResurrection::is_blocked(), "Invalid phase"); +class ZVerifyNMethodClosure : public NMethodClosure { +private: + OopClosure* const _cl; + BarrierSetNMethod* const _bs_nm; + const bool _verify_fixed; - if (ZVerifyRoots) { - ZVerifyRootClosure cl; - RootsIterator iter; - iter.oops_do(&cl); + bool trust_nmethod_state() const { + // The root iterator will visit non-processed + // nmethods class unloading is turned off. + return ClassUnloading || _verify_fixed; } -} -void ZVerify::roots_strong() { - roots(); -} +public: + ZVerifyNMethodClosure(OopClosure* cl, bool verify_fixed) : + _cl(cl), + _bs_nm(BarrierSet::barrier_set()->barrier_set_nmethod()), + _verify_fixed(verify_fixed) {} -void ZVerify::roots_weak() { - roots(); + virtual void do_nmethod(nmethod* nm) { + assert(!trust_nmethod_state() || !_bs_nm->is_armed(nm), "Should not encounter any armed nmethods"); + + ZNMethod::nmethod_oops_do(nm, _cl); + } +}; + +void ZVerify::roots_concurrent_strong(bool verify_fixed) { + ZVerifyRootClosure cl(verify_fixed); + ZVerifyCLDClosure cld_cl(&cl); + ZVerifyThreadClosure thread_cl(&cl); + ZVerifyNMethodClosure nm_cl(&cl, verify_fixed); + + ZConcurrentRootsIterator iter(ClassLoaderData::_claim_none); + iter.apply(&cl, + &cld_cl, + &thread_cl, + &nm_cl); } -void ZVerify::roots_concurrent_strong() { - roots(); +void ZVerify::roots_weak() { + AlwaysTrueClosure is_alive; + ZVerifyRootClosure cl(true /* verify_fixed */); + ZWeakRootsIterator iter; + iter.apply(&is_alive, &cl); } void ZVerify::roots_concurrent_weak() { - roots(); + ZVerifyRootClosure cl(true /* verify_fixed */); + ZConcurrentWeakRootsIterator iter; + iter.apply(&cl); } -void ZVerify::roots(bool verify_weaks) { - roots_strong(); - roots_concurrent_strong(); - if (verify_weaks) { - roots_weak(); - roots_concurrent_weak(); +void ZVerify::roots(bool verify_concurrent_strong, bool verify_weaks) { + assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); + assert(!ZResurrection::is_blocked(), "Invalid phase"); + + if (ZVerifyRoots) { + roots_concurrent_strong(verify_concurrent_strong); + if (verify_weaks) { + roots_weak(); + roots_concurrent_weak(); + } } } @@ -149,27 +302,27 @@ void ZVerify::objects(bool verify_weaks) { } } -void ZVerify::roots_and_objects(bool verify_weaks) { - roots(verify_weaks); +void ZVerify::roots_and_objects(bool verify_concurrent_strong, bool verify_weaks) { + roots(verify_concurrent_strong, verify_weaks); objects(verify_weaks); } void ZVerify::before_zoperation() { // Verify strong roots ZStatTimerDisable disable; - roots_strong(); + roots(false /* verify_concurrent_strong */, false /* verify_weaks */); } void ZVerify::after_mark() { // Verify all strong roots and strong references ZStatTimerDisable disable; - roots_and_objects(false /* verify_weaks */); + roots_and_objects(true /* verify_concurrent_strong*/, false /* verify_weaks */); } void ZVerify::after_weak_processing() { // Verify all roots and all references ZStatTimerDisable disable; - roots_and_objects(true /* verify_weaks */); + roots_and_objects(true /* verify_concurrent_strong*/, true /* verify_weaks */); } template @@ -206,3 +359,59 @@ ZVerifyViewsFlip::~ZVerifyViewsFlip() { ZHeap::heap()->pages_do(&cl); } } + +#ifdef ASSERT + +class ZVerifyBadOopClosure : public OopClosure { +public: + virtual void do_oop(oop* p) { + const oop o = *p; + assert(!ZAddress::is_good(ZOop::to_address(o)), "Should not be good: " PTR_FORMAT, p2i(o)); + } + + virtual void do_oop(narrowOop* p) { + ShouldNotReachHere(); + } +}; + +// This class encapsulates various marks we need to deal with calling the +// frame iteration code from arbitrary points in the runtime. It is mostly +// due to problems that we might want to eventually clean up inside of the +// frame iteration code, such as creating random handles even though there +// is no safepoint to protect against, and fiddling around with exceptions. +class StackWatermarkProcessingMark { + ResetNoHandleMark _rnhm; + HandleMark _hm; + PreserveExceptionMark _pem; + ResourceMark _rm; + +public: + StackWatermarkProcessingMark(Thread* thread) : + _rnhm(), + _hm(thread), + _pem(thread), + _rm(thread) { } +}; + +void ZVerify::verify_frame_bad(const frame& fr, RegisterMap& register_map) { + ZVerifyBadOopClosure verify_cl; + fr.oops_do(&verify_cl, NULL, ®ister_map, DerivedPointerIterationMode::_ignore); +} + +void ZVerify::verify_thread_head_bad(JavaThread* jt) { + ZVerifyBadOopClosure verify_cl; + jt->oops_do_no_frames(&verify_cl, NULL); +} + +void ZVerify::verify_thread_frames_bad(JavaThread* jt) { + if (jt->has_last_Java_frame()) { + ZVerifyBadOopClosure verify_cl; + StackWatermarkProcessingMark swpm(Thread::current()); + // Traverse the execution stack + for (StackFrameStream fst(jt, true /* update */, false /* process_frames */); !fst.is_done(); fst.next()) { + fst.current()->oops_do(&verify_cl, NULL /* code_cl */, fst.register_map(), DerivedPointerIterationMode::_ignore); + } + } +} + +#endif // ASSERT diff --git a/src/hotspot/share/gc/z/zVerify.hpp b/src/hotspot/share/gc/z/zVerify.hpp index 9473cae0cff..400fc05a589 100644 --- a/src/hotspot/share/gc/z/zVerify.hpp +++ b/src/hotspot/share/gc/z/zVerify.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,25 +26,27 @@ #include "memory/allocation.hpp" +class frame; class ZPageAllocator; class ZVerify : public AllStatic { private: - template static void roots(); - - static void roots_strong(); + static void roots_concurrent_strong(bool verify_fixed); static void roots_weak(); - static void roots_concurrent_strong(); static void roots_concurrent_weak(); - static void roots(bool verify_weaks); + static void roots(bool verify_concurrent_strong, bool verify_weaks); static void objects(bool verify_weaks); - static void roots_and_objects(bool verify_weaks); + static void roots_and_objects(bool verify_concurrent_strong, bool verify_weaks); public: static void before_zoperation(); static void after_mark(); static void after_weak_processing(); + + static void verify_thread_head_bad(JavaThread* thread) NOT_DEBUG_RETURN; + static void verify_thread_frames_bad(JavaThread* thread) NOT_DEBUG_RETURN; + static void verify_frame_bad(const frame& fr, RegisterMap& register_map) NOT_DEBUG_RETURN; }; class ZVerifyViewsFlip { diff --git a/src/hotspot/share/gc/z/zWeakRootsProcessor.cpp b/src/hotspot/share/gc/z/zWeakRootsProcessor.cpp index 927b5e4da18..7c88523b37c 100644 --- a/src/hotspot/share/gc/z/zWeakRootsProcessor.cpp +++ b/src/hotspot/share/gc/z/zWeakRootsProcessor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,12 +22,9 @@ */ #include "precompiled.hpp" -#include "gc/z/zHeap.inline.hpp" #include "gc/z/zOopClosures.inline.hpp" -#include "gc/z/zStat.hpp" #include "gc/z/zTask.hpp" -#include "gc/z/zThread.hpp" -#include "runtime/jniHandles.hpp" +#include "gc/z/zWorkers.hpp" ZWeakRootsProcessor::ZWeakRootsProcessor(ZWorkers* workers) : _workers(workers) {} @@ -44,13 +41,13 @@ class ZProcessWeakRootsTask : public ZTask { virtual void work() { ZPhantomIsAliveObjectClosure is_alive; ZPhantomKeepAliveOopClosure keep_alive; - _weak_roots.weak_oops_do(&is_alive, &keep_alive); + _weak_roots.apply(&is_alive, &keep_alive); } }; void ZWeakRootsProcessor::process_weak_roots() { ZProcessWeakRootsTask task; - _workers->run_parallel(&task); + _workers->run_serial(&task); } class ZProcessConcurrentWeakRootsTask : public ZTask { @@ -68,7 +65,7 @@ class ZProcessConcurrentWeakRootsTask : public ZTask { virtual void work() { ZPhantomCleanOopClosure cl; - _concurrent_weak_roots.oops_do(&cl); + _concurrent_weak_roots.apply(&cl); } }; diff --git a/src/hotspot/share/gc/z/zWorkers.cpp b/src/hotspot/share/gc/z/zWorkers.cpp index f3c8cb7c176..82851a80987 100644 --- a/src/hotspot/share/gc/z/zWorkers.cpp +++ b/src/hotspot/share/gc/z/zWorkers.cpp @@ -23,13 +23,12 @@ #include "precompiled.hpp" #include "gc/shared/gcLogPrecious.hpp" -#include "gc/z/zGlobals.hpp" #include "gc/z/zTask.hpp" #include "gc/z/zThread.hpp" #include "gc/z/zWorkers.inline.hpp" #include "runtime/java.hpp" +#include "runtime/mutex.hpp" #include "runtime/mutexLocker.hpp" -#include "runtime/safepoint.hpp" class ZWorkersInitializeTask : public ZTask { private: @@ -101,6 +100,10 @@ void ZWorkers::run(ZTask* task, uint nworkers) { _workers.run_task(task->gang_task()); } +void ZWorkers::run_serial(ZTask* task) { + run(task, 1 /* nworkers */); +} + void ZWorkers::run_parallel(ZTask* task) { run(task, nparallel()); } diff --git a/src/hotspot/share/gc/z/zWorkers.hpp b/src/hotspot/share/gc/z/zWorkers.hpp index c677a438c6c..03a0a8f4cea 100644 --- a/src/hotspot/share/gc/z/zWorkers.hpp +++ b/src/hotspot/share/gc/z/zWorkers.hpp @@ -25,7 +25,6 @@ #define SHARE_GC_Z_ZWORKERS_HPP #include "gc/shared/workgroup.hpp" -#include "memory/allocation.hpp" class ThreadClosure; class ZTask; @@ -48,6 +47,7 @@ class ZWorkers { void set_boost(bool boost); + void run_serial(ZTask* task); void run_parallel(ZTask* task); void run_concurrent(ZTask* task); diff --git a/src/hotspot/share/gc/z/z_globals.hpp b/src/hotspot/share/gc/z/z_globals.hpp index 5d921def086..9e92ec35d19 100644 --- a/src/hotspot/share/gc/z/z_globals.hpp +++ b/src/hotspot/share/gc/z/z_globals.hpp @@ -42,7 +42,7 @@ "Maximum number of bytes allocated for mark stacks") \ range(32*M, 1024*G) \ \ - product(uint, ZCollectionInterval, 0, \ + product(double, ZCollectionInterval, 0, \ "Force GC at a fixed time interval (in seconds)") \ \ product(bool, ZProactive, true, \ diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index fb9dfba4301..35220e2ea60 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -198,14 +198,20 @@ JVM_LookupLambdaProxyClassFromArchive(JNIEnv* env, jclass caller, jboolean initialize); JNIEXPORT jboolean JNICALL -JVM_IsDynamicDumpingEnabled(JNIEnv* env); +JVM_IsCDSDumpingEnabled(JNIEnv* env); JNIEXPORT jboolean JNICALL JVM_IsSharingEnabled(JNIEnv* env); +JNIEXPORT jboolean JNICALL +JVM_IsDumpingClassList(JNIEnv* env); + JNIEXPORT jlong JNICALL JVM_GetRandomSeedForDumping(); +JNIEXPORT void JNICALL +JVM_LogLambdaFormInvoker(JNIEnv* env, jstring line); + /* * java.lang.Throwable */ @@ -324,6 +330,15 @@ JVM_HasReferencePendingList(JNIEnv *env); JNIEXPORT void JNICALL JVM_WaitForReferencePendingList(JNIEnv *env); +JNIEXPORT jboolean JNICALL +JVM_ReferenceRefersTo(JNIEnv *env, jobject ref, jobject o); + +/* + * java.lang.ref.PhantomReference + */ +JNIEXPORT jboolean JNICALL +JVM_PhantomReferenceRefersTo(JNIEnv *env, jobject ref, jobject o); + /* * java.io.ObjectInputStream */ diff --git a/src/hotspot/share/interpreter/bootstrapInfo.cpp b/src/hotspot/share/interpreter/bootstrapInfo.cpp index 513882017c0..cae8ff49475 100644 --- a/src/hotspot/share/interpreter/bootstrapInfo.cpp +++ b/src/hotspot/share/interpreter/bootstrapInfo.cpp @@ -31,6 +31,7 @@ #include "logging/log.hpp" #include "logging/logStream.hpp" #include "memory/oopFactory.hpp" +#include "memory/resourceArea.hpp" #include "oops/cpCache.inline.hpp" #include "oops/objArrayOop.inline.hpp" #include "oops/typeArrayOop.inline.hpp" diff --git a/src/hotspot/share/interpreter/interpreter.cpp b/src/hotspot/share/interpreter/interpreter.cpp index f597950f38f..f32bb377466 100644 --- a/src/hotspot/share/interpreter/interpreter.cpp +++ b/src/hotspot/share/interpreter/interpreter.cpp @@ -76,7 +76,7 @@ void InterpreterCodelet::print_on(outputStream* st) const { if (PrintInterpreter) { st->cr(); - Disassembler::decode(code_begin(), code_end(), st, DEBUG_ONLY(_strings) NOT_DEBUG(CodeStrings())); + Disassembler::decode(code_begin(), code_end(), st DEBUG_ONLY(COMMA &_strings)); } } @@ -106,7 +106,8 @@ CodeletMark::~CodeletMark() { // Commit Codelet. int committed_code_size = (*_masm)->code()->pure_insts_size(); if (committed_code_size) { - AbstractInterpreter::code()->commit(committed_code_size, (*_masm)->code()->strings()); + CodeStrings cs NOT_PRODUCT(= (*_masm)->code()->strings()); + AbstractInterpreter::code()->commit(committed_code_size, cs); } // Make sure nobody can use _masm outside a CodeletMark lifespan. *_masm = NULL; diff --git a/src/hotspot/share/interpreter/interpreter.hpp b/src/hotspot/share/interpreter/interpreter.hpp index 288aa662eee..ac89b7b2ad0 100644 --- a/src/hotspot/share/interpreter/interpreter.hpp +++ b/src/hotspot/share/interpreter/interpreter.hpp @@ -49,14 +49,14 @@ class InterpreterCodelet: public Stub { int _size; // the size in bytes const char* _description; // a description of the codelet, for debugging & printing Bytecodes::Code _bytecode; // associated bytecode if any - DEBUG_ONLY(CodeStrings _strings;) // Comments for annotating assembler output. + NOT_PRODUCT(CodeStrings _strings;) // Comments for annotating assembler output. public: // Initialization/finalization void initialize(int size, CodeStrings& strings) { _size = size; - DEBUG_ONLY(::new(&_strings) CodeStrings();) - DEBUG_ONLY(_strings.assign(strings);) } + NOT_PRODUCT(_strings = CodeStrings();) + NOT_PRODUCT(_strings.copy(strings);) } void finalize() { ShouldNotCallThis(); } // General info/converters diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index 866637bad27..efe343bc006 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -50,6 +50,7 @@ #include "oops/oop.inline.hpp" #include "oops/symbol.hpp" #include "prims/jvmtiExport.hpp" +#include "prims/methodHandles.hpp" #include "prims/nativeLookup.hpp" #include "runtime/atomic.hpp" #include "runtime/biasedLocking.hpp" @@ -64,6 +65,7 @@ #include "runtime/jfieldIDWorkaround.hpp" #include "runtime/osThread.hpp" #include "runtime/sharedRuntime.hpp" +#include "runtime/stackWatermarkSet.hpp" #include "runtime/stubRoutines.hpp" #include "runtime/synchronizer.hpp" #include "runtime/threadCritical.hpp" @@ -447,6 +449,10 @@ JRT_END // from a call, the expression stack contains the values for the bci at the // invoke w/o arguments (i.e., as if one were inside the call). JRT_ENTRY(address, InterpreterRuntime::exception_handler_for_exception(JavaThread* thread, oopDesc* exception)) + // We get here after we have unwound from a callee throwing an exception + // into the interpreter. Any deferred stack processing is notified of + // the event via the StackWatermarkSet. + StackWatermarkSet::after_unwind(thread); LastFrameAccessor last_frame(thread); Handle h_exception(thread, exception); @@ -1153,6 +1159,11 @@ JRT_ENTRY(void, InterpreterRuntime::at_safepoint(JavaThread* thread)) // if this is called during a safepoint if (JvmtiExport::should_post_single_step()) { + // This function is called by the interpreter when single stepping. Such single + // stepping could unwind a frame. Then, it is important that we process any frames + // that we might return into. + StackWatermarkSet::before_unwind(thread); + // We are called during regular safepoints and when the VM is // single stepping. If any thread is marked for single stepping, // then we may have JVMTI work to do. @@ -1161,6 +1172,17 @@ JRT_ENTRY(void, InterpreterRuntime::at_safepoint(JavaThread* thread)) } JRT_END +JRT_LEAF(void, InterpreterRuntime::at_unwind(JavaThread* thread)) + // This function is called by the interpreter when the return poll found a reason + // to call the VM. The reason could be that we are returning into a not yet safe + // to access frame. We handle that below. + // Note that this path does not check for single stepping, because we do not want + // to single step when unwinding frames for an exception being thrown. Instead, + // such single stepping code will use the safepoint table, which will use the + // InterpreterRuntime::at_safepoint callback. + StackWatermarkSet::before_unwind(thread); +JRT_END + JRT_ENTRY(void, InterpreterRuntime::post_field_access(JavaThread *thread, oopDesc* obj, ConstantPoolCacheEntry *cp_entry)) @@ -1247,7 +1269,10 @@ JRT_ENTRY(void, InterpreterRuntime::post_method_entry(JavaThread *thread)) JRT_END -JRT_ENTRY(void, InterpreterRuntime::post_method_exit(JavaThread *thread)) +// This is a JRT_BLOCK_ENTRY because we have to stash away the return oop +// before transitioning to VM, and restore it after transitioning back +// to Java. The return oop at the top-of-stack, is not walked by the GC. +JRT_BLOCK_ENTRY(void, InterpreterRuntime::post_method_exit(JavaThread *thread)) LastFrameAccessor last_frame(thread); JvmtiExport::post_method_exit(thread, last_frame.method(), last_frame.get_frame()); JRT_END diff --git a/src/hotspot/share/interpreter/interpreterRuntime.hpp b/src/hotspot/share/interpreter/interpreterRuntime.hpp index 73fbf311288..14563906271 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.hpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.hpp @@ -113,6 +113,7 @@ class InterpreterRuntime: AllStatic { // Safepoints static void at_safepoint(JavaThread* thread); + static void at_unwind(JavaThread* thread); // Debugger support static void post_field_access(JavaThread *thread, oopDesc* obj, diff --git a/src/hotspot/share/interpreter/linkResolver.cpp b/src/hotspot/share/interpreter/linkResolver.cpp index c70eb63092b..b96a326ad38 100644 --- a/src/hotspot/share/interpreter/linkResolver.cpp +++ b/src/hotspot/share/interpreter/linkResolver.cpp @@ -39,6 +39,7 @@ #include "interpreter/linkResolver.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" +#include "memory/archiveUtils.hpp" #include "memory/resourceArea.hpp" #include "oops/constantPool.hpp" #include "oops/cpCache.inline.hpp" @@ -1765,6 +1766,9 @@ void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHan // of this method, via CPCE::set_dynamic_call, which uses // an ObjectLocker to do the final serialization of updates // to CPCE state, including f1. + + // Log dynamic info to CDS classlist. + ArchiveUtils::log_to_classlist(&bootstrap_specifier, THREAD); } void LinkResolver::resolve_dynamic_call(CallInfo& result, diff --git a/src/hotspot/share/interpreter/templateInterpreter.cpp b/src/hotspot/share/interpreter/templateInterpreter.cpp index 4bcd1333891..b65e1d57a8b 100644 --- a/src/hotspot/share/interpreter/templateInterpreter.cpp +++ b/src/hotspot/share/interpreter/templateInterpreter.cpp @@ -104,6 +104,19 @@ EntryPoint::EntryPoint(address bentry, address zentry, address centry, address s _entry[vtos] = ventry; } +EntryPoint::EntryPoint(address aentry, address ientry, address lentry, address fentry, address dentry, address ventry) { + assert(number_of_states == 10, "check the code below"); + _entry[btos] = ientry; + _entry[ztos] = ientry; + _entry[ctos] = ientry; + _entry[stos] = ientry; + _entry[atos] = aentry; + _entry[itos] = ientry; + _entry[ltos] = lentry; + _entry[ftos] = fentry; + _entry[dtos] = dentry; + _entry[vtos] = ventry; +} void EntryPoint::set_entry(TosState state, address entry) { assert(0 <= state && state < number_of_states, "state out of bounds"); diff --git a/src/hotspot/share/interpreter/templateInterpreter.hpp b/src/hotspot/share/interpreter/templateInterpreter.hpp index 2ff62e15d33..23a6a7f7007 100644 --- a/src/hotspot/share/interpreter/templateInterpreter.hpp +++ b/src/hotspot/share/interpreter/templateInterpreter.hpp @@ -48,7 +48,8 @@ class EntryPoint { // Construction EntryPoint(); EntryPoint(address bentry, address zentry, address centry, address sentry, address aentry, address ientry, address lentry, address fentry, address dentry, address ventry); - + // Will use the ientry for each of [bzcs]entry + EntryPoint(address aentry, address ientry, address lentry, address fentry, address dentry, address ventry); // Attributes address entry(TosState state) const; // return target address for a given tosca state void set_entry(TosState state, address entry); // set target address for a given tosca state diff --git a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp index 51b95bbd76b..be26ef206bd 100644 --- a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp +++ b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp @@ -68,10 +68,6 @@ void TemplateInterpreterGenerator::generate_all() { CodeletMark cm(_masm, "bytecode tracing support"); Interpreter::_trace_code = EntryPoint( - generate_trace_code(btos), - generate_trace_code(ztos), - generate_trace_code(ctos), - generate_trace_code(stos), generate_trace_code(atos), generate_trace_code(itos), generate_trace_code(ltos), @@ -83,50 +79,54 @@ void TemplateInterpreterGenerator::generate_all() { #endif // !PRODUCT { CodeletMark cm(_masm, "return entry points"); - const int index_size = sizeof(u2); Interpreter::_return_entry[0] = EntryPoint(); for (int i = 1; i < Interpreter::number_of_return_entries; i++) { - address return_itos = generate_return_entry_for(itos, i, index_size); Interpreter::_return_entry[i] = EntryPoint( - return_itos, - return_itos, - return_itos, - return_itos, - generate_return_entry_for(atos, i, index_size), - return_itos, - generate_return_entry_for(ltos, i, index_size), - generate_return_entry_for(ftos, i, index_size), - generate_return_entry_for(dtos, i, index_size), - generate_return_entry_for(vtos, i, index_size) + generate_return_entry_for(atos, i, sizeof(u2)), + generate_return_entry_for(itos, i, sizeof(u2)), + generate_return_entry_for(ltos, i, sizeof(u2)), + generate_return_entry_for(ftos, i, sizeof(u2)), + generate_return_entry_for(dtos, i, sizeof(u2)), + generate_return_entry_for(vtos, i, sizeof(u2)) ); } } { CodeletMark cm(_masm, "invoke return entry points"); - // These states are in order specified in TosState, except btos/ztos/ctos/stos are - // really the same as itos since there is no top of stack optimization for these types - const TosState states[] = {itos, itos, itos, itos, itos, ltos, ftos, dtos, atos, vtos, ilgl}; + // These states are in order specified in TosState, except btos/ztos/ctos/stos which + // are the same as itos since there is no top of stack optimization for these types + const TosState states[] = {ilgl, ilgl, ilgl, ilgl, itos, ltos, ftos, dtos, atos, vtos, ilgl}; const int invoke_length = Bytecodes::length_for(Bytecodes::_invokestatic); const int invokeinterface_length = Bytecodes::length_for(Bytecodes::_invokeinterface); const int invokedynamic_length = Bytecodes::length_for(Bytecodes::_invokedynamic); - for (int i = 0; i < Interpreter::number_of_return_addrs; i++) { + assert(invoke_length >= 0 && invoke_length < Interpreter::number_of_return_entries, "invariant"); + assert(invokeinterface_length >= 0 && invokeinterface_length < Interpreter::number_of_return_entries, "invariant"); + + for (int i = itos; i < Interpreter::number_of_return_addrs; i++) { TosState state = states[i]; assert(state != ilgl, "states array is wrong above"); - Interpreter::_invoke_return_entry[i] = generate_return_entry_for(state, invoke_length, sizeof(u2)); - Interpreter::_invokeinterface_return_entry[i] = generate_return_entry_for(state, invokeinterface_length, sizeof(u2)); - Interpreter::_invokedynamic_return_entry[i] = generate_return_entry_for(state, invokedynamic_length, sizeof(u4)); + + // Reuse generated entry points + Interpreter::_invoke_return_entry[i] = Interpreter::_return_entry[invoke_length].entry(state); + Interpreter::_invokeinterface_return_entry[i] = Interpreter::_return_entry[invokeinterface_length].entry(state); + + Interpreter::_invokedynamic_return_entry[i] = generate_return_entry_for(state, invokedynamic_length, sizeof(u4)); + } + + // set itos entry points for btos/ztos/ctos/stos + for (int i = 0; i < itos; i++) { + Interpreter::_invoke_return_entry[i] = Interpreter::_invoke_return_entry[itos]; + Interpreter::_invokeinterface_return_entry[i] = Interpreter::_invokeinterface_return_entry[itos]; + Interpreter::_invokedynamic_return_entry[i] = Interpreter::_invokedynamic_return_entry[itos]; } } { CodeletMark cm(_masm, "earlyret entry points"); + address earlyret_entry_itos = generate_earlyret_entry_for(itos); Interpreter::_earlyret_entry = EntryPoint( - generate_earlyret_entry_for(btos), - generate_earlyret_entry_for(ztos), - generate_earlyret_entry_for(ctos), - generate_earlyret_entry_for(stos), generate_earlyret_entry_for(atos), generate_earlyret_entry_for(itos), generate_earlyret_entry_for(ltos), @@ -153,10 +153,6 @@ void TemplateInterpreterGenerator::generate_all() { { CodeletMark cm(_masm, "safepoint entry points"); Interpreter::_safept_entry = EntryPoint( - generate_safept_entry_for(btos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)), - generate_safept_entry_for(ztos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)), - generate_safept_entry_for(ctos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)), - generate_safept_entry_for(stos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)), generate_safept_entry_for(atos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)), generate_safept_entry_for(itos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)), generate_safept_entry_for(ltos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)), @@ -242,12 +238,8 @@ void TemplateInterpreterGenerator::generate_all() { address deopt_itos = generate_deopt_entry_for(itos, i); Interpreter::_deopt_entry[i] = EntryPoint( - deopt_itos, /* btos */ - deopt_itos, /* ztos */ - deopt_itos, /* ctos */ - deopt_itos, /* stos */ generate_deopt_entry_for(atos, i), - deopt_itos, /* itos */ + generate_deopt_entry_for(itos, i), generate_deopt_entry_for(ltos, i), generate_deopt_entry_for(ftos, i), generate_deopt_entry_for(dtos, i), diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp index ba162c35703..06f66a13464 100644 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp +++ b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp @@ -28,7 +28,6 @@ #include "gc/shared/threadLocalAllocBuffer.inline.hpp" #include "interpreter/bytecodeHistogram.hpp" #include "interpreter/zero/bytecodeInterpreter.inline.hpp" -#include "interpreter/zero/bytecodeInterpreterProfiling.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" #include "logging/log.hpp" @@ -53,6 +52,7 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/threadCritical.hpp" #include "utilities/exceptions.hpp" +#include "utilities/macros.hpp" // no precompiled headers @@ -133,22 +133,28 @@ #ifdef PRODUCT #define DO_UPDATE_INSTRUCTION_COUNT(opcode) #else -#define DO_UPDATE_INSTRUCTION_COUNT(opcode) \ -{ \ - BytecodeCounter::_counter_value++; \ - BytecodeHistogram::_counters[(Bytecodes::Code)opcode]++; \ - if (StopInterpreterAt && StopInterpreterAt == BytecodeCounter::_counter_value) os::breakpoint(); \ - if (TraceBytecodes) { \ - CALL_VM((void)InterpreterRuntime::trace_bytecode(THREAD, 0, \ - topOfStack[Interpreter::expr_index_at(1)], \ - topOfStack[Interpreter::expr_index_at(2)]), \ - handle_exception); \ - } \ +#define DO_UPDATE_INSTRUCTION_COUNT(opcode) \ +{ \ + if (PrintBytecodeHistogram) { \ + BytecodeHistogram::_counters[(Bytecodes::Code)opcode]++; \ + } \ + if (CountBytecodes || TraceBytecodes || StopInterpreterAt > 0) { \ + BytecodeCounter::_counter_value++; \ + if (StopInterpreterAt == BytecodeCounter::_counter_value) { \ + os::breakpoint(); \ + } \ + if (TraceBytecodes) { \ + CALL_VM((void)InterpreterRuntime::trace_bytecode(THREAD, 0, \ + topOfStack[Interpreter::expr_index_at(1)], \ + topOfStack[Interpreter::expr_index_at(2)]), \ + handle_exception); \ + } \ + } \ } #endif #undef DEBUGGER_SINGLE_STEP_NOTIFY -#ifdef VM_JVMTI +#if INCLUDE_JVMTI /* NOTE: (kbr) This macro must be called AFTER the PC has been incremented. JvmtiExport::at_single_stepping_point() may cause a breakpoint opcode to get inserted at the current PC to allow the @@ -158,33 +164,31 @@ to get the current opcode. This will override any other prefetching that might have occurred. */ -#define DEBUGGER_SINGLE_STEP_NOTIFY() \ -{ \ - if (_jvmti_interp_events) { \ - if (JvmtiExport::should_post_single_step()) { \ - DECACHE_STATE(); \ - SET_LAST_JAVA_FRAME(); \ - ThreadInVMfromJava trans(THREAD); \ - JvmtiExport::at_single_stepping_point(THREAD, \ - istate->method(), \ - pc); \ - RESET_LAST_JAVA_FRAME(); \ - CACHE_STATE(); \ - if (THREAD->has_pending_popframe() && \ - !THREAD->pop_frame_in_process()) { \ - goto handle_Pop_Frame; \ - } \ - if (THREAD->jvmti_thread_state() && \ - THREAD->jvmti_thread_state()->is_earlyret_pending()) { \ - goto handle_Early_Return; \ - } \ - opcode = *pc; \ - } \ - } \ +#define DEBUGGER_SINGLE_STEP_NOTIFY() \ +{ \ + if (JVMTI_ENABLED && JvmtiExport::should_post_single_step()) { \ + DECACHE_STATE(); \ + SET_LAST_JAVA_FRAME(); \ + ThreadInVMfromJava trans(THREAD); \ + JvmtiExport::at_single_stepping_point(THREAD, \ + istate->method(), \ + pc); \ + RESET_LAST_JAVA_FRAME(); \ + CACHE_STATE(); \ + if (THREAD->has_pending_popframe() && \ + !THREAD->pop_frame_in_process()) { \ + goto handle_Pop_Frame; \ + } \ + if (THREAD->jvmti_thread_state() && \ + THREAD->jvmti_thread_state()->is_earlyret_pending()) { \ + goto handle_Early_Return; \ + } \ + opcode = *pc; \ + } \ } #else #define DEBUGGER_SINGLE_STEP_NOTIFY() -#endif +#endif // INCLUDE_JVMTI /* * CONTINUE - Macro for executing the next opcode. @@ -382,22 +386,18 @@ /* * BytecodeInterpreter::run(interpreterState istate) - * BytecodeInterpreter::runWithChecks(interpreterState istate) * * The real deal. This is where byte codes actually get interpreted. * Basically it's a big while loop that iterates until we return from * the method passed in. - * - * The runWithChecks is used if JVMTI is enabled. - * */ -#if defined(VM_JVMTI) -void -BytecodeInterpreter::runWithChecks(interpreterState istate) { -#else -void -BytecodeInterpreter::run(interpreterState istate) { -#endif + +// Instantiate two variants of the method for future linking. +template void BytecodeInterpreter::run(interpreterState istate); +template void BytecodeInterpreter::run(interpreterState istate); + +template +void BytecodeInterpreter::run(interpreterState istate) { // In order to simplify some tests based on switches set at runtime // we invoke the interpreter a single time after switches are enabled @@ -412,11 +412,6 @@ BytecodeInterpreter::run(interpreterState istate) { if (checkit && *c_addr != c_value) { os::breakpoint(); } -#ifdef VM_JVMTI - static bool _jvmti_interp_events = 0; -#endif - - static int _compiling; // (UseCompiler || CountCompiledCalls) #ifdef ASSERT if (istate->_msg != initialize) { @@ -546,15 +541,12 @@ BytecodeInterpreter::run(interpreterState istate) { topOfStack < istate->stack_base(), "Stack top out of range"); - const uint mdo_last_branch_taken_count = 0; + assert(!UseCompiler, "Zero does not support compilers"); + assert(!CountCompiledCalls, "Zero does not support counting compiled calls"); switch (istate->msg()) { case initialize: { if (initialized++) ShouldNotReachHere(); // Only one initialize call. - _compiling = (UseCompiler || CountCompiledCalls); -#ifdef VM_JVMTI - _jvmti_interp_events = JvmtiExport::can_post_interpreter_events(); -#endif return; } break; @@ -562,11 +554,6 @@ BytecodeInterpreter::run(interpreterState istate) { THREAD->set_do_not_unlock(); // count invocations assert(initialized, "Interpreter not initialized"); - if (_compiling) { - // Get or create profile data. Check for pending (async) exceptions. - BI_PROFILE_GET_OR_CREATE_METHOD_DATA(handle_exception); - SAFEPOINT; - } if ((istate->_stack_base - istate->_stack_limit) != istate->method()->max_stack() + 1) { // initialize @@ -669,17 +656,13 @@ BytecodeInterpreter::run(interpreterState istate) { } THREAD->clr_do_not_unlock(); - // Notify jvmti -#ifdef VM_JVMTI - if (_jvmti_interp_events) { - // Whenever JVMTI puts a thread in interp_only_mode, method - // entry/exit events are sent for that thread to track stack depth. - if (THREAD->is_interp_only_mode()) { - CALL_VM(InterpreterRuntime::post_method_entry(THREAD), - handle_exception); - } + // Notify jvmti. + // Whenever JVMTI puts a thread in interp_only_mode, method + // entry/exit events are sent for that thread to track stack depth. + if (JVMTI_ENABLED && THREAD->is_interp_only_mode()) { + CALL_VM(InterpreterRuntime::post_method_entry(THREAD), + handle_exception); } -#endif /* VM_JVMTI */ goto run; } @@ -689,12 +672,6 @@ BytecodeInterpreter::run(interpreterState istate) { // clear the message so we don't confuse ourselves later assert(THREAD->pop_frame_in_process(), "wrong frame pop state"); istate->set_msg(no_request); - if (_compiling) { - // Set MDX back to the ProfileData of the invoke bytecode that will be - // restarted. - SET_MDX(NULL); - BI_PROFILE_GET_OR_CREATE_METHOD_DATA(handle_exception); - } THREAD->clr_pop_frame_in_process(); goto run; } @@ -716,11 +693,6 @@ BytecodeInterpreter::run(interpreterState istate) { if (THREAD->has_pending_exception()) goto handle_exception; // Update the pc by the saved amount of the invoke bytecode size UPDATE_PC(istate->bcp_advance()); - - if (_compiling) { - // Get or create profile data. Check for pending (async) exceptions. - BI_PROFILE_GET_OR_CREATE_METHOD_DATA(handle_exception); - } goto run; } @@ -728,11 +700,6 @@ BytecodeInterpreter::run(interpreterState istate) { // Returned from an opcode that will reexecute. Deopt was // a result of a PopFrame request. // - - if (_compiling) { - // Get or create profile data. Check for pending (async) exceptions. - BI_PROFILE_GET_OR_CREATE_METHOD_DATA(handle_exception); - } goto run; } @@ -755,11 +722,6 @@ BytecodeInterpreter::run(interpreterState istate) { } UPDATE_PC(Bytecodes::length_at(METHOD, pc)); if (THREAD->has_pending_exception()) goto handle_exception; - - if (_compiling) { - // Get or create profile data. Check for pending (async) exceptions. - BI_PROFILE_GET_OR_CREATE_METHOD_DATA(handle_exception); - } goto run; } case got_monitors: { @@ -1058,9 +1020,6 @@ BytecodeInterpreter::run(interpreterState istate) { UPDATE_PC_AND_CONTINUE(6); } case Bytecodes::_ret: - // Profile ret. - BI_PROFILE_UPDATE_RET(/*bci=*/((int)(intptr_t)(LOCALS_ADDR(reg)))); - // Now, update the pc. pc = istate->method()->code_base() + (intptr_t)(LOCALS_ADDR(reg)); UPDATE_PC_AND_CONTINUE(0); default: @@ -1370,23 +1329,17 @@ BytecodeInterpreter::run(interpreterState istate) { #define COMPARISON_OP(name, comparison) \ CASE(_if_icmp##name): { \ - const bool cmp = (STACK_INT(-2) comparison STACK_INT(-1)); \ - int skip = cmp \ + int skip = (STACK_INT(-2) comparison STACK_INT(-1)) \ ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ address branch_pc = pc; \ - /* Profile branch. */ \ - BI_PROFILE_UPDATE_BRANCH(/*is_taken=*/cmp); \ UPDATE_PC_AND_TOS(skip, -2); \ DO_BACKEDGE_CHECKS(skip, branch_pc); \ CONTINUE; \ } \ CASE(_if##name): { \ - const bool cmp = (STACK_INT(-1) comparison 0); \ - int skip = cmp \ + int skip = (STACK_INT(-1) comparison 0) \ ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ address branch_pc = pc; \ - /* Profile branch. */ \ - BI_PROFILE_UPDATE_BRANCH(/*is_taken=*/cmp); \ UPDATE_PC_AND_TOS(skip, -1); \ DO_BACKEDGE_CHECKS(skip, branch_pc); \ CONTINUE; \ @@ -1395,12 +1348,9 @@ BytecodeInterpreter::run(interpreterState istate) { #define COMPARISON_OP2(name, comparison) \ COMPARISON_OP(name, comparison) \ CASE(_if_acmp##name): { \ - const bool cmp = (STACK_OBJECT(-2) comparison STACK_OBJECT(-1)); \ - int skip = cmp \ + int skip = (STACK_OBJECT(-2) comparison STACK_OBJECT(-1)) \ ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ address branch_pc = pc; \ - /* Profile branch. */ \ - BI_PROFILE_UPDATE_BRANCH(/*is_taken=*/cmp); \ UPDATE_PC_AND_TOS(skip, -2); \ DO_BACKEDGE_CHECKS(skip, branch_pc); \ CONTINUE; \ @@ -1408,12 +1358,9 @@ BytecodeInterpreter::run(interpreterState istate) { #define NULL_COMPARISON_NOT_OP(name) \ CASE(_if##name): { \ - const bool cmp = (!(STACK_OBJECT(-1) == NULL)); \ - int skip = cmp \ + int skip = (!(STACK_OBJECT(-1) == NULL)) \ ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ address branch_pc = pc; \ - /* Profile branch. */ \ - BI_PROFILE_UPDATE_BRANCH(/*is_taken=*/cmp); \ UPDATE_PC_AND_TOS(skip, -1); \ DO_BACKEDGE_CHECKS(skip, branch_pc); \ CONTINUE; \ @@ -1421,12 +1368,9 @@ BytecodeInterpreter::run(interpreterState istate) { #define NULL_COMPARISON_OP(name) \ CASE(_if##name): { \ - const bool cmp = ((STACK_OBJECT(-1) == NULL)); \ - int skip = cmp \ + int skip = ((STACK_OBJECT(-1) == NULL)) \ ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ address branch_pc = pc; \ - /* Profile branch. */ \ - BI_PROFILE_UPDATE_BRANCH(/*is_taken=*/cmp); \ UPDATE_PC_AND_TOS(skip, -1); \ DO_BACKEDGE_CHECKS(skip, branch_pc); \ CONTINUE; \ @@ -1450,13 +1394,10 @@ BytecodeInterpreter::run(interpreterState istate) { int32_t skip; key -= low; if (((uint32_t) key > (uint32_t)(high - low))) { - key = -1; skip = Bytes::get_Java_u4((address)&lpc[0]); } else { skip = Bytes::get_Java_u4((address)&lpc[key + 3]); } - // Profile switch. - BI_PROFILE_UPDATE_SWITCH(/*switch_index=*/key); // Does this really need a full backedge check (osr)? address branch_pc = pc; UPDATE_PC_AND_TOS(skip, -1); @@ -1470,21 +1411,14 @@ BytecodeInterpreter::run(interpreterState istate) { jint* lpc = (jint*)VMalignWordUp(pc+1); int32_t key = STACK_INT(-1); int32_t skip = Bytes::get_Java_u4((address) lpc); /* default amount */ - // Remember index. - int index = -1; - int newindex = 0; int32_t npairs = Bytes::get_Java_u4((address) &lpc[1]); while (--npairs >= 0) { lpc += 2; if (key == (int32_t)Bytes::get_Java_u4((address)lpc)) { skip = Bytes::get_Java_u4((address)&lpc[1]); - index = newindex; break; } - newindex += 1; } - // Profile switch. - BI_PROFILE_UPDATE_SWITCH(/*switch_index=*/index); address branch_pc = pc; UPDATE_PC_AND_TOS(skip, -1); DO_BACKEDGE_CHECKS(skip, branch_pc); @@ -1650,15 +1584,8 @@ BytecodeInterpreter::run(interpreterState istate) { // Seems way more expensive now that we must dispatch // if (rhsKlass != elemKlass && !rhsKlass->is_subtype_of(elemKlass)) { // ebx->is... - // Decrement counter if subtype check failed. - BI_PROFILE_SUBTYPECHECK_FAILED(rhsKlass); VM_JAVA_ERROR(vmSymbols::java_lang_ArrayStoreException(), ""); } - // Profile checkcast with null_seen and receiver. - BI_PROFILE_UPDATE_CHECKCAST(/*null_seen=*/false, rhsKlass); - } else { - // Profile checkcast with null_seen and receiver. - BI_PROFILE_UPDATE_CHECKCAST(/*null_seen=*/true, NULL); } ((objArrayOop) arrObj)->obj_at_put(index, rhsObject); UPDATE_PC_AND_TOS_AND_CONTINUE(1, -3); @@ -1858,8 +1785,7 @@ BytecodeInterpreter::run(interpreterState istate) { cache = cp->entry_at(index); } -#ifdef VM_JVMTI - if (_jvmti_interp_events) { + if (JVMTI_ENABLED) { int *count_addr; oop obj; // Check to see if a field modification watch has been set @@ -1878,7 +1804,6 @@ BytecodeInterpreter::run(interpreterState istate) { handle_exception); } } -#endif /* VM_JVMTI */ oop obj; if ((Bytecodes::Code)opcode == Bytecodes::_getstatic) { @@ -1956,8 +1881,7 @@ BytecodeInterpreter::run(interpreterState istate) { cache = cp->entry_at(index); } -#ifdef VM_JVMTI - if (_jvmti_interp_events) { + if (JVMTI_ENABLED) { int *count_addr; oop obj; // Check to see if a field modification watch has been set @@ -1983,7 +1907,6 @@ BytecodeInterpreter::run(interpreterState istate) { handle_exception); } } -#endif /* VM_JVMTI */ // QQQ Need to make this as inlined as possible. Probably need to split all the bytecode cases // out so c++ compiler has a chance for constant prop to fold everything possible away. @@ -2169,26 +2092,17 @@ BytecodeInterpreter::run(interpreterState istate) { // Seems way more expensive now that we must dispatch. // if (objKlass != klassOf && !objKlass->is_subtype_of(klassOf)) { - // Decrement counter at checkcast. - BI_PROFILE_SUBTYPECHECK_FAILED(objKlass); ResourceMark rm(THREAD); char* message = SharedRuntime::generate_class_cast_message( objKlass, klassOf); VM_JAVA_ERROR(vmSymbols::java_lang_ClassCastException(), message); } - // Profile checkcast with null_seen and receiver. - BI_PROFILE_UPDATE_CHECKCAST(/*null_seen=*/false, objKlass); - } else { - // Profile checkcast with null_seen and receiver. - BI_PROFILE_UPDATE_CHECKCAST(/*null_seen=*/true, NULL); } UPDATE_PC_AND_CONTINUE(3); CASE(_instanceof): if (STACK_OBJECT(-1) == NULL) { SET_STACK_INT(0, -1); - // Profile instanceof with null_seen and receiver. - BI_PROFILE_UPDATE_INSTANCEOF(/*null_seen=*/true, NULL); } else { VERIFY_OOP(STACK_OBJECT(-1)); u2 index = Bytes::get_Java_u2(pc+1); @@ -2207,11 +2121,7 @@ BytecodeInterpreter::run(interpreterState istate) { SET_STACK_INT(1, -1); } else { SET_STACK_INT(0, -1); - // Decrement counter at checkcast. - BI_PROFILE_SUBTYPECHECK_FAILED(objKlass); } - // Profile instanceof with null_seen and receiver. - BI_PROFILE_UPDATE_INSTANCEOF(/*null_seen=*/false, objKlass); } UPDATE_PC_AND_CONTINUE(3); @@ -2266,6 +2176,7 @@ BytecodeInterpreter::run(interpreterState istate) { break; case JVM_CONSTANT_Dynamic: + case JVM_CONSTANT_DynamicInError: { CALL_VM(InterpreterRuntime::resolve_ldc(THREAD, (Bytecodes::Code) opcode), handle_exception); oop result = THREAD->vm_result(); @@ -2307,6 +2218,7 @@ BytecodeInterpreter::run(interpreterState istate) { break; case JVM_CONSTANT_Dynamic: + case JVM_CONSTANT_DynamicInError: { CALL_VM(InterpreterRuntime::resolve_ldc(THREAD, (Bytecodes::Code) opcode), handle_exception); oop result = THREAD->vm_result(); @@ -2386,9 +2298,6 @@ BytecodeInterpreter::run(interpreterState istate) { istate->set_callee_entry_point(method->from_interpreted_entry()); istate->set_bcp_advance(5); - // Invokedynamic has got a call counter, just like an invokestatic -> increment! - BI_PROFILE_UPDATE_CALL(); - UPDATE_PC_AND_RETURN(0); // I'll be back... } @@ -2417,9 +2326,6 @@ BytecodeInterpreter::run(interpreterState istate) { istate->set_callee_entry_point(method->from_interpreted_entry()); istate->set_bcp_advance(3); - // Invokehandle has got a call counter, just like a final call -> increment! - BI_PROFILE_UPDATE_FINALCALL(); - UPDATE_PC_AND_RETURN(0); // I'll be back... } @@ -2445,8 +2351,6 @@ BytecodeInterpreter::run(interpreterState istate) { CHECK_NULL(STACK_OBJECT(-(cache->parameter_size()))); if (cache->is_vfinal()) { callee = cache->f2_as_vfinal_method(); - // Profile 'special case of invokeinterface' final call. - BI_PROFILE_UPDATE_FINALCALL(); } else { // Get receiver. int parms = cache->parameter_size(); @@ -2455,8 +2359,6 @@ BytecodeInterpreter::run(interpreterState istate) { VERIFY_OOP(rcvr); Klass* rcvrKlass = rcvr->klass(); callee = (Method*) rcvrKlass->method_at_vtable(cache->f2_as_index()); - // Profile 'special case of invokeinterface' virtual call. - BI_PROFILE_UPDATE_VIRTUALCALL(rcvrKlass); } } else if (cache->is_vfinal()) { // private interface method invocations @@ -2483,11 +2385,9 @@ BytecodeInterpreter::run(interpreterState istate) { if (callee != NULL) { istate->set_callee(callee); istate->set_callee_entry_point(callee->from_interpreted_entry()); -#ifdef VM_JVMTI - if (JvmtiExport::can_post_interpreter_events() && THREAD->is_interp_only_mode()) { + if (JVMTI_ENABLED && THREAD->is_interp_only_mode()) { istate->set_callee_entry_point(callee->interpreter_entry()); } -#endif /* VM_JVMTI */ istate->set_bcp_advance(5); UPDATE_PC_AND_RETURN(0); // I'll be back... } @@ -2543,16 +2443,11 @@ BytecodeInterpreter::run(interpreterState istate) { handle_exception); } - // Profile virtual call. - BI_PROFILE_UPDATE_VIRTUALCALL(rcvr->klass()); - istate->set_callee(callee); istate->set_callee_entry_point(callee->from_interpreted_entry()); -#ifdef VM_JVMTI - if (JvmtiExport::can_post_interpreter_events() && THREAD->is_interp_only_mode()) { + if (JVMTI_ENABLED && THREAD->is_interp_only_mode()) { istate->set_callee_entry_point(callee->interpreter_entry()); } -#endif /* VM_JVMTI */ istate->set_bcp_advance(5); UPDATE_PC_AND_RETURN(0); // I'll be back... } @@ -2579,8 +2474,6 @@ BytecodeInterpreter::run(interpreterState istate) { CHECK_NULL(STACK_OBJECT(-(cache->parameter_size()))); if (cache->is_vfinal()) { callee = cache->f2_as_vfinal_method(); - // Profile final call. - BI_PROFILE_UPDATE_FINALCALL(); } else { // get receiver int parms = cache->parameter_size(); @@ -2610,26 +2503,19 @@ BytecodeInterpreter::run(interpreterState istate) { Because vtables have the same offset for ArrayKlass and InstanceKlass. */ callee = (Method*) rcvrKlass->method_at_vtable(cache->f2_as_index()); - // Profile virtual call. - BI_PROFILE_UPDATE_VIRTUALCALL(rcvrKlass); } } else { if ((Bytecodes::Code)opcode == Bytecodes::_invokespecial) { CHECK_NULL(STACK_OBJECT(-(cache->parameter_size()))); } callee = cache->f1_as_method(); - - // Profile call. - BI_PROFILE_UPDATE_CALL(); } istate->set_callee(callee); istate->set_callee_entry_point(callee->from_interpreted_entry()); -#ifdef VM_JVMTI - if (JvmtiExport::can_post_interpreter_events() && THREAD->is_interp_only_mode()) { + if (JVMTI_ENABLED && THREAD->is_interp_only_mode()) { istate->set_callee_entry_point(callee->interpreter_entry()); } -#endif /* VM_JVMTI */ istate->set_bcp_advance(3); UPDATE_PC_AND_RETURN(0); // I'll be back... } @@ -2675,8 +2561,6 @@ BytecodeInterpreter::run(interpreterState istate) { CASE(_goto): { int16_t offset = (int16_t)Bytes::get_Java_u2(pc + 1); - // Profile jump. - BI_PROFILE_UPDATE_JUMP(); address branch_pc = pc; UPDATE_PC(offset); DO_BACKEDGE_CHECKS(offset, branch_pc); @@ -2693,8 +2577,6 @@ BytecodeInterpreter::run(interpreterState istate) { CASE(_goto_w): { int32_t offset = Bytes::get_Java_u4(pc + 1); - // Profile jump. - BI_PROFILE_UPDATE_JUMP(); address branch_pc = pc; UPDATE_PC(offset); DO_BACKEDGE_CHECKS(offset, branch_pc); @@ -2704,9 +2586,6 @@ BytecodeInterpreter::run(interpreterState istate) { /* return from a jsr or jsr_w */ CASE(_ret): { - // Profile ret. - BI_PROFILE_UPDATE_RET(/*bci=*/((int)(intptr_t)(LOCALS_ADDR(pc[1])))); - // Now, update the pc. pc = istate->method()->code_base() + (intptr_t)(LOCALS_ADDR(pc[1])); UPDATE_PC_AND_CONTINUE(0); } @@ -2789,9 +2668,6 @@ BytecodeInterpreter::run(interpreterState istate) { } // for AbortVMOnException flag Exceptions::debug_check_abort(except_oop); - - // Update profiling data. - BI_PROFILE_ALIGN_TO_CURRENT_BCI(); goto run; } if (log_is_enabled(Info, exceptions)) { @@ -2902,7 +2778,9 @@ BytecodeInterpreter::run(interpreterState istate) { // a NULL oop in it and then overwrite the oop later as needed. This isn't // unfortunately isn't possible. - THREAD->clear_pending_exception(); + if (THREAD->has_pending_exception()) { + THREAD->clear_pending_exception(); + } // // As far as we are concerned we have returned. If we have a pending exception @@ -3060,25 +2938,16 @@ BytecodeInterpreter::run(interpreterState istate) { // (with this note) in anticipation of changing the vm and the tests // simultaneously. - - // suppress_exit_event = suppress_exit_event || illegal_state_oop() != NULL; + // Whenever JVMTI puts a thread in interp_only_mode, method + // entry/exit events are sent for that thread to track stack depth. - -#ifdef VM_JVMTI - if (_jvmti_interp_events) { - // Whenever JVMTI puts a thread in interp_only_mode, method - // entry/exit events are sent for that thread to track stack depth. - if ( !suppress_exit_event && THREAD->is_interp_only_mode() ) { - { - // Prevent any HandleMarkCleaner from freeing our live handles - HandleMark __hm(THREAD); - CALL_VM_NOCHECK(InterpreterRuntime::post_method_exit(THREAD)); - } - } - } -#endif /* VM_JVMTI */ + if (JVMTI_ENABLED && !suppress_exit_event && THREAD->is_interp_only_mode()) { + // Prevent any HandleMarkCleaner from freeing our live handles + HandleMark __hm(THREAD); + CALL_VM_NOCHECK(InterpreterRuntime::post_method_exit(THREAD)); + } // // See if we are returning any exception @@ -3129,13 +2998,6 @@ BytecodeInterpreter::run(interpreterState istate) { return; } -/* - * All the code following this point is only produced once and is not present - * in the JVMTI version of the interpreter -*/ - -#ifndef VM_JVMTI - // This constructor should only be used to contruct the object to signal // interpreter initialization. All other instances should be created by // the frame manager. @@ -3146,151 +3008,12 @@ BytecodeInterpreter::BytecodeInterpreter(messages msg) { _prev_link = NULL; } -// Inline static functions for Java Stack and Local manipulation - -// The implementations are platform dependent. We have to worry about alignment -// issues on some machines which can change on the same platform depending on -// whether it is an LP64 machine also. -address BytecodeInterpreter::stack_slot(intptr_t *tos, int offset) { - return (address) tos[Interpreter::expr_index_at(-offset)]; -} - -jint BytecodeInterpreter::stack_int(intptr_t *tos, int offset) { - return *((jint*) &tos[Interpreter::expr_index_at(-offset)]); -} - -jfloat BytecodeInterpreter::stack_float(intptr_t *tos, int offset) { - return *((jfloat *) &tos[Interpreter::expr_index_at(-offset)]); -} - -oop BytecodeInterpreter::stack_object(intptr_t *tos, int offset) { - return cast_to_oop(tos [Interpreter::expr_index_at(-offset)]); -} - -jdouble BytecodeInterpreter::stack_double(intptr_t *tos, int offset) { - return ((VMJavaVal64*) &tos[Interpreter::expr_index_at(-offset)])->d; -} - -jlong BytecodeInterpreter::stack_long(intptr_t *tos, int offset) { - return ((VMJavaVal64 *) &tos[Interpreter::expr_index_at(-offset)])->l; -} - -// only used for value types -void BytecodeInterpreter::set_stack_slot(intptr_t *tos, address value, - int offset) { - *((address *)&tos[Interpreter::expr_index_at(-offset)]) = value; -} - -void BytecodeInterpreter::set_stack_int(intptr_t *tos, int value, - int offset) { - *((jint *)&tos[Interpreter::expr_index_at(-offset)]) = value; -} - -void BytecodeInterpreter::set_stack_float(intptr_t *tos, jfloat value, - int offset) { - *((jfloat *)&tos[Interpreter::expr_index_at(-offset)]) = value; -} - -void BytecodeInterpreter::set_stack_object(intptr_t *tos, oop value, - int offset) { - *((oop *)&tos[Interpreter::expr_index_at(-offset)]) = value; -} - -// needs to be platform dep for the 32 bit platforms. -void BytecodeInterpreter::set_stack_double(intptr_t *tos, jdouble value, - int offset) { - ((VMJavaVal64*)&tos[Interpreter::expr_index_at(-offset)])->d = value; -} - -void BytecodeInterpreter::set_stack_double_from_addr(intptr_t *tos, - address addr, int offset) { - (((VMJavaVal64*)&tos[Interpreter::expr_index_at(-offset)])->d = - ((VMJavaVal64*)addr)->d); -} - -void BytecodeInterpreter::set_stack_long(intptr_t *tos, jlong value, - int offset) { - ((VMJavaVal64*)&tos[Interpreter::expr_index_at(-offset+1)])->l = 0xdeedbeeb; - ((VMJavaVal64*)&tos[Interpreter::expr_index_at(-offset)])->l = value; -} - -void BytecodeInterpreter::set_stack_long_from_addr(intptr_t *tos, - address addr, int offset) { - ((VMJavaVal64*)&tos[Interpreter::expr_index_at(-offset+1)])->l = 0xdeedbeeb; - ((VMJavaVal64*)&tos[Interpreter::expr_index_at(-offset)])->l = - ((VMJavaVal64*)addr)->l; -} - -// Locals - -address BytecodeInterpreter::locals_slot(intptr_t* locals, int offset) { - return (address)locals[Interpreter::local_index_at(-offset)]; -} -jint BytecodeInterpreter::locals_int(intptr_t* locals, int offset) { - return (jint)locals[Interpreter::local_index_at(-offset)]; -} -jfloat BytecodeInterpreter::locals_float(intptr_t* locals, int offset) { - return (jfloat)locals[Interpreter::local_index_at(-offset)]; -} -oop BytecodeInterpreter::locals_object(intptr_t* locals, int offset) { - return cast_to_oop(locals[Interpreter::local_index_at(-offset)]); -} -jdouble BytecodeInterpreter::locals_double(intptr_t* locals, int offset) { - return ((VMJavaVal64*)&locals[Interpreter::local_index_at(-(offset+1))])->d; -} -jlong BytecodeInterpreter::locals_long(intptr_t* locals, int offset) { - return ((VMJavaVal64*)&locals[Interpreter::local_index_at(-(offset+1))])->l; -} - -// Returns the address of locals value. -address BytecodeInterpreter::locals_long_at(intptr_t* locals, int offset) { - return ((address)&locals[Interpreter::local_index_at(-(offset+1))]); -} -address BytecodeInterpreter::locals_double_at(intptr_t* locals, int offset) { - return ((address)&locals[Interpreter::local_index_at(-(offset+1))]); -} - -// Used for local value or returnAddress -void BytecodeInterpreter::set_locals_slot(intptr_t *locals, - address value, int offset) { - *((address*)&locals[Interpreter::local_index_at(-offset)]) = value; -} -void BytecodeInterpreter::set_locals_int(intptr_t *locals, - jint value, int offset) { - *((jint *)&locals[Interpreter::local_index_at(-offset)]) = value; -} -void BytecodeInterpreter::set_locals_float(intptr_t *locals, - jfloat value, int offset) { - *((jfloat *)&locals[Interpreter::local_index_at(-offset)]) = value; -} -void BytecodeInterpreter::set_locals_object(intptr_t *locals, - oop value, int offset) { - *((oop *)&locals[Interpreter::local_index_at(-offset)]) = value; -} -void BytecodeInterpreter::set_locals_double(intptr_t *locals, - jdouble value, int offset) { - ((VMJavaVal64*)&locals[Interpreter::local_index_at(-(offset+1))])->d = value; -} -void BytecodeInterpreter::set_locals_long(intptr_t *locals, - jlong value, int offset) { - ((VMJavaVal64*)&locals[Interpreter::local_index_at(-(offset+1))])->l = value; -} -void BytecodeInterpreter::set_locals_double_from_addr(intptr_t *locals, - address addr, int offset) { - ((VMJavaVal64*)&locals[Interpreter::local_index_at(-(offset+1))])->d = ((VMJavaVal64*)addr)->d; -} -void BytecodeInterpreter::set_locals_long_from_addr(intptr_t *locals, - address addr, int offset) { - ((VMJavaVal64*)&locals[Interpreter::local_index_at(-(offset+1))])->l = ((VMJavaVal64*)addr)->l; -} - void BytecodeInterpreter::astore(intptr_t* tos, int stack_offset, intptr_t* locals, int locals_offset) { intptr_t value = tos[Interpreter::expr_index_at(-stack_offset)]; locals[Interpreter::local_index_at(-locals_offset)] = value; } - void BytecodeInterpreter::copy_stack_slot(intptr_t *tos, int from_offset, int to_offset) { tos[Interpreter::expr_index_at(-to_offset)] = @@ -3300,6 +3023,7 @@ void BytecodeInterpreter::copy_stack_slot(intptr_t *tos, int from_offset, void BytecodeInterpreter::dup(intptr_t *tos) { copy_stack_slot(tos, -1, 0); } + void BytecodeInterpreter::dup2(intptr_t *tos) { copy_stack_slot(tos, -2, 0); copy_stack_slot(tos, -1, 1); @@ -3383,7 +3107,6 @@ BytecodeInterpreter::print() { char *method_name = _method->name_and_sig_as_C_string(); tty->print_cr("method: " INTPTR_FORMAT "[ %s ]", (uintptr_t) this->_method, method_name); } - tty->print_cr("mdx: " INTPTR_FORMAT, (uintptr_t) this->_mdx); tty->print_cr("stack: " INTPTR_FORMAT, (uintptr_t) this->_stack); tty->print_cr("msg: %s", C_msg(this->_msg)); tty->print_cr("result_to_call._callee: " INTPTR_FORMAT, (uintptr_t) this->_result._to_call._callee); @@ -3405,5 +3128,3 @@ extern "C" { } } #endif // PRODUCT - -#endif // JVMTI diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.hpp b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.hpp index 2d6f86c1f4f..e4a09d492bd 100644 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.hpp +++ b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.hpp @@ -26,7 +26,6 @@ #define SHARE_INTERPRETER_BYTECODEINTERPRETER_HPP #include "memory/allocation.hpp" -#include "oops/methodData.hpp" #include "oops/method.hpp" #include "runtime/basicLock.hpp" #include "runtime/frame.hpp" @@ -110,7 +109,6 @@ friend class VMStructs; ConstantPoolCache* _constants; // constant pool cache Method* _method; // method being executed oop _mirror; // mirror to klass containing method - DataLayout* _mdx; // compiler profiling data for current bytecode intptr_t* _stack; // expression stack messages _msg; // frame manager <-> interpreter message frame_manager_message _result; // result to frame manager @@ -188,8 +186,6 @@ inline intptr_t* locals() { return _locals; } inline ConstantPoolCache* constants() { return _constants; } inline Method* method() { return _method; } -inline DataLayout* mdx() { return _mdx; } -inline void set_mdx(DataLayout *new_mdx) { _mdx = new_mdx; } inline messages msg() { return _msg; } inline void set_msg(messages new_msg) { _msg = new_msg; } @@ -507,58 +503,8 @@ static void dup2_x1(intptr_t *tos); /* insert top 2 slots three down */ static void dup2_x2(intptr_t *tos); /* insert top 2 slots four down */ static void swap(intptr_t *tos); /* swap top two elements */ -// umm don't like this method modifies its object - -// The Interpreter used when +template static void run(interpreterState istate); -// The interpreter used if JVMTI needs interpreter events -static void runWithChecks(interpreterState istate); -static void End_Of_Interpreter(void); - -// Inline static functions for Java Stack and Local manipulation - -static address stack_slot(intptr_t *tos, int offset); -static jint stack_int(intptr_t *tos, int offset); -static jfloat stack_float(intptr_t *tos, int offset); -static oop stack_object(intptr_t *tos, int offset); -static jdouble stack_double(intptr_t *tos, int offset); -static jlong stack_long(intptr_t *tos, int offset); - -// only used for value types -static void set_stack_slot(intptr_t *tos, address value, int offset); -static void set_stack_int(intptr_t *tos, int value, int offset); -static void set_stack_float(intptr_t *tos, jfloat value, int offset); -static void set_stack_object(intptr_t *tos, oop value, int offset); - -// needs to be platform dep for the 32 bit platforms. -static void set_stack_double(intptr_t *tos, jdouble value, int offset); -static void set_stack_long(intptr_t *tos, jlong value, int offset); - -static void set_stack_double_from_addr(intptr_t *tos, address addr, int offset); -static void set_stack_long_from_addr(intptr_t *tos, address addr, int offset); - -// Locals - -static address locals_slot(intptr_t* locals, int offset); -static jint locals_int(intptr_t* locals, int offset); -static jfloat locals_float(intptr_t* locals, int offset); -static oop locals_object(intptr_t* locals, int offset); -static jdouble locals_double(intptr_t* locals, int offset); -static jlong locals_long(intptr_t* locals, int offset); - -static address locals_long_at(intptr_t* locals, int offset); -static address locals_double_at(intptr_t* locals, int offset); - -static void set_locals_slot(intptr_t *locals, address value, int offset); -static void set_locals_int(intptr_t *locals, jint value, int offset); -static void set_locals_float(intptr_t *locals, jfloat value, int offset); -static void set_locals_object(intptr_t *locals, oop value, int offset); -static void set_locals_double(intptr_t *locals, jdouble value, int offset); -static void set_locals_long(intptr_t *locals, jlong value, int offset); -static void set_locals_double_from_addr(intptr_t *locals, - address addr, int offset); -static void set_locals_long_from_addr(intptr_t *locals, - address addr, int offset); static void astore(intptr_t* topOfStack, int stack_offset, intptr_t* locals, int locals_offset); diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreterProfiling.hpp b/src/hotspot/share/interpreter/zero/bytecodeInterpreterProfiling.hpp deleted file mode 100644 index 49e8d4756fd..00000000000 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreterProfiling.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2014 SAP SE. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -// This file defines a set of macros which are used by the c++-interpreter -// for updating a method's methodData object. - - -#ifndef SHARE_INTERPRETER_BYTECODEINTERPRETERPROFILING_HPP -#define SHARE_INTERPRETER_BYTECODEINTERPRETERPROFILING_HPP - -// Empty dummy implementations if profiling code is switched off. ////////////// - -#define SET_MDX(mdx) - -#define BI_PROFILE_GET_OR_CREATE_METHOD_DATA(exception_handler) \ - if (ProfileInterpreter) { \ - ShouldNotReachHere(); \ - } - -#define BI_PROFILE_ALIGN_TO_CURRENT_BCI() - -#define BI_PROFILE_UPDATE_JUMP() -#define BI_PROFILE_UPDATE_BRANCH(is_taken) -#define BI_PROFILE_UPDATE_RET(bci) -#define BI_PROFILE_SUBTYPECHECK_FAILED(receiver) -#define BI_PROFILE_UPDATE_CHECKCAST(null_seen, receiver) -#define BI_PROFILE_UPDATE_INSTANCEOF(null_seen, receiver) -#define BI_PROFILE_UPDATE_CALL() -#define BI_PROFILE_UPDATE_FINALCALL() -#define BI_PROFILE_UPDATE_VIRTUALCALL(receiver) -#define BI_PROFILE_UPDATE_SWITCH(switch_index) - -#endif // SHARE_INTERPRETER_BYTECODEINTERPRETERPROFILING_HPP diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreterWithChecks.xml b/src/hotspot/share/interpreter/zero/bytecodeInterpreterWithChecks.xml deleted file mode 100644 index 8cbf66593c7..00000000000 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreterWithChecks.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - -]> - - diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreterWithChecks.xsl b/src/hotspot/share/interpreter/zero/bytecodeInterpreterWithChecks.xsl deleted file mode 100644 index 14546dfc9a3..00000000000 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreterWithChecks.xsl +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - -#define VM_JVMTI -#include "interpreter/zero/bytecodeInterpreter.cpp" - - - - - - - - diff --git a/src/hotspot/share/jfr/leakprofiler/chains/bfsClosure.hpp b/src/hotspot/share/jfr/leakprofiler/chains/bfsClosure.hpp index b722cde50b7..4cc6a716427 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/bfsClosure.hpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/bfsClosure.hpp @@ -64,7 +64,6 @@ class BFSClosure : public BasicOopIterateClosure { public: virtual ReferenceIterationMode reference_iteration_mode() { return DO_FIELDS_EXCEPT_REFERENT; } - virtual bool should_verify_oops() { return false; } BFSClosure(EdgeQueue* edge_queue, EdgeStore* edge_store, BitSet* mark_bits); void process(); diff --git a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp index ba88b8dcd78..ad99f7d2320 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp @@ -54,7 +54,6 @@ class DFSClosure : public BasicOopIterateClosure { public: virtual ReferenceIterationMode reference_iteration_mode() { return DO_FIELDS_EXCEPT_REFERENT; } - virtual bool should_verify_oops() { return false; } static void find_leaks_from_edge(EdgeStore* edge_store, BitSet* mark_bits, const Edge* start_edge); static void find_leaks_from_root_set(EdgeStore* edge_store, BitSet* mark_bits); diff --git a/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp b/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp index 0b270033463..b979dbc105f 100644 --- a/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp +++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp @@ -33,6 +33,7 @@ #include "jfr/leakprofiler/checkpoint/rootResolver.hpp" #include "jfr/utilities/jfrThreadIterator.hpp" #include "memory/iterator.hpp" +#include "prims/jvmtiDeferredUpdates.hpp" #include "oops/klass.hpp" #include "oops/oop.hpp" #include "prims/jvmtiThreadState.hpp" @@ -258,9 +259,6 @@ bool ReferenceToThreadRootClosure::do_thread_stack_detailed(JavaThread* jt) { ReferenceLocateClosure rcl(_callback, OldObjectRoot::_threads, OldObjectRoot::_stack_variable, jt); if (jt->has_last_Java_frame()) { - // traverse the registered growable array gc_array - // can't do this as it is not reachable from outside - // Traverse the monitor chunks MonitorChunk* chunk = jt->monitor_chunks(); for (; chunk != NULL; chunk = chunk->next()) { @@ -272,7 +270,7 @@ bool ReferenceToThreadRootClosure::do_thread_stack_detailed(JavaThread* jt) { } // Traverse the execution stack - for (StackFrameStream fst(jt); !fst.is_done(); fst.next()) { + for (StackFrameStream fst(jt, true /* update */, true /* process_frames */); !fst.is_done(); fst.next()) { fst.current()->oops_do(&rcl, NULL, fst.register_map()); } @@ -282,7 +280,7 @@ bool ReferenceToThreadRootClosure::do_thread_stack_detailed(JavaThread* jt) { return true; } - GrowableArray* const list = jt->deferred_locals(); + GrowableArray* const list = JvmtiDeferredUpdates::deferred_locals(jt); if (list != NULL) { for (int i = 0; i < list->length(); i++) { list->at(i)->oops_do(&rcl); @@ -297,7 +295,6 @@ bool ReferenceToThreadRootClosure::do_thread_stack_detailed(JavaThread* jt) { // around using this function /* * // can't reach these oop* from the outside - f->do_oop((oop*) &_threadObj); f->do_oop((oop*) &_vm_result); f->do_oop((oop*) &_exception_oop); f->do_oop((oop*) &_pending_async_exception); diff --git a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp index 946ff0e5bb0..2070e631a21 100644 --- a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp +++ b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp @@ -285,7 +285,7 @@ TRACE_REQUEST_FUNC(ThreadContextSwitchRate) { Event ## eventType event; \ event.set_name(flag->name()); \ event.set_value(flag->get_ ## flagType()); \ - event.set_origin(flag->get_origin()); \ + event.set_origin(static_cast(flag->get_origin())); \ event.commit(); \ } \ } \ diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCallTrace.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrCallTrace.cpp index 0777b7e39af..c822c1be1a0 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrCallTrace.cpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrCallTrace.cpp @@ -36,7 +36,7 @@ bool JfrGetCallTrace::find_top_frame(frame& top_frame, Method** method, frame& first_frame) { assert(top_frame.cb() != NULL, "invariant"); - RegisterMap map(_thread, false); + RegisterMap map(_thread, false, false); frame candidate = top_frame; for (u4 i = 0; i < MAX_STACK_DEPTH * 2; ++i) { if (candidate.is_entry_frame()) { diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp index 840d79d24ef..f867ce14226 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp @@ -41,7 +41,7 @@ #include "jfr/writers/jfrJavaEventWriter.hpp" #include "jfr/utilities/jfrThreadIterator.hpp" #include "memory/iterator.hpp" -#include "memory/metaspaceGCThresholdUpdater.hpp" +#include "memory/metaspace.hpp" #include "memory/referenceType.hpp" #include "memory/universe.hpp" #include "oops/compressedOops.hpp" @@ -122,27 +122,29 @@ void JfrThreadGroupConstant::serialize(JfrCheckpointWriter& writer) { JfrThreadGroup::serialize(writer); } -static const char* flag_value_origin_to_string(JVMFlag::Flags origin) { +static const char* flag_value_origin_to_string(JVMFlagOrigin origin) { switch (origin) { - case JVMFlag::DEFAULT: return "Default"; - case JVMFlag::COMMAND_LINE: return "Command line"; - case JVMFlag::ENVIRON_VAR: return "Environment variable"; - case JVMFlag::CONFIG_FILE: return "Config file"; - case JVMFlag::MANAGEMENT: return "Management"; - case JVMFlag::ERGONOMIC: return "Ergonomic"; - case JVMFlag::ATTACH_ON_DEMAND: return "Attach on demand"; - case JVMFlag::INTERNAL: return "Internal"; - case JVMFlag::JIMAGE_RESOURCE: return "JImage resource"; + case JVMFlagOrigin::DEFAULT: return "Default"; + case JVMFlagOrigin::COMMAND_LINE: return "Command line"; + case JVMFlagOrigin::ENVIRON_VAR: return "Environment variable"; + case JVMFlagOrigin::CONFIG_FILE: return "Config file"; + case JVMFlagOrigin::MANAGEMENT: return "Management"; + case JVMFlagOrigin::ERGONOMIC: return "Ergonomic"; + case JVMFlagOrigin::ATTACH_ON_DEMAND: return "Attach on demand"; + case JVMFlagOrigin::INTERNAL: return "Internal"; + case JVMFlagOrigin::JIMAGE_RESOURCE: return "JImage resource"; default: ShouldNotReachHere(); return ""; } } void FlagValueOriginConstant::serialize(JfrCheckpointWriter& writer) { - static const u4 nof_entries = JVMFlag::LAST_VALUE_ORIGIN + 1; - writer.write_count(nof_entries); - for (u4 i = 0; i < nof_entries; ++i) { - writer.write_key(i); - writer.write(flag_value_origin_to_string((JVMFlag::Flags)i)); + constexpr EnumRange range; + writer.write_count(static_cast(range.size())); + + for (EnumIterator it = range.begin(); it != range.end(); ++it) { + JVMFlagOrigin origin = *it; + writer.write_key(static_cast(origin)); + writer.write(flag_value_origin_to_string(origin)); } } diff --git a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp index dceccf2b3cd..b34004f01e3 100644 --- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp +++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp @@ -423,7 +423,6 @@ const char* JfrEmergencyDump::chunk_path(const char* repository_path) { */ static bool prepare_for_emergency_dump(Thread* thread) { assert(thread != NULL, "invariant"); - DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread)); if (thread->is_Watcher_thread()) { // need WatcherThread as a safeguard against potential deadlocks return false; diff --git a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.cpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.cpp index c427b7f3c1e..c417bae2177 100644 --- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.cpp +++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.cpp @@ -134,7 +134,7 @@ void JfrStackFrame::write(JfrCheckpointWriter& cpw) const { class vframeStreamSamples : public vframeStreamCommon { public: // constructor that starts with sender of frame fr (top_frame) - vframeStreamSamples(JavaThread *jt, frame fr, bool stop_at_java_call_stub) : vframeStreamCommon(jt) { + vframeStreamSamples(JavaThread *jt, frame fr, bool stop_at_java_call_stub) : vframeStreamCommon(jt, false /* process_frames */) { _stop_at_java_call_stub = stop_at_java_call_stub; _frame = fr; @@ -233,7 +233,7 @@ void JfrStackTrace::resolve_linenos() const { bool JfrStackTrace::record_safe(JavaThread* thread, int skip) { assert(thread == Thread::current(), "Thread stack needs to be walkable"); - vframeStream vfs(thread); + vframeStream vfs(thread, false /* stop_at_java_call_stub */, false /* process_frames */); u4 count = 0; _reached_root = true; for (int i = 0; i < skip; i++) { diff --git a/src/hotspot/share/jfr/writers/jfrWriterHost.inline.hpp b/src/hotspot/share/jfr/writers/jfrWriterHost.inline.hpp index b42298831f2..1f71b842946 100644 --- a/src/hotspot/share/jfr/writers/jfrWriterHost.inline.hpp +++ b/src/hotspot/share/jfr/writers/jfrWriterHost.inline.hpp @@ -71,7 +71,8 @@ template inline void WriterHost::write(const T* value, size_t len) { assert(value != NULL, "invariant"); assert(len > 0, "invariant"); - u1* const pos = ensure_size(sizeof(T) * len); + // Might need T + 1 size + u1* const pos = ensure_size(sizeof(T) * len + len); if (pos) { this->set_current_pos(write(value, len, pos)); } @@ -122,7 +123,8 @@ template inline void WriterHost::be_write(const T* value, size_t len) { assert(value != NULL, "invariant"); assert(len > 0, "invariant"); - u1* const pos = ensure_size(sizeof(T) * len); + // Might need T + 1 size + u1* const pos = ensure_size(sizeof(T) * len + len); if (pos) { this->set_current_pos(BE::be_write(value, len, pos)); } @@ -135,10 +137,17 @@ inline WriterHost::WriterHost(StorageType* storage, Th _compressed_integers(compressed_integers()) { } +// Extra size added as a safety cushion when dimensioning memory. +// With varint encoding, the worst case is +// associated with writing negative values. +// For example, writing a negative s1 (-1) +// will encode as 0xff 0x0f (2 bytes). +static const size_t size_safety_cushion = 1; + template template inline WriterHost::WriterHost(StorageType* storage, size_t size) : - WriterPolicyImpl(storage, size), + WriterPolicyImpl(storage, size + size_safety_cushion), _compressed_integers(compressed_integers()) { } @@ -148,30 +157,19 @@ inline WriterHost::WriterHost(Thread* thread) : _compressed_integers(compressed_integers()) { } -// Extra size added as a safety cushion when dimensioning memory. -// With varint encoding, the worst case is -// associated with writing negative values. -// For example, writing a negative s1 (-1) -// will encode as 0xff 0x0f (2 bytes). -// In this example, the sizeof(T) == 1 and length == 1, -// but the implementation will need to dimension -// 2 bytes for the encoding. -// Hopefully, negative values should be relatively rare. -static const size_t size_safety_cushion = 1; - template -inline u1* WriterHost::ensure_size(size_t requested) { +inline u1* WriterHost::ensure_size(size_t requested_size) { if (!this->is_valid()) { // cancelled return NULL; } - if (this->available_size() < requested + size_safety_cushion) { - if (!this->accommodate(this->used_size(), requested + size_safety_cushion)) { + if (this->available_size() < requested_size) { + if (!this->accommodate(this->used_size(), requested_size)) { assert(!this->is_valid(), "invariant"); return NULL; } } - assert(requested + size_safety_cushion <= this->available_size(), "invariant"); + assert(requested_size <= this->available_size(), "invariant"); return this->current_pos(); } diff --git a/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp b/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp index cd12abb9226..55fdf1748f6 100644 --- a/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp +++ b/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp @@ -30,6 +30,7 @@ #include "jvmci/jvmciRuntime.hpp" #include "memory/universe.hpp" #include "oops/compressedOops.inline.hpp" +#include "prims/methodHandles.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/jniHandles.inline.hpp" #include "runtime/sharedRuntime.hpp" @@ -1187,7 +1188,11 @@ void CodeInstaller::record_scope(jint pc_offset, JVMCIObject position, ScopeMode throw_exception = jvmci_env()->get_BytecodeFrame_rethrowException(frame) == JNI_TRUE; } + // has_ea_local_in_scope and arg_escape should be added to JVMCI + const bool has_ea_local_in_scope = false; + const bool arg_escape = false; _debug_recorder->describe_scope(pc_offset, method, NULL, bci, reexecute, throw_exception, is_mh_invoke, return_oop, + has_ea_local_in_scope, arg_escape, locals_token, expressions_token, monitors_token); } diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index 1a45c7ee455..5e3e6de80dd 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -43,6 +43,7 @@ #include "oops/constantPool.inline.hpp" #include "oops/method.inline.hpp" #include "oops/typeArrayOop.inline.hpp" +#include "prims/methodHandles.hpp" #include "prims/nativeLookup.hpp" #include "runtime/atomic.hpp" #include "runtime/deoptimization.hpp" @@ -1223,7 +1224,7 @@ C2V_VMENTRY_NULL(jobject, iterateFrames, (JNIEnv* env, jobject compilerToVM, job HotSpotJVMCI::HotSpotStackFrameReference::klass()->initialize(CHECK_NULL); Handle frame_reference = HotSpotJVMCI::HotSpotStackFrameReference::klass()->allocate_instance_handle(CHECK_NULL); - StackFrameStream fst(thread); + StackFrameStream fst(thread, true /* update */, true /* process_frames */); jobjectArray methods = initial_methods; int frame_number = 0; @@ -1330,7 +1331,7 @@ C2V_VMENTRY_NULL(jobject, iterateFrames, (JNIEnv* env, jobject compilerToVM, job if (HotSpotJVMCI::HotSpotStackFrameReference::objectsMaterialized(JVMCIENV, frame_reference()) == JNI_TRUE) { // the frame has been deoptimized, we need to re-synchronize the frame and vframe intptr_t* stack_pointer = (intptr_t*) HotSpotJVMCI::HotSpotStackFrameReference::stackPointer(JVMCIENV, frame_reference()); - fst = StackFrameStream(thread); + fst = StackFrameStream(thread, true /* update */, true /* process_frames */); while (fst.current()->sp() != stack_pointer && !fst.is_done()) { fst.next(); } @@ -1462,7 +1463,7 @@ C2V_VMENTRY(void, materializeVirtualObjects, (JNIEnv* env, jobject, jobject _hs_ JVMCIENV->HotSpotStackFrameReference_initialize(JVMCI_CHECK); // look for the given stack frame - StackFrameStream fst(thread, false); + StackFrameStream fst(thread, false /* update */, true /* process_frames */); intptr_t* stack_pointer = (intptr_t*) JVMCIENV->get_HotSpotStackFrameReference_stackPointer(hs_frame); while (fst.current()->sp() != stack_pointer && !fst.is_done()) { fst.next(); @@ -1480,7 +1481,7 @@ C2V_VMENTRY(void, materializeVirtualObjects, (JNIEnv* env, jobject, jobject _hs_ } Deoptimization::deoptimize(thread, *fst.current(), Deoptimization::Reason_none); // look for the frame again as it has been updated by deopt (pc, deopt state...) - StackFrameStream fstAfterDeopt(thread); + StackFrameStream fstAfterDeopt(thread, true /* update */, true /* process_frames */); while (fstAfterDeopt.current()->sp() != stack_pointer && !fstAfterDeopt.is_done()) { fstAfterDeopt.next(); } @@ -1994,6 +1995,14 @@ C2V_VMENTRY_NULL(jobject, readFieldValue, (JNIEnv* env, jobject, jobject object, JVMCI_THROW_MSG_NULL(IllegalArgumentException, err_msg("Unexpected type: %s", JVMCIENV->klass_name(base))); } + + if (displacement == java_lang_Class::component_mirror_offset() && java_lang_Class::is_instance(obj()) && + !java_lang_Class::as_Klass(obj())->is_array_klass()) { + // Class.componentType for non-array classes can transiently contain an int[] that's + // used for locking so always return null to mimic Class.getComponentType() + return JVMCIENV->get_jobject(JVMCIENV->get_JavaConstant_NULL_POINTER()); + } + jlong value = 0; JVMCIObject kind; switch (constant_type) { @@ -2219,6 +2228,13 @@ C2V_VMENTRY_NULL(jobject, getObject, (JNIEnv* env, jobject, jobject x, long disp JVMCI_THROW_0(NullPointerException); } Handle xobj = JVMCIENV->asConstant(JVMCIENV->wrap(x), JVMCI_CHECK_0); + if (displacement == java_lang_Class::component_mirror_offset() && java_lang_Class::is_instance(xobj()) && + !java_lang_Class::as_Klass(xobj())->is_array_klass()) { + // Class.componentType for non-array classes can transiently contain an int[] that's + // used for locking so always return null to mimic Class.getComponentType() + return JVMCIENV->get_jobject(JVMCIENV->get_JavaConstant_NULL_POINTER()); + } + oop res = xobj->obj_field(displacement); JVMCIObject result = JVMCIENV->get_object_constant(res); return JVMCIENV->get_jobject(result); @@ -2279,6 +2295,7 @@ C2V_VMENTRY_NULL(jlongArray, registerNativeMethods, (JNIEnv* env, jobject, jclas // 1) Try JNI short style stringStream st; char* pure_name = NativeLookup::pure_jni_name(method); + guarantee(pure_name != NULL, "Illegal native method name encountered"); os::print_jni_name_prefix_on(&st, args_size); st.print_raw(pure_name); os::print_jni_name_suffix_on(&st, args_size); @@ -2289,6 +2306,7 @@ C2V_VMENTRY_NULL(jlongArray, registerNativeMethods, (JNIEnv* env, jobject, jclas // 2) Try JNI long style st.reset(); char* long_name = NativeLookup::long_jni_name(method); + guarantee(long_name != NULL, "Illegal native method name encountered"); os::print_jni_name_prefix_on(&st, args_size); st.print_raw(pure_name); st.print_raw(long_name); @@ -2348,13 +2366,25 @@ C2V_VMENTRY_PREFIX(jlong, getCurrentJavaThread, (JNIEnv* env, jobject c2vm)) return (jlong) p2i(thread); C2V_END -C2V_VMENTRY_PREFIX(jboolean, attachCurrentThread, (JNIEnv* env, jobject c2vm, jboolean as_daemon)) +C2V_VMENTRY_PREFIX(jboolean, attachCurrentThread, (JNIEnv* env, jobject c2vm, jbyteArray name, jboolean as_daemon)) if (thread == NULL) { // Called from unattached JVMCI shared library thread + guarantee(name != NULL, "libjvmci caller must pass non-null name"); + extern struct JavaVM_ main_vm; JNIEnv* hotspotEnv; - jint res = as_daemon ? main_vm.AttachCurrentThreadAsDaemon((void**) &hotspotEnv, NULL) : - main_vm.AttachCurrentThread((void**) &hotspotEnv, NULL); + + int name_len = env->GetArrayLength(name); + char name_buf[64]; // Cannot use Resource heap as it requires a current thread + int to_copy = MIN2(name_len, (int) sizeof(name_buf) - 1); + env->GetByteArrayRegion(name, 0, to_copy, (jbyte*) name_buf); + name_buf[to_copy] = '\0'; + JavaVMAttachArgs attach_args; + attach_args.version = JNI_VERSION_1_2; + attach_args.name = name_buf; + attach_args.group = NULL; + jint res = as_daemon ? main_vm.AttachCurrentThreadAsDaemon((void**) &hotspotEnv, &attach_args) : + main_vm.AttachCurrentThread((void**) &hotspotEnv, &attach_args); if (res != JNI_OK) { JNI_THROW_("attachCurrentThread", InternalError, err_msg("Trying to attach thread returned %d", res), false); } @@ -2802,7 +2832,7 @@ JNINativeMethod CompilerToVM::methods[] = { {CC "registerNativeMethods", CC "(" CLASS ")[J", FN_PTR(registerNativeMethods)}, {CC "isCurrentThreadAttached", CC "()Z", FN_PTR(isCurrentThreadAttached)}, {CC "getCurrentJavaThread", CC "()J", FN_PTR(getCurrentJavaThread)}, - {CC "attachCurrentThread", CC "(Z)Z", FN_PTR(attachCurrentThread)}, + {CC "attachCurrentThread", CC "([BZ)Z", FN_PTR(attachCurrentThread)}, {CC "detachCurrentThread", CC "()V", FN_PTR(detachCurrentThread)}, {CC "translate", CC "(" OBJECT ")J", FN_PTR(translate)}, {CC "unhand", CC "(J)" OBJECT, FN_PTR(unhand)}, diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.hpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.hpp index 62c495ea3bf..04d4748105b 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.hpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.hpp @@ -29,6 +29,7 @@ #include "runtime/javaCalls.hpp" #include "runtime/signature.hpp" +class CollectedHeap; class JVMCIObjectArray; class CompilerToVM { diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp index 862bfde0ecc..3d5cbc213a3 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp @@ -32,6 +32,7 @@ #include "memory/universe.hpp" #include "oops/compressedOops.hpp" #include "oops/klass.inline.hpp" +#include "runtime/flags/jvmFlag.hpp" #include "runtime/sharedRuntime.hpp" #include "utilities/resourceHash.hpp" @@ -162,16 +163,16 @@ void CompilerToVM::Data::initialize(JVMCI_TRAPS) { JVMCIObjectArray CompilerToVM::initialize_intrinsics(JVMCI_TRAPS) { JVMCIObjectArray vmIntrinsics = JVMCIENV->new_VMIntrinsicMethod_array(vmIntrinsics::ID_LIMIT - 1, JVMCI_CHECK_NULL); int index = 0; - vmSymbols::SID kls_sid = vmSymbols::NO_SID; + vmSymbolID kls_sid = vmSymbolID::NO_SID; JVMCIObject kls_str; #define VM_SYMBOL_TO_STRING(s) \ - JVMCIENV->create_string(vmSymbols::symbol_at(vmSymbols::VM_SYMBOL_ENUM_NAME(s)), JVMCI_CHECK_NULL) -#define VM_INTRINSIC_INFO(id, kls, name, sig, ignore_fcode) { \ - vmSymbols::SID sid = vmSymbols::VM_SYMBOL_ENUM_NAME(kls); \ - if (kls_sid != sid) { \ - kls_str = VM_SYMBOL_TO_STRING(kls); \ - kls_sid = sid; \ - } \ + JVMCIENV->create_string(vmSymbols::symbol_at(VM_SYMBOL_ENUM_NAME(s)), JVMCI_CHECK_NULL) +#define VM_INTRINSIC_INFO(id, kls, name, sig, ignore_fcode) { \ + vmSymbolID sid = VM_SYMBOL_ENUM_NAME(kls); \ + if (kls_sid != sid) { \ + kls_str = VM_SYMBOL_TO_STRING(kls); \ + kls_sid = sid; \ + } \ JVMCIObject name_str = VM_SYMBOL_TO_STRING(name); \ JVMCIObject sig_str = VM_SYMBOL_TO_STRING(sig); \ JVMCIObject vmIntrinsicMethod = JVMCIENV->new_VMIntrinsicMethod(kls_str, name_str, sig_str, (jint) vmIntrinsics::id, JVMCI_CHECK_NULL); \ diff --git a/src/hotspot/share/jvmci/jvmciRuntime.cpp b/src/hotspot/share/jvmci/jvmciRuntime.cpp index eadd0e60a7d..ed9ad397aad 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.cpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp @@ -38,6 +38,7 @@ #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" #include "oops/typeArrayOop.inline.hpp" +#include "prims/methodHandles.hpp" #include "runtime/atomic.hpp" #include "runtime/biasedLocking.hpp" #include "runtime/deoptimization.hpp" @@ -644,10 +645,11 @@ void JVMCINMethodData::initialize( } void JVMCINMethodData::add_failed_speculation(nmethod* nm, jlong speculation) { - uint index = (speculation >> 32) & 0xFFFFFFFF; - int length = (int) speculation; + jlong index = speculation >> JVMCINMethodData::SPECULATION_LENGTH_BITS; + guarantee(index >= 0 && index <= max_jint, "Encoded JVMCI speculation index is not a positive Java int: " INTPTR_FORMAT, index); + int length = speculation & JVMCINMethodData::SPECULATION_LENGTH_MASK; if (index + length > (uint) nm->speculations_size()) { - fatal(INTPTR_FORMAT "[index: %d, length: %d] out of bounds wrt encoded speculations of length %u", speculation, index, length, nm->speculations_size()); + fatal(INTPTR_FORMAT "[index: " JLONG_FORMAT ", length: %d out of bounds wrt encoded speculations of length %u", speculation, index, length, nm->speculations_size()); } address data = nm->speculations_begin() + index; FailedSpeculation::add_failed_speculation(nm, _failed_speculations, data, length); @@ -1326,29 +1328,23 @@ Method* JVMCIRuntime::lookup_method(InstanceKlass* accessor, // Accessibility checks are performed in JVMCIEnv::get_method_by_index_impl(). assert(check_klass_accessibility(accessor, holder), "holder not accessible"); - Method* dest_method; - LinkInfo link_info(holder, name, sig, accessor, LinkInfo::AccessCheck::required, LinkInfo::LoaderConstraintCheck::required, tag); + LinkInfo link_info(holder, name, sig, accessor, + LinkInfo::AccessCheck::required, + LinkInfo::LoaderConstraintCheck::required, + tag); switch (bc) { - case Bytecodes::_invokestatic: - dest_method = - LinkResolver::resolve_static_call_or_null(link_info); - break; - case Bytecodes::_invokespecial: - dest_method = - LinkResolver::resolve_special_call_or_null(link_info); - break; - case Bytecodes::_invokeinterface: - dest_method = - LinkResolver::linktime_resolve_interface_method_or_null(link_info); - break; - case Bytecodes::_invokevirtual: - dest_method = - LinkResolver::linktime_resolve_virtual_method_or_null(link_info); - break; - default: ShouldNotReachHere(); - } - - return dest_method; + case Bytecodes::_invokestatic: + return LinkResolver::resolve_static_call_or_null(link_info); + case Bytecodes::_invokespecial: + return LinkResolver::resolve_special_call_or_null(link_info); + case Bytecodes::_invokeinterface: + return LinkResolver::linktime_resolve_interface_method_or_null(link_info); + case Bytecodes::_invokevirtual: + return LinkResolver::linktime_resolve_virtual_method_or_null(link_info); + default: + fatal("Unhandled bytecode: %s", Bytecodes::name(bc)); + return NULL; // silence compiler warnings + } } diff --git a/src/hotspot/share/jvmci/jvmciRuntime.hpp b/src/hotspot/share/jvmci/jvmciRuntime.hpp index bc3fd45bf95..d3069756a66 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.hpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.hpp @@ -39,6 +39,7 @@ class MetadataHandles; // JVMCINMethodData objects are inlined into nmethods // at nmethod::_jvmci_data_offset. class JVMCINMethodData { + friend class JVMCIVMStructs; // Index for the HotSpotNmethod mirror in the nmethod's oops table. // This is -1 if there is no mirror in the oops table. int _nmethod_mirror_index; @@ -51,6 +52,14 @@ class JVMCINMethodData { // is appended when it causes a deoptimization. FailedSpeculation** _failed_speculations; + // A speculation id is a length (low 5 bits) and an index into + // a jbyte array (i.e. 31 bits for a positive Java int). + enum { + // Keep in sync with HotSpotSpeculationEncoding. + SPECULATION_LENGTH_BITS = 5, + SPECULATION_LENGTH_MASK = (1 << SPECULATION_LENGTH_BITS) - 1 + }; + public: // Computes the size of a JVMCINMethodData object static int compute_size(const char* nmethod_mirror_name) { diff --git a/src/hotspot/share/jvmci/jvmci_globals.cpp b/src/hotspot/share/jvmci/jvmci_globals.cpp index c9b3869f2f8..bbf3ec67b1b 100644 --- a/src/hotspot/share/jvmci/jvmci_globals.cpp +++ b/src/hotspot/share/jvmci/jvmci_globals.cpp @@ -158,7 +158,7 @@ bool JVMCIGlobals::check_jvmci_flags_are_consistent() { } // Convert JVMCI flags from experimental to product -bool JVMCIGlobals::enable_jvmci_product_mode(JVMFlag::Flags origin) { +bool JVMCIGlobals::enable_jvmci_product_mode(JVMFlagOrigin origin) { const char *JVMCIFlags[] = { "EnableJVMCI", "EnableJVMCIProduct", diff --git a/src/hotspot/share/jvmci/jvmci_globals.hpp b/src/hotspot/share/jvmci/jvmci_globals.hpp index dd1a375c732..5009b17db62 100644 --- a/src/hotspot/share/jvmci/jvmci_globals.hpp +++ b/src/hotspot/share/jvmci/jvmci_globals.hpp @@ -25,7 +25,7 @@ #ifndef SHARE_JVMCI_JVMCI_GLOBALS_HPP #define SHARE_JVMCI_JVMCI_GLOBALS_HPP -#include "runtime/flags/jvmFlag.hpp" +#include "utilities/vmEnums.hpp" class fileStream; @@ -153,7 +153,7 @@ class JVMCIGlobals { static bool check_jvmci_flags_are_consistent(); // Convert JVMCI experimental flags to product - static bool enable_jvmci_product_mode(JVMFlag::Flags); + static bool enable_jvmci_product_mode(JVMFlagOrigin); // Check and exit VM with error if selected GC is not supported by JVMCI. static void check_jvmci_supported_gc(); diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 452b5e94246..4db20d040e9 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -33,6 +33,7 @@ #include "jvmci/vmStructs_jvmci.hpp" #include "oops/objArrayKlass.hpp" #include "runtime/deoptimization.hpp" +#include "runtime/flags/jvmFlag.hpp" #include "runtime/sharedRuntime.hpp" #if INCLUDE_G1GC #include "gc/g1/g1CardTable.hpp" @@ -229,6 +230,7 @@ JVMTI_ONLY(nonstatic_field(MethodCounters, _number_of_breakpoints, u2)) \ nonstatic_field(MethodCounters, _invocation_counter, InvocationCounter) \ nonstatic_field(MethodCounters, _backedge_counter, InvocationCounter) \ + AOT_ONLY(nonstatic_field(MethodCounters, _method, Method*)) \ \ nonstatic_field(MethodData, _size, int) \ nonstatic_field(MethodData, _method, Method*) \ @@ -328,7 +330,7 @@ \ nonstatic_field(Thread, _tlab, ThreadLocalAllocBuffer) \ nonstatic_field(Thread, _allocated_bytes, jlong) \ - nonstatic_field(Thread, _polling_page, volatile void*) \ + nonstatic_field(Thread, _poll_data, SafepointMechanism::ThreadData) \ \ nonstatic_field(ThreadLocalAllocBuffer, _start, HeapWord*) \ nonstatic_field(ThreadLocalAllocBuffer, _top, HeapWord*) \ @@ -340,9 +342,12 @@ nonstatic_field(ThreadLocalAllocBuffer, _fast_refill_waste, unsigned) \ nonstatic_field(ThreadLocalAllocBuffer, _slow_allocations, unsigned) \ \ + nonstatic_field(SafepointMechanism::ThreadData, _polling_word, volatile uintptr_t) \ + nonstatic_field(SafepointMechanism::ThreadData, _polling_page, volatile uintptr_t) \ + \ nonstatic_field(ThreadShadow, _pending_exception, oop) \ \ - static_field(vmSymbols, _symbols[0], Symbol*) \ + static_field(Symbol, _vm_symbols[0], Symbol*) \ \ nonstatic_field(vtableEntry, _method, Method*) \ @@ -387,6 +392,7 @@ declare_constant(HeapWordSize) \ declare_constant(InvocationEntryBci) \ declare_constant(LogKlassAlignmentInBytes) \ + declare_constant(JVMCINMethodData::SPECULATION_LENGTH_BITS) \ \ declare_constant(JVM_ACC_WRITTEN_FLAGS) \ declare_constant(JVM_ACC_MONITOR_MATCH) \ @@ -403,6 +409,7 @@ declare_preprocessor_constant("JVM_ACC_ENUM", JVM_ACC_ENUM) \ declare_preprocessor_constant("JVM_ACC_SYNTHETIC", JVM_ACC_SYNTHETIC) \ declare_preprocessor_constant("JVM_ACC_INTERFACE", JVM_ACC_INTERFACE) \ + declare_preprocessor_constant("JVM_ACC_FIELD_INITIALIZED_FINAL_UPDATE", JVM_ACC_FIELD_INITIALIZED_FINAL_UPDATE) \ \ declare_constant(JVM_CONSTANT_Utf8) \ declare_constant(JVM_CONSTANT_Unicode) \ diff --git a/src/hotspot/share/logging/logConfiguration.cpp b/src/hotspot/share/logging/logConfiguration.cpp index c6cd21c27f5..323537b012c 100644 --- a/src/hotspot/share/logging/logConfiguration.cpp +++ b/src/hotspot/share/logging/logConfiguration.cpp @@ -110,9 +110,7 @@ void LogConfiguration::initialize(jlong vm_start_time) { } void LogConfiguration::finalize() { - for (size_t i = _n_outputs; i > 0; i--) { - disable_output(i - 1); - } + disable_outputs(); FREE_C_HEAP_ARRAY(LogOutput*, _outputs); } @@ -272,28 +270,31 @@ void LogConfiguration::configure_output(size_t idx, const LogSelectionList& sele assert(strlen(output->config_string()) > 0, "should always have a config description"); } -void LogConfiguration::disable_output(size_t idx) { - assert(idx < _n_outputs, "invalid index: " SIZE_FORMAT " (_n_outputs: " SIZE_FORMAT ")", idx, _n_outputs); - LogOutput* out = _outputs[idx]; +void LogConfiguration::disable_outputs() { + size_t idx = _n_outputs; - // Remove the output from all tagsets. + // Remove all outputs from all tagsets. for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) { - ts->set_output_level(out, LogLevel::Off); - ts->update_decorators(); + ts->disable_outputs(); } - // Delete the output unless stdout or stderr (idx 0 or 1) - if (idx > 1) { - delete_output(idx); - } else { - out->set_config_string("all=off"); + while (idx > 0) { + LogOutput* out = _outputs[--idx]; + // Delete the output unless stdout or stderr (idx 0 or 1) + if (idx > 1) { + delete_output(idx); + } else { + out->set_config_string("all=off"); + } } } void LogConfiguration::disable_logging() { ConfigurationLock cl; - for (size_t i = _n_outputs; i > 0; i--) { - disable_output(i - 1); + disable_outputs(); + // Update the decorators on all tagsets to get rid of unused decorators + for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) { + ts->update_decorators(); } notify_update_listeners(); } diff --git a/src/hotspot/share/logging/logConfiguration.hpp b/src/hotspot/share/logging/logConfiguration.hpp index 635a801312b..34c0c07b76a 100644 --- a/src/hotspot/share/logging/logConfiguration.hpp +++ b/src/hotspot/share/logging/logConfiguration.hpp @@ -69,8 +69,8 @@ class LogConfiguration : public AllStatic { // Output should be completely disabled before it is deleted. static void delete_output(size_t idx); - // Disable all logging to the specified output and then delete it (unless it is stdout/stderr). - static void disable_output(size_t idx); + // Disable all logging to all outputs. All outputs except stdout/stderr will be deleted. + static void disable_outputs(); // Get output index by name. Returns SIZE_MAX if output not found. static size_t find_output(const char* name); diff --git a/src/hotspot/share/logging/logHandle.hpp b/src/hotspot/share/logging/logHandle.hpp index 3df30ac2900..fad63649b28 100644 --- a/src/hotspot/share/logging/logHandle.hpp +++ b/src/hotspot/share/logging/logHandle.hpp @@ -91,7 +91,9 @@ class LogTargetHandle { void print(const char* fmt, ...) ATTRIBUTE_PRINTF(2, 3) { va_list args; va_start(args, fmt); - _tagset->vwrite(_level, fmt, args); + if (is_enabled()) { + _tagset->vwrite(_level, fmt, args); + } va_end(args); } diff --git a/src/hotspot/share/logging/logOutputList.cpp b/src/hotspot/share/logging/logOutputList.cpp index 56525505ef4..91139c900cf 100644 --- a/src/hotspot/share/logging/logOutputList.cpp +++ b/src/hotspot/share/logging/logOutputList.cpp @@ -68,6 +68,25 @@ LogOutputList::LogOutputNode* LogOutputList::find(const LogOutput* output) const return NULL; } +void LogOutputList::clear() { + + // Grab the linked list + LogOutputNode* cur = _level_start[LogLevel::Last]; + + // Clear _level_start + for (uint level = LogLevel::First; level < LogLevel::Count; level++) { + _level_start[level] = NULL; + } + + // Delete all nodes from the linked list + wait_until_no_readers(); + while (cur != NULL) { + LogOutputNode* next = cur->_next; + delete cur; + cur = next; + } +} + void LogOutputList::remove_output(LogOutputList::LogOutputNode* node) { assert(node != NULL, "Node must be non-null"); diff --git a/src/hotspot/share/logging/logOutputList.hpp b/src/hotspot/share/logging/logOutputList.hpp index 47b37b6bbb0..d02f50cc8b2 100644 --- a/src/hotspot/share/logging/logOutputList.hpp +++ b/src/hotspot/share/logging/logOutputList.hpp @@ -88,6 +88,10 @@ class LogOutputList { // Set (add/update/remove) the output to the specified level. void set_output_level(LogOutput* output, LogLevelType level); + // Removes all outputs. Equivalent of set_output_level(out, Off) + // for all outputs. + void clear(); + class Iterator { friend class LogOutputList; private: diff --git a/src/hotspot/share/logging/logStream.cpp b/src/hotspot/share/logging/logStream.cpp index f8c7c54a719..c31637e8442 100644 --- a/src/hotspot/share/logging/logStream.cpp +++ b/src/hotspot/share/logging/logStream.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" +#include "utilities/align.hpp" LogStream::LineBuffer::LineBuffer() : _buf(_smallbuf), _cap(sizeof(_smallbuf)), _pos(0) diff --git a/src/hotspot/share/logging/logStream.hpp b/src/hotspot/share/logging/logStream.hpp index 7da8be05dd9..b141be65a6a 100644 --- a/src/hotspot/share/logging/logStream.hpp +++ b/src/hotspot/share/logging/logStream.hpp @@ -27,7 +27,6 @@ #include "logging/log.hpp" #include "logging/logHandle.hpp" -#include "memory/resourceArea.hpp" #include "utilities/ostream.hpp" diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp index e52562668e1..e2d92ce20de 100644 --- a/src/hotspot/share/logging/logTag.hpp +++ b/src/hotspot/share/logging/logTag.hpp @@ -51,6 +51,7 @@ LOG_TAG(classhisto) \ LOG_TAG(cleanup) \ LOG_TAG(codecache) \ + NOT_PRODUCT(LOG_TAG(codestrings)) \ LOG_TAG(compaction) \ LOG_TAG(compilation) \ LOG_TAG(condy) \ @@ -76,9 +77,11 @@ LOG_TAG(free) \ LOG_TAG(freelist) \ LOG_TAG(gc) \ + NOT_PRODUCT(LOG_TAG(generate)) \ LOG_TAG(handshake) \ LOG_TAG(hashtables) \ LOG_TAG(heap) \ + NOT_PRODUCT(LOG_TAG(heapsampling)) \ LOG_TAG(humongous) \ LOG_TAG(ihop) \ LOG_TAG(iklass) \ @@ -92,6 +95,7 @@ LOG_TAG(jit) \ LOG_TAG(jni) \ LOG_TAG(jvmti) \ + LOG_TAG(lambda) \ LOG_TAG(library) \ LOG_TAG(liveness) \ LOG_TAG(load) /* Trace all classes loaded */ \ @@ -154,6 +158,7 @@ LOG_TAG(sealed) \ LOG_TAG(setting) \ LOG_TAG(smr) \ + LOG_TAG(stackbarrier) \ LOG_TAG(stackmap) \ LOG_TAG(stacktrace) \ LOG_TAG(stackwalk) \ diff --git a/src/hotspot/share/logging/logTagSet.hpp b/src/hotspot/share/logging/logTagSet.hpp index 45c33c96c58..af092473eff 100644 --- a/src/hotspot/share/logging/logTagSet.hpp +++ b/src/hotspot/share/logging/logTagSet.hpp @@ -98,6 +98,10 @@ class LogTagSet { return _output_list.level_for(output); } + void disable_outputs() { + _output_list.clear(); + } + void set_output_level(LogOutput* output, LogLevelType level) { _output_list.set_output_level(output, level); } diff --git a/src/hotspot/share/memory/allocation.hpp b/src/hotspot/share/memory/allocation.hpp index 9ba938f948d..ce4e4da9304 100644 --- a/src/hotspot/share/memory/allocation.hpp +++ b/src/hotspot/share/memory/allocation.hpp @@ -142,6 +142,7 @@ class AllocatedObj { f(mtSafepoint, "Safepoint") \ f(mtSynchronizer, "Synchronization") \ f(mtServiceability, "Serviceability") \ + f(mtMetaspace, "Metaspace") \ f(mtNone, "Unknown") \ //end diff --git a/src/hotspot/share/memory/archiveBuilder.cpp b/src/hotspot/share/memory/archiveBuilder.cpp index f86840189a5..d53ec907b41 100644 --- a/src/hotspot/share/memory/archiveBuilder.cpp +++ b/src/hotspot/share/memory/archiveBuilder.cpp @@ -38,6 +38,7 @@ #include "oops/instanceKlass.hpp" #include "oops/objArrayKlass.hpp" #include "oops/oopHandle.inline.hpp" +#include "runtime/sharedRuntime.hpp" #include "utilities/align.hpp" #include "utilities/bitMap.inline.hpp" #include "utilities/hashtable.inline.hpp" @@ -45,6 +46,27 @@ ArchiveBuilder* ArchiveBuilder::_singleton = NULL; intx ArchiveBuilder::_buffer_to_target_delta = 0; +class AdapterHandlerEntry; + +class MethodTrampolineInfo { + address _c2i_entry_trampoline; + AdapterHandlerEntry** _adapter_trampoline; +public: + address c2i_entry_trampoline() { return _c2i_entry_trampoline; } + AdapterHandlerEntry** adapter_trampoline() { return _adapter_trampoline; } + void set_c2i_entry_trampoline(address addr) { _c2i_entry_trampoline = addr; } + void set_adapter_trampoline(AdapterHandlerEntry** entry) { _adapter_trampoline = entry; } +}; + +class AdapterToTrampoline : public ResourceHashtable< + AdapterHandlerEntry*, MethodTrampolineInfo, + primitive_hash, + primitive_equals, + 941, // prime number + ResourceObj::C_HEAP> {}; + +static AdapterToTrampoline* _adapter_to_trampoline = NULL; + ArchiveBuilder::OtherROAllocMark::~OtherROAllocMark() { char* newtop = ArchiveBuilder::singleton()->_ro_region->top(); ArchiveBuilder::alloc_stats()->record_other_type(int(newtop - _oldtop), true); @@ -154,7 +176,7 @@ ArchiveBuilder::ArchiveBuilder(DumpRegion* mc_region, DumpRegion* rw_region, Dum _rw_region = rw_region; _ro_region = ro_region; - _estimated_metsapceobj_bytes = 0; + _estimated_metaspaceobj_bytes = 0; } ArchiveBuilder::~ArchiveBuilder() { @@ -205,7 +227,8 @@ bool ArchiveBuilder::gather_klass_and_symbol(MetaspaceClosure::Ref* ref, bool re _num_type_array_klasses ++; } } - _estimated_metsapceobj_bytes += BytesPerWord; // See RunTimeSharedClassInfo::get_for() + // See RunTimeSharedClassInfo::get_for() + _estimated_metaspaceobj_bytes += align_up(BytesPerWord, SharedSpaceObjectAlignment); } else if (ref->msotype() == MetaspaceObj::SymbolType) { // Make sure the symbol won't be GC'ed while we are dumping the archive. Symbol* sym = (Symbol*)ref->obj(); @@ -214,7 +237,7 @@ bool ArchiveBuilder::gather_klass_and_symbol(MetaspaceClosure::Ref* ref, bool re } int bytes = ref->size() * BytesPerWord; - _estimated_metsapceobj_bytes += bytes; + _estimated_metaspaceobj_bytes += align_up(bytes, SharedSpaceObjectAlignment); return true; // recurse } @@ -238,8 +261,8 @@ void ArchiveBuilder::gather_klasses_and_symbols() { if (DumpSharedSpaces) { // To ensure deterministic contents in the static archive, we need to ensure that - // we iterate the MetsapceObjs in a deterministic order. It doesn't matter where - // the MetsapceObjs are located originally, as they are copied sequentially into + // we iterate the MetaspaceObjs in a deterministic order. It doesn't matter where + // the MetaspaceObjs are located originally, as they are copied sequentially into // the archive during the iteration. // // The only issue here is that the symbol table and the system directories may be @@ -258,6 +281,8 @@ void ArchiveBuilder::gather_klasses_and_symbols() { // DynamicArchiveBuilder::sort_methods()). sort_symbols_and_fix_hash(); sort_klasses(); + allocate_method_trampoline_info(); + allocate_method_trampolines(); } } @@ -483,7 +508,7 @@ void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* s memcpy(dest, src, bytes); - intptr_t* archived_vtable = CppVtables::get_archived_cpp_vtable(ref->msotype(), (address)dest); + intptr_t* archived_vtable = CppVtables::get_archived_vtable(ref->msotype(), (address)dest); if (archived_vtable != NULL) { *(address*)dest = (address)archived_vtable; ArchivePtrMarker::mark_pointer((address*)dest); @@ -797,3 +822,89 @@ void ArchiveBuilder::clean_up_src_obj_table() { SrcObjTableCleaner cleaner; _src_obj_table.iterate(&cleaner); } + +void ArchiveBuilder::allocate_method_trampolines_for(InstanceKlass* ik) { + if (ik->methods() != NULL) { + for (int j = 0; j < ik->methods()->length(); j++) { + // Walk the methods in a deterministic order so that the trampolines are + // created in a deterministic order. + Method* m = ik->methods()->at(j); + AdapterHandlerEntry* ent = m->adapter(); // different methods can share the same AdapterHandlerEntry + MethodTrampolineInfo* info = _adapter_to_trampoline->get(ent); + if (info->c2i_entry_trampoline() == NULL) { + info->set_c2i_entry_trampoline( + (address)MetaspaceShared::misc_code_space_alloc(SharedRuntime::trampoline_size())); + info->set_adapter_trampoline( + (AdapterHandlerEntry**)MetaspaceShared::misc_code_space_alloc(sizeof(AdapterHandlerEntry*))); + } + } + } +} + +void ArchiveBuilder::allocate_method_trampolines() { + for (int i = 0; i < _klasses->length(); i++) { + Klass* k = _klasses->at(i); + if (k->is_instance_klass()) { + InstanceKlass* ik = InstanceKlass::cast(k); + allocate_method_trampolines_for(ik); + } + } +} + +// Allocate MethodTrampolineInfo for all Methods that will be archived. Also +// return the total number of bytes needed by the method trampolines in the MC +// region. +size_t ArchiveBuilder::allocate_method_trampoline_info() { + size_t total = 0; + size_t each_method_bytes = + align_up(SharedRuntime::trampoline_size(), BytesPerWord) + + align_up(sizeof(AdapterHandlerEntry*), BytesPerWord); + + if (_adapter_to_trampoline == NULL) { + _adapter_to_trampoline = new (ResourceObj::C_HEAP, mtClass)AdapterToTrampoline(); + } + int count = 0; + for (int i = 0; i < _klasses->length(); i++) { + Klass* k = _klasses->at(i); + if (k->is_instance_klass()) { + InstanceKlass* ik = InstanceKlass::cast(k); + if (ik->methods() != NULL) { + for (int j = 0; j < ik->methods()->length(); j++) { + Method* m = ik->methods()->at(j); + AdapterHandlerEntry* ent = m->adapter(); // different methods can share the same AdapterHandlerEntry + bool is_created = false; + MethodTrampolineInfo* info = _adapter_to_trampoline->put_if_absent(ent, &is_created); + if (is_created) { + count++; + } + } + } + } + } + if (count == 0) { + // We have nothing to archive, but let's avoid having an empty region. + total = SharedRuntime::trampoline_size(); + } else { + total = count * each_method_bytes; + } + return align_up(total, SharedSpaceObjectAlignment); +} + +void ArchiveBuilder::update_method_trampolines() { + for (int i = 0; i < klasses()->length(); i++) { + Klass* k = klasses()->at(i); + if (k->is_instance_klass()) { + InstanceKlass* ik = InstanceKlass::cast(k); + Array* methods = ik->methods(); + for (int j = 0; j < methods->length(); j++) { + Method* m = methods->at(j); + AdapterHandlerEntry* ent = m->adapter(); + MethodTrampolineInfo* info = _adapter_to_trampoline->get(ent); + // m is the "copy" of the original Method, but its adapter() field is still valid because + // we haven't called make_klasses_shareable() yet. + m->set_from_compiled_entry(info->c2i_entry_trampoline()); + m->set_adapter_trampoline(info->adapter_trampoline()); + } + } + } +} diff --git a/src/hotspot/share/memory/archiveBuilder.hpp b/src/hotspot/share/memory/archiveBuilder.hpp index 7b1443cb436..e39df1cd897 100644 --- a/src/hotspot/share/memory/archiveBuilder.hpp +++ b/src/hotspot/share/memory/archiveBuilder.hpp @@ -198,7 +198,7 @@ class ArchiveBuilder : public StackObj { virtual void iterate_roots(MetaspaceClosure* it, bool is_relocating_pointers) = 0; // Conservative estimate for number of bytes needed for: - size_t _estimated_metsapceobj_bytes; // all archived MetsapceObj's. + size_t _estimated_metaspaceobj_bytes; // all archived MetaspaceObj's. protected: DumpRegion* _current_dump_space; @@ -279,6 +279,13 @@ class ArchiveBuilder : public StackObj { void print_stats(int ro_all, int rw_all, int mc_all); static intx _buffer_to_target_delta; + + // Method trampolines related functions + void allocate_method_trampolines(); + void allocate_method_trampolines_for(InstanceKlass* ik); + size_t allocate_method_trampoline_info(); + void update_method_trampolines(); + }; #endif // SHARE_MEMORY_ARCHIVEBUILDER_HPP diff --git a/src/hotspot/share/memory/archiveUtils.cpp b/src/hotspot/share/memory/archiveUtils.cpp index b30ec8b4575..4474519fd72 100644 --- a/src/hotspot/share/memory/archiveUtils.cpp +++ b/src/hotspot/share/memory/archiveUtils.cpp @@ -23,12 +23,17 @@ */ #include "precompiled.hpp" +#include "classfile/classListParser.hpp" +#include "classfile/classListWriter.hpp" +#include "classfile/systemDictionaryShared.hpp" +#include "interpreter/bootstrapInfo.hpp" #include "memory/archiveUtils.hpp" #include "memory/dynamicArchive.hpp" #include "memory/filemap.hpp" #include "memory/heapShared.inline.hpp" #include "memory/metaspace.hpp" #include "memory/metaspaceShared.hpp" +#include "memory/resourceArea.hpp" #include "oops/compressedOops.inline.hpp" #include "utilities/bitMap.inline.hpp" @@ -37,10 +42,6 @@ address* ArchivePtrMarker::_ptr_base; address* ArchivePtrMarker::_ptr_end; bool ArchivePtrMarker::_compacted; -// Metaspace::allocate() requires that all blocks must be aligned with KlassAlignmentInBytes. -// We enforce the same alignment rule in blocks allocated from the shared space. -const int SharedSpaceObjectAlignment = KlassAlignmentInBytes; - void ArchivePtrMarker::initialize(CHeapBitMap* ptrmap, address* ptr_base, address* ptr_end) { assert(_ptrmap == NULL, "initialize only once"); _ptr_base = ptr_base; @@ -297,3 +298,33 @@ void ReadClosure::do_region(u_char* start, size_t size) { size -= sizeof(intptr_t); } } + +fileStream* ClassListWriter::_classlist_file = NULL; + +void ArchiveUtils::log_to_classlist(BootstrapInfo* bootstrap_specifier, TRAPS) { + if (ClassListWriter::is_enabled()) { + if (SystemDictionaryShared::is_supported_invokedynamic(bootstrap_specifier)) { + ResourceMark rm(THREAD); + const constantPoolHandle& pool = bootstrap_specifier->pool(); + int pool_index = bootstrap_specifier->bss_index(); + ClassListWriter w; + w.stream()->print("%s %s", LAMBDA_PROXY_TAG, pool->pool_holder()->name()->as_C_string()); + CDSIndyInfo cii; + ClassListParser::populate_cds_indy_info(pool, pool_index, &cii, THREAD); + GrowableArray* indy_items = cii.items(); + for (int i = 0; i < indy_items->length(); i++) { + w.stream()->print(" %s", indy_items->at(i)); + } + w.stream()->cr(); + } + } +} + +void ArchiveUtils::check_for_oom(oop exception) { + assert(exception != nullptr, "Sanity check"); + if (exception->is_a(SystemDictionary::OutOfMemoryError_klass())) { + vm_direct_exit(-1, + err_msg("Out of memory. Please run with a larger Java heap, current MaxHeapSize = " + SIZE_FORMAT "M", MaxHeapSize/M)); + } +} diff --git a/src/hotspot/share/memory/archiveUtils.hpp b/src/hotspot/share/memory/archiveUtils.hpp index 68cd060cef4..4f2ae6f6276 100644 --- a/src/hotspot/share/memory/archiveUtils.hpp +++ b/src/hotspot/share/memory/archiveUtils.hpp @@ -30,6 +30,7 @@ #include "runtime/arguments.hpp" #include "utilities/bitMap.hpp" +class BootstrapInfo; class ReservedSpace; class VirtualSpace; @@ -239,4 +240,10 @@ class ReadClosure : public SerializeClosure { bool reading() const { return true; } }; +class ArchiveUtils { +public: + static void log_to_classlist(BootstrapInfo* bootstrap_specifier, TRAPS) NOT_CDS_RETURN; + static void check_for_oom(oop exception) NOT_CDS_RETURN; +}; + #endif // SHARE_MEMORY_ARCHIVEUTILS_HPP diff --git a/src/hotspot/share/memory/binaryTreeDictionary.hpp b/src/hotspot/share/memory/binaryTreeDictionary.hpp deleted file mode 100644 index e8e08934bcd..00000000000 --- a/src/hotspot/share/memory/binaryTreeDictionary.hpp +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_MEMORY_BINARYTREEDICTIONARY_HPP -#define SHARE_MEMORY_BINARYTREEDICTIONARY_HPP - -#include "memory/freeList.hpp" -#include "memory/memRegion.hpp" - -class Mutex; - -/* - * A binary tree based search structure for free blocks. - * This is currently used in the Concurrent Mark&Sweep implementation, but - * will be used for free block management for metadata. - */ - -// A TreeList is a FreeList which can be used to maintain a -// binary tree of free lists. - -template class TreeChunk; -template class BinaryTreeDictionary; -template class AscendTreeCensusClosure; -template class DescendTreeCensusClosure; -template class DescendTreeSearchClosure; - -template -class TreeList : public FreeList_t { - friend class TreeChunk; - friend class BinaryTreeDictionary; - friend class AscendTreeCensusClosure; - friend class DescendTreeCensusClosure; - friend class DescendTreeSearchClosure; - - TreeList* _parent; - TreeList* _left; - TreeList* _right; - - protected: - - TreeList* parent() const { return _parent; } - TreeList* left() const { return _left; } - TreeList* right() const { return _right; } - - // Wrapper on call to base class, to get the template to compile. - Chunk_t* head() const { return FreeList_t::head(); } - Chunk_t* tail() const { return FreeList_t::tail(); } - void set_head(Chunk_t* head) { FreeList_t::set_head(head); } - void set_tail(Chunk_t* tail) { FreeList_t::set_tail(tail); } - - size_t size() const { return FreeList_t::size(); } - - // Accessors for links in tree. - - void set_left(TreeList* tl) { - _left = tl; - if (tl != NULL) - tl->set_parent(this); - } - void set_right(TreeList* tl) { - _right = tl; - if (tl != NULL) - tl->set_parent(this); - } - void set_parent(TreeList* tl) { _parent = tl; } - - void clear_left() { _left = NULL; } - void clear_right() { _right = NULL; } - void clear_parent() { _parent = NULL; } - void initialize() { clear_left(); clear_right(), clear_parent(); FreeList_t::initialize(); } - - // For constructing a TreeList from a Tree chunk or - // address and size. - TreeList(); - static TreeList* - as_TreeList(TreeChunk* tc); - static TreeList* as_TreeList(HeapWord* addr, size_t size); - - // Returns the head of the free list as a pointer to a TreeChunk. - TreeChunk* head_as_TreeChunk(); - - // Returns the first available chunk in the free list as a pointer - // to a TreeChunk. - TreeChunk* first_available(); - - // Returns the block with the largest heap address amongst - // those in the list for this size; potentially slow and expensive, - // use with caution! - TreeChunk* largest_address(); - - TreeList* get_better_list( - BinaryTreeDictionary* dictionary); - - // remove_chunk_replace_if_needed() removes the given "tc" from the TreeList. - // If "tc" is the first chunk in the list, it is also the - // TreeList that is the node in the tree. remove_chunk_replace_if_needed() - // returns the possibly replaced TreeList* for the node in - // the tree. It also updates the parent of the original - // node to point to the new node. - TreeList* remove_chunk_replace_if_needed(TreeChunk* tc); - // See FreeList. - void return_chunk_at_tail(TreeChunk* tc); -}; - -// A TreeChunk is a subclass of a Chunk that additionally -// maintains a pointer to the free list on which it is currently -// linked. -// A TreeChunk is also used as a node in the binary tree. This -// allows the binary tree to be maintained without any additional -// storage (the free chunks are used). In a binary tree the first -// chunk in the free list is also the tree node. Note that the -// TreeChunk has an embedded TreeList for this purpose. Because -// the first chunk in the list is distinguished in this fashion -// (also is the node in the tree), it is the last chunk to be found -// on the free list for a node in the tree and is only removed if -// it is the last chunk on the free list. - -template -class TreeChunk : public Chunk_t { - friend class TreeList; - TreeList* _list; - TreeList _embedded_list; // if non-null, this chunk is on _list - - static size_t _min_tree_chunk_size; - - protected: - TreeList* embedded_list() const { return (TreeList*) &_embedded_list; } - void set_embedded_list(TreeList* v) { _embedded_list = *v; } - public: - TreeList* list() { return _list; } - void set_list(TreeList* v) { _list = v; } - static TreeChunk* as_TreeChunk(Chunk_t* fc); - // Initialize fields in a TreeChunk that should be - // initialized when the TreeChunk is being added to - // a free list in the tree. - void initialize() { embedded_list()->initialize(); } - - Chunk_t* next() const { return Chunk_t::next(); } - Chunk_t* prev() const { return Chunk_t::prev(); } - size_t size() const { return Chunk_t::size(); } - - static size_t min_size(); - - // debugging - void verify_tree_chunk_list() const; - void assert_is_mangled() const; -}; - -template -size_t TreeChunk::_min_tree_chunk_size = sizeof(TreeChunk)/HeapWordSize; -template -size_t TreeChunk::min_size() { return _min_tree_chunk_size; } - -template -class BinaryTreeDictionary: public CHeapObj { - friend class VMStructs; - - protected: - size_t _total_size; - size_t _total_free_blocks; - TreeList* _root; - - // private accessors - void set_total_size(size_t v) { _total_size = v; } - void inc_total_size(size_t v); - void dec_total_size(size_t v); - void set_total_free_blocks(size_t v) { _total_free_blocks = v; } - TreeList* root() const { return _root; } - void set_root(TreeList* v) { _root = v; } - - // This field is added and can be set to point to the - // the Mutex used to synchronize access to the - // dictionary so that assertion checking can be done. - // For example it is set to point to _parDictionaryAllocLock. - NOT_PRODUCT(Mutex* _lock;) - - // Remove a chunk of size "size" or larger from the tree and - // return it. If the chunk - // is the last chunk of that size, remove the node for that size - // from the tree. - TreeChunk* get_chunk_from_tree(size_t size); - // Remove this chunk from the tree. If the removal results - // in an empty list in the tree, remove the empty list. - TreeChunk* remove_chunk_from_tree(TreeChunk* tc); - // Remove the node in the trees starting at tl that has the - // minimum value and return it. Repair the tree as needed. - TreeList* remove_tree_minimum(TreeList* tl); - // Add this free chunk to the tree. - void insert_chunk_in_tree(Chunk_t* freeChunk); - public: - - // Return a list of the specified size or NULL from the tree. - // The list is not removed from the tree. - TreeList* find_list (size_t size) const; - - void verify_tree() const; - // verify that the given chunk is in the tree. - bool verify_chunk_in_free_list(Chunk_t* tc) const; - private: - void verify_tree_helper(TreeList* tl) const; - static size_t verify_prev_free_ptrs(TreeList* tl); - - // Returns the total number of chunks in the list. - size_t total_list_length(TreeList* tl) const; - // Returns the total number of words in the chunks in the tree - // starting at "tl". - size_t total_size_in_tree(TreeList* tl) const; - // Returns the sum of the square of the size of each block - // in the tree starting at "tl". - double sum_of_squared_block_sizes(TreeList* const tl) const; - // Returns the total number of free blocks in the tree starting - // at "tl". - size_t total_free_blocks_in_tree(TreeList* tl) const; - size_t num_free_blocks() const; - size_t tree_height() const; - size_t tree_height_helper(TreeList* tl) const; - size_t total_nodes_helper(TreeList* tl) const; - - public: - // Constructor - BinaryTreeDictionary() : - _total_size(0), _total_free_blocks(0), _root(0) {} - - BinaryTreeDictionary(MemRegion mr); - - // Public accessors - size_t total_size() const { return _total_size; } - size_t total_free_blocks() const { return _total_free_blocks; } - - // Reset the dictionary to the initial conditions with - // a single free chunk. - void reset(MemRegion mr); - void reset(HeapWord* addr, size_t size); - // Reset the dictionary to be empty. - void reset(); - - // Return a chunk of size "size" or greater from - // the tree. - Chunk_t* get_chunk(size_t size) { - verify_par_locked(); - Chunk_t* res = get_chunk_from_tree(size); - assert(res == NULL || res->is_free(), - "Should be returning a free chunk"); - return res; - } - - void return_chunk(Chunk_t* chunk) { - verify_par_locked(); - insert_chunk_in_tree(chunk); - } - - void remove_chunk(Chunk_t* chunk) { - verify_par_locked(); - remove_chunk_from_tree((TreeChunk*)chunk); - assert(chunk->is_free(), "Should still be a free chunk"); - } - - size_t max_chunk_size() const; - inline size_t total_chunk_size(debug_only(const Mutex* lock)) const; - - size_t min_size() const { - return TreeChunk::min_size(); - } - - double sum_of_squared_block_sizes() const { - return sum_of_squared_block_sizes(root()); - } - - Chunk_t* find_chunk_ends_at(HeapWord* target) const; - - // Return the largest free chunk in the tree. - Chunk_t* find_largest_dict() const; - - void print_free_lists(outputStream* st) const; - - // For debugging. Returns the sum of the _returned_bytes for - // all lists in the tree. - size_t sum_dict_returned_bytes() PRODUCT_RETURN0; - // Sets the _returned_bytes for all the lists in the tree to zero. - void initialize_dict_returned_bytes() PRODUCT_RETURN; - // For debugging. Return the total number of chunks in the dictionary. - size_t total_count() PRODUCT_RETURN0; - - void report_statistics(outputStream* st) const; - - void verify() const; - - Mutex* par_lock() const PRODUCT_RETURN0; - void set_par_lock(Mutex* lock) PRODUCT_RETURN; - void verify_par_locked() const PRODUCT_RETURN; -}; - - -// Closures for walking the binary tree. -// do_list() walks the free list in a node applying the closure -// to each free chunk in the list -// do_tree() walks the nodes in the binary tree applying do_list() -// to each list at each node. - -template -class TreeCensusClosure : public StackObj { - protected: - virtual void do_list(FreeList_t* fl) = 0; - public: - virtual void do_tree(TreeList* tl) = 0; -}; - -template -class AscendTreeCensusClosure : public TreeCensusClosure { - public: - void do_tree(TreeList* tl) { - if (tl != NULL) { - do_tree(tl->left()); - this->do_list(tl); - do_tree(tl->right()); - } - } -}; - -template -class DescendTreeCensusClosure : public TreeCensusClosure { - public: - void do_tree(TreeList* tl) { - if (tl != NULL) { - do_tree(tl->right()); - this->do_list(tl); - do_tree(tl->left()); - } - } -}; - -// Used to search the tree until a condition is met. -// Similar to TreeCensusClosure but searches the -// tree and returns promptly when found. - -template -class TreeSearchClosure : public StackObj { - protected: - virtual bool do_list(FreeList_t* fl) = 0; - public: - virtual bool do_tree(TreeList* tl) = 0; -}; - -#if 0 // Don't need this yet but here for symmetry. -template -class AscendTreeSearchClosure : public TreeSearchClosure { - public: - bool do_tree(TreeList* tl) { - if (tl != NULL) { - if (do_tree(tl->left())) return true; - if (do_list(tl)) return true; - if (do_tree(tl->right())) return true; - } - return false; - } -}; -#endif - -template -class DescendTreeSearchClosure : public TreeSearchClosure { - public: - bool do_tree(TreeList* tl) { - if (tl != NULL) { - if (do_tree(tl->right())) return true; - if (this->do_list(tl)) return true; - if (do_tree(tl->left())) return true; - } - return false; - } -}; - -#endif // SHARE_MEMORY_BINARYTREEDICTIONARY_HPP diff --git a/src/hotspot/share/memory/binaryTreeDictionary.inline.hpp b/src/hotspot/share/memory/binaryTreeDictionary.inline.hpp deleted file mode 100644 index 3acc9b44cd9..00000000000 --- a/src/hotspot/share/memory/binaryTreeDictionary.inline.hpp +++ /dev/null @@ -1,1004 +0,0 @@ -/* - * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_MEMORY_BINARYTREEDICTIONARY_INLINE_HPP -#define SHARE_MEMORY_BINARYTREEDICTIONARY_INLINE_HPP - -#include "gc/shared/spaceDecorator.hpp" -#include "logging/log.hpp" -#include "logging/logStream.hpp" -#include "memory/binaryTreeDictionary.hpp" -#include "memory/freeList.inline.hpp" -#include "memory/resourceArea.hpp" -#include "runtime/mutex.hpp" -#include "runtime/globals.hpp" -#include "utilities/macros.hpp" -#include "utilities/ostream.hpp" - -//////////////////////////////////////////////////////////////////////////////// -// A binary tree based search structure for free blocks. -// This is currently used in the Concurrent Mark&Sweep implementation. -//////////////////////////////////////////////////////////////////////////////// - -template -TreeChunk* TreeChunk::as_TreeChunk(Chunk_t* fc) { - // Do some assertion checking here. - return (TreeChunk*) fc; -} - -template -void TreeChunk::verify_tree_chunk_list() const { - TreeChunk* nextTC = (TreeChunk*)next(); - if (prev() != NULL) { // interior list node shouldn't have tree fields - guarantee(embedded_list()->parent() == NULL && embedded_list()->left() == NULL && - embedded_list()->right() == NULL, "should be clear"); - } - if (nextTC != NULL) { - guarantee(as_TreeChunk(nextTC->prev()) == this, "broken chain"); - guarantee(nextTC->size() == size(), "wrong size"); - nextTC->verify_tree_chunk_list(); - } -} - -template -TreeList::TreeList() : _parent(NULL), - _left(NULL), _right(NULL) {} - -template -TreeList* -TreeList::as_TreeList(TreeChunk* tc) { - // This first free chunk in the list will be the tree list. - assert((tc->size() >= (TreeChunk::min_size())), - "Chunk is too small for a TreeChunk"); - TreeList* tl = tc->embedded_list(); - tl->initialize(); - tc->set_list(tl); - tl->set_size(tc->size()); - tl->link_head(tc); - tl->link_tail(tc); - tl->set_count(1); - assert(tl->parent() == NULL, "Should be clear"); - return tl; -} - -template -TreeList* -TreeList::as_TreeList(HeapWord* addr, size_t size) { - TreeChunk* tc = (TreeChunk*) addr; - assert((size >= TreeChunk::min_size()), - "Chunk is too small for a TreeChunk"); - // The space will have been mangled initially but - // is not remangled when a Chunk_t is returned to the free list - // (since it is used to maintain the chunk on the free list). - tc->assert_is_mangled(); - tc->set_size(size); - tc->link_prev(NULL); - tc->link_next(NULL); - TreeList* tl = TreeList::as_TreeList(tc); - return tl; -} - - -template -TreeList* -TreeList::get_better_list( - BinaryTreeDictionary* dictionary) { - return this; -} - -template -TreeList* TreeList::remove_chunk_replace_if_needed(TreeChunk* tc) { - - TreeList* retTL = this; - Chunk_t* list = head(); - assert(!list || list != list->next(), "Chunk on list twice"); - assert(tc != NULL, "Chunk being removed is NULL"); - assert(parent() == NULL || this == parent()->left() || - this == parent()->right(), "list is inconsistent"); - assert(tc->is_free(), "Header is not marked correctly"); - assert(head() == NULL || head()->prev() == NULL, "list invariant"); - assert(tail() == NULL || tail()->next() == NULL, "list invariant"); - - Chunk_t* prevFC = tc->prev(); - TreeChunk* nextTC = TreeChunk::as_TreeChunk(tc->next()); - assert(list != NULL, "should have at least the target chunk"); - - // Is this the first item on the list? - if (tc == list) { - // The "getChunk..." functions for a TreeList will not return the - // first chunk in the list unless it is the last chunk in the list - // because the first chunk is also acting as the tree node. - // When coalescing happens, however, the first chunk in the a tree - // list can be the start of a free range. Free ranges are removed - // from the free lists so that they are not available to be - // allocated when the sweeper yields (giving up the free list lock) - // to allow mutator activity. If this chunk is the first in the - // list and is not the last in the list, do the work to copy the - // TreeList from the first chunk to the next chunk and update all - // the TreeList pointers in the chunks in the list. - if (nextTC == NULL) { - assert(prevFC == NULL, "Not last chunk in the list"); - set_tail(NULL); - set_head(NULL); - } else { - // copy embedded list. - nextTC->set_embedded_list(tc->embedded_list()); - retTL = nextTC->embedded_list(); - // Fix the pointer to the list in each chunk in the list. - // This can be slow for a long list. Consider having - // an option that does not allow the first chunk on the - // list to be coalesced. - for (TreeChunk* curTC = nextTC; curTC != NULL; - curTC = TreeChunk::as_TreeChunk(curTC->next())) { - curTC->set_list(retTL); - } - // Fix the parent to point to the new TreeList. - if (retTL->parent() != NULL) { - if (this == retTL->parent()->left()) { - retTL->parent()->set_left(retTL); - } else { - assert(this == retTL->parent()->right(), "Parent is incorrect"); - retTL->parent()->set_right(retTL); - } - } - // Fix the children's parent pointers to point to the - // new list. - assert(right() == retTL->right(), "Should have been copied"); - if (retTL->right() != NULL) { - retTL->right()->set_parent(retTL); - } - assert(left() == retTL->left(), "Should have been copied"); - if (retTL->left() != NULL) { - retTL->left()->set_parent(retTL); - } - retTL->link_head(nextTC); - assert(nextTC->is_free(), "Should be a free chunk"); - } - } else { - if (nextTC == NULL) { - // Removing chunk at tail of list - this->link_tail(prevFC); - } - // Chunk is interior to the list - prevFC->link_after(nextTC); - } - - // Below this point the embedded TreeList being used for the - // tree node may have changed. Don't use "this" - // TreeList*. - // chunk should still be a free chunk (bit set in _prev) - assert(!retTL->head() || retTL->size() == retTL->head()->size(), - "Wrong sized chunk in list"); - debug_only( - tc->link_prev(NULL); - tc->link_next(NULL); - tc->set_list(NULL); - bool prev_found = false; - bool next_found = false; - for (Chunk_t* curFC = retTL->head(); - curFC != NULL; curFC = curFC->next()) { - assert(curFC != tc, "Chunk is still in list"); - if (curFC == prevFC) { - prev_found = true; - } - if (curFC == nextTC) { - next_found = true; - } - } - assert(prevFC == NULL || prev_found, "Chunk was lost from list"); - assert(nextTC == NULL || next_found, "Chunk was lost from list"); - assert(retTL->parent() == NULL || - retTL == retTL->parent()->left() || - retTL == retTL->parent()->right(), - "list is inconsistent"); - ) - retTL->decrement_count(); - - assert(tc->is_free(), "Should still be a free chunk"); - assert(retTL->head() == NULL || retTL->head()->prev() == NULL, - "list invariant"); - assert(retTL->tail() == NULL || retTL->tail()->next() == NULL, - "list invariant"); - return retTL; -} - -template -void TreeList::return_chunk_at_tail(TreeChunk* chunk) { - assert(chunk != NULL, "returning NULL chunk"); - assert(chunk->list() == this, "list should be set for chunk"); - assert(tail() != NULL, "The tree list is embedded in the first chunk"); - // which means that the list can never be empty. - // This is expensive for metaspace - assert(!FLSVerifyDictionary || !this->verify_chunk_in_free_list(chunk), "Double entry"); - assert(head() == NULL || head()->prev() == NULL, "list invariant"); - assert(tail() == NULL || tail()->next() == NULL, "list invariant"); - - Chunk_t* fc = tail(); - fc->link_after(chunk); - this->link_tail(chunk); - - assert(!tail() || size() == tail()->size(), "Wrong sized chunk in list"); - FreeList_t::increment_count(); - debug_only(this->increment_returned_bytes_by(chunk->size()*sizeof(HeapWord));) - assert(head() == NULL || head()->prev() == NULL, "list invariant"); - assert(tail() == NULL || tail()->next() == NULL, "list invariant"); -} - -template -void TreeChunk::assert_is_mangled() const { - assert((ZapUnusedHeapArea && - SpaceMangler::is_mangled((HeapWord*) Chunk_t::size_addr()) && - SpaceMangler::is_mangled((HeapWord*) Chunk_t::prev_addr()) && - SpaceMangler::is_mangled((HeapWord*) Chunk_t::next_addr())) || - (size() == 0 && prev() == NULL && next() == NULL), - "Space should be clear or mangled"); -} - -template -TreeChunk* TreeList::head_as_TreeChunk() { - assert(head() == NULL || (TreeChunk::as_TreeChunk(head())->list() == this), - "Wrong type of chunk?"); - return TreeChunk::as_TreeChunk(head()); -} - -template -TreeChunk* TreeList::first_available() { - assert(head() != NULL, "The head of the list cannot be NULL"); - Chunk_t* fc = head()->next(); - TreeChunk* retTC; - if (fc == NULL) { - retTC = head_as_TreeChunk(); - } else { - retTC = TreeChunk::as_TreeChunk(fc); - } - assert(retTC->list() == this, "Wrong type of chunk."); - return retTC; -} - -// Returns the block with the largest heap address amongst -// those in the list for this size; potentially slow and expensive, -// use with caution! -template -TreeChunk* TreeList::largest_address() { - assert(head() != NULL, "The head of the list cannot be NULL"); - Chunk_t* fc = head()->next(); - TreeChunk* retTC; - if (fc == NULL) { - retTC = head_as_TreeChunk(); - } else { - // walk down the list and return the one with the highest - // heap address among chunks of this size. - Chunk_t* last = fc; - while (fc->next() != NULL) { - if ((HeapWord*)last < (HeapWord*)fc) { - last = fc; - } - fc = fc->next(); - } - retTC = TreeChunk::as_TreeChunk(last); - } - assert(retTC->list() == this, "Wrong type of chunk."); - return retTC; -} - -template -BinaryTreeDictionary::BinaryTreeDictionary(MemRegion mr) { - assert((mr.byte_size() > min_size()), "minimum chunk size"); - - reset(mr); - assert(root()->left() == NULL, "reset check failed"); - assert(root()->right() == NULL, "reset check failed"); - assert(root()->head()->next() == NULL, "reset check failed"); - assert(root()->head()->prev() == NULL, "reset check failed"); - assert(total_size() == root()->size(), "reset check failed"); - assert(total_free_blocks() == 1, "reset check failed"); -} - -template -void BinaryTreeDictionary::inc_total_size(size_t inc) { - _total_size = _total_size + inc; -} - -template -void BinaryTreeDictionary::dec_total_size(size_t dec) { - _total_size = _total_size - dec; -} - -template -void BinaryTreeDictionary::reset(MemRegion mr) { - assert((mr.byte_size() > min_size()), "minimum chunk size"); - set_root(TreeList::as_TreeList(mr.start(), mr.word_size())); - set_total_size(mr.word_size()); - set_total_free_blocks(1); -} - -template -void BinaryTreeDictionary::reset(HeapWord* addr, size_t byte_size) { - MemRegion mr(addr, heap_word_size(byte_size)); - reset(mr); -} - -template -void BinaryTreeDictionary::reset() { - set_root(NULL); - set_total_size(0); - set_total_free_blocks(0); -} - -// Get a free block of size at least size from tree, or NULL. -template -TreeChunk* -BinaryTreeDictionary::get_chunk_from_tree(size_t size) -{ - TreeList *curTL, *prevTL; - TreeChunk* retTC = NULL; - - assert((size >= min_size()), "minimum chunk size"); - if (FLSVerifyDictionary) { - verify_tree(); - } - // starting at the root, work downwards trying to find match. - // Remember the last node of size too great or too small. - for (prevTL = curTL = root(); curTL != NULL;) { - if (curTL->size() == size) { // exact match - break; - } - prevTL = curTL; - if (curTL->size() < size) { // proceed to right sub-tree - curTL = curTL->right(); - } else { // proceed to left sub-tree - assert(curTL->size() > size, "size inconsistency"); - curTL = curTL->left(); - } - } - if (curTL == NULL) { // couldn't find exact match - - // try and find the next larger size by walking back up the search path - for (curTL = prevTL; curTL != NULL;) { - if (curTL->size() >= size) break; - else curTL = curTL->parent(); - } - assert(curTL == NULL || curTL->count() > 0, - "An empty list should not be in the tree"); - } - if (curTL != NULL) { - assert(curTL->size() >= size, "size inconsistency"); - - curTL = curTL->get_better_list(this); - - retTC = curTL->first_available(); - assert((retTC != NULL) && (curTL->count() > 0), - "A list in the binary tree should not be NULL"); - assert(retTC->size() >= size, - "A chunk of the wrong size was found"); - remove_chunk_from_tree(retTC); - assert(retTC->is_free(), "Header is not marked correctly"); - } - - if (FLSVerifyDictionary) { - verify(); - } - return retTC; -} - -template -TreeList* BinaryTreeDictionary::find_list(size_t size) const { - TreeList* curTL; - for (curTL = root(); curTL != NULL;) { - if (curTL->size() == size) { // exact match - break; - } - - if (curTL->size() < size) { // proceed to right sub-tree - curTL = curTL->right(); - } else { // proceed to left sub-tree - assert(curTL->size() > size, "size inconsistency"); - curTL = curTL->left(); - } - } - return curTL; -} - - -template -bool BinaryTreeDictionary::verify_chunk_in_free_list(Chunk_t* tc) const { - size_t size = tc->size(); - TreeList* tl = find_list(size); - if (tl == NULL) { - return false; - } else { - return tl->verify_chunk_in_free_list(tc); - } -} - -template -Chunk_t* BinaryTreeDictionary::find_largest_dict() const { - TreeList *curTL = root(); - if (curTL != NULL) { - while(curTL->right() != NULL) curTL = curTL->right(); - return curTL->largest_address(); - } else { - return NULL; - } -} - -// Remove the current chunk from the tree. If it is not the last -// chunk in a list on a tree node, just unlink it. -// If it is the last chunk in the list (the next link is NULL), -// remove the node and repair the tree. -template -TreeChunk* -BinaryTreeDictionary::remove_chunk_from_tree(TreeChunk* tc) { - assert(tc != NULL, "Should not call with a NULL chunk"); - assert(tc->is_free(), "Header is not marked correctly"); - - TreeList *newTL, *parentTL; - TreeChunk* retTC; - TreeList* tl = tc->list(); - debug_only( - bool removing_only_chunk = false; - if (tl == _root) { - if ((_root->left() == NULL) && (_root->right() == NULL)) { - if (_root->count() == 1) { - assert(_root->head() == tc, "Should only be this one chunk"); - removing_only_chunk = true; - } - } - } - ) - assert(tl != NULL, "List should be set"); - assert(tl->parent() == NULL || tl == tl->parent()->left() || - tl == tl->parent()->right(), "list is inconsistent"); - - bool complicated_splice = false; - - retTC = tc; - // Removing this chunk can have the side effect of changing the node - // (TreeList*) in the tree. If the node is the root, update it. - TreeList* replacementTL = tl->remove_chunk_replace_if_needed(tc); - assert(tc->is_free(), "Chunk should still be free"); - assert(replacementTL->parent() == NULL || - replacementTL == replacementTL->parent()->left() || - replacementTL == replacementTL->parent()->right(), - "list is inconsistent"); - if (tl == root()) { - assert(replacementTL->parent() == NULL, "Incorrectly replacing root"); - set_root(replacementTL); - } -#ifdef ASSERT - if (tl != replacementTL) { - assert(replacementTL->head() != NULL, - "If the tree list was replaced, it should not be a NULL list"); - TreeList* rhl = replacementTL->head_as_TreeChunk()->list(); - TreeList* rtl = - TreeChunk::as_TreeChunk(replacementTL->tail())->list(); - assert(rhl == replacementTL, "Broken head"); - assert(rtl == replacementTL, "Broken tail"); - assert(replacementTL->size() == tc->size(), "Broken size"); - } -#endif - - // Does the tree need to be repaired? - if (replacementTL->count() == 0) { - assert(replacementTL->head() == NULL && - replacementTL->tail() == NULL, "list count is incorrect"); - // Find the replacement node for the (soon to be empty) node being removed. - // if we have a single (or no) child, splice child in our stead - if (replacementTL->left() == NULL) { - // left is NULL so pick right. right may also be NULL. - newTL = replacementTL->right(); - debug_only(replacementTL->clear_right();) - } else if (replacementTL->right() == NULL) { - // right is NULL - newTL = replacementTL->left(); - debug_only(replacementTL->clear_left();) - } else { // we have both children, so, by patriarchal convention, - // my replacement is least node in right sub-tree - complicated_splice = true; - newTL = remove_tree_minimum(replacementTL->right()); - assert(newTL != NULL && newTL->left() == NULL && - newTL->right() == NULL, "sub-tree minimum exists"); - } - // newTL is the replacement for the (soon to be empty) node. - // newTL may be NULL. - // should verify; we just cleanly excised our replacement - if (FLSVerifyDictionary) { - verify_tree(); - } - // first make newTL my parent's child - if ((parentTL = replacementTL->parent()) == NULL) { - // newTL should be root - assert(tl == root(), "Incorrectly replacing root"); - set_root(newTL); - if (newTL != NULL) { - newTL->clear_parent(); - } - } else if (parentTL->right() == replacementTL) { - // replacementTL is a right child - parentTL->set_right(newTL); - } else { // replacementTL is a left child - assert(parentTL->left() == replacementTL, "should be left child"); - parentTL->set_left(newTL); - } - debug_only(replacementTL->clear_parent();) - if (complicated_splice) { // we need newTL to get replacementTL's - // two children - assert(newTL != NULL && - newTL->left() == NULL && newTL->right() == NULL, - "newTL should not have encumbrances from the past"); - // we'd like to assert as below: - // assert(replacementTL->left() != NULL && replacementTL->right() != NULL, - // "else !complicated_splice"); - // ... however, the above assertion is too strong because we aren't - // guaranteed that replacementTL->right() is still NULL. - // Recall that we removed - // the right sub-tree minimum from replacementTL. - // That may well have been its right - // child! So we'll just assert half of the above: - assert(replacementTL->left() != NULL, "else !complicated_splice"); - newTL->set_left(replacementTL->left()); - newTL->set_right(replacementTL->right()); - debug_only( - replacementTL->clear_right(); - replacementTL->clear_left(); - ) - } - assert(replacementTL->right() == NULL && - replacementTL->left() == NULL && - replacementTL->parent() == NULL, - "delete without encumbrances"); - } - - assert(total_size() >= retTC->size(), "Incorrect total size"); - dec_total_size(retTC->size()); // size book-keeping - assert(total_free_blocks() > 0, "Incorrect total count"); - set_total_free_blocks(total_free_blocks() - 1); - - assert(retTC != NULL, "null chunk?"); - assert(retTC->prev() == NULL && retTC->next() == NULL, - "should return without encumbrances"); - if (FLSVerifyDictionary) { - verify_tree(); - } - assert(!removing_only_chunk || _root == NULL, "root should be NULL"); - return TreeChunk::as_TreeChunk(retTC); -} - -// Remove the leftmost node (lm) in the tree and return it. -// If lm has a right child, link it to the left node of -// the parent of lm. -template -TreeList* BinaryTreeDictionary::remove_tree_minimum(TreeList* tl) { - assert(tl != NULL && tl->parent() != NULL, "really need a proper sub-tree"); - // locate the subtree minimum by walking down left branches - TreeList* curTL = tl; - for (; curTL->left() != NULL; curTL = curTL->left()); - // obviously curTL now has at most one child, a right child - if (curTL != root()) { // Should this test just be removed? - TreeList* parentTL = curTL->parent(); - if (parentTL->left() == curTL) { // curTL is a left child - parentTL->set_left(curTL->right()); - } else { - // If the list tl has no left child, then curTL may be - // the right child of parentTL. - assert(parentTL->right() == curTL, "should be a right child"); - parentTL->set_right(curTL->right()); - } - } else { - // The only use of this method would not pass the root of the - // tree (as indicated by the assertion above that the tree list - // has a parent) but the specification does not explicitly exclude the - // passing of the root so accommodate it. - set_root(NULL); - } - debug_only( - curTL->clear_parent(); // Test if this needs to be cleared - curTL->clear_right(); // recall, above, left child is already null - ) - // we just excised a (non-root) node, we should still verify all tree invariants - if (FLSVerifyDictionary) { - verify_tree(); - } - return curTL; -} - -template -void BinaryTreeDictionary::insert_chunk_in_tree(Chunk_t* fc) { - TreeList *curTL, *prevTL; - size_t size = fc->size(); - - assert((size >= min_size()), - SIZE_FORMAT " is too small to be a TreeChunk " SIZE_FORMAT, - size, min_size()); - if (FLSVerifyDictionary) { - verify_tree(); - } - - fc->clear_next(); - fc->link_prev(NULL); - - // work down from the _root, looking for insertion point - for (prevTL = curTL = root(); curTL != NULL;) { - if (curTL->size() == size) // exact match - break; - prevTL = curTL; - if (curTL->size() > size) { // follow left branch - curTL = curTL->left(); - } else { // follow right branch - assert(curTL->size() < size, "size inconsistency"); - curTL = curTL->right(); - } - } - TreeChunk* tc = TreeChunk::as_TreeChunk(fc); - // This chunk is being returned to the binary tree. Its embedded - // TreeList should be unused at this point. - tc->initialize(); - if (curTL != NULL) { // exact match - tc->set_list(curTL); - curTL->return_chunk_at_tail(tc); - } else { // need a new node in tree - tc->clear_next(); - tc->link_prev(NULL); - TreeList* newTL = TreeList::as_TreeList(tc); - assert(((TreeChunk*)tc)->list() == newTL, - "List was not initialized correctly"); - if (prevTL == NULL) { // we are the only tree node - assert(root() == NULL, "control point invariant"); - set_root(newTL); - } else { // insert under prevTL ... - if (prevTL->size() < size) { // am right child - assert(prevTL->right() == NULL, "control point invariant"); - prevTL->set_right(newTL); - } else { // am left child - assert(prevTL->size() > size && prevTL->left() == NULL, "cpt pt inv"); - prevTL->set_left(newTL); - } - } - } - assert(tc->list() != NULL, "Tree list should be set"); - - inc_total_size(size); - // Method 'total_size_in_tree' walks through the every block in the - // tree, so it can cause significant performance loss if there are - // many blocks in the tree - assert(!FLSVerifyDictionary || total_size_in_tree(root()) == total_size(), "_total_size inconsistency"); - set_total_free_blocks(total_free_blocks() + 1); - if (FLSVerifyDictionary) { - verify_tree(); - } -} - -template -size_t BinaryTreeDictionary::max_chunk_size() const { - verify_par_locked(); - TreeList* tc = root(); - if (tc == NULL) return 0; - for (; tc->right() != NULL; tc = tc->right()); - return tc->size(); -} - -template -size_t BinaryTreeDictionary::total_list_length(TreeList* tl) const { - size_t res; - res = tl->count(); -#ifdef ASSERT - size_t cnt; - Chunk_t* tc = tl->head(); - for (cnt = 0; tc != NULL; tc = tc->next(), cnt++); - assert(res == cnt, "The count is not being maintained correctly"); -#endif - return res; -} - -template -size_t BinaryTreeDictionary::total_size_in_tree(TreeList* tl) const { - if (tl == NULL) - return 0; - return (tl->size() * total_list_length(tl)) + - total_size_in_tree(tl->left()) + - total_size_in_tree(tl->right()); -} - -template -double BinaryTreeDictionary::sum_of_squared_block_sizes(TreeList* const tl) const { - if (tl == NULL) { - return 0.0; - } - double size = (double)(tl->size()); - double curr = size * size * total_list_length(tl); - curr += sum_of_squared_block_sizes(tl->left()); - curr += sum_of_squared_block_sizes(tl->right()); - return curr; -} - -template -size_t BinaryTreeDictionary::total_free_blocks_in_tree(TreeList* tl) const { - if (tl == NULL) - return 0; - return total_list_length(tl) + - total_free_blocks_in_tree(tl->left()) + - total_free_blocks_in_tree(tl->right()); -} - -template -size_t BinaryTreeDictionary::num_free_blocks() const { - assert(total_free_blocks_in_tree(root()) == total_free_blocks(), - "_total_free_blocks inconsistency"); - return total_free_blocks(); -} - -template -size_t BinaryTreeDictionary::tree_height_helper(TreeList* tl) const { - if (tl == NULL) - return 0; - return 1 + MAX2(tree_height_helper(tl->left()), - tree_height_helper(tl->right())); -} - -template -size_t BinaryTreeDictionary::tree_height() const { - return tree_height_helper(root()); -} - -template -size_t BinaryTreeDictionary::total_nodes_helper(TreeList* tl) const { - if (tl == NULL) { - return 0; - } - return 1 + total_nodes_helper(tl->left()) + - total_nodes_helper(tl->right()); -} - -// Searches the tree for a chunk that ends at the -// specified address. -template -class EndTreeSearchClosure : public DescendTreeSearchClosure { - HeapWord* _target; - Chunk_t* _found; - - public: - EndTreeSearchClosure(HeapWord* target) : _target(target), _found(NULL) {} - bool do_list(FreeList_t* fl) { - Chunk_t* item = fl->head(); - while (item != NULL) { - if (item->end() == (uintptr_t*) _target) { - _found = item; - return true; - } - item = item->next(); - } - return false; - } - Chunk_t* found() { return _found; } -}; - -template -Chunk_t* BinaryTreeDictionary::find_chunk_ends_at(HeapWord* target) const { - EndTreeSearchClosure etsc(target); - bool found_target = etsc.do_tree(root()); - assert(found_target || etsc.found() == NULL, "Consistency check"); - assert(!found_target || etsc.found() != NULL, "Consistency check"); - return etsc.found(); -} - -// Closures and methods for calculating total bytes returned to the -// free lists in the tree. -#ifndef PRODUCT -template -class InitializeDictReturnedBytesClosure : public AscendTreeCensusClosure { - public: - void do_list(FreeList_t* fl) { - fl->set_returned_bytes(0); - } -}; - -template -void BinaryTreeDictionary::initialize_dict_returned_bytes() { - InitializeDictReturnedBytesClosure idrb; - idrb.do_tree(root()); -} - -template -class ReturnedBytesClosure : public AscendTreeCensusClosure { - size_t _dict_returned_bytes; - public: - ReturnedBytesClosure() { _dict_returned_bytes = 0; } - void do_list(FreeList_t* fl) { - _dict_returned_bytes += fl->returned_bytes(); - } - size_t dict_returned_bytes() { return _dict_returned_bytes; } -}; - -template -size_t BinaryTreeDictionary::sum_dict_returned_bytes() { - ReturnedBytesClosure rbc; - rbc.do_tree(root()); - - return rbc.dict_returned_bytes(); -} - -// Count the number of entries in the tree. -template -class treeCountClosure : public DescendTreeCensusClosure { - public: - uint count; - treeCountClosure(uint c) { count = c; } - void do_list(FreeList_t* fl) { - count++; - } -}; - -template -size_t BinaryTreeDictionary::total_count() { - treeCountClosure ctc(0); - ctc.do_tree(root()); - return ctc.count; -} - -template -Mutex* BinaryTreeDictionary::par_lock() const { - return _lock; -} - -template -void BinaryTreeDictionary::set_par_lock(Mutex* lock) { - _lock = lock; -} - -template -void BinaryTreeDictionary::verify_par_locked() const { -#ifdef ASSERT - Thread* my_thread = Thread::current(); - if (my_thread->is_GC_task_thread()) { - assert(par_lock() != NULL, "Should be using locking?"); - assert_lock_strong(par_lock()); - } -#endif // ASSERT -} -#endif // PRODUCT - -// Print summary statistics -template -void BinaryTreeDictionary::report_statistics(outputStream* st) const { - verify_par_locked(); - st->print_cr("Statistics for BinaryTreeDictionary:"); - st->print_cr("------------------------------------"); - size_t total_size = total_chunk_size(debug_only(NULL)); - size_t free_blocks = num_free_blocks(); - st->print_cr("Total Free Space: " SIZE_FORMAT, total_size); - st->print_cr("Max Chunk Size: " SIZE_FORMAT, max_chunk_size()); - st->print_cr("Number of Blocks: " SIZE_FORMAT, free_blocks); - if (free_blocks > 0) { - st->print_cr("Av. Block Size: " SIZE_FORMAT, total_size/free_blocks); - } - st->print_cr("Tree Height: " SIZE_FORMAT, tree_height()); -} - -template -class PrintFreeListsClosure : public AscendTreeCensusClosure { - outputStream* _st; - int _print_line; - - public: - PrintFreeListsClosure(outputStream* st) { - _st = st; - _print_line = 0; - } - void do_list(FreeList_t* fl) { - if (++_print_line >= 40) { - FreeList_t::print_labels_on(_st, "size"); - _print_line = 0; - } - fl->print_on(_st); - size_t sz = fl->size(); - for (Chunk_t* fc = fl->head(); fc != NULL; - fc = fc->next()) { - _st->print_cr("\t[" PTR_FORMAT "," PTR_FORMAT ") %s", - p2i(fc), p2i((HeapWord*)fc + sz), - fc->cantCoalesce() ? "\t CC" : ""); - } - } -}; - -template -void BinaryTreeDictionary::print_free_lists(outputStream* st) const { - - FreeList_t::print_labels_on(st, "size"); - PrintFreeListsClosure pflc(st); - pflc.do_tree(root()); -} - -// Verify the following tree invariants: -// . _root has no parent -// . parent and child point to each other -// . each node's key correctly related to that of its child(ren) -template -void BinaryTreeDictionary::verify_tree() const { - guarantee(root() == NULL || total_free_blocks() == 0 || - total_size() != 0, "_total_size shouldn't be 0?"); - guarantee(root() == NULL || root()->parent() == NULL, "_root shouldn't have parent"); - verify_tree_helper(root()); -} - -template -size_t BinaryTreeDictionary::verify_prev_free_ptrs(TreeList* tl) { - size_t ct = 0; - for (Chunk_t* curFC = tl->head(); curFC != NULL; curFC = curFC->next()) { - ct++; - assert(curFC->prev() == NULL || curFC->prev()->is_free(), - "Chunk should be free"); - } - return ct; -} - -// Note: this helper is recursive rather than iterative, so use with -// caution on very deep trees; and watch out for stack overflow errors; -// In general, to be used only for debugging. -template -void BinaryTreeDictionary::verify_tree_helper(TreeList* tl) const { - if (tl == NULL) - return; - guarantee(tl->size() != 0, "A list must has a size"); - guarantee(tl->left() == NULL || tl->left()->parent() == tl, - "parent<-/->left"); - guarantee(tl->right() == NULL || tl->right()->parent() == tl, - "parent<-/->right");; - guarantee(tl->left() == NULL || tl->left()->size() < tl->size(), - "parent !> left"); - guarantee(tl->right() == NULL || tl->right()->size() > tl->size(), - "parent !< left"); - guarantee(tl->head() == NULL || tl->head()->is_free(), "!Free"); - guarantee(tl->head() == NULL || tl->head_as_TreeChunk()->list() == tl, - "list inconsistency"); - guarantee(tl->count() > 0 || (tl->head() == NULL && tl->tail() == NULL), - "list count is inconsistent"); - guarantee(tl->count() > 1 || tl->head() == tl->tail(), - "list is incorrectly constructed"); - size_t count = verify_prev_free_ptrs(tl); - guarantee(count == (size_t)tl->count(), "Node count is incorrect"); - if (tl->head() != NULL) { - tl->head_as_TreeChunk()->verify_tree_chunk_list(); - } - verify_tree_helper(tl->left()); - verify_tree_helper(tl->right()); -} - -template -void BinaryTreeDictionary::verify() const { - verify_tree(); - guarantee(total_size() == total_size_in_tree(root()), "Total Size inconsistency"); -} - -template -size_t BinaryTreeDictionary::total_chunk_size(debug_only(const Mutex* lock)) const { - debug_only( - if (lock != NULL && lock->owned_by_self()) { - assert(total_size_in_tree(root()) == total_size(), - "_total_size inconsistency"); - } - ) - return total_size(); -} - -#endif // SHARE_MEMORY_BINARYTREEDICTIONARY_INLINE_HPP diff --git a/src/hotspot/share/memory/classLoaderMetaspace.cpp b/src/hotspot/share/memory/classLoaderMetaspace.cpp new file mode 100644 index 00000000000..6ec474e6d97 --- /dev/null +++ b/src/hotspot/share/memory/classLoaderMetaspace.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "logging/log.hpp" +#include "memory/classLoaderMetaspace.hpp" +#include "memory/metaspace.hpp" +#include "memory/metaspace/chunkManager.hpp" +#include "memory/metaspace/internalStats.hpp" +#include "memory/metaspace/metaspaceArena.hpp" +#include "memory/metaspace/metaspaceArenaGrowthPolicy.hpp" +#include "memory/metaspace/metaspaceSettings.hpp" +#include "memory/metaspace/metaspaceStatistics.hpp" +#include "memory/metaspace/runningCounters.hpp" +#include "memory/metaspaceTracer.hpp" +#include "utilities/debug.hpp" + +using metaspace::ChunkManager; +using metaspace::MetaspaceArena; +using metaspace::ArenaGrowthPolicy; +using metaspace::RunningCounters; +using metaspace::InternalStats; + +#define LOGFMT "CLMS @" PTR_FORMAT " " +#define LOGFMT_ARGS p2i(this) + +ClassLoaderMetaspace::ClassLoaderMetaspace(Mutex* lock, Metaspace::MetaspaceType space_type) : + _lock(lock), + _space_type(space_type), + _non_class_space_arena(NULL), + _class_space_arena(NULL) +{ + ChunkManager* const non_class_cm = + ChunkManager::chunkmanager_nonclass(); + + // Initialize non-class Arena + _non_class_space_arena = new MetaspaceArena( + non_class_cm, + ArenaGrowthPolicy::policy_for_space_type(space_type, false), + lock, + RunningCounters::used_nonclass_counter(), + "non-class sm"); + + // If needed, initialize class arena + if (Metaspace::using_class_space()) { + ChunkManager* const class_cm = + ChunkManager::chunkmanager_class(); + _class_space_arena = new MetaspaceArena( + class_cm, + ArenaGrowthPolicy::policy_for_space_type(space_type, true), + lock, + RunningCounters::used_class_counter(), + "class sm"); + } + + UL2(debug, "born (nonclass arena: " PTR_FORMAT ", class arena: " PTR_FORMAT ".", + p2i(_non_class_space_arena), p2i(_class_space_arena)); +} + +ClassLoaderMetaspace::~ClassLoaderMetaspace() { + Metaspace::assert_not_frozen(); + + UL(debug, "dies."); + + delete _non_class_space_arena; + delete _class_space_arena; + +} + +// Allocate word_size words from Metaspace. +MetaWord* ClassLoaderMetaspace::allocate(size_t word_size, Metaspace::MetadataType mdType) { + Metaspace::assert_not_frozen(); + if (Metaspace::is_class_space_allocation(mdType)) { + return class_space_arena()->allocate(word_size); + } else { + return non_class_space_arena()->allocate(word_size); + } +} + +// Attempt to expand the GC threshold to be good for at least another word_size words +// and allocate. Returns NULL if failure. Used during Metaspace GC. +MetaWord* ClassLoaderMetaspace::expand_and_allocate(size_t word_size, Metaspace::MetadataType mdType) { + Metaspace::assert_not_frozen(); + size_t delta_bytes = MetaspaceGC::delta_capacity_until_GC(word_size * BytesPerWord); + assert(delta_bytes > 0, "Must be"); + + size_t before = 0; + size_t after = 0; + bool can_retry = true; + MetaWord* res; + bool incremented; + + // Each thread increments the HWM at most once. Even if the thread fails to increment + // the HWM, an allocation is still attempted. This is because another thread must then + // have incremented the HWM and therefore the allocation might still succeed. + do { + incremented = MetaspaceGC::inc_capacity_until_GC(delta_bytes, &after, &before, &can_retry); + res = allocate(word_size, mdType); + } while (!incremented && res == NULL && can_retry); + + if (incremented) { + Metaspace::tracer()->report_gc_threshold(before, after, + MetaspaceGCThresholdUpdater::ExpandAndAllocate); + // Keeping both for now until I am sure the old variant (gc + metaspace) is not needed anymore + log_trace(gc, metaspace)("Increase capacity to GC from " SIZE_FORMAT " to " SIZE_FORMAT, before, after); + UL2(info, "GC threshold increased: " SIZE_FORMAT "->" SIZE_FORMAT ".", before, after); + } + + return res; +} + +// Prematurely returns a metaspace allocation to the _block_freelists +// because it is not needed anymore. +void ClassLoaderMetaspace::deallocate(MetaWord* ptr, size_t word_size, bool is_class) { + Metaspace::assert_not_frozen(); + if (Metaspace::using_class_space() && is_class) { + class_space_arena()->deallocate(ptr, word_size); + } else { + non_class_space_arena()->deallocate(ptr, word_size); + } + DEBUG_ONLY(InternalStats::inc_num_deallocs();) +} + +// Update statistics. This walks all in-use chunks. +void ClassLoaderMetaspace::add_to_statistics(metaspace::ClmsStats* out) const { + if (non_class_space_arena() != NULL) { + non_class_space_arena()->add_to_statistics(&out->_arena_stats_nonclass); + } + if (class_space_arena() != NULL) { + class_space_arena()->add_to_statistics(&out->_arena_stats_class); + } +} + +#ifdef ASSERT +void ClassLoaderMetaspace::verify() const { + if (non_class_space_arena() != NULL) { + non_class_space_arena()->verify(); + } + if (class_space_arena() != NULL) { + class_space_arena()->verify(); + } +} +#endif // ASSERT + +// This only exists for JFR and jcmd VM.classloader_stats. We may want to +// change this. Capacity as a stat is of questionable use since it may +// contain committed and uncommitted areas. For now we do this to maintain +// backward compatibility with JFR. +void ClassLoaderMetaspace::calculate_jfr_stats(size_t* p_used_bytes, size_t* p_capacity_bytes) const { + // Implement this using the standard statistics objects. + size_t used_c = 0, cap_c = 0, used_nc = 0, cap_nc = 0; + if (non_class_space_arena() != NULL) { + non_class_space_arena()->usage_numbers(&used_nc, NULL, &cap_nc); + } + if (class_space_arena() != NULL) { + class_space_arena()->usage_numbers(&used_c, NULL, &cap_c); + } + if (p_used_bytes != NULL) { + *p_used_bytes = used_c + used_nc; + } + if (p_capacity_bytes != NULL) { + *p_capacity_bytes = cap_c + cap_nc; + } +} + diff --git a/src/hotspot/share/memory/classLoaderMetaspace.hpp b/src/hotspot/share/memory/classLoaderMetaspace.hpp new file mode 100644 index 00000000000..34fd99b0a59 --- /dev/null +++ b/src/hotspot/share/memory/classLoaderMetaspace.hpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +#ifndef SHARE_MEMORY_CLASSLOADERMETASPACE_HPP +#define SHARE_MEMORY_CLASSLOADERMETASPACE_HPP + +#include "memory/allocation.hpp" +#include "memory/metaspace.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" + +class outputStream; + +namespace metaspace { + struct ClmsStats; + class MetaspaceArena; +} + +// A ClassLoaderMetaspace manages MetaspaceArena(s) for a CLD. +// +// A CLD owns one MetaspaceArena if UseCompressedClassPointers is false. Otherwise +// it owns two - one for the Klass* objects from the class space, one for the other +// types of MetaspaceObjs from the non-class space. +// +// +------+ +----------------------+ +-------------------+ +// | CLD | ---> | ClassLoaderMetaspace | ----> | (non class) Arena | +// +------+ +----------------------+ | +-------------------+ allocation top +// | | v +// | + chunk -- chunk ... -- chunk +// | +// | +-------------------+ +// +--> | (class) Arena | +// +-------------------+ +// | +// + chunk ... chunk +// ^ +// alloc top +// +class ClassLoaderMetaspace : public CHeapObj { + + // A reference to an outside lock, held by the CLD. + Mutex* const _lock; + + const Metaspace::MetaspaceType _space_type; + + // Arena for allocations from non-class metaspace + // (resp. for all allocations if -XX:-UseCompressedClassPointers). + metaspace::MetaspaceArena* _non_class_space_arena; + + // Arena for allocations from class space + // (NULL if -XX:-UseCompressedClassPointers). + metaspace::MetaspaceArena* _class_space_arena; + + Mutex* lock() const { return _lock; } + metaspace::MetaspaceArena* non_class_space_arena() const { return _non_class_space_arena; } + metaspace::MetaspaceArena* class_space_arena() const { return _class_space_arena; } + +public: + + ClassLoaderMetaspace(Mutex* lock, Metaspace::MetaspaceType space_type); + + ~ClassLoaderMetaspace(); + + Metaspace::MetaspaceType space_type() const { return _space_type; } + + // Allocate word_size words from Metaspace. + MetaWord* allocate(size_t word_size, Metaspace::MetadataType mdType); + + // Attempt to expand the GC threshold to be good for at least another word_size words + // and allocate. Returns NULL if failure. Used during Metaspace GC. + MetaWord* expand_and_allocate(size_t word_size, Metaspace::MetadataType mdType); + + // Prematurely returns a metaspace allocation to the _block_freelists + // because it is not needed anymore. + void deallocate(MetaWord* ptr, size_t word_size, bool is_class); + + // Update statistics. This walks all in-use chunks. + void add_to_statistics(metaspace::ClmsStats* out) const; + + DEBUG_ONLY(void verify() const;) + + // This only exists for JFR and jcmd VM.classloader_stats. We may want to + // change this. Capacity as a stat is of questionable use since it may + // contain committed and uncommitted areas. For now we do this to maintain + // backward compatibility with JFR. + void calculate_jfr_stats(size_t* p_used_bytes, size_t* p_capacity_bytes) const; + +}; // end: ClassLoaderMetaspace + + +#endif // SHARE_MEMORY_CLASSLOADERMETASPACE_HPP diff --git a/src/hotspot/share/memory/cppVtables.cpp b/src/hotspot/share/memory/cppVtables.cpp index b79573f7b08..617c4f4c8ef 100644 --- a/src/hotspot/share/memory/cppVtables.cpp +++ b/src/hotspot/share/memory/cppVtables.cpp @@ -49,8 +49,8 @@ // + at run time: we clone the actual contents of the vtables from libjvm.so // into our own tables. -// Currently, the archive contain ONLY the following types of objects that have C++ vtables. -#define CPP_VTABLE_PATCH_TYPES_DO(f) \ +// Currently, the archive contains ONLY the following types of objects that have C++ vtables. +#define CPP_VTABLE_TYPES_DO(f) \ f(ConstantPool) \ f(InstanceKlass) \ f(InstanceClassLoaderKlass) \ @@ -78,60 +78,36 @@ class CppVtableInfo { } }; -static inline intptr_t* vtable_of(Metadata* m) { +static inline intptr_t* vtable_of(const Metadata* m) { return *((intptr_t**)m); } -static inline DumpRegion* mc_region() { - return MetaspaceShared::misc_code_dump_space(); -} - -template class CppVtableCloner : public T { - static CppVtableInfo* _info; - +template class CppVtableCloner { static int get_vtable_length(const char* name); public: - // Allocate and initialize the C++ vtable, starting from top, but do not go past end. - static intptr_t* allocate(const char* name); + // Allocate a clone of the vtable of T from the shared metaspace; + // Initialize the contents of this clone. + static CppVtableInfo* allocate_and_initialize(const char* name); - // Clone the vtable to ... - static intptr_t* clone_vtable(const char* name, CppVtableInfo* info); - - static void zero_vtable_clone() { - assert(DumpSharedSpaces, "dump-time only"); - _info->zero(); - } - - static bool is_valid_shared_object(const T* obj) { - intptr_t* vptr = *(intptr_t**)obj; - return vptr == _info->cloned_vtable(); - } + // Copy the contents of the vtable of T into info->_cloned_vtable; + static void initialize(const char* name, CppVtableInfo* info); static void init_orig_cpp_vtptr(int kind); }; -template CppVtableInfo* CppVtableCloner::_info = NULL; - template -intptr_t* CppVtableCloner::allocate(const char* name) { - assert(is_aligned(mc_region()->top(), sizeof(intptr_t)), "bad alignment"); +CppVtableInfo* CppVtableCloner::allocate_and_initialize(const char* name) { int n = get_vtable_length(name); - _info = (CppVtableInfo*)mc_region()->allocate(CppVtableInfo::byte_size(n)); - _info->set_vtable_size(n); - - intptr_t* p = clone_vtable(name, _info); - assert((char*)p == mc_region()->top(), "must be"); - - return _info->cloned_vtable(); + CppVtableInfo* info = + (CppVtableInfo*)MetaspaceShared::misc_code_dump_space()->allocate(CppVtableInfo::byte_size(n)); + info->set_vtable_size(n); + initialize(name, info); + return info; } template -intptr_t* CppVtableCloner::clone_vtable(const char* name, CppVtableInfo* info) { - if (!DumpSharedSpaces) { - assert(_info == 0, "_info is initialized only at dump time"); - _info = info; // Remember it -- it will be used by MetaspaceShared::is_valid_shared_method() - } +void CppVtableCloner::initialize(const char* name, CppVtableInfo* info) { T tmp; // Allocate temporary dummy metadata object to get to the original vtable. int n = info->vtable_size(); intptr_t* srcvtable = vtable_of(&tmp); @@ -141,7 +117,6 @@ intptr_t* CppVtableCloner::clone_vtable(const char* name, CppVtableInfo* info // safe to do memcpy. log_debug(cds, vtables)("Copying %3d vtable entries for %s", n, name); memcpy(dstvtable, srcvtable, sizeof(intptr_t) * n); - return dstvtable + n; } // To determine the size of the vtable for each type, we use the following @@ -195,15 +170,12 @@ int CppVtableCloner::get_vtable_length(const char* name) { return vtable_len; } -#define ALLOC_CPP_VTABLE_CLONE(c) \ - _cloned_cpp_vtptrs[c##_Kind] = CppVtableCloner::allocate(#c); \ - ArchivePtrMarker::mark_pointer(&_cloned_cpp_vtptrs[c##_Kind]); +#define ALLOCATE_AND_INITIALIZE_VTABLE(c) \ + _index[c##_Kind] = CppVtableCloner::allocate_and_initialize(#c); \ + ArchivePtrMarker::mark_pointer(&_index[c##_Kind]); -#define CLONE_CPP_VTABLE(c) \ - p = CppVtableCloner::clone_vtable(#c, (CppVtableInfo*)p); - -#define ZERO_CPP_VTABLE(c) \ - CppVtableCloner::zero_vtable_clone(); +#define INITIALIZE_VTABLE(c) \ + CppVtableCloner::initialize(#c, _index[c##_Kind]); #define INIT_ORIG_CPP_VTPTRS(c) \ CppVtableCloner::init_orig_cpp_vtptr(c##_Kind); @@ -212,7 +184,7 @@ int CppVtableCloner::get_vtable_length(const char* name) { enum ClonedVtableKind { // E.g., ConstantPool_Kind == 0, InstanceKlass_Kind == 1, etc. - CPP_VTABLE_PATCH_TYPES_DO(DECLARE_CLONED_VTABLE_KIND) + CPP_VTABLE_TYPES_DO(DECLARE_CLONED_VTABLE_KIND) _num_cloned_vtable_kinds }; @@ -235,23 +207,30 @@ void CppVtableCloner::init_orig_cpp_vtptr(int kind) { // ConstantPool* cp = ....; // an archived constant pool // InstanceKlass* ik = ....;// an archived class // the following holds true: -// _cloned_cpp_vtptrs[ConstantPool_Kind] == ((intptr_t**)cp)[0] -// _cloned_cpp_vtptrs[InstanceKlass_Kind] == ((intptr_t**)ik)[0] -static intptr_t** _cloned_cpp_vtptrs = NULL; +// _index[ConstantPool_Kind]->cloned_vtable() == ((intptr_t**)cp)[0] +// _index[InstanceKlass_Kind]->cloned_vtable() == ((intptr_t**)ik)[0] +CppVtableInfo** CppVtables::_index = NULL; -void CppVtables::allocate_cloned_cpp_vtptrs() { +char* CppVtables::dumptime_init() { assert(DumpSharedSpaces, "must"); - size_t vtptrs_bytes = _num_cloned_vtable_kinds * sizeof(intptr_t*); - _cloned_cpp_vtptrs = (intptr_t**)mc_region()->allocate(vtptrs_bytes); + size_t vtptrs_bytes = _num_cloned_vtable_kinds * sizeof(CppVtableInfo*); + _index = (CppVtableInfo**)MetaspaceShared::misc_code_dump_space()->allocate(vtptrs_bytes); + + CPP_VTABLE_TYPES_DO(ALLOCATE_AND_INITIALIZE_VTABLE); + + return (char*)_index; } -void CppVtables::serialize_cloned_cpp_vtptrs(SerializeClosure* soc) { - soc->do_ptr((void**)&_cloned_cpp_vtptrs); +void CppVtables::serialize(SerializeClosure* soc) { + soc->do_ptr((void**)&_index); + if (soc->reading()) { + CPP_VTABLE_TYPES_DO(INITIALIZE_VTABLE); + } } -intptr_t* CppVtables::get_archived_cpp_vtable(MetaspaceObj::Type msotype, address obj) { +intptr_t* CppVtables::get_archived_vtable(MetaspaceObj::Type msotype, address obj) { if (!_orig_cpp_vtptrs_inited) { - CPP_VTABLE_PATCH_TYPES_DO(INIT_ORIG_CPP_VTPTRS); + CPP_VTABLE_TYPES_DO(INIT_ORIG_CPP_VTPTRS); _orig_cpp_vtptrs_inited = true; } @@ -283,50 +262,27 @@ intptr_t* CppVtables::get_archived_cpp_vtable(MetaspaceObj::Type msotype, addres } if (kind >= _num_cloned_vtable_kinds) { fatal("Cannot find C++ vtable for " INTPTR_FORMAT " -- you probably added" - " a new subtype of Klass or MetaData without updating CPP_VTABLE_PATCH_TYPES_DO", + " a new subtype of Klass or MetaData without updating CPP_VTABLE_TYPES_DO", p2i(obj)); } } if (kind >= 0) { assert(kind < _num_cloned_vtable_kinds, "must be"); - return _cloned_cpp_vtptrs[kind]; + return _index[kind]->cloned_vtable(); } else { return NULL; } } -// This can be called at both dump time and run time: -// - clone the contents of the c++ vtables into the space -// allocated by allocate_cpp_vtable_clones() -void CppVtables::clone_cpp_vtables(intptr_t* p) { - assert(DumpSharedSpaces || UseSharedSpaces, "sanity"); - CPP_VTABLE_PATCH_TYPES_DO(CLONE_CPP_VTABLE); -} - -void CppVtables::zero_cpp_vtable_clones_for_writing() { +void CppVtables::zero_archived_vtables() { assert(DumpSharedSpaces, "dump-time only"); - CPP_VTABLE_PATCH_TYPES_DO(ZERO_CPP_VTABLE); -} - -// Allocate and initialize the C++ vtables, starting from top, but do not go past end. -char* CppVtables::allocate_cpp_vtable_clones() { - char* cloned_vtables = mc_region()->top(); // This is the beginning of all the cloned vtables - - assert(DumpSharedSpaces, "dump-time only"); - // Layout (each slot is a intptr_t): - // [number of slots in the first vtable = n1] - // [ slots for the first vtable] - // [number of slots in the first second = n2] - // [ slots for the second vtable] - // ... - // The order of the vtables is the same as the CPP_VTAB_PATCH_TYPES_DO macro. - CPP_VTABLE_PATCH_TYPES_DO(ALLOC_CPP_VTABLE_CLONE); - - return cloned_vtables; + for (int kind = 0; kind < _num_cloned_vtable_kinds; kind ++) { + _index[kind]->zero(); + } } bool CppVtables::is_valid_shared_method(const Method* m) { assert(MetaspaceShared::is_in_shared_metaspace(m), "must be"); - return CppVtableCloner::is_valid_shared_object(m); + return vtable_of(m) == _index[Method_Kind]->cloned_vtable(); } diff --git a/src/hotspot/share/memory/cppVtables.hpp b/src/hotspot/share/memory/cppVtables.hpp index 8604424c6fc..c476d675755 100644 --- a/src/hotspot/share/memory/cppVtables.hpp +++ b/src/hotspot/share/memory/cppVtables.hpp @@ -31,17 +31,16 @@ class Method; class SerializeClosure; +class CppVtableInfo; // Support for C++ vtables in CDS archive. class CppVtables : AllStatic { - static void patch_cpp_vtable_pointers(); + static CppVtableInfo** _index; public: - static char* allocate_cpp_vtable_clones(); - static void allocate_cloned_cpp_vtptrs(); - static void clone_cpp_vtables(intptr_t* p); - static void zero_cpp_vtable_clones_for_writing(); - static intptr_t* get_archived_cpp_vtable(MetaspaceObj::Type msotype, address obj); - static void serialize_cloned_cpp_vtptrs(SerializeClosure* sc); + static char* dumptime_init(); + static void zero_archived_vtables(); + static intptr_t* get_archived_vtable(MetaspaceObj::Type msotype, address obj); + static void serialize(SerializeClosure* sc); static bool is_valid_shared_method(const Method* m) NOT_CDS_RETURN_(false); }; diff --git a/src/hotspot/share/memory/dynamicArchive.cpp b/src/hotspot/share/memory/dynamicArchive.cpp index 4275d89eda6..5cb889f60cf 100644 --- a/src/hotspot/share/memory/dynamicArchive.cpp +++ b/src/hotspot/share/memory/dynamicArchive.cpp @@ -93,12 +93,10 @@ class DynamicArchiveBuilder : public ArchiveBuilder { size_t _estimated_trampoline_bytes; // method entry trampolines size_t estimate_archive_size(); - size_t estimate_trampoline_size(); size_t estimate_class_file_size(); address reserve_space_and_init_buffer_to_target_delta(); void init_header(address addr); void release_header(); - void make_trampolines(); void sort_methods(); void sort_methods(InstanceKlass* ik) const; void remark_pointers_for_instance_klass(InstanceKlass* k, bool should_mark) const; @@ -116,12 +114,6 @@ class DynamicArchiveBuilder : public ArchiveBuilder { _num_dump_regions_used = 1; } - void reserve_buffers_for_trampolines() { - size_t n = _estimated_trampoline_bytes; - assert(n >= SharedRuntime::trampoline_size(), "dont want to be empty"); - MetaspaceShared::misc_code_space_alloc(n); - } - public: DynamicArchiveBuilder() : ArchiveBuilder(MetaspaceShared::misc_code_dump_space(), MetaspaceShared::read_write_dump_space(), @@ -184,7 +176,7 @@ class DynamicArchiveBuilder : public ArchiveBuilder { CHeapBitMap ptrmap; ArchivePtrMarker::initialize(&ptrmap, (address*)reserved_bottom, (address*)current_dump_space()->top()); - reserve_buffers_for_trampolines(); + allocate_method_trampolines(); verify_estimate_size(_estimated_trampoline_bytes, "Trampolines"); gather_source_objs(); @@ -201,7 +193,7 @@ class DynamicArchiveBuilder : public ArchiveBuilder { dump_ro_region(); relocate_pointers(); - verify_estimate_size(_estimated_metsapceobj_bytes, "MetaspaceObjs"); + verify_estimate_size(_estimated_metaspaceobj_bytes, "MetaspaceObjs"); char* serialized_data; { @@ -221,7 +213,7 @@ class DynamicArchiveBuilder : public ArchiveBuilder { verify_estimate_size(_estimated_hashtable_bytes, "Hashtables"); - make_trampolines(); + update_method_trampolines(); sort_methods(); log_info(cds)("Make classes shareable"); @@ -250,21 +242,27 @@ class DynamicArchiveBuilder : public ArchiveBuilder { size_t DynamicArchiveBuilder::estimate_archive_size() { // size of the symbol table and two dictionaries, plus the RunTimeSharedClassInfo's - _estimated_hashtable_bytes = 0; - _estimated_hashtable_bytes += SymbolTable::estimate_size_for_archive(); - _estimated_hashtable_bytes += SystemDictionaryShared::estimate_size_for_archive(); + size_t symbol_table_est = SymbolTable::estimate_size_for_archive(); + size_t dictionary_est = SystemDictionaryShared::estimate_size_for_archive(); + _estimated_hashtable_bytes = symbol_table_est + dictionary_est; - _estimated_trampoline_bytes = estimate_trampoline_size(); + _estimated_trampoline_bytes = allocate_method_trampoline_info(); size_t total = 0; - total += _estimated_metsapceobj_bytes; + total += _estimated_metaspaceobj_bytes; total += _estimated_hashtable_bytes; total += _estimated_trampoline_bytes; // allow fragmentation at the end of each dump region total += _total_dump_regions * reserve_alignment(); + log_info(cds, dynamic)("_estimated_hashtable_bytes = " SIZE_FORMAT " + " SIZE_FORMAT " = " SIZE_FORMAT, + symbol_table_est, dictionary_est, _estimated_hashtable_bytes); + log_info(cds, dynamic)("_estimated_metaspaceobj_bytes = " SIZE_FORMAT, _estimated_metaspaceobj_bytes); + log_info(cds, dynamic)("_estimated_trampoline_bytes = " SIZE_FORMAT, _estimated_trampoline_bytes); + log_info(cds, dynamic)("total estimate bytes = " SIZE_FORMAT, total); + return align_up(total, reserve_alignment()); } @@ -331,54 +329,6 @@ void DynamicArchiveBuilder::release_header() { _header = NULL; } -size_t DynamicArchiveBuilder::estimate_trampoline_size() { - size_t total = 0; - size_t each_method_bytes = - align_up(SharedRuntime::trampoline_size(), BytesPerWord) + - align_up(sizeof(AdapterHandlerEntry*), BytesPerWord); - - for (int i = 0; i < klasses()->length(); i++) { - Klass* k = klasses()->at(i); - if (k->is_instance_klass()) { - Array* methods = InstanceKlass::cast(k)->methods(); - total += each_method_bytes * methods->length(); - } - } - if (total == 0) { - // We have nothing to archive, but let's avoid having an empty region. - total = SharedRuntime::trampoline_size(); - } - return total; -} - -void DynamicArchiveBuilder::make_trampolines() { - DumpRegion* mc_space = MetaspaceShared::misc_code_dump_space(); - char* p = mc_space->base(); - for (int i = 0; i < klasses()->length(); i++) { - Klass* k = klasses()->at(i); - if (!k->is_instance_klass()) { - continue; - } - InstanceKlass* ik = InstanceKlass::cast(k); - Array* methods = ik->methods(); - for (int j = 0; j < methods->length(); j++) { - Method* m = methods->at(j); - address c2i_entry_trampoline = (address)p; - p += SharedRuntime::trampoline_size(); - assert(p >= mc_space->base() && p <= mc_space->top(), "must be"); - m->set_from_compiled_entry(to_target(c2i_entry_trampoline)); - - AdapterHandlerEntry** adapter_trampoline =(AdapterHandlerEntry**)p; - p += sizeof(AdapterHandlerEntry*); - assert(p >= mc_space->base() && p <= mc_space->top(), "must be"); - *adapter_trampoline = NULL; - m->set_adapter_trampoline(to_target(adapter_trampoline)); - } - } - - guarantee(p <= mc_space->top(), "Estimate of trampoline size is insufficient"); -} - void DynamicArchiveBuilder::sort_methods() { InstanceKlass::disable_method_binary_search(); for (int i = 0; i < klasses()->length(); i++) { diff --git a/src/hotspot/share/memory/filemap.cpp b/src/hotspot/share/memory/filemap.cpp index 044cf68274f..ab183ca623d 100644 --- a/src/hotspot/share/memory/filemap.cpp +++ b/src/hotspot/share/memory/filemap.cpp @@ -150,7 +150,7 @@ template static void get_header_version(char (&header_version) [N]) { } else { // Get the hash value. Use a static seed because the hash needs to return the same // value over multiple jvm invocations. - unsigned int hash = AltHashing::murmur3_32(8191, (const jbyte*)vm_version, version_len); + uint32_t hash = AltHashing::halfsiphash_32(8191, (const uint8_t*)vm_version, version_len); // Truncate the ident, saving room for the 8 hex character hash value. strncpy(header_version, vm_version, JVM_IDENT_MAX-9); @@ -1737,7 +1737,7 @@ address FileMapInfo::decode_start_address(FileMapRegion* spc, bool with_current_ size_t offset = spc->mapping_offset(); narrowOop n = CompressedOops::narrow_oop_cast(offset); if (with_current_oop_encoding_mode) { - return cast_from_oop
    (CompressedOops::decode_not_null(n)); + return cast_from_oop
    (CompressedOops::decode_raw_not_null(n)); } else { return cast_from_oop
    (HeapShared::decode_from_archive(n)); } diff --git a/src/hotspot/share/memory/freeList.hpp b/src/hotspot/share/memory/freeList.hpp deleted file mode 100644 index a919422af9c..00000000000 --- a/src/hotspot/share/memory/freeList.hpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_MEMORY_FREELIST_HPP -#define SHARE_MEMORY_FREELIST_HPP - -// A class for maintaining a free list of Chunk's. The FreeList -// maintains a the structure of the list (head, tail, etc.) plus -// statistics for allocations from the list. The links between items -// are not part of FreeList. The statistics are -// used to make decisions about coalescing Chunk's when they -// are swept during collection. -// -// See the corresponding .cpp file for a description of the specifics -// for that implementation. - -class Mutex; - -template -class FreeList { - friend class VMStructs; - - private: - Chunk_t* _head; // Head of list of free chunks - Chunk_t* _tail; // Tail of list of free chunks - size_t _size; // Size in Heap words of each chunk - ssize_t _count; // Number of entries in list - - protected: - -#ifdef ASSERT - Mutex* _protecting_lock; - void assert_proper_lock_protection_work() const; -#endif - - // Asserts false if the protecting lock (if any) is not held. - void assert_proper_lock_protection() const { - DEBUG_ONLY(assert_proper_lock_protection_work()); - } - - void increment_count() { - _count++; - } - - void decrement_count() { - _count--; - assert(_count >= 0, "Count should not be negative"); - } - - public: - // Constructor - // Construct a list without any entries. - FreeList(); - - // Do initialization - void initialize(); - - // Reset the head, tail, and count of a free list. - void reset(); - - // Declare the current free list to be protected by the given lock. -#ifdef ASSERT - Mutex* protecting_lock() const { return _protecting_lock; } - void set_protecting_lock(Mutex* v) { - _protecting_lock = v; - } -#endif - - // Accessors. - Chunk_t* head() const { - assert_proper_lock_protection(); - return _head; - } - void set_head(Chunk_t* v) { - assert_proper_lock_protection(); - _head = v; - assert(!_head || _head->size() == _size, "bad chunk size"); - } - // Set the head of the list and set the prev field of non-null - // values to NULL. - void link_head(Chunk_t* v); - - Chunk_t* tail() const { - assert_proper_lock_protection(); - return _tail; - } - void set_tail(Chunk_t* v) { - assert_proper_lock_protection(); - _tail = v; - assert(!_tail || _tail->size() == _size, "bad chunk size"); - } - // Set the tail of the list and set the next field of non-null - // values to NULL. - void link_tail(Chunk_t* v) { - assert_proper_lock_protection(); - set_tail(v); - if (v != NULL) { - v->clear_next(); - } - } - - // No locking checks in read-accessors: lock-free reads (only) are benign. - // Readers are expected to have the lock if they are doing work that - // requires atomicity guarantees in sections of code. - size_t size() const { - return _size; - } - void set_size(size_t v) { - assert_proper_lock_protection(); - _size = v; - } - ssize_t count() const { return _count; } - void set_count(ssize_t v) { _count = v;} - - size_t get_better_size() { return size(); } - - size_t returned_bytes() const { ShouldNotReachHere(); return 0; } - void set_returned_bytes(size_t v) {} - void increment_returned_bytes_by(size_t v) {} - - // Unlink head of list and return it. Returns NULL if - // the list is empty. - Chunk_t* get_chunk_at_head(); - - // Remove the first "n" or "count", whichever is smaller, chunks from the - // list, setting "fl", which is required to be empty, to point to them. - void getFirstNChunksFromList(size_t n, FreeList* fl); - - // Unlink this chunk from it's free list - void remove_chunk(Chunk_t* fc); - - // Add this chunk to this free list. - void return_chunk_at_head(Chunk_t* fc); - void return_chunk_at_tail(Chunk_t* fc); - - // Similar to returnChunk* but also records some diagnostic - // information. - void return_chunk_at_head(Chunk_t* fc, bool record_return); - void return_chunk_at_tail(Chunk_t* fc, bool record_return); - - // Prepend "fl" (whose size is required to be the same as that of "this") - // to the front of "this" list. - void prepend(FreeList* fl); - - // Verify that the chunk is in the list. - // found. Return NULL if "fc" is not found. - bool verify_chunk_in_free_list(Chunk_t* fc) const; - - // Printing support - static void print_labels_on(outputStream* st, const char* c); - void print_on(outputStream* st, const char* c = NULL) const; -}; - -#endif // SHARE_MEMORY_FREELIST_HPP diff --git a/src/hotspot/share/memory/freeList.inline.hpp b/src/hotspot/share/memory/freeList.inline.hpp deleted file mode 100644 index d73b9acfeeb..00000000000 --- a/src/hotspot/share/memory/freeList.inline.hpp +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_MEMORY_FREELIST_INLINE_HPP -#define SHARE_MEMORY_FREELIST_INLINE_HPP - -#include "gc/shared/collectedHeap.hpp" -#include "memory/freeList.hpp" -#include "runtime/globals.hpp" -#include "runtime/mutex.hpp" -#include "runtime/vmThread.hpp" -#include "utilities/macros.hpp" - -// Free list. A FreeList is used to access a linked list of chunks -// of space in the heap. The head and tail are maintained so that -// items can be (as in the current implementation) added at the -// at the tail of the list and removed from the head of the list to -// maintain a FIFO queue. - -template -FreeList::FreeList() : - _head(NULL), _tail(NULL) -#ifdef ASSERT - , _protecting_lock(NULL) -#endif -{ - _size = 0; - _count = 0; -} - -template -void FreeList::link_head(Chunk* v) { - assert_proper_lock_protection(); - set_head(v); - // If this method is not used (just set the head instead), - // this check can be avoided. - if (v != NULL) { - v->link_prev(NULL); - } -} - - - -template -void FreeList::reset() { - // Don't set the _size to 0 because this method is - // used with a existing list that has a size but which has - // been emptied. - // Don't clear the _protecting_lock of an existing list. - set_count(0); - set_head(NULL); - set_tail(NULL); -} - -template -void FreeList::initialize() { -#ifdef ASSERT - // Needed early because it might be checked in other initializing code. - set_protecting_lock(NULL); -#endif - reset(); - set_size(0); -} - -template -Chunk_t* FreeList::get_chunk_at_head() { - assert_proper_lock_protection(); - assert(head() == NULL || head()->prev() == NULL, "list invariant"); - assert(tail() == NULL || tail()->next() == NULL, "list invariant"); - Chunk_t* fc = head(); - if (fc != NULL) { - Chunk_t* nextFC = fc->next(); - if (nextFC != NULL) { - // The chunk fc being removed has a "next". Set the "next" to the - // "prev" of fc. - nextFC->link_prev(NULL); - } else { // removed tail of list - link_tail(NULL); - } - link_head(nextFC); - decrement_count(); - } - assert(head() == NULL || head()->prev() == NULL, "list invariant"); - assert(tail() == NULL || tail()->next() == NULL, "list invariant"); - return fc; -} - - -template -void FreeList::getFirstNChunksFromList(size_t n, FreeList* fl) { - assert_proper_lock_protection(); - assert(fl->count() == 0, "Precondition"); - if (count() > 0) { - int k = 1; - fl->set_head(head()); n--; - Chunk* tl = head(); - while (tl->next() != NULL && n > 0) { - tl = tl->next(); n--; k++; - } - assert(tl != NULL, "Loop Inv."); - - // First, fix up the list we took from. - Chunk* new_head = tl->next(); - set_head(new_head); - set_count(count() - k); - if (new_head == NULL) { - set_tail(NULL); - } else { - new_head->link_prev(NULL); - } - // Now we can fix up the tail. - tl->link_next(NULL); - // And return the result. - fl->set_tail(tl); - fl->set_count(k); - } -} - -// Remove this chunk from the list -template -void FreeList::remove_chunk(Chunk*fc) { - assert_proper_lock_protection(); - assert(head() != NULL, "Remove from empty list"); - assert(fc != NULL, "Remove a NULL chunk"); - assert(size() == fc->size(), "Wrong list"); - assert(head() == NULL || head()->prev() == NULL, "list invariant"); - assert(tail() == NULL || tail()->next() == NULL, "list invariant"); - - Chunk* prevFC = fc->prev(); - Chunk* nextFC = fc->next(); - if (nextFC != NULL) { - // The chunk fc being removed has a "next". Set the "next" to the - // "prev" of fc. - nextFC->link_prev(prevFC); - } else { // removed tail of list - link_tail(prevFC); - } - if (prevFC == NULL) { // removed head of list - link_head(nextFC); - assert(nextFC == NULL || nextFC->prev() == NULL, - "Prev of head should be NULL"); - } else { - prevFC->link_next(nextFC); - assert(tail() != prevFC || prevFC->next() == NULL, - "Next of tail should be NULL"); - } - decrement_count(); - assert(((head() == NULL) + (tail() == NULL) + (count() == 0)) % 3 == 0, - "H/T/C Inconsistency"); - // clear next and prev fields of fc, debug only - NOT_PRODUCT( - fc->link_prev(NULL); - fc->link_next(NULL); - ) - assert(fc->is_free(), "Should still be a free chunk"); - assert(head() == NULL || head()->prev() == NULL, "list invariant"); - assert(tail() == NULL || tail()->next() == NULL, "list invariant"); - assert(head() == NULL || head()->size() == size(), "wrong item on list"); - assert(tail() == NULL || tail()->size() == size(), "wrong item on list"); -} - -// Add this chunk at the head of the list. -template -void FreeList::return_chunk_at_head(Chunk* chunk, bool record_return) { - assert_proper_lock_protection(); - assert(chunk != NULL, "insert a NULL chunk"); - assert(size() == chunk->size(), "Wrong size"); - assert(head() == NULL || head()->prev() == NULL, "list invariant"); - assert(tail() == NULL || tail()->next() == NULL, "list invariant"); - - Chunk* oldHead = head(); - assert(chunk != oldHead, "double insertion"); - chunk->link_after(oldHead); - link_head(chunk); - if (oldHead == NULL) { // only chunk in list - assert(tail() == NULL, "inconsistent FreeList"); - link_tail(chunk); - } - increment_count(); // of # of chunks in list - assert(head() == NULL || head()->prev() == NULL, "list invariant"); - assert(tail() == NULL || tail()->next() == NULL, "list invariant"); - assert(head() == NULL || head()->size() == size(), "wrong item on list"); - assert(tail() == NULL || tail()->size() == size(), "wrong item on list"); -} - -template -void FreeList::return_chunk_at_head(Chunk* chunk) { - assert_proper_lock_protection(); - return_chunk_at_head(chunk, true); -} - -// Add this chunk at the tail of the list. -template -void FreeList::return_chunk_at_tail(Chunk* chunk, bool record_return) { - assert_proper_lock_protection(); - assert(head() == NULL || head()->prev() == NULL, "list invariant"); - assert(tail() == NULL || tail()->next() == NULL, "list invariant"); - assert(chunk != NULL, "insert a NULL chunk"); - assert(size() == chunk->size(), "wrong size"); - - Chunk* oldTail = tail(); - assert(chunk != oldTail, "double insertion"); - if (oldTail != NULL) { - oldTail->link_after(chunk); - } else { // only chunk in list - assert(head() == NULL, "inconsistent FreeList"); - link_head(chunk); - } - link_tail(chunk); - increment_count(); // of # of chunks in list - assert(head() == NULL || head()->prev() == NULL, "list invariant"); - assert(tail() == NULL || tail()->next() == NULL, "list invariant"); - assert(head() == NULL || head()->size() == size(), "wrong item on list"); - assert(tail() == NULL || tail()->size() == size(), "wrong item on list"); -} - -template -void FreeList::return_chunk_at_tail(Chunk* chunk) { - return_chunk_at_tail(chunk, true); -} - -template -void FreeList::prepend(FreeList* fl) { - assert_proper_lock_protection(); - if (fl->count() > 0) { - if (count() == 0) { - set_head(fl->head()); - set_tail(fl->tail()); - set_count(fl->count()); - } else { - // Both are non-empty. - Chunk* fl_tail = fl->tail(); - Chunk* this_head = head(); - assert(fl_tail->next() == NULL, "Well-formedness of fl"); - fl_tail->link_next(this_head); - this_head->link_prev(fl_tail); - set_head(fl->head()); - set_count(count() + fl->count()); - } - fl->set_head(NULL); - fl->set_tail(NULL); - fl->set_count(0); - } -} - -// verify_chunk_in_free_lists() is used to verify that an item is in this free list. -// It is used as a debugging aid. -template -bool FreeList::verify_chunk_in_free_list(Chunk* fc) const { - // This is an internal consistency check, not part of the check that the - // chunk is in the free lists. - guarantee(fc->size() == size(), "Wrong list is being searched"); - Chunk* curFC = head(); - while (curFC) { - // This is an internal consistency check. - guarantee(size() == curFC->size(), "Chunk is in wrong list."); - if (fc == curFC) { - return true; - } - curFC = curFC->next(); - } - return false; -} - -#ifdef ASSERT -template -void FreeList::assert_proper_lock_protection_work() const { - // Nothing to do if the list has no assigned protecting lock - if (protecting_lock() == NULL) { - return; - } - - Thread* thr = Thread::current(); - if (thr->is_VM_thread() || thr->is_ConcurrentGC_thread()) { - // assert that we are holding the freelist lock - } else if (thr->is_GC_task_thread()) { - assert(protecting_lock()->owned_by_self(), "FreeList RACE DETECTED"); - } else if (thr->is_Java_thread()) { - assert(!SafepointSynchronize::is_at_safepoint(), "Should not be executing"); - } else { - ShouldNotReachHere(); // unaccounted thread type? - } -} -#endif - -// Print the "label line" for free list stats. -template -void FreeList::print_labels_on(outputStream* st, const char* c) { - st->print("%16s\t", c); - st->print("%14s\t" "%14s\t" "%14s\t" "%14s\t" "%14s\t" - "%14s\t" "%14s\t" "%14s\t" "%14s\t" "%14s\t" "\n", - "bfrsurp", "surplus", "desired", "prvSwep", "bfrSwep", - "count", "cBirths", "cDeaths", "sBirths", "sDeaths"); -} - -// Print the AllocationStats for the given free list. If the second argument -// to the call is a non-null string, it is printed in the first column; -// otherwise, if the argument is null (the default), then the size of the -// (free list) block is printed in the first column. -template -void FreeList::print_on(outputStream* st, const char* c) const { - if (c != NULL) { - st->print("%16s", c); - } else { - st->print(SIZE_FORMAT_W(16), size()); - } -} - -#endif // SHARE_MEMORY_FREELIST_INLINE_HPP diff --git a/src/hotspot/share/memory/heap.cpp b/src/hotspot/share/memory/heap.cpp index 2db317b5a87..d55bc07e2c0 100644 --- a/src/hotspot/share/memory/heap.cpp +++ b/src/hotspot/share/memory/heap.cpp @@ -30,11 +30,6 @@ #include "utilities/align.hpp" #include "utilities/powerOfTwo.hpp" -size_t CodeHeap::header_size() { - return sizeof(HeapBlock); -} - - // Implementation of Heap CodeHeap::CodeHeap(const char* name, const int code_blob_type) @@ -775,7 +770,8 @@ int CodeHeap::segmap_hops(size_t beg, size_t end) { if (beg < end) { // setup _segmap pointers for faster indexing address p = (address)_segmap.low() + beg; - int hops_expected = (int)(((end-beg-1)+(free_sentinel-2))/(free_sentinel-1)); + int hops_expected + = checked_cast(((end-beg-1)+(free_sentinel-2))/(free_sentinel-1)); int nhops = 0; size_t ix = end-beg-1; while (p[ix] > 0) { diff --git a/src/hotspot/share/memory/heap.hpp b/src/hotspot/share/memory/heap.hpp index cd4f1572e62..a3746b03909 100644 --- a/src/hotspot/share/memory/heap.hpp +++ b/src/hotspot/share/memory/heap.hpp @@ -187,7 +187,7 @@ class CodeHeap : public CHeapObj { virtual CodeBlob* find_blob_unsafe(void* start) const; size_t alignment_unit() const; // alignment of any block size_t alignment_offset() const; // offset of first byte of any block, within the enclosing alignment unit - static size_t header_size(); // returns the header size for each heap block + static size_t header_size() { return sizeof(HeapBlock); } // returns the header size for each heap block size_t segment_size() const { return _segment_size; } // for CodeHeapState HeapBlock* first_block() const; // for CodeHeapState diff --git a/src/hotspot/share/memory/heapShared.cpp b/src/hotspot/share/memory/heapShared.cpp index 1d9821300f4..c575541c2d5 100644 --- a/src/hotspot/share/memory/heapShared.cpp +++ b/src/hotspot/share/memory/heapShared.cpp @@ -53,6 +53,7 @@ #include "runtime/javaCalls.hpp" #include "runtime/safepointVerifiers.hpp" #include "utilities/bitMap.inline.hpp" +#include "utilities/copy.hpp" #if INCLUDE_G1GC #include "gc/g1/g1CollectedHeap.hpp" #endif @@ -206,7 +207,9 @@ oop HeapShared::archive_heap_object(oop obj, Thread* THREAD) { log_error(cds, heap)( "Cannot allocate space for object " PTR_FORMAT " in archived heap region", p2i(obj)); - vm_exit(1); + vm_direct_exit(-1, + err_msg("Out of memory. Please run with a larger Java heap, current MaxHeapSize = " + SIZE_FORMAT "M", MaxHeapSize/M)); } return archived_oop; } @@ -259,15 +262,6 @@ void HeapShared::run_full_gc_in_vm_thread() { void HeapShared::archive_java_heap_objects(GrowableArray *closed, GrowableArray *open) { - if (!is_heap_object_archiving_allowed()) { - log_info(cds)( - "Archived java heap is not supported as UseG1GC, " - "UseCompressedOops and UseCompressedClassPointers are required." - "Current settings: UseG1GC=%s, UseCompressedOops=%s, UseCompressedClassPointers=%s.", - BOOL_TO_STR(UseG1GC), BOOL_TO_STR(UseCompressedOops), - BOOL_TO_STR(UseCompressedClassPointers)); - return; - } G1HeapVerifier::verify_ready_for_archiving(); @@ -734,7 +728,7 @@ oop HeapShared::archive_reachable_objects_from(int level, // these objects that are referenced (directly or indirectly) by static fields. ResourceMark rm; log_error(cds, heap)("Cannot archive object of class %s", orig_obj->klass()->external_name()); - vm_exit(1); + vm_direct_exit(1); } // java.lang.Class instances cannot be included in an archived object sub-graph. We only support @@ -744,7 +738,7 @@ oop HeapShared::archive_reachable_objects_from(int level, // object that is referenced (directly or indirectly) by static fields. if (java_lang_Class::is_instance(orig_obj)) { log_error(cds, heap)("(%d) Unknown java.lang.Class object is in the archived sub-graph", level); - vm_exit(1); + vm_direct_exit(1); } oop archived_obj = find_archived_heap_object(orig_obj); @@ -780,7 +774,7 @@ oop HeapShared::archive_reachable_objects_from(int level, // We don't know how to handle an object that has been archived, but some of its reachable // objects cannot be archived. Bail out for now. We might need to fix this in the future if // we have a real use case. - vm_exit(1); + vm_direct_exit(1); } } @@ -1035,7 +1029,13 @@ void HeapShared::init_subgraph_entry_fields(ArchivableStaticFieldInfo fields[], TempNewSymbol field_name = SymbolTable::new_symbol(info->field_name); Klass* k = SystemDictionary::resolve_or_null(klass_name, THREAD); - assert(k != NULL && !HAS_PENDING_EXCEPTION, "class must exist"); + if (HAS_PENDING_EXCEPTION) { + ResourceMark rm(THREAD); + ArchiveUtils::check_for_oom(PENDING_EXCEPTION); // exit on OOM + log_info(cds)("%s: %s", PENDING_EXCEPTION->klass()->external_name(), + java_lang_String::as_utf8_string(java_lang_Throwable::message(PENDING_EXCEPTION))); + vm_direct_exit(-1, "VM exits due to exception, use -Xlog:cds,exceptions=trace for detail"); + } InstanceKlass* ik = InstanceKlass::cast(k); assert(InstanceKlass::cast(ik)->is_shared_boot_class(), "Only support boot classes"); @@ -1052,8 +1052,8 @@ void HeapShared::init_subgraph_entry_fields(ArchivableStaticFieldInfo fields[], } void HeapShared::init_subgraph_entry_fields(Thread* THREAD) { + assert(is_heap_object_archiving_allowed(), "Sanity check"); _dump_time_subgraph_info_table = new (ResourceObj::C_HEAP, mtClass)DumpTimeKlassSubGraphInfoTable(); - init_subgraph_entry_fields(closed_archive_subgraph_entry_fields, num_closed_archive_subgraph_entry_fields, THREAD); @@ -1068,8 +1068,10 @@ void HeapShared::init_subgraph_entry_fields(Thread* THREAD) { } void HeapShared::init_for_dumping(Thread* THREAD) { - _dumped_interned_strings = new (ResourceObj::C_HEAP, mtClass)DumpedInternedStrings(); - init_subgraph_entry_fields(THREAD); + if (is_heap_object_archiving_allowed()) { + _dumped_interned_strings = new (ResourceObj::C_HEAP, mtClass)DumpedInternedStrings(); + init_subgraph_entry_fields(THREAD); + } } void HeapShared::archive_object_subgraphs(ArchivableStaticFieldInfo fields[], @@ -1150,9 +1152,6 @@ class FindEmbeddedNonNullPointers: public BasicOopIterateClosure { FindEmbeddedNonNullPointers(narrowOop* start, BitMap* oopmap) : _start(start), _oopmap(oopmap), _num_total_oops(0), _num_null_oops(0) {} - virtual bool should_verify_oops(void) { - return false; - } virtual void do_oop(narrowOop* p) { _num_total_oops ++; narrowOop v = *p; diff --git a/src/hotspot/share/memory/heapShared.inline.hpp b/src/hotspot/share/memory/heapShared.inline.hpp index b27996251c1..a022fcfb4bc 100644 --- a/src/hotspot/share/memory/heapShared.inline.hpp +++ b/src/hotspot/share/memory/heapShared.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,17 +25,15 @@ #ifndef SHARE_MEMORY_HEAPSHARED_INLINE_HPP #define SHARE_MEMORY_HEAPSHARED_INLINE_HPP +#include "gc/shared/collectedHeap.inline.hpp" #include "oops/compressedOops.inline.hpp" #include "memory/heapShared.hpp" #include "utilities/align.hpp" -#if INCLUDE_G1GC -#include "gc/g1/g1Allocator.inline.hpp" -#endif #if INCLUDE_CDS_JAVA_HEAP bool HeapShared::is_archived_object(oop p) { - return (p == NULL) ? false : G1ArchiveAllocator::is_archived_object(p); + return Universe::heap()->is_archived_object(p); } inline oop HeapShared::decode_from_archive(narrowOop v) { diff --git a/src/hotspot/share/memory/iterator.cpp b/src/hotspot/share/memory/iterator.cpp index b0692d5217a..f9fbaf51b90 100644 --- a/src/hotspot/share/memory/iterator.cpp +++ b/src/hotspot/share/memory/iterator.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "classfile/classLoaderDataGraph.hpp" #include "code/nmethod.hpp" #include "memory/iterator.inline.hpp" #include "oops/oop.inline.hpp" @@ -63,3 +64,10 @@ void MarkingCodeBlobClosure::do_code_blob(CodeBlob* cb) { do_nmethod(nm); } } + +void CodeBlobToNMethodClosure::do_code_blob(CodeBlob* cb) { + nmethod* nm = cb->as_nmethod_or_null(); + if (nm != NULL) { + _nm_cl->do_nmethod(nm); + } +} diff --git a/src/hotspot/share/memory/iterator.hpp b/src/hotspot/share/memory/iterator.hpp index fb3a4c2ca34..9c2611a24c2 100644 --- a/src/hotspot/share/memory/iterator.hpp +++ b/src/hotspot/share/memory/iterator.hpp @@ -102,14 +102,6 @@ class OopIterateClosure : public OopClosure { virtual bool do_metadata() = 0; virtual void do_klass(Klass* k) = 0; virtual void do_cld(ClassLoaderData* cld) = 0; - -#ifdef ASSERT - // Default verification of each visited oop field. - template void verify(T* p); - - // Can be used by subclasses to turn off the default verification of oop fields. - virtual bool should_verify_oops() { return true; } -#endif }; // An OopIterateClosure that can be used when there's no need to visit the Metadata. @@ -151,6 +143,12 @@ class CLDToOopClosure : public CLDClosure { void do_cld(ClassLoaderData* cld); }; +template +class ClaimingCLDToOopClosure : public CLDToOopClosure { +public: + ClaimingCLDToOopClosure(OopClosure* cl) : CLDToOopClosure(cl, claim) {} +}; + class ClaimMetadataVisitingOopIterateClosure : public OopIterateClosure { protected: const int _claim; @@ -263,6 +261,15 @@ class NMethodClosure : public Closure { virtual void do_nmethod(nmethod* n) = 0; }; +class CodeBlobToNMethodClosure : public CodeBlobClosure { + NMethodClosure* const _nm_cl; + + public: + CodeBlobToNMethodClosure(NMethodClosure* nm_cl) : _nm_cl(nm_cl) {} + + virtual void do_code_blob(CodeBlob* cb); +}; + // MonitorClosure is used for iterating over monitors in the monitors cache class ObjectMonitor; @@ -357,7 +364,6 @@ class CompareClosure : public Closure { // a concrete implementation, otherwise a virtual call is taken. class Devirtualizer { public: - template static void do_oop_no_verify(OopClosureType* closure, T* p); template static void do_oop(OopClosureType* closure, T* p); template static void do_klass(OopClosureType* closure, Klass* k); template static void do_cld(OopClosureType* closure, ClassLoaderData* cld); diff --git a/src/hotspot/share/memory/iterator.inline.hpp b/src/hotspot/share/memory/iterator.inline.hpp index 5d0300b6d3f..36ee100397d 100644 --- a/src/hotspot/share/memory/iterator.inline.hpp +++ b/src/hotspot/share/memory/iterator.inline.hpp @@ -27,7 +27,6 @@ #include "classfile/classLoaderData.hpp" #include "memory/iterator.hpp" -#include "memory/universe.hpp" #include "oops/access.inline.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/klass.hpp" @@ -52,22 +51,6 @@ inline void ClaimMetadataVisitingOopIterateClosure::do_klass(Klass* k) { ClaimMetadataVisitingOopIterateClosure::do_cld(cld); } -#ifdef ASSERT -// This verification is applied to all visited oops. -// The closures can turn is off by overriding should_verify_oops(). -template -void OopIterateClosure::verify(T* p) { - if (should_verify_oops()) { - T heap_oop = RawAccess<>::oop_load(p); - if (!CompressedOops::is_null(heap_oop)) { - oop o = CompressedOops::decode_not_null(heap_oop); - assert(Universe::heap()->is_in(o), - "should be in closed *p " PTR_FORMAT " " PTR_FORMAT, p2i(p), p2i(o)); - } - } -} -#endif - // Implementation of the non-virtual do_oop dispatch. // // The same implementation is used for do_metadata, do_klass, and do_cld. @@ -123,16 +106,9 @@ call_do_oop(void (Receiver::*)(T*), void (Base::*)(T*), OopClosureType* closure, closure->OopClosureType::do_oop(p); } -template -inline void Devirtualizer::do_oop_no_verify(OopClosureType* closure, T* p) { - call_do_oop(&OopClosureType::do_oop, &OopClosure::do_oop, closure, p); -} - template inline void Devirtualizer::do_oop(OopClosureType* closure, T* p) { - debug_only(closure->verify(p)); - - do_oop_no_verify(closure, p); + call_do_oop(&OopClosureType::do_oop, &OopClosure::do_oop, closure, p); } // Implementation of the non-virtual do_metadata dispatch. diff --git a/src/hotspot/share/memory/metadataFactory.hpp b/src/hotspot/share/memory/metadataFactory.hpp index 36d3ab1dff7..d18f1301120 100644 --- a/src/hotspot/share/memory/metadataFactory.hpp +++ b/src/hotspot/share/memory/metadataFactory.hpp @@ -26,6 +26,7 @@ #define SHARE_MEMORY_METADATAFACTORY_HPP #include "classfile/classLoaderData.hpp" +#include "memory/classLoaderMetaspace.hpp" #include "oops/array.hpp" #include "utilities/exceptions.hpp" #include "utilities/globalDefinitions.hpp" diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index bf8a21f40ca..81881c76ef3 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -24,64 +24,185 @@ #include "precompiled.hpp" #include "aot/aotLoader.hpp" -#include "classfile/classLoaderDataGraph.hpp" #include "gc/shared/collectedHeap.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" #include "memory/filemap.hpp" +#include "memory/classLoaderMetaspace.hpp" #include "memory/metaspace.hpp" +#include "memory/metaspace/chunkHeaderPool.hpp" #include "memory/metaspace/chunkManager.hpp" -#include "memory/metaspace/metachunk.hpp" +#include "memory/metaspace/commitLimiter.hpp" #include "memory/metaspace/metaspaceCommon.hpp" -#include "memory/metaspace/printCLDMetaspaceInfoClosure.hpp" -#include "memory/metaspace/spaceManager.hpp" +#include "memory/metaspace/metaspaceContext.hpp" +#include "memory/metaspace/metaspaceReporter.hpp" +#include "memory/metaspace/metaspaceSettings.hpp" +#include "memory/metaspace/metaspaceSizesSnapshot.hpp" +#include "memory/metaspace/runningCounters.hpp" #include "memory/metaspace/virtualSpaceList.hpp" #include "memory/metaspaceShared.hpp" #include "memory/metaspaceTracer.hpp" +#include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/compressedOops.hpp" #include "runtime/arguments.hpp" #include "runtime/atomic.hpp" #include "runtime/globals_extension.hpp" #include "runtime/init.hpp" +#include "runtime/java.hpp" #include "services/memTracker.hpp" #include "utilities/copy.hpp" #include "utilities/debug.hpp" #include "utilities/formatBuffer.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/vmError.hpp" +using metaspace::ChunkManager; +using metaspace::CommitLimiter; +using metaspace::MetaspaceContext; +using metaspace::MetaspaceReporter; +using metaspace::RunningCounters; +using metaspace::VirtualSpaceList; -using namespace metaspace; +size_t MetaspaceUtils::used_words() { + return RunningCounters::used_words(); +} -MetaWord* last_allocated = 0; +size_t MetaspaceUtils::used_words(Metaspace::MetadataType mdtype) { + return Metaspace::is_class_space_allocation(mdtype) ? RunningCounters::used_words_class() : RunningCounters::used_words_nonclass(); +} -size_t Metaspace::_compressed_class_space_size; -const MetaspaceTracer* Metaspace::_tracer = NULL; +size_t MetaspaceUtils::reserved_words() { + return RunningCounters::reserved_words(); +} -DEBUG_ONLY(bool Metaspace::_frozen = false;) +size_t MetaspaceUtils::reserved_words(Metaspace::MetadataType mdtype) { + return Metaspace::is_class_space_allocation(mdtype) ? RunningCounters::reserved_words_class() : RunningCounters::reserved_words_nonclass(); +} + +size_t MetaspaceUtils::committed_words() { + return RunningCounters::committed_words(); +} + +size_t MetaspaceUtils::committed_words(Metaspace::MetadataType mdtype) { + return Metaspace::is_class_space_allocation(mdtype) ? RunningCounters::committed_words_class() : RunningCounters::committed_words_nonclass(); +} + +void MetaspaceUtils::print_metaspace_change(const metaspace::MetaspaceSizesSnapshot& pre_meta_values) { + const metaspace::MetaspaceSizesSnapshot meta_values; -static const char* space_type_name(Metaspace::MetaspaceType t) { - const char* s = NULL; - switch (t) { - case Metaspace::StandardMetaspaceType: s = "Standard"; break; - case Metaspace::BootMetaspaceType: s = "Boot"; break; - case Metaspace::ClassMirrorHolderMetaspaceType: s = "ClassMirrorHolder"; break; - case Metaspace::ReflectionMetaspaceType: s = "Reflection"; break; - default: ShouldNotReachHere(); + // We print used and committed since these are the most useful at-a-glance vitals for Metaspace: + // - used tells you how much memory is actually used for metadata + // - committed tells you how much memory is committed for the purpose of metadata + // The difference between those two would be waste, which can have various forms (freelists, + // unused parts of committed chunks etc) + // + // Left out is reserved, since this is not as exciting as the first two values: for class space, + // it is a constant (to uninformed users, often confusingly large). For non-class space, it would + // be interesting since free chunks can be uncommitted, but for now it is left out. + + if (Metaspace::using_class_space()) { + log_info(gc, metaspace)(HEAP_CHANGE_FORMAT" " + HEAP_CHANGE_FORMAT" " + HEAP_CHANGE_FORMAT, + HEAP_CHANGE_FORMAT_ARGS("Metaspace", + pre_meta_values.used(), + pre_meta_values.committed(), + meta_values.used(), + meta_values.committed()), + HEAP_CHANGE_FORMAT_ARGS("NonClass", + pre_meta_values.non_class_used(), + pre_meta_values.non_class_committed(), + meta_values.non_class_used(), + meta_values.non_class_committed()), + HEAP_CHANGE_FORMAT_ARGS("Class", + pre_meta_values.class_used(), + pre_meta_values.class_committed(), + meta_values.class_used(), + meta_values.class_committed())); + } else { + log_info(gc, metaspace)(HEAP_CHANGE_FORMAT, + HEAP_CHANGE_FORMAT_ARGS("Metaspace", + pre_meta_values.used(), + pre_meta_values.committed(), + meta_values.used(), + meta_values.committed())); } - return s; } -volatile size_t MetaspaceGC::_capacity_until_GC = 0; -uint MetaspaceGC::_shrink_factor = 0; +// This will print out a basic metaspace usage report but +// unlike print_report() is guaranteed not to lock or to walk the CLDG. +void MetaspaceUtils::print_basic_report(outputStream* out, size_t scale) { + MetaspaceReporter::print_basic_report(out, scale); +} -// BlockFreelist methods +// Prints a report about the current metaspace state. +// Optional parts can be enabled via flags. +// Function will walk the CLDG and will lock the expand lock; if that is not +// convenient, use print_basic_report() instead. +void MetaspaceUtils::print_report(outputStream* out, size_t scale) { + const int flags = + (int)MetaspaceReporter::Option::ShowLoaders | + (int)MetaspaceReporter::Option::BreakDownByChunkType | + (int)MetaspaceReporter::Option::ShowClasses; + MetaspaceReporter::print_report(out, scale, flags); +} + +void MetaspaceUtils::print_on(outputStream* out) { -// VirtualSpaceNode methods + // Used from all GCs. It first prints out totals, then, separately, the class space portion. + out->print_cr(" Metaspace " + "used " SIZE_FORMAT "K, " + "committed " SIZE_FORMAT "K, " + "reserved " SIZE_FORMAT "K", + used_bytes()/K, + committed_bytes()/K, + reserved_bytes()/K); + + if (Metaspace::using_class_space()) { + const Metaspace::MetadataType ct = Metaspace::ClassType; + out->print_cr(" class space " + "used " SIZE_FORMAT "K, " + "committed " SIZE_FORMAT "K, " + "reserved " SIZE_FORMAT "K", + used_bytes(ct)/K, + committed_bytes(ct)/K, + reserved_bytes(ct)/K); + } +} + +#ifdef ASSERT +void MetaspaceUtils::verify() { + if (Metaspace::initialized()) { + + // Verify non-class chunkmanager... + ChunkManager* cm = ChunkManager::chunkmanager_nonclass(); + cm->verify(); + + // ... and space list. + VirtualSpaceList* vsl = VirtualSpaceList::vslist_nonclass(); + vsl->verify(); + + if (Metaspace::using_class_space()) { + // If we use compressed class pointers, verify class chunkmanager... + cm = ChunkManager::chunkmanager_class(); + cm->verify(); + + // ... and class spacelist. + vsl = VirtualSpaceList::vslist_class(); + vsl->verify(); + } + + } +} +#endif + +////////////////////////////////7 // MetaspaceGC methods +volatile size_t MetaspaceGC::_capacity_until_GC = 0; +uint MetaspaceGC::_shrink_factor = 0; + // VM_CollectForMetadataAllocation is the vm operation used to GC. // Within the VM operation after the GC the attempt to allocate the metadata // should succeed. If the GC did not free enough space for the metaspace @@ -151,7 +272,7 @@ bool MetaspaceGC::inc_capacity_until_GC(size_t v, size_t* new_cap_until_GC, size if (new_value < old_capacity_until_GC) { // The addition wrapped around, set new_value to aligned max value. - new_value = align_down(max_uintx, Metaspace::commit_alignment()); + new_value = align_down(max_uintx, Metaspace::reserve_alignment()); } if (new_value > MaxMetaspaceSize) { @@ -267,7 +388,6 @@ void MetaspaceGC::compute_new_size() { minimum_free_percentage, maximum_used_percentage); log_trace(gc, metaspace)(" used_after_gc : %6.1fKB", used_after_gc / (double) K); - size_t shrink_bytes = 0; if (capacity_until_GC < minimum_desired_capacity) { // If we have less capacity below the metaspace HWM, then @@ -353,643 +473,35 @@ void MetaspaceGC::compute_new_size() { } } -// MetaspaceUtils -size_t MetaspaceUtils::_capacity_words [Metaspace:: MetadataTypeCount] = {0, 0}; -size_t MetaspaceUtils::_overhead_words [Metaspace:: MetadataTypeCount] = {0, 0}; -volatile size_t MetaspaceUtils::_used_words [Metaspace:: MetadataTypeCount] = {0, 0}; - -// Collect used metaspace statistics. This involves walking the CLDG. The resulting -// output will be the accumulated values for all live metaspaces. -// Note: method does not do any locking. -void MetaspaceUtils::collect_statistics(ClassLoaderMetaspaceStatistics* out) { - out->reset(); - ClassLoaderDataGraphMetaspaceIterator iter; - while (iter.repeat()) { - ClassLoaderMetaspace* msp = iter.get_next(); - if (msp != NULL) { - msp->add_to_statistics(out); - } - } -} - -size_t MetaspaceUtils::free_in_vs_bytes(Metaspace::MetadataType mdtype) { - VirtualSpaceList* list = Metaspace::get_space_list(mdtype); - return list == NULL ? 0 : list->free_bytes(); -} - -size_t MetaspaceUtils::free_in_vs_bytes() { - return free_in_vs_bytes(Metaspace::ClassType) + free_in_vs_bytes(Metaspace::NonClassType); -} - -static void inc_stat_nonatomically(size_t* pstat, size_t words) { - assert_lock_strong(MetaspaceExpand_lock); - (*pstat) += words; -} - -static void dec_stat_nonatomically(size_t* pstat, size_t words) { - assert_lock_strong(MetaspaceExpand_lock); - const size_t size_now = *pstat; - assert(size_now >= words, "About to decrement counter below zero " - "(current value: " SIZE_FORMAT ", decrement value: " SIZE_FORMAT ".", - size_now, words); - *pstat = size_now - words; -} - -static void inc_stat_atomically(volatile size_t* pstat, size_t words) { - Atomic::add(pstat, words); -} - -static void dec_stat_atomically(volatile size_t* pstat, size_t words) { - const size_t size_now = *pstat; - assert(size_now >= words, "About to decrement counter below zero " - "(current value: " SIZE_FORMAT ", decrement value: " SIZE_FORMAT ".", - size_now, words); - Atomic::sub(pstat, words); -} - -void MetaspaceUtils::dec_capacity(Metaspace::MetadataType mdtype, size_t words) { - dec_stat_nonatomically(&_capacity_words[mdtype], words); -} -void MetaspaceUtils::inc_capacity(Metaspace::MetadataType mdtype, size_t words) { - inc_stat_nonatomically(&_capacity_words[mdtype], words); -} -void MetaspaceUtils::dec_used(Metaspace::MetadataType mdtype, size_t words) { - dec_stat_atomically(&_used_words[mdtype], words); -} -void MetaspaceUtils::inc_used(Metaspace::MetadataType mdtype, size_t words) { - inc_stat_atomically(&_used_words[mdtype], words); -} -void MetaspaceUtils::dec_overhead(Metaspace::MetadataType mdtype, size_t words) { - dec_stat_nonatomically(&_overhead_words[mdtype], words); -} -void MetaspaceUtils::inc_overhead(Metaspace::MetadataType mdtype, size_t words) { - inc_stat_nonatomically(&_overhead_words[mdtype], words); -} - -size_t MetaspaceUtils::reserved_bytes(Metaspace::MetadataType mdtype) { - VirtualSpaceList* list = Metaspace::get_space_list(mdtype); - return list == NULL ? 0 : list->reserved_bytes(); -} - -size_t MetaspaceUtils::committed_bytes(Metaspace::MetadataType mdtype) { - VirtualSpaceList* list = Metaspace::get_space_list(mdtype); - return list == NULL ? 0 : list->committed_bytes(); -} - -size_t MetaspaceUtils::min_chunk_size_words() { return Metaspace::first_chunk_word_size(); } - -size_t MetaspaceUtils::free_chunks_total_words(Metaspace::MetadataType mdtype) { - ChunkManager* chunk_manager = Metaspace::get_chunk_manager(mdtype); - if (chunk_manager == NULL) { - return 0; - } - return chunk_manager->free_chunks_total_words(); -} - -size_t MetaspaceUtils::free_chunks_total_bytes(Metaspace::MetadataType mdtype) { - return free_chunks_total_words(mdtype) * BytesPerWord; -} - -size_t MetaspaceUtils::free_chunks_total_words() { - return free_chunks_total_words(Metaspace::ClassType) + - free_chunks_total_words(Metaspace::NonClassType); -} - -size_t MetaspaceUtils::free_chunks_total_bytes() { - return free_chunks_total_words() * BytesPerWord; -} - -bool MetaspaceUtils::has_chunk_free_list(Metaspace::MetadataType mdtype) { - return Metaspace::get_chunk_manager(mdtype) != NULL; -} - -MetaspaceChunkFreeListSummary MetaspaceUtils::chunk_free_list_summary(Metaspace::MetadataType mdtype) { - if (!has_chunk_free_list(mdtype)) { - return MetaspaceChunkFreeListSummary(); - } - - const ChunkManager* cm = Metaspace::get_chunk_manager(mdtype); - return cm->chunk_free_list_summary(); -} - -void MetaspaceUtils::print_metaspace_change(const metaspace::MetaspaceSizesSnapshot& pre_meta_values) { - const metaspace::MetaspaceSizesSnapshot meta_values; - - if (Metaspace::using_class_space()) { - log_info(gc, metaspace)(HEAP_CHANGE_FORMAT" " - HEAP_CHANGE_FORMAT" " - HEAP_CHANGE_FORMAT, - HEAP_CHANGE_FORMAT_ARGS("Metaspace", - pre_meta_values.used(), - pre_meta_values.committed(), - meta_values.used(), - meta_values.committed()), - HEAP_CHANGE_FORMAT_ARGS("NonClass", - pre_meta_values.non_class_used(), - pre_meta_values.non_class_committed(), - meta_values.non_class_used(), - meta_values.non_class_committed()), - HEAP_CHANGE_FORMAT_ARGS("Class", - pre_meta_values.class_used(), - pre_meta_values.class_committed(), - meta_values.class_used(), - meta_values.class_committed())); - } else { - log_info(gc, metaspace)(HEAP_CHANGE_FORMAT, - HEAP_CHANGE_FORMAT_ARGS("Metaspace", - pre_meta_values.used(), - pre_meta_values.committed(), - meta_values.used(), - meta_values.committed())); - } -} - -void MetaspaceUtils::print_on(outputStream* out) { - Metaspace::MetadataType nct = Metaspace::NonClassType; - - out->print_cr(" Metaspace " - "used " SIZE_FORMAT "K, " - "capacity " SIZE_FORMAT "K, " - "committed " SIZE_FORMAT "K, " - "reserved " SIZE_FORMAT "K", - used_bytes()/K, - capacity_bytes()/K, - committed_bytes()/K, - reserved_bytes()/K); - - if (Metaspace::using_class_space()) { - Metaspace::MetadataType ct = Metaspace::ClassType; - out->print_cr(" class space " - "used " SIZE_FORMAT "K, " - "capacity " SIZE_FORMAT "K, " - "committed " SIZE_FORMAT "K, " - "reserved " SIZE_FORMAT "K", - used_bytes(ct)/K, - capacity_bytes(ct)/K, - committed_bytes(ct)/K, - reserved_bytes(ct)/K); - } -} - - -void MetaspaceUtils::print_vs(outputStream* out, size_t scale) { - const size_t reserved_nonclass_words = reserved_bytes(Metaspace::NonClassType) / sizeof(MetaWord); - const size_t committed_nonclass_words = committed_bytes(Metaspace::NonClassType) / sizeof(MetaWord); - { - if (Metaspace::using_class_space()) { - out->print(" Non-class space: "); - } - print_scaled_words(out, reserved_nonclass_words, scale, 7); - out->print(" reserved, "); - print_scaled_words_and_percentage(out, committed_nonclass_words, reserved_nonclass_words, scale, 7); - out->print_cr(" committed "); - - if (Metaspace::using_class_space()) { - const size_t reserved_class_words = reserved_bytes(Metaspace::ClassType) / sizeof(MetaWord); - const size_t committed_class_words = committed_bytes(Metaspace::ClassType) / sizeof(MetaWord); - out->print(" Class space: "); - print_scaled_words(out, reserved_class_words, scale, 7); - out->print(" reserved, "); - print_scaled_words_and_percentage(out, committed_class_words, reserved_class_words, scale, 7); - out->print_cr(" committed "); - - const size_t reserved_words = reserved_nonclass_words + reserved_class_words; - const size_t committed_words = committed_nonclass_words + committed_class_words; - out->print(" Both: "); - print_scaled_words(out, reserved_words, scale, 7); - out->print(" reserved, "); - print_scaled_words_and_percentage(out, committed_words, reserved_words, scale, 7); - out->print_cr(" committed "); - } - } -} - -static void print_basic_switches(outputStream* out, size_t scale) { - out->print("MaxMetaspaceSize: "); - if (MaxMetaspaceSize >= (max_uintx) - (2 * os::vm_page_size())) { - // aka "very big". Default is max_uintx, but due to rounding in arg parsing the real - // value is smaller. - out->print("unlimited"); - } else { - print_human_readable_size(out, MaxMetaspaceSize, scale); - } - out->cr(); - if (Metaspace::using_class_space()) { - out->print("CompressedClassSpaceSize: "); - print_human_readable_size(out, CompressedClassSpaceSize, scale); - } - out->cr(); -} - -// This will print out a basic metaspace usage report but -// unlike print_report() is guaranteed not to lock or to walk the CLDG. -void MetaspaceUtils::print_basic_report(outputStream* out, size_t scale) { - - if (!Metaspace::initialized()) { - out->print_cr("Metaspace not yet initialized."); - return; - } - - out->cr(); - out->print_cr("Usage:"); - - if (Metaspace::using_class_space()) { - out->print(" Non-class: "); - } - - // In its most basic form, we do not require walking the CLDG. Instead, just print the running totals from - // MetaspaceUtils. - const size_t cap_nc = MetaspaceUtils::capacity_words(Metaspace::NonClassType); - const size_t overhead_nc = MetaspaceUtils::overhead_words(Metaspace::NonClassType); - const size_t used_nc = MetaspaceUtils::used_words(Metaspace::NonClassType); - const size_t free_and_waste_nc = cap_nc - overhead_nc - used_nc; - - print_scaled_words(out, cap_nc, scale, 5); - out->print(" capacity, "); - print_scaled_words_and_percentage(out, used_nc, cap_nc, scale, 5); - out->print(" used, "); - print_scaled_words_and_percentage(out, free_and_waste_nc, cap_nc, scale, 5); - out->print(" free+waste, "); - print_scaled_words_and_percentage(out, overhead_nc, cap_nc, scale, 5); - out->print(" overhead. "); - out->cr(); - - if (Metaspace::using_class_space()) { - const size_t cap_c = MetaspaceUtils::capacity_words(Metaspace::ClassType); - const size_t overhead_c = MetaspaceUtils::overhead_words(Metaspace::ClassType); - const size_t used_c = MetaspaceUtils::used_words(Metaspace::ClassType); - const size_t free_and_waste_c = cap_c - overhead_c - used_c; - out->print(" Class: "); - print_scaled_words(out, cap_c, scale, 5); - out->print(" capacity, "); - print_scaled_words_and_percentage(out, used_c, cap_c, scale, 5); - out->print(" used, "); - print_scaled_words_and_percentage(out, free_and_waste_c, cap_c, scale, 5); - out->print(" free+waste, "); - print_scaled_words_and_percentage(out, overhead_c, cap_c, scale, 5); - out->print(" overhead. "); - out->cr(); - - out->print(" Both: "); - const size_t cap = cap_nc + cap_c; - - print_scaled_words(out, cap, scale, 5); - out->print(" capacity, "); - print_scaled_words_and_percentage(out, used_nc + used_c, cap, scale, 5); - out->print(" used, "); - print_scaled_words_and_percentage(out, free_and_waste_nc + free_and_waste_c, cap, scale, 5); - out->print(" free+waste, "); - print_scaled_words_and_percentage(out, overhead_nc + overhead_c, cap, scale, 5); - out->print(" overhead. "); - out->cr(); - } - - out->cr(); - out->print_cr("Virtual space:"); - - print_vs(out, scale); - - out->cr(); - out->print_cr("Chunk freelists:"); - - if (Metaspace::using_class_space()) { - out->print(" Non-Class: "); - } - print_human_readable_size(out, Metaspace::chunk_manager_metadata()->free_chunks_total_bytes(), scale); - out->cr(); - if (Metaspace::using_class_space()) { - out->print(" Class: "); - print_human_readable_size(out, Metaspace::chunk_manager_class()->free_chunks_total_bytes(), scale); - out->cr(); - out->print(" Both: "); - print_human_readable_size(out, Metaspace::chunk_manager_class()->free_chunks_total_bytes() + - Metaspace::chunk_manager_metadata()->free_chunks_total_bytes(), scale); - out->cr(); - } - - out->cr(); - - // Print basic settings - print_basic_switches(out, scale); - - out->cr(); - -} - -void MetaspaceUtils::print_report(outputStream* out, size_t scale, int flags) { - - if (!Metaspace::initialized()) { - out->print_cr("Metaspace not yet initialized."); - return; - } - - const bool print_loaders = (flags & rf_show_loaders) > 0; - const bool print_classes = (flags & rf_show_classes) > 0; - const bool print_by_chunktype = (flags & rf_break_down_by_chunktype) > 0; - const bool print_by_spacetype = (flags & rf_break_down_by_spacetype) > 0; - - // Some report options require walking the class loader data graph. - PrintCLDMetaspaceInfoClosure cl(out, scale, print_loaders, print_classes, print_by_chunktype); - if (print_loaders) { - out->cr(); - out->print_cr("Usage per loader:"); - out->cr(); - } - - ClassLoaderDataGraph::loaded_cld_do(&cl); // collect data and optionally print - - // Print totals, broken up by space type. - if (print_by_spacetype) { - out->cr(); - out->print_cr("Usage per space type:"); - out->cr(); - for (int space_type = (int)Metaspace::ZeroMetaspaceType; - space_type < (int)Metaspace::MetaspaceTypeCount; space_type ++) - { - uintx num_loaders = cl._num_loaders_by_spacetype[space_type]; - uintx num_classes = cl._num_classes_by_spacetype[space_type]; - out->print("%s - " UINTX_FORMAT " %s", - space_type_name((Metaspace::MetaspaceType)space_type), - num_loaders, loaders_plural(num_loaders)); - if (num_classes > 0) { - out->print(", "); - print_number_of_classes(out, num_classes, cl._num_classes_shared_by_spacetype[space_type]); - out->print(":"); - cl._stats_by_spacetype[space_type].print_on(out, scale, print_by_chunktype); - } else { - out->print("."); - out->cr(); - } - out->cr(); - } - } - - // Print totals for in-use data: - out->cr(); - { - uintx num_loaders = cl._num_loaders; - out->print("Total Usage - " UINTX_FORMAT " %s, ", - num_loaders, loaders_plural(num_loaders)); - print_number_of_classes(out, cl._num_classes, cl._num_classes_shared); - out->print(":"); - cl._stats_total.print_on(out, scale, print_by_chunktype); - out->cr(); - } - - // -- Print Virtual space. - out->cr(); - out->print_cr("Virtual space:"); - - print_vs(out, scale); +////// Metaspace methods ///// - // -- Print VirtualSpaceList details. - if ((flags & rf_show_vslist) > 0) { - out->cr(); - out->print_cr("Virtual space list%s:", Metaspace::using_class_space() ? "s" : ""); - - if (Metaspace::using_class_space()) { - out->print_cr(" Non-Class:"); - } - Metaspace::space_list()->print_on(out, scale); - if (Metaspace::using_class_space()) { - out->print_cr(" Class:"); - Metaspace::class_space_list()->print_on(out, scale); - } - } - out->cr(); - - // -- Print VirtualSpaceList map. - if ((flags & rf_show_vsmap) > 0) { - out->cr(); - out->print_cr("Virtual space map:"); - - if (Metaspace::using_class_space()) { - out->print_cr(" Non-Class:"); - } - Metaspace::space_list()->print_map(out); - if (Metaspace::using_class_space()) { - out->print_cr(" Class:"); - Metaspace::class_space_list()->print_map(out); - } - } - out->cr(); - - // -- Print Freelists (ChunkManager) details - out->cr(); - out->print_cr("Chunk freelist%s:", Metaspace::using_class_space() ? "s" : ""); - - ChunkManagerStatistics non_class_cm_stat; - Metaspace::chunk_manager_metadata()->collect_statistics(&non_class_cm_stat); - - if (Metaspace::using_class_space()) { - out->print_cr(" Non-Class:"); - } - non_class_cm_stat.print_on(out, scale); - - if (Metaspace::using_class_space()) { - ChunkManagerStatistics class_cm_stat; - Metaspace::chunk_manager_class()->collect_statistics(&class_cm_stat); - out->print_cr(" Class:"); - class_cm_stat.print_on(out, scale); - } - - // As a convenience, print a summary of common waste. - out->cr(); - out->print("Waste "); - // For all wastages, print percentages from total. As total use the total size of memory committed for metaspace. - const size_t committed_words = committed_bytes() / BytesPerWord; - - out->print("(percentages refer to total committed size "); - print_scaled_words(out, committed_words, scale); - out->print_cr("):"); - - // Print space committed but not yet used by any class loader - const size_t unused_words_in_vs = MetaspaceUtils::free_in_vs_bytes() / BytesPerWord; - out->print(" Committed unused: "); - print_scaled_words_and_percentage(out, unused_words_in_vs, committed_words, scale, 6); - out->cr(); - - // Print waste for in-use chunks. - UsedChunksStatistics ucs_nonclass = cl._stats_total.nonclass_sm_stats().totals(); - UsedChunksStatistics ucs_class = cl._stats_total.class_sm_stats().totals(); - UsedChunksStatistics ucs_all; - ucs_all.add(ucs_nonclass); - ucs_all.add(ucs_class); - - out->print(" Waste in chunks in use: "); - print_scaled_words_and_percentage(out, ucs_all.waste(), committed_words, scale, 6); - out->cr(); - out->print(" Free in chunks in use: "); - print_scaled_words_and_percentage(out, ucs_all.free(), committed_words, scale, 6); - out->cr(); - out->print(" Overhead in chunks in use: "); - print_scaled_words_and_percentage(out, ucs_all.overhead(), committed_words, scale, 6); - out->cr(); - - // Print waste in free chunks. - const size_t total_capacity_in_free_chunks = - Metaspace::chunk_manager_metadata()->free_chunks_total_words() + - (Metaspace::using_class_space() ? Metaspace::chunk_manager_class()->free_chunks_total_words() : 0); - out->print(" In free chunks: "); - print_scaled_words_and_percentage(out, total_capacity_in_free_chunks, committed_words, scale, 6); - out->cr(); - - // Print waste in deallocated blocks. - const uintx free_blocks_num = - cl._stats_total.nonclass_sm_stats().free_blocks_num() + - cl._stats_total.class_sm_stats().free_blocks_num(); - const size_t free_blocks_cap_words = - cl._stats_total.nonclass_sm_stats().free_blocks_cap_words() + - cl._stats_total.class_sm_stats().free_blocks_cap_words(); - out->print("Deallocated from chunks in use: "); - print_scaled_words_and_percentage(out, free_blocks_cap_words, committed_words, scale, 6); - out->print(" (" UINTX_FORMAT " blocks)", free_blocks_num); - out->cr(); - - // Print total waste. - const size_t total_waste = ucs_all.waste() + ucs_all.free() + ucs_all.overhead() + total_capacity_in_free_chunks - + free_blocks_cap_words + unused_words_in_vs; - out->print(" -total-: "); - print_scaled_words_and_percentage(out, total_waste, committed_words, scale, 6); - out->cr(); - - // Print internal statistics -#ifdef ASSERT - out->cr(); - out->cr(); - out->print_cr("Internal statistics:"); - out->cr(); - out->print_cr("Number of allocations: " UINTX_FORMAT ".", g_internal_statistics.num_allocs); - out->print_cr("Number of space births: " UINTX_FORMAT ".", g_internal_statistics.num_metaspace_births); - out->print_cr("Number of space deaths: " UINTX_FORMAT ".", g_internal_statistics.num_metaspace_deaths); - out->print_cr("Number of virtual space node births: " UINTX_FORMAT ".", g_internal_statistics.num_vsnodes_created); - out->print_cr("Number of virtual space node deaths: " UINTX_FORMAT ".", g_internal_statistics.num_vsnodes_purged); - out->print_cr("Number of times virtual space nodes were expanded: " UINTX_FORMAT ".", g_internal_statistics.num_committed_space_expanded); - out->print_cr("Number of deallocations: " UINTX_FORMAT " (" UINTX_FORMAT " external).", g_internal_statistics.num_deallocs, g_internal_statistics.num_external_deallocs); - out->print_cr("Allocations from deallocated blocks: " UINTX_FORMAT ".", g_internal_statistics.num_allocs_from_deallocated_blocks); - out->print_cr("Number of chunks added to freelist: " UINTX_FORMAT ".", - g_internal_statistics.num_chunks_added_to_freelist); - out->print_cr("Number of chunks removed from freelist: " UINTX_FORMAT ".", - g_internal_statistics.num_chunks_removed_from_freelist); - out->print_cr("Number of chunk merges: " UINTX_FORMAT ", split-ups: " UINTX_FORMAT ".", - g_internal_statistics.num_chunk_merges, g_internal_statistics.num_chunk_splits); - - out->cr(); -#endif +const MetaspaceTracer* Metaspace::_tracer = NULL; - // Print some interesting settings - out->cr(); - out->cr(); - print_basic_switches(out, scale); - - out->cr(); - out->print("InitialBootClassLoaderMetaspaceSize: "); - print_human_readable_size(out, InitialBootClassLoaderMetaspaceSize, scale); - - out->cr(); - out->cr(); - -} // MetaspaceUtils::print_report() - -// Prints an ASCII representation of the given space. -void MetaspaceUtils::print_metaspace_map(outputStream* out, Metaspace::MetadataType mdtype) { - MutexLocker cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); - const bool for_class = mdtype == Metaspace::ClassType ? true : false; - VirtualSpaceList* const vsl = for_class ? Metaspace::class_space_list() : Metaspace::space_list(); - if (vsl != NULL) { - if (for_class) { - if (!Metaspace::using_class_space()) { - out->print_cr("No Class Space."); - return; - } - out->print_raw("---- Metaspace Map (Class Space) ----"); - } else { - out->print_raw("---- Metaspace Map (Non-Class Space) ----"); - } - // Print legend: - out->cr(); - out->print_cr("Chunk Types (uppercase chunks are in use): x-specialized, s-small, m-medium, h-humongous."); - out->cr(); - VirtualSpaceList* const vsl = for_class ? Metaspace::class_space_list() : Metaspace::space_list(); - vsl->print_map(out); - out->cr(); - } -} +DEBUG_ONLY(bool Metaspace::_frozen = false;) -void MetaspaceUtils::verify_free_chunks() { -#ifdef ASSERT - Metaspace::chunk_manager_metadata()->verify(false); - if (Metaspace::using_class_space()) { - Metaspace::chunk_manager_class()->verify(false); - } -#endif +bool Metaspace::initialized() { + return metaspace::MetaspaceContext::context_nonclass() != NULL + LP64_ONLY(&& (using_class_space() ? Metaspace::class_space_is_initialized() : true)); } -void MetaspaceUtils::verify_metrics() { -#ifdef ASSERT - // Please note: there are time windows where the internal counters are out of sync with - // reality. For example, when a newly created ClassLoaderMetaspace creates its first chunk - - // the ClassLoaderMetaspace is not yet attached to its ClassLoaderData object and hence will - // not be counted when iterating the CLDG. So be careful when you call this method. - ClassLoaderMetaspaceStatistics total_stat; - collect_statistics(&total_stat); - UsedChunksStatistics nonclass_chunk_stat = total_stat.nonclass_sm_stats().totals(); - UsedChunksStatistics class_chunk_stat = total_stat.class_sm_stats().totals(); - - bool mismatch = false; - for (int i = 0; i < Metaspace::MetadataTypeCount; i ++) { - Metaspace::MetadataType mdtype = (Metaspace::MetadataType)i; - UsedChunksStatistics chunk_stat = total_stat.sm_stats(mdtype).totals(); - if (capacity_words(mdtype) != chunk_stat.cap() || - used_words(mdtype) != chunk_stat.used() || - overhead_words(mdtype) != chunk_stat.overhead()) { - mismatch = true; - tty->print_cr("MetaspaceUtils::verify_metrics: counter mismatch for mdtype=%u:", mdtype); - tty->print_cr("Expected cap " SIZE_FORMAT ", used " SIZE_FORMAT ", overhead " SIZE_FORMAT ".", - capacity_words(mdtype), used_words(mdtype), overhead_words(mdtype)); - tty->print_cr("Got cap " SIZE_FORMAT ", used " SIZE_FORMAT ", overhead " SIZE_FORMAT ".", - chunk_stat.cap(), chunk_stat.used(), chunk_stat.overhead()); - tty->flush(); - } - } - assert(mismatch == false, "MetaspaceUtils::verify_metrics: counter mismatch."); -#endif -} - -// Metaspace methods - -size_t Metaspace::_first_chunk_word_size = 0; -size_t Metaspace::_first_class_chunk_word_size = 0; - -size_t Metaspace::_commit_alignment = 0; -size_t Metaspace::_reserve_alignment = 0; - -VirtualSpaceList* Metaspace::_space_list = NULL; -VirtualSpaceList* Metaspace::_class_space_list = NULL; - -ChunkManager* Metaspace::_chunk_manager_metadata = NULL; -ChunkManager* Metaspace::_chunk_manager_class = NULL; - -bool Metaspace::_initialized = false; - -#define VIRTUALSPACEMULTIPLIER 2 - #ifdef _LP64 void Metaspace::print_compressed_class_space(outputStream* st) { - if (_class_space_list != NULL) { - address base = (address)_class_space_list->current_virtual_space()->bottom(); - address top = base + compressed_class_space_size(); - st->print("Compressed class space mapped at: " PTR_FORMAT "-" PTR_FORMAT ", size: " SIZE_FORMAT, - p2i(base), p2i(top), top - base); + if (VirtualSpaceList::vslist_class() != NULL) { + MetaWord* base = VirtualSpaceList::vslist_class()->base_of_first_node(); + size_t size = VirtualSpaceList::vslist_class()->word_size_of_first_node(); + MetaWord* top = base + size; + st->print("Compressed class space mapped at: " PTR_FORMAT "-" PTR_FORMAT ", reserved size: " SIZE_FORMAT, + p2i(base), p2i(top), (top - base) * BytesPerWord); st->cr(); } } // Given a prereserved space, use that to set up the compressed class space list. void Metaspace::initialize_class_space(ReservedSpace rs) { + assert(rs.size() >= CompressedClassSpaceSize, + SIZE_FORMAT " != " SIZE_FORMAT, rs.size(), CompressedClassSpaceSize); assert(using_class_space(), "Must be using class space"); - assert(_class_space_list == NULL && _chunk_manager_class == NULL, "Only call once"); assert(rs.size() == CompressedClassSpaceSize, SIZE_FORMAT " != " SIZE_FORMAT, rs.size(), CompressedClassSpaceSize); @@ -997,18 +509,18 @@ void Metaspace::initialize_class_space(ReservedSpace rs) { is_aligned(rs.size(), Metaspace::reserve_alignment()), "wrong alignment"); - _class_space_list = new VirtualSpaceList(rs); - _chunk_manager_class = new ChunkManager(true/*is_class*/); + MetaspaceContext::initialize_class_space_context(rs); // This does currently not work because rs may be the result of a split // operation and NMT seems not to be able to handle splits. // Will be fixed with JDK-8243535. // MemTracker::record_virtual_memory_type((address)rs.base(), mtClass); - if (!_class_space_list->initialization_succeeded()) { - vm_exit_during_initialization("Failed to setup compressed class space virtual space list."); - } +} +// Returns true if class space has been setup (initialize_class_space). +bool Metaspace::class_space_is_initialized() { + return MetaspaceContext::context_class() != NULL; } // Reserve a range of memory at an address suitable for en/decoding narrow @@ -1064,73 +576,90 @@ ReservedSpace Metaspace::reserve_address_space_for_compressed_classes(size_t siz #endif // _LP64 +size_t Metaspace::reserve_alignment_words() { + return metaspace::Settings::virtual_space_node_reserve_alignment_words(); +} + +size_t Metaspace::commit_alignment_words() { + return metaspace::Settings::commit_granule_words(); +} void Metaspace::ergo_initialize() { - if (DumpSharedSpaces) { - // Using large pages when dumping the shared archive is currently not implemented. - FLAG_SET_ERGO(UseLargePagesInMetaspace, false); - } - size_t page_size = os::vm_page_size(); - if (UseLargePages && UseLargePagesInMetaspace) { - page_size = os::large_page_size(); - } + // Must happen before using any setting from Settings::--- + metaspace::Settings::ergo_initialize(); - _commit_alignment = page_size; - _reserve_alignment = MAX2(page_size, (size_t)os::vm_allocation_granularity()); + // MaxMetaspaceSize and CompressedClassSpaceSize: + // + // MaxMetaspaceSize is the maximum size, in bytes, of memory we are allowed + // to commit for the Metaspace. + // It is just a number; a limit we compare against before committing. It + // does not have to be aligned to anything. + // It gets used as compare value in class CommitLimiter. + // It is set to max_uintx in globals.hpp by default, so by default it does + // not limit anything. + // + // CompressedClassSpaceSize is the size, in bytes, of the address range we + // pre-reserve for the compressed class space (if we use class space). + // This size has to be aligned to the metaspace reserve alignment (to the + // size of a root chunk). It gets aligned up from whatever value the caller + // gave us to the next multiple of root chunk size. + // + // Note: Strictly speaking MaxMetaspaceSize and CompressedClassSpaceSize have + // very little to do with each other. The notion often encountered: + // MaxMetaspaceSize = CompressedClassSpaceSize + + // is subtly wrong: MaxMetaspaceSize can besmaller than CompressedClassSpaceSize, + // in which case we just would not be able to fully commit the class space range. + // + // We still adjust CompressedClassSpaceSize to reasonable limits, mainly to + // save on reserved space, and to make ergnonomics less confusing. - // The upcoming Metaspace rewrite will impose a higher alignment granularity. - // To prepare for that and to catch/prevent any misuse of Metaspace alignment - // which may creep in, up the alignment a bit. - if (_reserve_alignment == 4 * K) { - _reserve_alignment *= 4; - } + // (aligned just for cleanliness:) + MaxMetaspaceSize = MAX2(align_down(MaxMetaspaceSize, commit_alignment()), commit_alignment()); - // Do not use FLAG_SET_ERGO to update MaxMetaspaceSize, since this will - // override if MaxMetaspaceSize was set on the command line or not. - // This information is needed later to conform to the specification of the - // java.lang.management.MemoryUsage API. - // - // Ideally, we would be able to set the default value of MaxMetaspaceSize in - // globals.hpp to the aligned value, but this is not possible, since the - // alignment depends on other flags being parsed. - MaxMetaspaceSize = align_down_bounded(MaxMetaspaceSize, _reserve_alignment); + if (UseCompressedClassPointers) { + // Let CCS size not be larger than 80% of MaxMetaspaceSize. Note that is + // grossly over-dimensioned for most usage scenarios; typical ratio of + // class space : non class space usage is about 1:6. With many small classes, + // it can get as low as 1:2. It is not a big deal though since ccs is only + // reserved and will be committed on demand only. + size_t max_ccs_size = MaxMetaspaceSize * 0.8; + size_t adjusted_ccs_size = MIN2(CompressedClassSpaceSize, max_ccs_size); + + // CCS must be aligned to root chunk size, and be at least the size of one + // root chunk. + adjusted_ccs_size = align_up(adjusted_ccs_size, reserve_alignment()); + adjusted_ccs_size = MAX2(adjusted_ccs_size, reserve_alignment()); + + // Note: re-adjusting may have us left with a CompressedClassSpaceSize + // larger than MaxMetaspaceSize for very small values of MaxMetaspaceSize. + // Lets just live with that, its not a big deal. + + if (adjusted_ccs_size != CompressedClassSpaceSize) { + FLAG_SET_ERGO(CompressedClassSpaceSize, adjusted_ccs_size); + log_info(metaspace)("Setting CompressedClassSpaceSize to " SIZE_FORMAT ".", + CompressedClassSpaceSize); + } + } + // Set MetaspaceSize, MinMetaspaceExpansion and MaxMetaspaceExpansion if (MetaspaceSize > MaxMetaspaceSize) { MetaspaceSize = MaxMetaspaceSize; } - MetaspaceSize = align_down_bounded(MetaspaceSize, _commit_alignment); + MetaspaceSize = align_down_bounded(MetaspaceSize, commit_alignment()); assert(MetaspaceSize <= MaxMetaspaceSize, "MetaspaceSize should be limited by MaxMetaspaceSize"); - MinMetaspaceExpansion = align_down_bounded(MinMetaspaceExpansion, _commit_alignment); - MaxMetaspaceExpansion = align_down_bounded(MaxMetaspaceExpansion, _commit_alignment); - - CompressedClassSpaceSize = align_down_bounded(CompressedClassSpaceSize, _reserve_alignment); + MinMetaspaceExpansion = align_down_bounded(MinMetaspaceExpansion, commit_alignment()); + MaxMetaspaceExpansion = align_down_bounded(MaxMetaspaceExpansion, commit_alignment()); - // Initial virtual space size will be calculated at global_initialize() - size_t min_metaspace_sz = - VIRTUALSPACEMULTIPLIER * InitialBootClassLoaderMetaspaceSize; - if (UseCompressedClassPointers) { - if ((min_metaspace_sz + CompressedClassSpaceSize) > MaxMetaspaceSize) { - if (min_metaspace_sz >= MaxMetaspaceSize) { - vm_exit_during_initialization("MaxMetaspaceSize is too small."); - } else { - FLAG_SET_ERGO(CompressedClassSpaceSize, - MaxMetaspaceSize - min_metaspace_sz); - } - } - } else if (min_metaspace_sz >= MaxMetaspaceSize) { - FLAG_SET_ERGO(InitialBootClassLoaderMetaspaceSize, - min_metaspace_sz); - } - - set_compressed_class_space_size(CompressedClassSpaceSize); } void Metaspace::global_initialize() { - MetaspaceGC::initialize(); + MetaspaceGC::initialize(); // <- since we do not prealloc init chunks anymore is this still needed? + + metaspace::ChunkHeaderPool::initialize(); // If UseCompressedClassPointers=1, we have two cases: // a) if CDS is active (either dump time or runtime), it will create the ccs @@ -1190,7 +719,7 @@ void Metaspace::global_initialize() { if (!rs.is_reserved()) { vm_exit_during_initialization( err_msg("Could not allocate compressed class space: " SIZE_FORMAT " bytes", - compressed_class_space_size())); + CompressedClassSpaceSize)); } // Initialize space @@ -1202,31 +731,24 @@ void Metaspace::global_initialize() { #endif - // Initialize these before initializing the VirtualSpaceList - _first_chunk_word_size = InitialBootClassLoaderMetaspaceSize / BytesPerWord; - _first_chunk_word_size = align_word_size_up(_first_chunk_word_size); - // Make the first class chunk bigger than a medium chunk so it's not put - // on the medium chunk list. The next chunk will be small and progress - // from there. This size calculated by -version. - _first_class_chunk_word_size = MIN2((size_t)MediumChunk*6, - (CompressedClassSpaceSize/BytesPerWord)*2); - _first_class_chunk_word_size = align_word_size_up(_first_class_chunk_word_size); - // Arbitrarily set the initial virtual space to a multiple - // of the boot class loader size. - size_t word_size = VIRTUALSPACEMULTIPLIER * _first_chunk_word_size; - word_size = align_up(word_size, Metaspace::reserve_alignment_words()); - - // Initialize the list of virtual spaces. - _space_list = new VirtualSpaceList(word_size); - _chunk_manager_metadata = new ChunkManager(false/*metaspace*/); - - if (!_space_list->initialization_succeeded()) { - vm_exit_during_initialization("Unable to setup metadata virtual space list.", NULL); - } + // Initialize non-class virtual space list, and its chunk manager: + MetaspaceContext::initialize_nonclass_space_context(); _tracer = new MetaspaceTracer(); - _initialized = true; + // We must prevent the very first address of the ccs from being used to store + // metadata, since that address would translate to a narrow pointer of 0, and the + // VM does not distinguish between "narrow 0 as in NULL" and "narrow 0 as in start + // of ccs". + // Before Elastic Metaspace that did not happen due to the fact that every Metachunk + // had a header and therefore could not allocate anything at offset 0. +#ifdef _LP64 + if (using_class_space()) { + // The simplest way to fix this is to allocate a tiny dummy chunk right at the + // start of ccs and do not use it for anything. + MetaspaceContext::context_class()->cm()->get_chunk(metaspace::chunklevel::HIGHEST_CHUNK_LEVEL); + } +#endif #ifdef _LP64 if (UseCompressedClassPointers) { @@ -1248,23 +770,15 @@ void Metaspace::post_initialize() { MetaspaceGC::post_initialize(); } -void Metaspace::verify_global_initialization() { - assert(space_list() != NULL, "Metadata VirtualSpaceList has not been initialized"); - assert(chunk_manager_metadata() != NULL, "Metadata ChunkManager has not been initialized"); - - if (using_class_space()) { - assert(class_space_list() != NULL, "Class VirtualSpaceList has not been initialized"); - assert(chunk_manager_class() != NULL, "Class ChunkManager has not been initialized"); - } -} - -size_t Metaspace::align_word_size_up(size_t word_size) { - size_t byte_size = word_size * wordSize; - return ReservedSpace::allocation_align_size_up(byte_size) / wordSize; +size_t Metaspace::max_allocation_word_size() { + const size_t max_overhead_words = metaspace::get_raw_word_size_for_requested_word_size(1); + return metaspace::chunklevel::MAX_CHUNK_WORD_SIZE - max_overhead_words; } MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size, MetaspaceObj::Type type, TRAPS) { + assert(word_size <= Metaspace::max_allocation_word_size(), + "allocation size too large (" SIZE_FORMAT ")", word_size); assert(!_frozen, "sanity"); assert(!(DumpSharedSpaces && THREAD->is_VM_thread()), "sanity"); @@ -1309,6 +823,8 @@ MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size, // Zero initialize. Copy::fill_to_words((HeapWord*)result, word_size, 0); + log_trace(metaspace)("Metaspace::allocate: type %d return " PTR_FORMAT ".", (int)type, p2i(result)); + return result; } @@ -1332,12 +848,13 @@ void Metaspace::report_metadata_oome(ClassLoaderData* loader_data, size_t word_s MetaspaceUtils::print_basic_report(&ls, 0); } + // TODO: this exception text may be wrong and misleading. This needs more thinking. See JDK-8252189. bool out_of_compressed_class_space = false; if (is_class_space_allocation(mdtype)) { ClassLoaderMetaspace* metaspace = loader_data->metaspace_non_null(); out_of_compressed_class_space = MetaspaceUtils::committed_bytes(Metaspace::ClassType) + - (metaspace->class_chunk_size(word_size) * BytesPerWord) > + align_up(word_size * BytesPerWord, 4 * M) > CompressedClassSpaceSize; } @@ -1374,16 +891,16 @@ const char* Metaspace::metadata_type_name(Metaspace::MetadataType mdtype) { } } -void Metaspace::purge(MetadataType mdtype) { - get_space_list(mdtype)->purge(get_chunk_manager(mdtype)); -} - void Metaspace::purge() { - MutexLocker cl(MetaspaceExpand_lock, - Mutex::_no_safepoint_check_flag); - purge(NonClassType); + ChunkManager* cm = ChunkManager::chunkmanager_nonclass(); + if (cm != NULL) { + cm->purge(); + } if (using_class_space()) { - purge(ClassType); + cm = ChunkManager::chunkmanager_class(); + if (cm != NULL) { + cm->purge(); + } } } @@ -1395,214 +912,9 @@ bool Metaspace::contains(const void* ptr) { } bool Metaspace::contains_non_shared(const void* ptr) { - if (using_class_space() && get_space_list(ClassType)->contains(ptr)) { + if (using_class_space() && VirtualSpaceList::vslist_class()->contains((MetaWord*)ptr)) { return true; } - return get_space_list(NonClassType)->contains(ptr); -} - -// ClassLoaderMetaspace - -ClassLoaderMetaspace::ClassLoaderMetaspace(Mutex* lock, Metaspace::MetaspaceType type) - : _space_type(type) - , _lock(lock) - , _vsm(NULL) - , _class_vsm(NULL) -{ - initialize(lock, type); -} - -ClassLoaderMetaspace::~ClassLoaderMetaspace() { - Metaspace::assert_not_frozen(); - DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_metaspace_deaths)); - delete _vsm; - if (Metaspace::using_class_space()) { - delete _class_vsm; - } -} - -void ClassLoaderMetaspace::initialize_first_chunk(Metaspace::MetaspaceType type, Metaspace::MetadataType mdtype) { - Metachunk* chunk = get_initialization_chunk(type, mdtype); - if (chunk != NULL) { - // Add to this manager's list of chunks in use and make it the current_chunk(). - get_space_manager(mdtype)->add_chunk(chunk, true); - } -} - -Metachunk* ClassLoaderMetaspace::get_initialization_chunk(Metaspace::MetaspaceType type, Metaspace::MetadataType mdtype) { - size_t chunk_word_size = get_space_manager(mdtype)->get_initial_chunk_size(type); - - // Get a chunk from the chunk freelist - Metachunk* chunk = Metaspace::get_chunk_manager(mdtype)->chunk_freelist_allocate(chunk_word_size); - - if (chunk == NULL) { - chunk = Metaspace::get_space_list(mdtype)->get_new_chunk(chunk_word_size, - get_space_manager(mdtype)->medium_chunk_bunch()); - } - - return chunk; -} - -void ClassLoaderMetaspace::initialize(Mutex* lock, Metaspace::MetaspaceType type) { - Metaspace::verify_global_initialization(); - - DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_metaspace_births)); - - // Allocate SpaceManager for metadata objects. - _vsm = new SpaceManager(Metaspace::NonClassType, type, lock); - - if (Metaspace::using_class_space()) { - // Allocate SpaceManager for classes. - _class_vsm = new SpaceManager(Metaspace::ClassType, type, lock); - } - - MutexLocker cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); - - // Allocate chunk for metadata objects - initialize_first_chunk(type, Metaspace::NonClassType); - - // Allocate chunk for class metadata objects - if (Metaspace::using_class_space()) { - initialize_first_chunk(type, Metaspace::ClassType); - } -} - -MetaWord* ClassLoaderMetaspace::allocate(size_t word_size, Metaspace::MetadataType mdtype) { - Metaspace::assert_not_frozen(); - - DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_allocs)); - - // Don't use class_vsm() unless UseCompressedClassPointers is true. - if (Metaspace::is_class_space_allocation(mdtype)) { - return class_vsm()->allocate(word_size); - } else { - return vsm()->allocate(word_size); - } -} - -MetaWord* ClassLoaderMetaspace::expand_and_allocate(size_t word_size, Metaspace::MetadataType mdtype) { - Metaspace::assert_not_frozen(); - size_t delta_bytes = MetaspaceGC::delta_capacity_until_GC(word_size * BytesPerWord); - assert(delta_bytes > 0, "Must be"); - - size_t before = 0; - size_t after = 0; - bool can_retry = true; - MetaWord* res; - bool incremented; - - // Each thread increments the HWM at most once. Even if the thread fails to increment - // the HWM, an allocation is still attempted. This is because another thread must then - // have incremented the HWM and therefore the allocation might still succeed. - do { - incremented = MetaspaceGC::inc_capacity_until_GC(delta_bytes, &after, &before, &can_retry); - res = allocate(word_size, mdtype); - } while (!incremented && res == NULL && can_retry); - - if (incremented) { - Metaspace::tracer()->report_gc_threshold(before, after, - MetaspaceGCThresholdUpdater::ExpandAndAllocate); - log_trace(gc, metaspace)("Increase capacity to GC from " SIZE_FORMAT " to " SIZE_FORMAT, before, after); - } - - return res; -} - -size_t ClassLoaderMetaspace::allocated_blocks_bytes() const { - return (vsm()->used_words() + - (Metaspace::using_class_space() ? class_vsm()->used_words() : 0)) * BytesPerWord; -} - -size_t ClassLoaderMetaspace::allocated_chunks_bytes() const { - return (vsm()->capacity_words() + - (Metaspace::using_class_space() ? class_vsm()->capacity_words() : 0)) * BytesPerWord; -} - -void ClassLoaderMetaspace::deallocate(MetaWord* ptr, size_t word_size, bool is_class) { - Metaspace::assert_not_frozen(); - assert(!SafepointSynchronize::is_at_safepoint() - || Thread::current()->is_VM_thread(), "should be the VM thread"); - - DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_external_deallocs)); - - MutexLocker ml(vsm()->lock(), Mutex::_no_safepoint_check_flag); - - if (is_class && Metaspace::using_class_space()) { - class_vsm()->deallocate(ptr, word_size); - } else { - vsm()->deallocate(ptr, word_size); - } -} - -size_t ClassLoaderMetaspace::class_chunk_size(size_t word_size) { - assert(Metaspace::using_class_space(), "Has to use class space"); - return class_vsm()->calc_chunk_size(word_size); -} - -void ClassLoaderMetaspace::print_on(outputStream* out) const { - // Print both class virtual space counts and metaspace. - if (Verbose) { - vsm()->print_on(out); - if (Metaspace::using_class_space()) { - class_vsm()->print_on(out); - } - } -} - -void ClassLoaderMetaspace::verify() { - vsm()->verify(); - if (Metaspace::using_class_space()) { - class_vsm()->verify(); - } -} - -void ClassLoaderMetaspace::add_to_statistics_locked(ClassLoaderMetaspaceStatistics* out) const { - assert_lock_strong(lock()); - vsm()->add_to_statistics_locked(&out->nonclass_sm_stats()); - if (Metaspace::using_class_space()) { - class_vsm()->add_to_statistics_locked(&out->class_sm_stats()); - } -} - -void ClassLoaderMetaspace::add_to_statistics(ClassLoaderMetaspaceStatistics* out) const { - MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag); - add_to_statistics_locked(out); -} - -/////////////// Unit tests /////////////// - -struct chunkmanager_statistics_t { - int num_specialized_chunks; - int num_small_chunks; - int num_medium_chunks; - int num_humongous_chunks; -}; - -extern void test_metaspace_retrieve_chunkmanager_statistics(Metaspace::MetadataType mdType, chunkmanager_statistics_t* out) { - ChunkManager* const chunk_manager = Metaspace::get_chunk_manager(mdType); - ChunkManagerStatistics stat; - chunk_manager->collect_statistics(&stat); - out->num_specialized_chunks = (int)stat.chunk_stats(SpecializedIndex).num(); - out->num_small_chunks = (int)stat.chunk_stats(SmallIndex).num(); - out->num_medium_chunks = (int)stat.chunk_stats(MediumIndex).num(); - out->num_humongous_chunks = (int)stat.chunk_stats(HumongousIndex).num(); -} - -struct chunk_geometry_t { - size_t specialized_chunk_word_size; - size_t small_chunk_word_size; - size_t medium_chunk_word_size; -}; - -extern void test_metaspace_retrieve_chunk_geometry(Metaspace::MetadataType mdType, chunk_geometry_t* out) { - if (mdType == Metaspace::NonClassType) { - out->specialized_chunk_word_size = SpecializedChunk; - out->small_chunk_word_size = SmallChunk; - out->medium_chunk_word_size = MediumChunk; - } else { - out->specialized_chunk_word_size = ClassSpecializedChunk; - out->small_chunk_word_size = ClassSmallChunk; - out->medium_chunk_word_size = ClassMediumChunk; - } + return VirtualSpaceList::vslist_nonclass()->contains((MetaWord*)ptr); } diff --git a/src/hotspot/share/memory/metaspace.hpp b/src/hotspot/share/memory/metaspace.hpp index 32552cc4c44..182660f0112 100644 --- a/src/hotspot/share/memory/metaspace.hpp +++ b/src/hotspot/share/memory/metaspace.hpp @@ -28,65 +28,21 @@ #include "memory/memRegion.hpp" #include "memory/metaspaceChunkFreeListSummary.hpp" #include "memory/virtualspace.hpp" -#include "memory/metaspace/metaspaceSizesSnapshot.hpp" #include "runtime/globals.hpp" #include "utilities/exceptions.hpp" - -// Metaspace -// -// Metaspaces are Arenas for the VM's metadata. -// They are allocated one per class loader object, and one for the null -// bootstrap class loader -// -// block X ---+ +-------------------+ -// | | Virtualspace | -// | | | -// | | | -// | |-------------------| -// | || Chunk | -// | || | -// | ||---------- | -// +------>||| block 0 | | -// ||---------- | -// ||| block 1 | | -// ||---------- | -// || | -// |-------------------| -// | | -// | | -// +-------------------+ -// +#include "utilities/globalDefinitions.hpp" class ClassLoaderData; +class MetaspaceShared; class MetaspaceTracer; class Mutex; class outputStream; -class CollectedHeap; - namespace metaspace { - class ChunkManager; - class ClassLoaderMetaspaceStatistics; - class Metablock; - class Metachunk; - class PrintCLDMetaspaceInfoClosure; - class SpaceManager; - class VirtualSpaceList; - class VirtualSpaceNode; + class MetaspaceSizesSnapshot; } -// Metaspaces each have a SpaceManager and allocations -// are done by the SpaceManager. Allocations are done -// out of the current Metachunk. When the current Metachunk -// is exhausted, the SpaceManager gets a new one from -// the current VirtualSpace. When the VirtualSpace is exhausted -// the SpaceManager gets a new one. The SpaceManager -// also manages freelists of available Chunks. -// -// Currently the space manager maintains the list of -// virtual spaces and the list of chunks in use. Its -// allocate() method returns a block for use as a -// quantum of metadata. +////////////////// Metaspace /////////////////////// // Namespace for important central static functions // (auxiliary stuff goes into MetaspaceUtils) @@ -94,7 +50,7 @@ class Metaspace : public AllStatic { friend class MetaspaceShared; - public: +public: enum MetadataType { ClassType, NonClassType, @@ -109,59 +65,15 @@ class Metaspace : public AllStatic { MetaspaceTypeCount }; - private: - - // Align up the word size to the allocation word size - static size_t align_word_size_up(size_t); - - // Aligned size of the metaspace. - static size_t _compressed_class_space_size; +private: - static size_t compressed_class_space_size() { - return _compressed_class_space_size; - } - - static void set_compressed_class_space_size(size_t size) { - _compressed_class_space_size = size; - } - - static size_t _first_chunk_word_size; - static size_t _first_class_chunk_word_size; - - static size_t _commit_alignment; - static size_t _reserve_alignment; DEBUG_ONLY(static bool _frozen;) - // Virtual Space lists for both classes and other metadata - static metaspace::VirtualSpaceList* _space_list; - static metaspace::VirtualSpaceList* _class_space_list; - - static metaspace::ChunkManager* _chunk_manager_metadata; - static metaspace::ChunkManager* _chunk_manager_class; - static const MetaspaceTracer* _tracer; static bool _initialized; - public: - static metaspace::VirtualSpaceList* space_list() { return _space_list; } - static metaspace::VirtualSpaceList* class_space_list() { return _class_space_list; } - static metaspace::VirtualSpaceList* get_space_list(MetadataType mdtype) { - assert(mdtype != MetadataTypeCount, "MetadaTypeCount can't be used as mdtype"); - return mdtype == ClassType ? class_space_list() : space_list(); - } - - static metaspace::ChunkManager* chunk_manager_metadata() { return _chunk_manager_metadata; } - static metaspace::ChunkManager* chunk_manager_class() { return _chunk_manager_class; } - static metaspace::ChunkManager* get_chunk_manager(MetadataType mdtype) { - assert(mdtype != MetadataTypeCount, "MetadaTypeCount can't be used as mdtype"); - return mdtype == ClassType ? chunk_manager_class() : chunk_manager_metadata(); - } - - // convenience function - static metaspace::ChunkManager* get_chunk_manager(bool is_class) { - return is_class ? chunk_manager_class() : chunk_manager_metadata(); - } +public: static const MetaspaceTracer* tracer() { return _tracer; } static void freeze() { @@ -188,7 +100,7 @@ class Metaspace : public AllStatic { static void initialize_class_space(ReservedSpace rs); // Returns true if class space has been setup (initialize_class_space). - static bool class_space_is_initialized() { return _class_space_list != NULL; } + static bool class_space_is_initialized(); #endif @@ -198,15 +110,18 @@ class Metaspace : public AllStatic { static void global_initialize(); static void post_initialize(); - static void verify_global_initialization(); + // Alignment, in bytes, of metaspace mappings + static size_t reserve_alignment() { return reserve_alignment_words() * BytesPerWord; } + // Alignment, in words, of metaspace mappings + static size_t reserve_alignment_words(); - static size_t first_chunk_word_size() { return _first_chunk_word_size; } - static size_t first_class_chunk_word_size() { return _first_class_chunk_word_size; } + // The granularity at which Metaspace is committed and uncommitted. + // (Todo: Why does this have to be exposed?) + static size_t commit_alignment() { return commit_alignment_words() * BytesPerWord; } + static size_t commit_alignment_words(); - static size_t reserve_alignment() { return _reserve_alignment; } - static size_t reserve_alignment_words() { return _reserve_alignment / BytesPerWord; } - static size_t commit_alignment() { return _commit_alignment; } - static size_t commit_alignment_words() { return _commit_alignment / BytesPerWord; } + // The largest possible single allocation + static size_t max_allocation_word_size(); static MetaWord* allocate(ClassLoaderData* loader_data, size_t word_size, MetaspaceObj::Type type, TRAPS); @@ -215,7 +130,6 @@ class Metaspace : public AllStatic { static bool contains_non_shared(const void* ptr); // Free empty virtualspaces - static void purge(MetadataType mdtype); static void purge(); static void report_metadata_oome(ClassLoaderData* loader_data, size_t word_size, @@ -234,212 +148,38 @@ class Metaspace : public AllStatic { return mdType == ClassType && using_class_space(); } - static bool initialized() { return _initialized; } + static bool initialized(); }; -// Manages the metaspace portion belonging to a class loader -class ClassLoaderMetaspace : public CHeapObj { - friend class CollectedHeap; // For expand_and_allocate() - friend class ZCollectedHeap; // For expand_and_allocate() - friend class ShenandoahHeap; // For expand_and_allocate() - friend class Metaspace; - friend class MetaspaceUtils; - friend class metaspace::PrintCLDMetaspaceInfoClosure; - friend class VM_CollectForMetadataAllocation; // For expand_and_allocate() - - private: - - void initialize(Mutex* lock, Metaspace::MetaspaceType type); - - // Initialize the first chunk for a Metaspace. Used for - // special cases such as the boot class loader, reflection - // class loader and hidden class loader. - void initialize_first_chunk(Metaspace::MetaspaceType type, Metaspace::MetadataType mdtype); - metaspace::Metachunk* get_initialization_chunk(Metaspace::MetaspaceType type, Metaspace::MetadataType mdtype); - - const Metaspace::MetaspaceType _space_type; - Mutex* const _lock; - metaspace::SpaceManager* _vsm; - metaspace::SpaceManager* _class_vsm; - - metaspace::SpaceManager* vsm() const { return _vsm; } - metaspace::SpaceManager* class_vsm() const { return _class_vsm; } - metaspace::SpaceManager* get_space_manager(Metaspace::MetadataType mdtype) { - assert(mdtype != Metaspace::MetadataTypeCount, "MetadaTypeCount can't be used as mdtype"); - return mdtype == Metaspace::ClassType ? class_vsm() : vsm(); - } - - Mutex* lock() const { return _lock; } - - MetaWord* expand_and_allocate(size_t size, Metaspace::MetadataType mdtype); - - size_t class_chunk_size(size_t word_size); +////////////////// MetaspaceGC /////////////////////// - // Adds to the given statistic object. Must be locked with CLD metaspace lock. - void add_to_statistics_locked(metaspace::ClassLoaderMetaspaceStatistics* out) const; - - Metaspace::MetaspaceType space_type() const { return _space_type; } +// Metaspace are deallocated when their class loader are GC'ed. +// This class implements a policy for inducing GC's to recover +// Metaspaces. +class MetaspaceGCThresholdUpdater : public AllStatic { public: - - ClassLoaderMetaspace(Mutex* lock, Metaspace::MetaspaceType type); - ~ClassLoaderMetaspace(); - - // Allocate space for metadata of type mdtype. This is space - // within a Metachunk and is used by - // allocate(ClassLoaderData*, size_t, bool, MetadataType, TRAPS) - MetaWord* allocate(size_t word_size, Metaspace::MetadataType mdtype); - - size_t allocated_blocks_bytes() const; - size_t allocated_chunks_bytes() const; - - void deallocate(MetaWord* ptr, size_t byte_size, bool is_class); - - void print_on(outputStream* st) const; - // Debugging support - void verify(); - - // Adds to the given statistic object. Will lock with CLD metaspace lock. - void add_to_statistics(metaspace::ClassLoaderMetaspaceStatistics* out) const; - -}; // ClassLoaderMetaspace - -class MetaspaceUtils : AllStatic { - - // Spacemanager updates running counters. - friend class metaspace::SpaceManager; - - // Special access for error reporting (checks without locks). - friend class oopDesc; - friend class Klass; - - // Running counters for statistics concerning in-use chunks. - // Note: capacity = used + free + waste + overhead. Note that we do not - // count free and waste. Their sum can be deduces from the three other values. - // For more details, one should call print_report() from within a safe point. - static size_t _capacity_words [Metaspace:: MetadataTypeCount]; - static size_t _overhead_words [Metaspace:: MetadataTypeCount]; - static volatile size_t _used_words [Metaspace:: MetadataTypeCount]; - - // Atomically decrement or increment in-use statistic counters - static void dec_capacity(Metaspace::MetadataType mdtype, size_t words); - static void inc_capacity(Metaspace::MetadataType mdtype, size_t words); - static void dec_used(Metaspace::MetadataType mdtype, size_t words); - static void inc_used(Metaspace::MetadataType mdtype, size_t words); - static void dec_overhead(Metaspace::MetadataType mdtype, size_t words); - static void inc_overhead(Metaspace::MetadataType mdtype, size_t words); - - - // Getters for the in-use counters. - static size_t capacity_words(Metaspace::MetadataType mdtype) { return _capacity_words[mdtype]; } - static size_t used_words(Metaspace::MetadataType mdtype) { return _used_words[mdtype]; } - static size_t overhead_words(Metaspace::MetadataType mdtype) { return _overhead_words[mdtype]; } - - static size_t free_chunks_total_words(Metaspace::MetadataType mdtype); - - // Helper for print_xx_report. - static void print_vs(outputStream* out, size_t scale); - -public: - - // Collect used metaspace statistics. This involves walking the CLDG. The resulting - // output will be the accumulated values for all live metaspaces. - // Note: method does not do any locking. - static void collect_statistics(metaspace::ClassLoaderMetaspaceStatistics* out); - - // Used by MetaspaceCounters - static size_t free_chunks_total_words(); - static size_t free_chunks_total_bytes(); - static size_t free_chunks_total_bytes(Metaspace::MetadataType mdtype); - - static size_t capacity_words() { - return capacity_words(Metaspace::NonClassType) + - capacity_words(Metaspace::ClassType); - } - static size_t capacity_bytes(Metaspace::MetadataType mdtype) { - return capacity_words(mdtype) * BytesPerWord; - } - static size_t capacity_bytes() { - return capacity_words() * BytesPerWord; - } - - static size_t used_words() { - return used_words(Metaspace::NonClassType) + - used_words(Metaspace::ClassType); - } - static size_t used_bytes(Metaspace::MetadataType mdtype) { - return used_words(mdtype) * BytesPerWord; - } - static size_t used_bytes() { - return used_words() * BytesPerWord; - } - - // Space committed but yet unclaimed by any class loader. - static size_t free_in_vs_bytes(); - static size_t free_in_vs_bytes(Metaspace::MetadataType mdtype); - - static size_t reserved_bytes(Metaspace::MetadataType mdtype); - static size_t reserved_bytes() { - return reserved_bytes(Metaspace::ClassType) + - reserved_bytes(Metaspace::NonClassType); - } - - static size_t committed_bytes(Metaspace::MetadataType mdtype); - static size_t committed_bytes() { - return committed_bytes(Metaspace::ClassType) + - committed_bytes(Metaspace::NonClassType); - } - - static size_t min_chunk_size_words(); - - // Flags for print_report(). - enum ReportFlag { - // Show usage by class loader. - rf_show_loaders = (1 << 0), - // Breaks report down by chunk type (small, medium, ...). - rf_break_down_by_chunktype = (1 << 1), - // Breaks report down by space type (hidden, reflection, ...). - rf_break_down_by_spacetype = (1 << 2), - // Print details about the underlying virtual spaces. - rf_show_vslist = (1 << 3), - // Print metaspace map. - rf_show_vsmap = (1 << 4), - // If show_loaders: show loaded classes for each loader. - rf_show_classes = (1 << 5) + enum Type { + ComputeNewSize, + ExpandAndAllocate, + Last }; - // This will print out a basic metaspace usage report but - // unlike print_report() is guaranteed not to lock or to walk the CLDG. - static void print_basic_report(outputStream* st, size_t scale); - - // Prints a report about the current metaspace state. - // Optional parts can be enabled via flags. - // Function will walk the CLDG and will lock the expand lock; if that is not - // convenient, use print_basic_report() instead. - static void print_report(outputStream* out, size_t scale = 0, int flags = 0); - - static bool has_chunk_free_list(Metaspace::MetadataType mdtype); - static MetaspaceChunkFreeListSummary chunk_free_list_summary(Metaspace::MetadataType mdtype); - - // Log change in used metadata. - static void print_metaspace_change(const metaspace::MetaspaceSizesSnapshot& pre_meta_values); - static void print_on(outputStream * out); - - // Prints an ASCII representation of the given space. - static void print_metaspace_map(outputStream* out, Metaspace::MetadataType mdtype); - - static void dump(outputStream* out); - static void verify_free_chunks(); - // Check internal counters (capacity, used). - static void verify_metrics(); + static const char* to_string(MetaspaceGCThresholdUpdater::Type updater) { + switch (updater) { + case ComputeNewSize: + return "compute_new_size"; + case ExpandAndAllocate: + return "expand_and_allocate"; + default: + assert(false, "Got bad updater: %d", (int) updater); + return NULL; + }; + } }; -// Metaspace are deallocated when their class loader are GC'ed. -// This class implements a policy for inducing GC's to recover -// Metaspaces. - -class MetaspaceGC : AllStatic { +class MetaspaceGC : public AllStatic { // The current high-water-mark for inducing a GC. // When committed memory of all metaspaces reaches this value, @@ -477,4 +217,50 @@ class MetaspaceGC : AllStatic { static void compute_new_size(); }; +class MetaspaceUtils : AllStatic { +public: + + // Committed space actually in use by Metadata + static size_t used_words(); + static size_t used_words(Metaspace::MetadataType mdtype); + + // Space committed for Metaspace + static size_t committed_words(); + static size_t committed_words(Metaspace::MetadataType mdtype); + + // Space reserved for Metaspace + static size_t reserved_words(); + static size_t reserved_words(Metaspace::MetadataType mdtype); + + // _bytes() variants for convenience... + static size_t used_bytes() { return used_words() * BytesPerWord; } + static size_t used_bytes(Metaspace::MetadataType mdtype) { return used_words(mdtype) * BytesPerWord; } + static size_t committed_bytes() { return committed_words() * BytesPerWord; } + static size_t committed_bytes(Metaspace::MetadataType mdtype) { return committed_words(mdtype) * BytesPerWord; } + static size_t reserved_bytes() { return reserved_words() * BytesPerWord; } + static size_t reserved_bytes(Metaspace::MetadataType mdtype) { return reserved_words(mdtype) * BytesPerWord; } + + // (See JDK-8251342). Implement or Consolidate. + static MetaspaceChunkFreeListSummary chunk_free_list_summary(Metaspace::MetadataType mdtype) { + return MetaspaceChunkFreeListSummary(0,0,0,0,0,0,0,0); + } + + // Log change in used metadata. + static void print_metaspace_change(const metaspace::MetaspaceSizesSnapshot& pre_meta_values); + + // This will print out a basic metaspace usage report but + // unlike print_report() is guaranteed not to lock or to walk the CLDG. + static void print_basic_report(outputStream* st, size_t scale = 0); + + // Prints a report about the current metaspace state. + // Function will walk the CLDG and will lock the expand lock; if that is not + // convenient, use print_basic_report() instead. + static void print_report(outputStream* out, size_t scale = 0); + + static void print_on(outputStream * out); + + DEBUG_ONLY(static void verify();) + +}; + #endif // SHARE_MEMORY_METASPACE_HPP diff --git a/src/hotspot/share/memory/metaspace/allocationGuard.hpp b/src/hotspot/share/memory/metaspace/allocationGuard.hpp new file mode 100644 index 00000000000..0125c23ed61 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/allocationGuard.hpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_ALLOCATIONGUARD_HPP +#define SHARE_MEMORY_METASPACE_ALLOCATIONGUARD_HPP + +#include "memory/allocation.hpp" +#include "memory/metaspace/chunklevel.hpp" +#include "utilities/globalDefinitions.hpp" + +// In Debug builds, Metadata in Metaspace can be optionally guarded - enclosed in canaries - +// to detect memory overwriters. +// +// These canaries are periodically checked, e.g. when the Metaspace is purged in a context +// of a GC. + +// The canaries precede any allocated block... +// +// +---------------+ +// | 'METAMETA' | +// +---------------+ +// | block size | +// +---------------+ +// | block... | +// . . +// . . +// . . +// | | +// +---------------+ +// . . +// +---------------+ +// | 'METAMETA' | +// +---------------+ +// | block size | +// +---------------+ +// | block... | + +// ... and since the blocks are allocated via pointer bump and closely follow each other, +// one block's prefix is its predecessor's suffix, so apart from the last block all +// blocks have an overwriter canary on both ends. +// + +// Note: this feature is only available in debug, and is activated using +// -XX:+MetaspaceGuardAllocations. When active, it disables deallocation handling - since +// freeblock handling in the freeblock lists would get too complex - so one may run leaks +// in deallocation-heavy scenarios (e.g. lots of class redefinitions). +// + +namespace metaspace { + +#ifdef ASSERT + +struct Prefix { + static const uintx EyeCatcher = + NOT_LP64(0x77698465) LP64_ONLY(0x7769846577698465ULL); // "META" resp "METAMETA" + + const uintx _mark; + const size_t _word_size; // raw word size including prefix + // MetaWord payload [0]; // varsized (but unfortunately not all our compilers understand that) + + Prefix(size_t word_size) : + _mark(EyeCatcher), + _word_size(word_size) + {} + + MetaWord* payload() const { + return (MetaWord*)(this + 1); + } + + bool is_valid() const { + return _mark == EyeCatcher && _word_size > 0 && _word_size < chunklevel::MAX_CHUNK_WORD_SIZE; + } + +}; + +// The prefix structure must be aligned to MetaWord size. +STATIC_ASSERT((sizeof(Prefix) & WordAlignmentMask) == 0); + +inline size_t prefix_size() { + return sizeof(Prefix); +} + +// Given a pointer to a memory area, establish the prefix at the start of that area and +// return the starting pointer to the payload. +inline MetaWord* establish_prefix(MetaWord* p_raw, size_t raw_word_size) { + const Prefix* pp = new(p_raw)Prefix(raw_word_size); + return pp->payload(); +} + +#endif + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_ALLOCATIONGUARD_HPP diff --git a/src/hotspot/share/memory/metaspace/binList.hpp b/src/hotspot/share/memory/metaspace/binList.hpp new file mode 100644 index 00000000000..4e8c665ecf1 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/binList.hpp @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_BINLIST_HPP +#define SHARE_MEMORY_METASPACE_BINLIST_HPP + +#include "memory/metaspace/counters.hpp" +#include "memory/metaspace/metaspaceCommon.hpp" +#include "utilities/align.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" + +namespace metaspace { + +// BinList is a data structure to manage small to very small memory blocks +// (only a few words). It is used to manage deallocated blocks - see +// class FreeBlocks. + +// Memory blocks are kept in linked lists. Each list +// contains blocks of only one size. There is a list for blocks of two words, +// for blocks of three words, etc. The list heads are kept in a vector, +// ordered by block size. +// + +// wordsize +// +// +---+ +---+ +---+ +---+ +// 1 | |-->| |-->| |-...->| | +// +---+ +---+ +---+ +---+ +// +// +----+ +----+ +----+ +----+ +// 2 | |-->| |-->| |-...->| | +// +----+ +----+ +----+ +----+ +// +// +-----+ +-----+ +-----+ +-----+ +// 3 | |-->| |-->| |-...->| | +// +-----+ +-----+ +-----+ +-----+ +// . +// . +// . +// +// +----------+ +----------+ +----------+ +----------+ +// n | |-->| |-->| |-...->| | +// +----------+ +----------+ +----------+ +----------+ + +// Insertion is of course fast, O(1). +// +// On retrieval, we attempt to find the closest fit to a given size, walking the +// list head vector (a bitmask is used to speed that part up). +// +// This structure is a bit expensive in memory costs (we pay one pointer per managed +// block size) so we only use it for a small number of sizes. + +template +class BinListImpl { + + struct Block { + Block* const _next; + const size_t _word_size; + Block(Block* next, size_t word_size) : + _next(next), + _word_size(word_size) + {} + }; + + // Smallest block size must be large enough to hold a Block structure. + STATIC_ASSERT(smallest_word_size * sizeof(MetaWord) >= sizeof(Block)); + STATIC_ASSERT(num_lists > 0); + +public: + + // Minimal word size a block must have to be manageable by this structure. + const static size_t MinWordSize = smallest_word_size; + + // Maximal (incl) word size a block can have to be manageable by this structure. + const static size_t MaxWordSize = MinWordSize + num_lists - 1; + +private: + + Block* _blocks[num_lists]; + + MemRangeCounter _counter; + + static int index_for_word_size(size_t word_size) { + int index = (int)(word_size - MinWordSize); + assert(index >= 0 && index < num_lists, "Invalid index %d", index); + return index; + } + + static size_t word_size_for_index(int index) { + assert(index >= 0 && index < num_lists, "Invalid index %d", index); + return MinWordSize + index; + } + + // Search the range [index, _num_lists) for the smallest non-empty list. Returns -1 on fail. + int index_for_next_non_empty_list(int index) { + assert(index >= 0 && index < num_lists, "Invalid index %d", index); + int i2 = index; + while (i2 < num_lists && _blocks[i2] == NULL) { + i2 ++; + } + return i2 == num_lists ? -1 : i2; + } + +public: + + BinListImpl() { + for (int i = 0; i < num_lists; i++) { + _blocks[i] = NULL; + } + } + + void add_block(MetaWord* p, size_t word_size) { + assert(word_size >= MinWordSize && + word_size <= MaxWordSize, "bad block size"); + const int index = index_for_word_size(word_size); + Block* old_head = _blocks[index]; + Block* new_head = new(p)Block(old_head, word_size); + _blocks[index] = new_head; + _counter.add(word_size); + } + + // Given a word_size, searches and returns a block of at least that size. + // Block may be larger. Real block size is returned in *p_real_word_size. + MetaWord* remove_block(size_t word_size, size_t* p_real_word_size) { + assert(word_size >= MinWordSize && + word_size <= MaxWordSize, "bad block size " SIZE_FORMAT ".", word_size); + int index = index_for_word_size(word_size); + index = index_for_next_non_empty_list(index); + if (index != -1) { + assert(_blocks[index] != NULL && + _blocks[index]->_word_size >= word_size, "sanity"); + + MetaWord* const p = (MetaWord*)_blocks[index]; + const size_t real_word_size = word_size_for_index(index); + + _blocks[index] = _blocks[index]->_next; + + _counter.sub(real_word_size); + *p_real_word_size = real_word_size; + + return p; + + } else { + *p_real_word_size = 0; + return NULL; + } + } + + // Returns number of blocks in this structure + unsigned count() const { return _counter.count(); } + + // Returns total size, in words, of all elements. + size_t total_size() const { return _counter.total_size(); } + + bool is_empty() const { return count() == 0; } + +#ifdef ASSERT + void verify() const { + MemRangeCounter local_counter; + for (int i = 0; i < num_lists; i++) { + const size_t s = MinWordSize + i; + for (Block* b = _blocks[i]; b != NULL; b = b->_next) { + assert(b->_word_size == s, "bad block size"); + local_counter.add(s); + } + } + local_counter.check(_counter); + } +#endif // ASSERT + +}; + +typedef BinListImpl<2, 32> BinList32; + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_BINLIST_HPP diff --git a/src/hotspot/share/memory/metaspace/blockFreelist.cpp b/src/hotspot/share/memory/metaspace/blockFreelist.cpp deleted file mode 100644 index 475a1079220..00000000000 --- a/src/hotspot/share/memory/metaspace/blockFreelist.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ -#include "precompiled.hpp" - -#include "logging/log.hpp" -#include "memory/binaryTreeDictionary.inline.hpp" -#include "memory/metaspace/blockFreelist.hpp" -#include "utilities/ostream.hpp" -#include "utilities/globalDefinitions.hpp" - - -namespace metaspace { - - -BlockFreelist::BlockFreelist() : _dictionary(new BlockTreeDictionary()), _small_blocks(NULL) {} - -BlockFreelist::~BlockFreelist() { - delete _dictionary; - if (_small_blocks != NULL) { - delete _small_blocks; - } -} - -void BlockFreelist::return_block(MetaWord* p, size_t word_size) { - assert(word_size >= SmallBlocks::small_block_min_size(), "never return dark matter"); - - Metablock* free_chunk = ::new (p) Metablock(word_size); - if (word_size < SmallBlocks::small_block_max_size()) { - small_blocks()->return_block(free_chunk, word_size); - } else { - dictionary()->return_chunk(free_chunk); -} - log_trace(gc, metaspace, freelist, blocks)("returning block at " INTPTR_FORMAT " size = " - SIZE_FORMAT, p2i(free_chunk), word_size); -} - -MetaWord* BlockFreelist::get_block(size_t word_size) { - assert(word_size >= SmallBlocks::small_block_min_size(), "never get dark matter"); - - // Try small_blocks first. - if (word_size < SmallBlocks::small_block_max_size()) { - // Don't create small_blocks() until needed. small_blocks() allocates the small block list for - // this space manager. - MetaWord* new_block = (MetaWord*) small_blocks()->get_block(word_size); - if (new_block != NULL) { - log_trace(gc, metaspace, freelist, blocks)("getting block at " INTPTR_FORMAT " size = " SIZE_FORMAT, - p2i(new_block), word_size); - return new_block; - } - } - - if (word_size < BlockFreelist::min_dictionary_size()) { - // If allocation in small blocks fails, this is Dark Matter. Too small for dictionary. - return NULL; - } - - Metablock* free_block = dictionary()->get_chunk(word_size); - if (free_block == NULL) { - return NULL; - } - - const size_t block_size = free_block->size(); - if (block_size > WasteMultiplier * word_size) { - return_block((MetaWord*)free_block, block_size); - return NULL; - } - - MetaWord* new_block = (MetaWord*)free_block; - assert(block_size >= word_size, "Incorrect size of block from freelist"); - const size_t unused = block_size - word_size; - if (unused >= SmallBlocks::small_block_min_size()) { - return_block(new_block + word_size, unused); - } - - log_trace(gc, metaspace, freelist, blocks)("getting block at " INTPTR_FORMAT " size = " SIZE_FORMAT, - p2i(new_block), word_size); - return new_block; -} - -void BlockFreelist::print_on(outputStream* st) const { - dictionary()->print_free_lists(st); - if (_small_blocks != NULL) { - _small_blocks->print_on(st); - } -} - -} // namespace metaspace - diff --git a/src/hotspot/share/memory/metaspace/blockFreelist.hpp b/src/hotspot/share/memory/metaspace/blockFreelist.hpp deleted file mode 100644 index db57b6d3a78..00000000000 --- a/src/hotspot/share/memory/metaspace/blockFreelist.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_MEMORY_METASPACE_BLOCKFREELIST_HPP -#define SHARE_MEMORY_METASPACE_BLOCKFREELIST_HPP - -#include "memory/allocation.hpp" -#include "memory/binaryTreeDictionary.hpp" -#include "memory/freeList.hpp" -#include "memory/metaspace/smallBlocks.hpp" -#include "memory/metaspace/metablock.hpp" -#include "utilities/globalDefinitions.hpp" - -namespace metaspace { - -typedef BinaryTreeDictionary > BlockTreeDictionary; - -// Used to manage the free list of Metablocks (a block corresponds -// to the allocation of a quantum of metadata). -class BlockFreelist : public CHeapObj { - - BlockTreeDictionary* const _dictionary; - SmallBlocks* _small_blocks; - - // Only allocate and split from freelist if the size of the allocation - // is at least 1/4th the size of the available block. - const static int WasteMultiplier = 4; - - // Accessors - BlockTreeDictionary* dictionary() const { return _dictionary; } - SmallBlocks* small_blocks() { - if (_small_blocks == NULL) { - _small_blocks = new SmallBlocks(); - } - return _small_blocks; - } - - public: - - BlockFreelist(); - ~BlockFreelist(); - - // Get and return a block to the free list - MetaWord* get_block(size_t word_size); - void return_block(MetaWord* p, size_t word_size); - - // Returns the total size, in words, of all blocks kept in this structure. - size_t total_size() const { - size_t result = dictionary()->total_size(); - if (_small_blocks != NULL) { - result = result + _small_blocks->total_size(); - } - return result; - } - - // Returns the number of all blocks kept in this structure. - uintx num_blocks() const { - uintx result = dictionary()->total_free_blocks(); - if (_small_blocks != NULL) { - result = result + _small_blocks->total_num_blocks(); - } - return result; - } - - static size_t min_dictionary_size() { return TreeChunk >::min_size(); } - void print_on(outputStream* st) const; -}; - -} // namespace metaspace - -#endif // SHARE_MEMORY_METASPACE_BLOCKFREELIST_HPP diff --git a/src/hotspot/share/memory/metaspace/blockTree.cpp b/src/hotspot/share/memory/metaspace/blockTree.cpp new file mode 100644 index 00000000000..4459945c5ff --- /dev/null +++ b/src/hotspot/share/memory/metaspace/blockTree.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "memory/metaspace/blockTree.hpp" +#include "memory/resourceArea.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/growableArray.hpp" +#include "utilities/ostream.hpp" + +namespace metaspace { + +// Needed to prevent linker errors on MacOS and AIX +const size_t BlockTree::MinWordSize; + +#ifdef ASSERT + +// Tree verification + +// These asserts prints the tree, then asserts +#define assrt(cond, format, ...) \ + do { \ + if (!(cond)) { \ + print_tree(tty); \ + assert(cond, format, __VA_ARGS__); \ + } \ + } while (0) + + // This assert prints the tree, then stops (generic message) +#define assrt0(cond) \ + do { \ + if (!(cond)) { \ + print_tree(tty); \ + assert(cond, "sanity"); \ + } \ + } while (0) + +// walkinfo keeps a node plus the size corridor it and its children +// are supposed to be in. +struct BlockTree::walkinfo { + BlockTree::Node* n; + int depth; + size_t lim1; // ( + size_t lim2; // ) +}; + +void BlockTree::verify() const { + // Traverse the tree and test that all nodes are in the correct order. + + MemRangeCounter counter; + int longest_edge = 0; + + if (_root != NULL) { + + ResourceMark rm; + GrowableArray stack; + + walkinfo info; + info.n = _root; + info.lim1 = 0; + info.lim2 = SIZE_MAX; + info.depth = 0; + + stack.push(info); + + while (stack.length() > 0) { + info = stack.pop(); + const Node* n = info.n; + + // Assume a (ridiculously large) edge limit to catch cases + // of badly degenerated or circular trees. + assrt0(info.depth < 10000); + counter.add(n->_word_size); + + // Verify node. + if (n == _root) { + assrt0(n->_parent == NULL); + } else { + assrt0(n->_parent != NULL); + } + + // check size and ordering + assrt(n->_word_size >= MinWordSize, "bad node size " SIZE_FORMAT, n->_word_size); + assrt0(n->_word_size > info.lim1); + assrt0(n->_word_size < info.lim2); + + // Check children + if (n->_left != NULL) { + assrt0(n->_left != n); + assrt0(n->_left->_parent == n); + + walkinfo info2; + info2.n = n->_left; + info2.lim1 = info.lim1; + info2.lim2 = n->_word_size; + info2.depth = info.depth + 1; + stack.push(info2); + } + + if (n->_right != NULL) { + assrt0(n->_right != n); + assrt0(n->_right->_parent == n); + + walkinfo info2; + info2.n = n->_right; + info2.lim1 = n->_word_size; + info2.lim2 = info.lim2; + info2.depth = info.depth + 1; + stack.push(info2); + } + + // If node has same-sized siblings check those too. + const Node* n2 = n->_next; + while (n2 != NULL) { + assrt0(n2 != n); + assrt0(n2->_word_size == n->_word_size); + counter.add(n2->_word_size); + n2 = n2->_next; + } + } + } + + // At the end, check that counters match + _counter.check(counter); +} + +void BlockTree::zap_range(MetaWord* p, size_t word_size) { + memset(p, 0xF3, word_size * sizeof(MetaWord)); +} + +#undef assrt +#undef assrt0 + +void BlockTree::print_tree(outputStream* st) const { + if (_root != NULL) { + + ResourceMark rm; + GrowableArray stack; + + walkinfo info; + info.n = _root; + info.depth = 0; + + stack.push(info); + while (stack.length() > 0) { + info = stack.pop(); + const Node* n = info.n; + // Print node. + for (int i = 0; i < info.depth; i++) { + st->print("---"); + } + st->print_cr("<" PTR_FORMAT " (size " SIZE_FORMAT ")", p2i(n), n->_word_size); + // Handle children. + if (n->_right != NULL) { + walkinfo info2; + info2.n = n->_right; + info2.depth = info.depth + 1; + stack.push(info2); + } + if (n->_left != NULL) { + walkinfo info2; + info2.n = n->_left; + info2.depth = info.depth + 1; + stack.push(info2); + } + } + + } else { + st->print_cr(""); + } +} + +#endif // ASSERT + +} // namespace metaspace diff --git a/src/hotspot/share/memory/metaspace/blockTree.hpp b/src/hotspot/share/memory/metaspace/blockTree.hpp new file mode 100644 index 00000000000..e95b6779a9d --- /dev/null +++ b/src/hotspot/share/memory/metaspace/blockTree.hpp @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_BLOCKTREE_HPP +#define SHARE_MEMORY_METASPACE_BLOCKTREE_HPP + +#include "memory/allocation.hpp" +#include "memory/metaspace/chunklevel.hpp" +#include "memory/metaspace/counters.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" + +namespace metaspace { + +// BlockTree is a rather simple binary search tree. It is used to +// manage small to medium free memory blocks (see class FreeBlocks). +// +// There is no separation between payload (managed blocks) and nodes: the +// memory blocks themselves are the nodes, with the block size being the key. +// +// We store node pointer information in these blocks when storing them. That +// imposes a minimum size to the managed memory blocks. +// See get_raw_word_size_for_requested_word_size() (msCommon.hpp). +// +// We want to manage many memory blocks of the same size, but we want +// to prevent the tree from blowing up and degenerating into a list. Therefore +// there is only one node for each unique block size; subsequent blocks of the +// same size are stacked below that first node: +// +// +-----+ +// | 100 | +// +-----+ +// / \ +// +-----+ +// | 80 | +// +-----+ +// / | \ +// / +-----+ \ +// +-----+ | 80 | +-----+ +// | 70 | +-----+ | 85 | +// +-----+ | +-----+ +// +-----+ +// | 80 | +// +-----+ +// +// +// Todo: This tree is unbalanced. It would be a good fit for a red-black tree. +// In order to make this a red-black tree, we need an algorithm which can deal +// with nodes which are their own payload (most red-black tree implementations +// swap payloads of their nodes at some point, see e.g. j.u.TreeSet). +// A good example is the Linux kernel rbtree, which is a clean, easy-to-read +// implementation. + +class BlockTree: public CHeapObj { + + struct Node { + + // Normal tree node stuff... + Node* _parent; + Node* _left; + Node* _right; + + // Blocks with the same size are put in a list with this node as head. + Node* _next; + + // Word size of node. Note that size cannot be larger than max metaspace size, + // so this could be very well a 32bit value (in case we ever make this a balancing + // tree and need additional space for weighting information). + const size_t _word_size; + + Node(size_t word_size) : + _parent(NULL), + _left(NULL), + _right(NULL), + _next(NULL), + _word_size(word_size) + {} + + }; + + // Needed for verify() and print_tree() + struct walkinfo; + +public: + + // Minimum word size a block has to be to be added to this structure (note ceil division). + const static size_t MinWordSize = + (sizeof(Node) + sizeof(MetaWord) - 1) / sizeof(MetaWord); + +private: + + Node* _root; + + MemRangeCounter _counter; + + // Given a node n, add it to the list starting at head + static void add_to_list(Node* n, Node* head) { + assert(head->_word_size == n->_word_size, "sanity"); + n->_next = head->_next; + head->_next = n; + DEBUG_ONLY(n->_left = n->_right = n->_parent = NULL;) + } + + // Given a node list starting at head, remove one of the follow up nodes from + // that list and return it. The head node gets not modified and remains in the + // tree. + // List must contain at least one other node. + static Node* remove_from_list(Node* head) { + assert(head->_next != NULL, "sanity"); + Node* n = head->_next; + head->_next = n->_next; + return n; + } + + // Given a node c and a node p, wire up c as left child of p. + static void set_left_child(Node* p, Node* c) { + p->_left = c; + if (c != NULL) { + assert(c->_word_size < p->_word_size, "sanity"); + c->_parent = p; + } + } + + // Given a node c and a node p, wire up c as right child of p. + static void set_right_child(Node* p, Node* c) { + p->_right = c; + if (c != NULL) { + assert(c->_word_size > p->_word_size, "sanity"); + c->_parent = p; + } + } + + // Given a node n, return its successor in the tree + // (node with the next-larger size). + static Node* successor(Node* n) { + Node* succ = NULL; + if (n->_right != NULL) { + // If there is a right child, search the left-most + // child of that child. + succ = n->_right; + while (succ->_left != NULL) { + succ = succ->_left; + } + } else { + succ = n->_parent; + Node* n2 = n; + // As long as I am the right child of my parent, search upward + while (succ != NULL && n2 == succ->_right) { + n2 = succ; + succ = succ->_parent; + } + } + return succ; + } + + // Given a node, replace it with a replacement node as a child for its parent. + // If the node is root and has no parent, sets it as root. + void replace_node_in_parent(Node* child, Node* replace) { + Node* parent = child->_parent; + if (parent != NULL) { + if (parent->_left == child) { // Child is left child + set_left_child(parent, replace); + } else { + set_right_child(parent, replace); + } + } else { + assert(child == _root, "must be root"); + _root = replace; + if (replace != NULL) { + replace->_parent = NULL; + } + } + return; + } + + // Given a node n and an insertion point, insert n under insertion point. + static void insert(Node* insertion_point, Node* n) { + assert(n->_parent == NULL, "Sanity"); + for (;;) { + if (n->_word_size == insertion_point->_word_size) { + add_to_list(n, insertion_point); // parent stays NULL in this case. + break; + } else if (n->_word_size > insertion_point->_word_size) { + if (insertion_point->_right == NULL) { + set_right_child(insertion_point, n); + break; + } else { + insertion_point = insertion_point->_right; + } + } else { + if (insertion_point->_left == NULL) { + set_left_child(insertion_point, n); + break; + } else { + insertion_point = insertion_point->_left; + } + } + } + } + + // Given a node and a wish size, search this node and all children for + // the node closest (equal or larger sized) to the size s. + static Node* find_closest_fit(Node* n, size_t s) { + Node* best_match = NULL; + while (n != NULL) { + if (n->_word_size >= s) { + best_match = n; + if (n->_word_size == s) { + break; // perfect match or max depth reached + } + n = n->_left; + } else { + n = n->_right; + } + } + return best_match; + } + + // Given a wish size, search the whole tree for a + // node closest (equal or larger sized) to the size s. + Node* find_closest_fit(size_t s) { + if (_root != NULL) { + return find_closest_fit(_root, s); + } + return NULL; + } + + // Given a node n, remove it from the tree and repair tree. + void remove_node_from_tree(Node* n) { + assert(n->_next == NULL, "do not delete a node which has a non-empty list"); + + if (n->_left == NULL && n->_right == NULL) { + replace_node_in_parent(n, NULL); + + } else if (n->_left == NULL && n->_right != NULL) { + replace_node_in_parent(n, n->_right); + + } else if (n->_left != NULL && n->_right == NULL) { + replace_node_in_parent(n, n->_left); + + } else { + // Node has two children. + + // 1) Find direct successor (the next larger node). + Node* succ = successor(n); + + // There has to be a successor since n->right was != NULL... + assert(succ != NULL, "must be"); + + // ... and it should not have a left child since successor + // is supposed to be the next larger node, so it must be the mostleft node + // in the sub tree rooted at n->right + assert(succ->_left == NULL, "must be"); + assert(succ->_word_size > n->_word_size, "sanity"); + + Node* successor_parent = succ->_parent; + Node* successor_right_child = succ->_right; + + // Remove successor from its parent. + if (successor_parent == n) { + + // special case: successor is a direct child of n. Has to be the right child then. + assert(n->_right == succ, "sanity"); + + // Just replace n with this successor. + replace_node_in_parent(n, succ); + + // Take over n's old left child, too. + // We keep the successor's right child. + set_left_child(succ, n->_left); + } else { + // If the successors parent is not n, we are deeper in the tree, + // the successor has to be the left child of its parent. + assert(successor_parent->_left == succ, "sanity"); + + // The right child of the successor (if there was one) replaces + // the successor at its parent's left child. + set_left_child(successor_parent, succ->_right); + + // and the successor replaces n at its parent + replace_node_in_parent(n, succ); + + // and takes over n's old children + set_left_child(succ, n->_left); + set_right_child(succ, n->_right); + } + } + } + +#ifdef ASSERT + void zap_range(MetaWord* p, size_t word_size); +#endif // ASSERT + +public: + + BlockTree() : _root(NULL) {} + + // Add a memory block to the tree. Its content will be overwritten. + void add_block(MetaWord* p, size_t word_size) { + DEBUG_ONLY(zap_range(p, word_size)); + assert(word_size >= MinWordSize, "invalid block size " SIZE_FORMAT, word_size); + Node* n = new(p) Node(word_size); + if (_root == NULL) { + _root = n; + } else { + insert(_root, n); + } + _counter.add(word_size); + } + + // Given a word_size, search and return the smallest block that is equal or + // larger than that size. Upon return, *p_real_word_size contains the actual + // block size. + MetaWord* remove_block(size_t word_size, size_t* p_real_word_size) { + assert(word_size >= MinWordSize, "invalid block size " SIZE_FORMAT, word_size); + + Node* n = find_closest_fit(word_size); + + if (n != NULL) { + assert(n->_word_size >= word_size, "sanity"); + + if (n->_next != NULL) { + // If the node is head of a chain of same sized nodes, we leave it alone + // and instead remove one of the follow up nodes (which is simpler than + // removing the chain head node and then having to graft the follow up + // node into its place in the tree). + n = remove_from_list(n); + } else { + remove_node_from_tree(n); + } + + MetaWord* p = (MetaWord*)n; + *p_real_word_size = n->_word_size; + + _counter.sub(n->_word_size); + + DEBUG_ONLY(zap_range(p, n->_word_size)); + return p; + } + return NULL; + } + + // Returns number of blocks in this structure + unsigned count() const { return _counter.count(); } + + // Returns total size, in words, of all elements. + size_t total_size() const { return _counter.total_size(); } + + bool is_empty() const { return _root == NULL; } + + DEBUG_ONLY(void print_tree(outputStream* st) const;) + DEBUG_ONLY(void verify() const;) +}; + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_BLOCKTREE_HPP diff --git a/src/hotspot/share/memory/metaspace/chunkHeaderPool.cpp b/src/hotspot/share/memory/metaspace/chunkHeaderPool.cpp new file mode 100644 index 00000000000..cbf7971ec2a --- /dev/null +++ b/src/hotspot/share/memory/metaspace/chunkHeaderPool.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "memory/metaspace/chunkHeaderPool.hpp" +#include "runtime/os.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" + +namespace metaspace { + +// Returns reference to the one global chunk header pool. +ChunkHeaderPool* ChunkHeaderPool::_chunkHeaderPool = NULL; + +ChunkHeaderPool::ChunkHeaderPool() : + _num_slabs(), + _first_slab(NULL), + _current_slab(NULL) +{} + +// Note: the global chunk header pool gets never deleted; so this destructor only +// exists for the sake of tests. +ChunkHeaderPool::~ChunkHeaderPool() { + Slab* s = _first_slab; + while (s != NULL) { + Slab* next_slab = s->_next; + os::free(s); + s = next_slab; + } +} + +void ChunkHeaderPool::allocate_new_slab() { + Slab* slab = new Slab(); + if (_current_slab != NULL) { + _current_slab->_next = slab; + } + _current_slab = slab; + if (_first_slab == NULL) { + _first_slab = slab; + } + _num_slabs.increment(); +} + +// Returns size of memory used. +size_t ChunkHeaderPool::memory_footprint_words() const { + return (_num_slabs.get() * sizeof(Slab)) / BytesPerWord; +} + +void ChunkHeaderPool::initialize() { + assert(_chunkHeaderPool == NULL, "only once"); + _chunkHeaderPool = new ChunkHeaderPool(); +} + +#ifdef ASSERT +void ChunkHeaderPool::verify() const { + const Slab* s = _first_slab; + int num = 0; + while (s != NULL) { + assert(s->_top >= 0 && s->_top <= SlabCapacity, + "invalid slab at " PTR_FORMAT ", top: %d, slab cap: %d", + p2i(s), s->_top, SlabCapacity ); + s = s->_next; + num++; + } + _num_slabs.check(num); +} +#endif + +} // namespace metaspace + diff --git a/src/hotspot/share/memory/metaspace/chunkHeaderPool.hpp b/src/hotspot/share/memory/metaspace/chunkHeaderPool.hpp new file mode 100644 index 00000000000..59c82143afa --- /dev/null +++ b/src/hotspot/share/memory/metaspace/chunkHeaderPool.hpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_CHUNKHEADERPOOL_HPP +#define SHARE_MEMORY_METASPACE_CHUNKHEADERPOOL_HPP + +#include "memory/allocation.hpp" +#include "memory/metaspace/counters.hpp" +#include "memory/metaspace/metachunk.hpp" +#include "memory/metaspace/metachunkList.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" + +namespace metaspace { + +// Chunk headers (Metachunk objects) are separate entities from their payload. +// Since they are allocated and released frequently in the course of buddy allocation +// (splitting, merging chunks happens often) we want allocation of them fast. Therefore +// we keep them in a simple pool (somewhat like a primitive slab allocator). + +class ChunkHeaderPool : public CHeapObj { + + static const int SlabCapacity = 128; + + struct Slab : public CHeapObj { + Slab* _next; + int _top; + Metachunk _elems [SlabCapacity]; + Slab() : _next(NULL), _top(0) { + for (int i = 0; i < SlabCapacity; i++) { + _elems[i].clear(); + } + } + }; + + IntCounter _num_slabs; + Slab* _first_slab; + Slab* _current_slab; + + IntCounter _num_handed_out; + + MetachunkList _freelist; + + void allocate_new_slab(); + + static ChunkHeaderPool* _chunkHeaderPool; + +public: + + ChunkHeaderPool(); + + ~ChunkHeaderPool(); + + // Allocates a Metachunk structure. The structure is uninitialized. + Metachunk* allocate_chunk_header() { + DEBUG_ONLY(verify()); + + Metachunk* c = NULL; + c = _freelist.remove_first(); + assert(c == NULL || c->is_dead(), "Not a freelist chunk header?"); + if (c == NULL) { + if (_current_slab == NULL || + _current_slab->_top == SlabCapacity) { + allocate_new_slab(); + assert(_current_slab->_top < SlabCapacity, "Sanity"); + } + c = _current_slab->_elems + _current_slab->_top; + _current_slab->_top++; + } + _num_handed_out.increment(); + // By contract, the returned structure is uninitialized. + // Zap to make this clear. + DEBUG_ONLY(c->zap_header(0xBB);) + + return c; + } + + void return_chunk_header(Metachunk* c) { + // We only ever should return free chunks, since returning chunks + // happens only on merging and merging only works with free chunks. + assert(c != NULL && c->is_free(), "Sanity"); +#ifdef ASSERT + // In debug, fill dead header with pattern. + c->zap_header(0xCC); + c->set_next(NULL); + c->set_prev(NULL); +#endif + c->set_dead(); + _freelist.add(c); + _num_handed_out.decrement(); + } + + // Returns number of allocated elements. + int used() const { return _num_handed_out.get(); } + + // Returns number of elements in free list. + int freelist_size() const { return _freelist.count(); } + + // Returns size of memory used. + size_t memory_footprint_words() const; + + DEBUG_ONLY(void verify() const;) + + static void initialize(); + + // Returns reference to the one global chunk header pool. + static ChunkHeaderPool* pool() { return _chunkHeaderPool; } + +}; + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_CHUNKHEADERPOOL_HPP diff --git a/src/hotspot/share/memory/metaspace/chunkManager.cpp b/src/hotspot/share/memory/metaspace/chunkManager.cpp index 72315584cf4..7be0a2e2626 100644 --- a/src/hotspot/share/memory/metaspace/chunkManager.cpp +++ b/src/hotspot/share/memory/metaspace/chunkManager.cpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,622 +22,405 @@ * questions. * */ -#include "precompiled.hpp" +#include "precompiled.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" -#include "memory/binaryTreeDictionary.inline.hpp" -#include "memory/freeList.inline.hpp" #include "memory/metaspace/chunkManager.hpp" +#include "memory/metaspace/internalStats.hpp" #include "memory/metaspace/metachunk.hpp" -#include "memory/metaspace/metaDebug.hpp" +#include "memory/metaspace/metaspaceArenaGrowthPolicy.hpp" #include "memory/metaspace/metaspaceCommon.hpp" +#include "memory/metaspace/metaspaceContext.hpp" +#include "memory/metaspace/metaspaceSettings.hpp" #include "memory/metaspace/metaspaceStatistics.hpp" -#include "memory/metaspace/occupancyMap.hpp" +#include "memory/metaspace/virtualSpaceList.hpp" #include "memory/metaspace/virtualSpaceNode.hpp" #include "runtime/mutexLocker.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/ostream.hpp" namespace metaspace { -ChunkManager::ChunkManager(bool is_class) - : _is_class(is_class), _free_chunks_total(0), _free_chunks_count(0) { - _free_chunks[SpecializedIndex].set_size(get_size_for_nonhumongous_chunktype(SpecializedIndex, is_class)); - _free_chunks[SmallIndex].set_size(get_size_for_nonhumongous_chunktype(SmallIndex, is_class)); - _free_chunks[MediumIndex].set_size(get_size_for_nonhumongous_chunktype(MediumIndex, is_class)); -} +#define LOGFMT "ChkMgr @" PTR_FORMAT " (%s)" +#define LOGFMT_ARGS p2i(this), this->_name -void ChunkManager::remove_chunk(Metachunk* chunk) { - size_t word_size = chunk->word_size(); - ChunkIndex index = list_index(word_size); - if (index != HumongousIndex) { - free_chunks(index)->remove_chunk(chunk); - } else { - humongous_dictionary()->remove_chunk(chunk); - } +// Return a single chunk to the freelist and adjust accounting. No merge is attempted. +void ChunkManager::return_chunk_simple_locked(Metachunk* c) { + assert_lock_strong(MetaspaceExpand_lock); + DEBUG_ONLY(c->verify()); + _chunks.add(c); + c->reset_used_words(); + // Tracing + log_debug(metaspace)("ChunkManager %s: returned chunk " METACHUNK_FORMAT ".", + _name, METACHUNK_FORMAT_ARGS(c)); +} - // Chunk has been removed from the chunks free list, update counters. - account_for_removed_chunk(chunk); +// Creates a chunk manager with a given name (which is for debug purposes only) +// and an associated space list which will be used to request new chunks from +// (see get_chunk()) +ChunkManager::ChunkManager(const char* name, VirtualSpaceList* space_list) : + _vslist(space_list), + _name(name), + _chunks() +{ } -bool ChunkManager::attempt_to_coalesce_around_chunk(Metachunk* chunk, ChunkIndex target_chunk_type) { +// Given a chunk, split it into a target chunk of a smaller size (higher target level) +// and at least one, possible several splinter chunks. +// The original chunk must be outside of the freelist and its state must be free. +// The splinter chunks are added to the freelist. +// The resulting target chunk will be located at the same address as the original +// chunk, but it will of course be smaller (of a higher level). +// The committed areas within the original chunk carry over to the resulting +// chunks. +void ChunkManager::split_chunk_and_add_splinters(Metachunk* c, chunklevel_t target_level) { assert_lock_strong(MetaspaceExpand_lock); - assert(chunk != NULL, "invalid chunk pointer"); - // Check for valid merge combinations. - assert((chunk->get_chunk_type() == SpecializedIndex && - (target_chunk_type == SmallIndex || target_chunk_type == MediumIndex)) || - (chunk->get_chunk_type() == SmallIndex && target_chunk_type == MediumIndex), - "Invalid chunk merge combination."); - - const size_t target_chunk_word_size = - get_size_for_nonhumongous_chunktype(target_chunk_type, this->is_class()); - - // [ prospective merge region ) - MetaWord* const p_merge_region_start = - (MetaWord*) align_down(chunk, target_chunk_word_size * sizeof(MetaWord)); - MetaWord* const p_merge_region_end = - p_merge_region_start + target_chunk_word_size; - - // We need the VirtualSpaceNode containing this chunk and its occupancy map. - VirtualSpaceNode* const vsn = chunk->container(); - OccupancyMap* const ocmap = vsn->occupancy_map(); - - // The prospective chunk merge range must be completely contained by the - // committed range of the virtual space node. - if (p_merge_region_start < vsn->bottom() || p_merge_region_end > vsn->top()) { - return false; - } - - // Only attempt to merge this range if at its start a chunk starts and at its end - // a chunk ends. If a chunk (can only be humongous) straddles either start or end - // of that range, we cannot merge. - if (!ocmap->chunk_starts_at_address(p_merge_region_start)) { - return false; - } - if (p_merge_region_end < vsn->top() && - !ocmap->chunk_starts_at_address(p_merge_region_end)) { - return false; - } - - // Now check if the prospective merge area contains live chunks. If it does we cannot merge. - if (ocmap->is_region_in_use(p_merge_region_start, target_chunk_word_size)) { - return false; - } - - // Success! Remove all chunks in this region... - log_trace(gc, metaspace, freelist)("%s: coalescing chunks in area [%p-%p)...", - (is_class() ? "class space" : "metaspace"), - p_merge_region_start, p_merge_region_end); - - const int num_chunks_removed = - remove_chunks_in_area(p_merge_region_start, target_chunk_word_size); - - // ... and create a single new bigger chunk. - Metachunk* const p_new_chunk = - ::new (p_merge_region_start) Metachunk(target_chunk_type, is_class(), target_chunk_word_size, vsn); - assert(p_new_chunk == (Metachunk*)p_merge_region_start, "Sanity"); - p_new_chunk->set_origin(origin_merge); - - log_trace(gc, metaspace, freelist)("%s: created coalesced chunk at %p, size " SIZE_FORMAT_HEX ".", - (is_class() ? "class space" : "metaspace"), - p_new_chunk, p_new_chunk->word_size() * sizeof(MetaWord)); + assert(c->is_free(), "chunk to be split must be free."); + assert(c->level() < target_level, "Target level must be higher than current level."); + assert(c->prev() == NULL && c->next() == NULL, "Chunk must be outside of any list."); - // Fix occupancy map: remove old start bits of the small chunks and set new start bit. - ocmap->wipe_chunk_start_bits_in_region(p_merge_region_start, target_chunk_word_size); - ocmap->set_chunk_starts_at_address(p_merge_region_start, true); + DEBUG_ONLY(chunklevel::check_valid_level(target_level);) + DEBUG_ONLY(c->verify();) - // Mark chunk as free. Note: it is not necessary to update the occupancy - // map in-use map, because the old chunks were also free, so nothing - // should have changed. - p_new_chunk->set_is_tagged_free(true); + UL2(debug, "splitting chunk " METACHUNK_FORMAT " to " CHKLVL_FORMAT ".", + METACHUNK_FORMAT_ARGS(c), target_level); - // Add new chunk to its freelist. - ChunkList* const list = free_chunks(target_chunk_type); - list->return_chunk_at_head(p_new_chunk); + DEBUG_ONLY(size_t committed_words_before = c->committed_words();) - // And adjust ChunkManager:: _free_chunks_count (_free_chunks_total - // should not have changed, because the size of the space should be the same) - _free_chunks_count -= num_chunks_removed; - _free_chunks_count ++; + const chunklevel_t orig_level = c->level(); + c->vsnode()->split(target_level, c, &_chunks); - // VirtualSpaceNode::chunk_count does not have to be modified: - // it means "number of active (non-free) chunks", so merging free chunks - // should not affect that count. + // Splitting should never fail. + assert(c->level() == target_level, "Sanity"); - // At the end of a chunk merge, run verification tests. + // The size of the committed portion should not change (subject to the reduced chunk size of course) #ifdef ASSERT - - EVERY_NTH(VerifyMetaspaceInterval) - locked_verify(true); - vsn->verify(true); - END_EVERY_NTH - - g_internal_statistics.num_chunk_merges ++; - + if (committed_words_before > c->word_size()) { + assert(c->is_fully_committed(), "Sanity"); + } else { + assert(c->committed_words() == committed_words_before, "Sanity"); + } + c->verify(); + verify_locked(); + SOMETIMES(c->vsnode()->verify_locked();) #endif - - return true; + InternalStats::inc_num_chunk_splits(); } -// Remove all chunks in the given area - the chunks are supposed to be free - -// from their corresponding freelists. Mark them as invalid. -// - This does not correct the occupancy map. -// - This does not adjust the counters in ChunkManager. -// - Does not adjust container count counter in containing VirtualSpaceNode -// Returns number of chunks removed. -int ChunkManager::remove_chunks_in_area(MetaWord* p, size_t word_size) { - assert(p != NULL && word_size > 0, "Invalid range."); - const size_t smallest_chunk_size = get_size_for_nonhumongous_chunktype(SpecializedIndex, is_class()); - assert_is_aligned(word_size, smallest_chunk_size); - - Metachunk* const start = (Metachunk*) p; - const Metachunk* const end = (Metachunk*)(p + word_size); - Metachunk* cur = start; - int num_removed = 0; - while (cur < end) { - Metachunk* next = (Metachunk*)(((MetaWord*)cur) + cur->word_size()); - DEBUG_ONLY(do_verify_chunk(cur)); - assert(cur->get_chunk_type() != HumongousIndex, "Unexpected humongous chunk found at %p.", cur); - assert(cur->is_tagged_free(), "Chunk expected to be free (%p)", cur); - log_trace(gc, metaspace, freelist)("%s: removing chunk %p, size " SIZE_FORMAT_HEX ".", - (is_class() ? "class space" : "metaspace"), - cur, cur->word_size() * sizeof(MetaWord)); - cur->remove_sentinel(); - // Note: cannot call ChunkManager::remove_chunk, because that - // modifies the counters in ChunkManager, which we do not want. So - // we call remove_chunk on the freelist directly (see also the - // splitting function which does the same). - ChunkList* const list = free_chunks(list_index(cur->word_size())); - list->remove_chunk(cur); - num_removed ++; - cur = next; +// On success, returns a chunk of level of , but at most . +// The first first of the chunk are guaranteed to be committed. +// On error, will return NULL. +// +// This function may fail for two reasons: +// - Either we are unable to reserve space for a new chunk (if the underlying VirtualSpaceList +// is non-expandable but needs expanding - aka out of compressed class space). +// - Or, if the necessary space cannot be committed because we hit a commit limit. +// This may be either the GC threshold or MaxMetaspaceSize. +Metachunk* ChunkManager::get_chunk(chunklevel_t preferred_level, chunklevel_t max_level, size_t min_committed_words) { + assert(preferred_level <= max_level, "Sanity"); + assert(chunklevel::level_fitting_word_size(min_committed_words) >= max_level, "Sanity"); + + MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); + + DEBUG_ONLY(verify_locked();) + DEBUG_ONLY(chunklevel::check_valid_level(max_level);) + DEBUG_ONLY(chunklevel::check_valid_level(preferred_level);) + + UL2(debug, "requested chunk: pref_level: " CHKLVL_FORMAT + ", max_level: " CHKLVL_FORMAT ", min committed size: " SIZE_FORMAT ".", + preferred_level, max_level, min_committed_words); + + // First, optimistically look for a chunk which is already committed far enough to hold min_word_size. + + // 1) Search best or smaller committed chunks (first attempt): + // Start at the preferred chunk size and work your way down (level up). + // But for now, only consider chunks larger than a certain threshold - + // this is to prevent large loaders (eg boot) from unnecessarily gobbling up + // all the tiny splinter chunks lambdas leave around. + Metachunk* c = NULL; + c = _chunks.search_chunk_ascending(preferred_level, MIN2((chunklevel_t)(preferred_level + 2), max_level), min_committed_words); + + // 2) Search larger committed chunks: + // If that did not yield anything, look at larger chunks, which may be committed. We would have to split + // them first, of course. + if (c == NULL) { + c = _chunks.search_chunk_descending(preferred_level, min_committed_words); + } + // 3) Search best or smaller committed chunks (second attempt): + // Repeat (1) but now consider even the tiniest chunks as long as they are large enough to hold the + // committed min size. + if (c == NULL) { + c = _chunks.search_chunk_ascending(preferred_level, max_level, min_committed_words); + } + // if we did not get anything yet, there are no free chunks commmitted enough. Repeat search but look for uncommitted chunks too: + // 4) Search best or smaller chunks, can be uncommitted: + if (c == NULL) { + c = _chunks.search_chunk_ascending(preferred_level, max_level, 0); + } + // 5) Search a larger uncommitted chunk: + if (c == NULL) { + c = _chunks.search_chunk_descending(preferred_level, 0); } - return num_removed; -} - -// Update internal accounting after a chunk was added -void ChunkManager::account_for_added_chunk(const Metachunk* c) { - assert_lock_strong(MetaspaceExpand_lock); - _free_chunks_count ++; - _free_chunks_total += c->word_size(); -} - -// Update internal accounting after a chunk was removed -void ChunkManager::account_for_removed_chunk(const Metachunk* c) { - assert_lock_strong(MetaspaceExpand_lock); - assert(_free_chunks_count >= 1, - "ChunkManager::_free_chunks_count: about to go negative (" SIZE_FORMAT ").", _free_chunks_count); - assert(_free_chunks_total >= c->word_size(), - "ChunkManager::_free_chunks_total: about to go negative" - "(now: " SIZE_FORMAT ", decrement value: " SIZE_FORMAT ").", _free_chunks_total, c->word_size()); - _free_chunks_count --; - _free_chunks_total -= c->word_size(); -} - -ChunkIndex ChunkManager::list_index(size_t size) { - return get_chunk_type_by_size(size, is_class()); -} - -size_t ChunkManager::size_by_index(ChunkIndex index) const { - index_bounds_check(index); - assert(index != HumongousIndex, "Do not call for humongous chunks."); - return get_size_for_nonhumongous_chunktype(index, is_class()); -} - -#ifdef ASSERT -void ChunkManager::verify(bool slow) const { - MutexLocker cl(MetaspaceExpand_lock, - Mutex::_no_safepoint_check_flag); - locked_verify(slow); -} - -void ChunkManager::locked_verify(bool slow) const { - log_trace(gc, metaspace, freelist)("verifying %s chunkmanager (%s).", - (is_class() ? "class space" : "metaspace"), (slow ? "slow" : "quick")); - assert_lock_strong(MetaspaceExpand_lock); + if (c != NULL) { + UL(trace, "taken from freelist."); + } - size_t chunks_counted = 0; - size_t wordsize_chunks_counted = 0; - for (ChunkIndex i = ZeroIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) { - const ChunkList* list = _free_chunks + i; - if (list != NULL) { - Metachunk* chunk = list->head(); - while (chunk) { - if (slow) { - do_verify_chunk(chunk); - } - assert(chunk->is_tagged_free(), "Chunk should be tagged as free."); - chunks_counted ++; - wordsize_chunks_counted += chunk->size(); - chunk = chunk->next(); - } + // Failing all that, allocate a new root chunk from the connected virtual space. + // This may fail if the underlying vslist cannot be expanded (e.g. compressed class space) + if (c == NULL) { + c = _vslist->allocate_root_chunk(); + if (c == NULL) { + UL(info, "failed to get new root chunk."); + } else { + assert(c->level() == chunklevel::ROOT_CHUNK_LEVEL, "root chunk expected"); + UL(debug, "allocated new root chunk."); } } - - chunks_counted += humongous_dictionary()->total_free_blocks(); - wordsize_chunks_counted += humongous_dictionary()->total_size(); - - assert(chunks_counted == _free_chunks_count && wordsize_chunks_counted == _free_chunks_total, - "freelist accounting mismatch: " - "we think: " SIZE_FORMAT " chunks, total " SIZE_FORMAT " words, " - "reality: " SIZE_FORMAT " chunks, total " SIZE_FORMAT " words.", - _free_chunks_count, _free_chunks_total, - chunks_counted, wordsize_chunks_counted); -} -#endif // ASSERT - -void ChunkManager::locked_print_free_chunks(outputStream* st) { - assert_lock_strong(MetaspaceExpand_lock); - st->print_cr("Free chunk total " SIZE_FORMAT " count " SIZE_FORMAT, - _free_chunks_total, _free_chunks_count); -} - -ChunkList* ChunkManager::free_chunks(ChunkIndex index) { - assert(index == SpecializedIndex || index == SmallIndex || index == MediumIndex, - "Bad index: %d", (int)index); - return &_free_chunks[index]; -} - -ChunkList* ChunkManager::find_free_chunks_list(size_t word_size) { - ChunkIndex index = list_index(word_size); - assert(index < HumongousIndex, "No humongous list"); - return free_chunks(index); -} - -// Helper for chunk splitting: given a target chunk size and a larger free chunk, -// split up the larger chunk into n smaller chunks, at least one of which should be -// the target chunk of target chunk size. The smaller chunks, including the target -// chunk, are returned to the freelist. The pointer to the target chunk is returned. -// Note that this chunk is supposed to be removed from the freelist right away. -Metachunk* ChunkManager::split_chunk(size_t target_chunk_word_size, Metachunk* larger_chunk) { - assert(larger_chunk->word_size() > target_chunk_word_size, "Sanity"); - - const ChunkIndex larger_chunk_index = larger_chunk->get_chunk_type(); - const ChunkIndex target_chunk_index = get_chunk_type_by_size(target_chunk_word_size, is_class()); - - MetaWord* const region_start = (MetaWord*)larger_chunk; - const size_t region_word_len = larger_chunk->word_size(); - MetaWord* const region_end = region_start + region_word_len; - VirtualSpaceNode* const vsn = larger_chunk->container(); - OccupancyMap* const ocmap = vsn->occupancy_map(); - - // Any larger non-humongous chunk size is a multiple of any smaller chunk size. - // Since non-humongous chunks are aligned to their chunk size, the larger chunk should start - // at an address suitable to place the smaller target chunk. - assert_is_aligned(region_start, target_chunk_word_size); - - // Remove old chunk. - free_chunks(larger_chunk_index)->remove_chunk(larger_chunk); - larger_chunk->remove_sentinel(); - - // Prevent access to the old chunk from here on. - larger_chunk = NULL; - // ... and wipe it. - DEBUG_ONLY(memset(region_start, 0xfe, region_word_len * BytesPerWord)); - - // In its place create first the target chunk... - MetaWord* p = region_start; - Metachunk* target_chunk = ::new (p) Metachunk(target_chunk_index, is_class(), target_chunk_word_size, vsn); - assert(target_chunk == (Metachunk*)p, "Sanity"); - target_chunk->set_origin(origin_split); - - // Note: we do not need to mark its start in the occupancy map - // because it coincides with the old chunk start. - - // Mark chunk as free and return to the freelist. - do_update_in_use_info_for_chunk(target_chunk, false); - free_chunks(target_chunk_index)->return_chunk_at_head(target_chunk); - - // This chunk should now be valid and can be verified. - DEBUG_ONLY(do_verify_chunk(target_chunk)); - - // In the remaining space create the remainder chunks. - p += target_chunk->word_size(); - assert(p < region_end, "Sanity"); - - while (p < region_end) { - - // Find the largest chunk size which fits the alignment requirements at address p. - ChunkIndex this_chunk_index = prev_chunk_index(larger_chunk_index); - size_t this_chunk_word_size = 0; - for(;;) { - this_chunk_word_size = get_size_for_nonhumongous_chunktype(this_chunk_index, is_class()); - if (is_aligned(p, this_chunk_word_size * BytesPerWord)) { - break; - } else { - this_chunk_index = prev_chunk_index(this_chunk_index); - assert(this_chunk_index >= target_chunk_index, "Sanity"); + if (c == NULL) { + // If we end up here, we found no match in the freelists and were unable to get a new + // root chunk (so we used up all address space, e.g. out of CompressedClassSpace). + UL2(info, "failed to get chunk (preferred level: " CHKLVL_FORMAT + ", max level " CHKLVL_FORMAT ".", preferred_level, max_level); + c = NULL; + } + if (c != NULL) { + // Now we have a chunk. + // It may be larger than what the caller wanted, so we may want to split it. This should + // always work. + if (c->level() < preferred_level) { + split_chunk_and_add_splinters(c, preferred_level); + assert(c->level() == preferred_level, "split failed?"); + } + // Attempt to commit the chunk (depending on settings, we either fully commit it or just + // commit enough to get the caller going). That may fail if we hit a commit limit. In + // that case put the chunk back to the freelist (re-merging it with its neighbors if we + // did split it) and return NULL. + const size_t to_commit = Settings::new_chunks_are_fully_committed() ? c->word_size() : min_committed_words; + if (c->committed_words() < to_commit) { + if (c->ensure_committed_locked(to_commit) == false) { + UL2(info, "failed to commit " SIZE_FORMAT " words on chunk " METACHUNK_FORMAT ".", + to_commit, METACHUNK_FORMAT_ARGS(c)); + return_chunk_locked(c); + c = NULL; } } + if (c != NULL) { + // Still here? We have now a good chunk, all is well. + assert(c->committed_words() >= min_committed_words, "Sanity"); - assert(this_chunk_word_size >= target_chunk_word_size, "Sanity"); - assert(is_aligned(p, this_chunk_word_size * BytesPerWord), "Sanity"); - assert(p + this_chunk_word_size <= region_end, "Sanity"); + // Any chunk returned from ChunkManager shall be marked as in use. + c->set_in_use(); - // Create splitting chunk. - Metachunk* this_chunk = ::new (p) Metachunk(this_chunk_index, is_class(), this_chunk_word_size, vsn); - assert(this_chunk == (Metachunk*)p, "Sanity"); - this_chunk->set_origin(origin_split); - ocmap->set_chunk_starts_at_address(p, true); - do_update_in_use_info_for_chunk(this_chunk, false); + UL2(debug, "handing out chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(c)); - // This chunk should be valid and can be verified. - DEBUG_ONLY(do_verify_chunk(this_chunk)); - - // Return this chunk to freelist and correct counter. - free_chunks(this_chunk_index)->return_chunk_at_head(this_chunk); - _free_chunks_count ++; - - log_trace(gc, metaspace, freelist)("Created chunk at " PTR_FORMAT ", word size " - SIZE_FORMAT_HEX " (%s), in split region [" PTR_FORMAT "..." PTR_FORMAT ").", - p2i(this_chunk), this_chunk->word_size(), chunk_size_name(this_chunk_index), - p2i(region_start), p2i(region_end)); - - p += this_chunk_word_size; + InternalStats::inc_num_chunks_taken_from_freelist(); + SOMETIMES(c->vsnode()->verify_locked();) + } } - // Note: at this point, the VirtualSpaceNode is invalid since we split a chunk and - // did not yet hand out part of that split; so, vsn->verify_free_chunks_are_ideally_merged() - // would assert. Instead, do all verifications in the caller. - - DEBUG_ONLY(g_internal_statistics.num_chunk_splits ++); + DEBUG_ONLY(verify_locked();) + return c; +} - return target_chunk; +// Return a single chunk to the ChunkManager and adjust accounting. May merge chunk +// with neighbors. +// As a side effect this removes the chunk from whatever list it has been in previously. +// Happens after a Classloader was unloaded and releases its metaspace chunks. +// !! Note: this may invalidate the chunk. Do not access the chunk after +// this function returns !! +void ChunkManager::return_chunk(Metachunk* c) { + MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); + return_chunk_locked(c); } -Metachunk* ChunkManager::free_chunks_get(size_t word_size) { +// See return_chunk(). +void ChunkManager::return_chunk_locked(Metachunk* c) { assert_lock_strong(MetaspaceExpand_lock); + UL2(debug, ": returning chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(c)); + DEBUG_ONLY(c->verify();) + assert(contains_chunk(c) == false, "A chunk to be added to the freelist must not be in the freelist already."); + assert(c->is_in_use() || c->is_free(), "Unexpected chunk state"); + assert(!c->in_list(), "Remove from list first"); + + c->set_free(); + c->reset_used_words(); + const chunklevel_t orig_lvl = c->level(); + + Metachunk* merged = NULL; + if (!c->is_root_chunk()) { + // Only attempt merging if we are not of the lowest level already. + merged = c->vsnode()->merge(c, &_chunks); + } - Metachunk* chunk = NULL; - bool we_did_split_a_chunk = false; - - if (list_index(word_size) != HumongousIndex) { - - ChunkList* free_list = find_free_chunks_list(word_size); - assert(free_list != NULL, "Sanity check"); - - chunk = free_list->head(); - - if (chunk == NULL) { - // Split large chunks into smaller chunks if there are no smaller chunks, just large chunks. - // This is the counterpart of the coalescing-upon-chunk-return. - - ChunkIndex target_chunk_index = get_chunk_type_by_size(word_size, is_class()); - - // Is there a larger chunk we could split? - Metachunk* larger_chunk = NULL; - ChunkIndex larger_chunk_index = next_chunk_index(target_chunk_index); - while (larger_chunk == NULL && larger_chunk_index < NumberOfFreeLists) { - larger_chunk = free_chunks(larger_chunk_index)->head(); - if (larger_chunk == NULL) { - larger_chunk_index = next_chunk_index(larger_chunk_index); - } - } - - if (larger_chunk != NULL) { - assert(larger_chunk->word_size() > word_size, "Sanity"); - assert(larger_chunk->get_chunk_type() == larger_chunk_index, "Sanity"); - - // We found a larger chunk. Lets split it up: - // - remove old chunk - // - in its place, create new smaller chunks, with at least one chunk - // being of target size, the others sized as large as possible. This - // is to make sure the resulting chunks are "as coalesced as possible" - // (similar to VirtualSpaceNode::retire()). - // Note: during this operation both ChunkManager and VirtualSpaceNode - // are temporarily invalid, so be careful with asserts. - - log_trace(gc, metaspace, freelist)("%s: splitting chunk " PTR_FORMAT - ", word size " SIZE_FORMAT_HEX " (%s), to get a chunk of word size " SIZE_FORMAT_HEX " (%s)...", - (is_class() ? "class space" : "metaspace"), p2i(larger_chunk), larger_chunk->word_size(), - chunk_size_name(larger_chunk_index), word_size, chunk_size_name(target_chunk_index)); - - chunk = split_chunk(word_size, larger_chunk); - - // This should have worked. - assert(chunk != NULL, "Sanity"); - assert(chunk->word_size() == word_size, "Sanity"); - assert(chunk->is_tagged_free(), "Sanity"); - - we_did_split_a_chunk = true; - - } - } + if (merged != NULL) { + InternalStats::inc_num_chunk_merges(); + DEBUG_ONLY(merged->verify()); + // We did merge chunks and now have a bigger chunk. + assert(merged->level() < orig_lvl, "Sanity"); + UL2(debug, "merged into chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(merged)); + c = merged; + } - if (chunk == NULL) { - return NULL; - } + if (Settings::uncommit_free_chunks() && + c->word_size() >= Settings::commit_granule_words()) { + UL2(debug, "uncommitting free chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(c)); + c->uncommit_locked(); + } - // Remove the chunk as the head of the list. - free_list->remove_chunk(chunk); + return_chunk_simple_locked(c); + DEBUG_ONLY(verify_locked();) + SOMETIMES(c->vsnode()->verify_locked();) + InternalStats::inc_num_chunks_returned_to_freelist(); +} - log_trace(gc, metaspace, freelist)("ChunkManager::free_chunks_get: free_list: " PTR_FORMAT " chunks left: " SSIZE_FORMAT ".", - p2i(free_list), free_list->count()); +// Given a chunk c, whose state must be "in-use" and must not be a root chunk, attempt to +// enlarge it in place by claiming its trailing buddy. +// +// This will only work if c is the leader of the buddy pair and the trailing buddy is free. +// +// If successful, the follower chunk will be removed from the freelists, the leader chunk c will +// double in size (level decreased by one). +// +// On success, true is returned, false otherwise. +bool ChunkManager::attempt_enlarge_chunk(Metachunk* c) { + MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); + return c->vsnode()->attempt_enlarge_chunk(c, &_chunks); +} +static void print_word_size_delta(outputStream* st, size_t word_size_1, size_t word_size_2) { + if (word_size_1 == word_size_2) { + print_scaled_words(st, word_size_1); + st->print (" (no change)"); } else { - chunk = humongous_dictionary()->get_chunk(word_size); - - if (chunk == NULL) { - return NULL; + print_scaled_words(st, word_size_1); + st->print("->"); + print_scaled_words(st, word_size_2); + st->print(" ("); + if (word_size_2 <= word_size_1) { + st->print("-"); + print_scaled_words(st, word_size_1 - word_size_2); + } else { + st->print("+"); + print_scaled_words(st, word_size_2 - word_size_1); } - - log_trace(gc, metaspace, alloc)("Free list allocate humongous chunk size " SIZE_FORMAT " for requested size " SIZE_FORMAT " waste " SIZE_FORMAT, - chunk->word_size(), word_size, chunk->word_size() - word_size); + st->print(")"); } - - // Chunk has been removed from the chunk manager; update counters. - account_for_removed_chunk(chunk); - do_update_in_use_info_for_chunk(chunk, true); - chunk->container()->inc_container_count(); - chunk->inc_use_count(); - - // Remove it from the links to this freelist - chunk->set_next(NULL); - chunk->set_prev(NULL); - - // Run some verifications (some more if we did a chunk split) -#ifdef ASSERT - - EVERY_NTH(VerifyMetaspaceInterval) - // Be extra verify-y when chunk split happened. - locked_verify(true); - VirtualSpaceNode* const vsn = chunk->container(); - vsn->verify(true); - if (we_did_split_a_chunk) { - vsn->verify_free_chunks_are_ideally_merged(); - } - END_EVERY_NTH - - g_internal_statistics.num_chunks_removed_from_freelist ++; - -#endif - - return chunk; } -Metachunk* ChunkManager::chunk_freelist_allocate(size_t word_size) { - assert_lock_strong(MetaspaceExpand_lock); - - // Take from the beginning of the list - Metachunk* chunk = free_chunks_get(word_size); - if (chunk == NULL) { - return NULL; +void ChunkManager::purge() { + MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); + UL(info, ": reclaiming memory..."); + + const size_t reserved_before = _vslist->reserved_words(); + const size_t committed_before = _vslist->committed_words(); + int num_nodes_purged = 0; + + // We purge to return unused memory to the Operating System. We do this in + // two independent steps. + + // 1) We purge the virtual space list: any memory mappings which are + // completely deserted can be potentially unmapped. We iterate over the list + // of mappings (VirtualSpaceList::purge) and delete every node whose memory + // only contains free chunks. Deleting that node includes unmapping its memory, + // so all chunk vanish automatically. + // Of course we need to remove the chunk headers of those vanished chunks from + // the ChunkManager freelist. + num_nodes_purged = _vslist->purge(&_chunks); + InternalStats::inc_num_purges(); + + // 2) Since (1) is rather ineffective - it is rare that a whole node only contains + // free chunks - we now iterate over all remaining free chunks and + // and uncommit those which can be uncommitted (>= commit granule size). + if (Settings::uncommit_free_chunks()) { + const chunklevel_t max_level = + chunklevel::level_fitting_word_size(Settings::commit_granule_words()); + for (chunklevel_t l = chunklevel::LOWEST_CHUNK_LEVEL; + l <= max_level; + l++) { + // Since we uncommit all chunks at this level, we do not break the "committed chunks are + // at the front of the list" condition. + for (Metachunk* c = _chunks.first_at_level(l); c != NULL; c = c->next()) { + c->uncommit_locked(); + } + } } - assert((word_size <= chunk->word_size()) || - (list_index(chunk->word_size()) == HumongousIndex), - "Non-humongous variable sized chunk"); - LogTarget(Trace, gc, metaspace, freelist) lt; - if (lt.is_enabled()) { - size_t list_count; - if (list_index(word_size) < HumongousIndex) { - ChunkList* list = find_free_chunks_list(word_size); - list_count = list->count(); - } else { - list_count = humongous_dictionary()->total_count(); + const size_t reserved_after = _vslist->reserved_words(); + const size_t committed_after = _vslist->committed_words(); + + // Print a nice report. + if (reserved_after == reserved_before && committed_after == committed_before) { + UL(info, "nothing reclaimed."); + } else { + LogTarget(Info, metaspace) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + ls.print_cr(LOGFMT ": finished reclaiming memory: ", LOGFMT_ARGS); + ls.print("reserved: "); + print_word_size_delta(&ls, reserved_before, reserved_after); + ls.cr(); + ls.print("committed: "); + print_word_size_delta(&ls, committed_before, committed_after); + ls.cr(); + ls.print_cr("full nodes purged: %d", num_nodes_purged); } - LogStream ls(lt); - ls.print("ChunkManager::chunk_freelist_allocate: " PTR_FORMAT " chunk " PTR_FORMAT " size " SIZE_FORMAT " count " SIZE_FORMAT " ", - p2i(this), p2i(chunk), chunk->word_size(), list_count); - ResourceMark rm; - locked_print_free_chunks(&ls); } - - return chunk; + DEBUG_ONLY(_vslist->verify_locked()); + DEBUG_ONLY(verify_locked()); } -void ChunkManager::return_single_chunk(Metachunk* chunk) { +// Convenience methods to return the global class-space chunkmanager +// and non-class chunkmanager, respectively. +ChunkManager* ChunkManager::chunkmanager_class() { + return MetaspaceContext::context_class() == NULL ? NULL : MetaspaceContext::context_class()->cm(); +} -#ifdef ASSERT - EVERY_NTH(VerifyMetaspaceInterval) - this->locked_verify(false); - do_verify_chunk(chunk); - END_EVERY_NTH -#endif +ChunkManager* ChunkManager::chunkmanager_nonclass() { + return MetaspaceContext::context_nonclass() == NULL ? NULL : MetaspaceContext::context_nonclass()->cm(); +} - const ChunkIndex index = chunk->get_chunk_type(); - assert_lock_strong(MetaspaceExpand_lock); - DEBUG_ONLY(g_internal_statistics.num_chunks_added_to_freelist ++;) - assert(chunk != NULL, "Expected chunk."); - assert(chunk->container() != NULL, "Container should have been set."); - assert(chunk->is_tagged_free() == false, "Chunk should be in use."); - index_bounds_check(index); - - // Note: mangle *before* returning the chunk to the freelist or dictionary. It does not - // matter for the freelist (non-humongous chunks), but the humongous chunk dictionary - // keeps tree node pointers in the chunk payload area which mangle will overwrite. - DEBUG_ONLY(chunk->mangle(badMetaWordVal);) - - // may need node for verification later after chunk may have been merged away. - DEBUG_ONLY(VirtualSpaceNode* vsn = chunk->container(); ) - - if (index != HumongousIndex) { - // Return non-humongous chunk to freelist. - ChunkList* list = free_chunks(index); - assert(list->size() == chunk->word_size(), "Wrong chunk type."); - list->return_chunk_at_head(chunk); - log_trace(gc, metaspace, freelist)("returned one %s chunk at " PTR_FORMAT " to freelist.", - chunk_size_name(index), p2i(chunk)); - } else { - // Return humongous chunk to dictionary. - assert(chunk->word_size() > free_chunks(MediumIndex)->size(), "Wrong chunk type."); - assert(chunk->word_size() % free_chunks(SpecializedIndex)->size() == 0, - "Humongous chunk has wrong alignment."); - _humongous_dictionary.return_chunk(chunk); - log_trace(gc, metaspace, freelist)("returned one %s chunk at " PTR_FORMAT " (word size " SIZE_FORMAT ") to freelist.", - chunk_size_name(index), p2i(chunk), chunk->word_size()); - } - chunk->container()->dec_container_count(); - do_update_in_use_info_for_chunk(chunk, false); - - // Chunk has been added; update counters. - account_for_added_chunk(chunk); - - // Attempt coalesce returned chunks with its neighboring chunks: - // if this chunk is small or special, attempt to coalesce to a medium chunk. - if (index == SmallIndex || index == SpecializedIndex) { - if (!attempt_to_coalesce_around_chunk(chunk, MediumIndex)) { - // This did not work. But if this chunk is special, we still may form a small chunk? - if (index == SpecializedIndex) { - if (!attempt_to_coalesce_around_chunk(chunk, SmallIndex)) { - // give up. - } - } - } +// Update statistics. +void ChunkManager::add_to_statistics(ChunkManagerStats* out) const { + MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); + for (chunklevel_t l = chunklevel::ROOT_CHUNK_LEVEL; l <= chunklevel::HIGHEST_CHUNK_LEVEL; l++) { + out->_num_chunks[l] += _chunks.num_chunks_at_level(l); + out->_committed_word_size[l] += _chunks.committed_word_size_at_level(l); } - - // From here on do not access chunk anymore, it may have been merged with another chunk. + DEBUG_ONLY(out->verify();) +} #ifdef ASSERT - EVERY_NTH(VerifyMetaspaceInterval) - this->locked_verify(true); - vsn->verify(true); - vsn->verify_free_chunks_are_ideally_merged(); - END_EVERY_NTH -#endif +void ChunkManager::verify() const { + MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); + verify_locked(); } -void ChunkManager::return_chunk_list(Metachunk* chunks) { - if (chunks == NULL) { - return; - } - LogTarget(Trace, gc, metaspace, freelist) log; - if (log.is_enabled()) { // tracing - log.print("returning list of chunks..."); - } - unsigned num_chunks_returned = 0; - size_t size_chunks_returned = 0; - Metachunk* cur = chunks; - while (cur != NULL) { - // Capture the next link before it is changed - // by the call to return_chunk_at_head(); - Metachunk* next = cur->next(); - if (log.is_enabled()) { // tracing - num_chunks_returned ++; - size_chunks_returned += cur->word_size(); - } - return_single_chunk(cur); - cur = next; - } - if (log.is_enabled()) { // tracing - log.print("returned %u chunks to freelist, total word size " SIZE_FORMAT ".", - num_chunks_returned, size_chunks_returned); - } +void ChunkManager::verify_locked() const { + assert_lock_strong(MetaspaceExpand_lock); + assert(_vslist != NULL, "No vslist"); + _chunks.verify(); } -void ChunkManager::collect_statistics(ChunkManagerStatistics* out) const { - MutexLocker cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); - for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { - out->chunk_stats(i).add(num_free_chunks(i), size_free_chunks_in_bytes(i) / sizeof(MetaWord)); - } +bool ChunkManager::contains_chunk(Metachunk* c) const { + return _chunks.contains(c); } -} // namespace metaspace +#endif // ASSERT +void ChunkManager::print_on(outputStream* st) const { + MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); + print_on_locked(st); +} +void ChunkManager::print_on_locked(outputStream* st) const { + assert_lock_strong(MetaspaceExpand_lock); + st->print_cr("cm %s: %d chunks, total word size: " SIZE_FORMAT ", committed word size: " SIZE_FORMAT, _name, + total_num_chunks(), total_word_size(), _chunks.committed_word_size()); + _chunks.print_on(st); +} +} // namespace metaspace diff --git a/src/hotspot/share/memory/metaspace/chunkManager.hpp b/src/hotspot/share/memory/metaspace/chunkManager.hpp index c64585e44b7..ffc5113fd23 100644 --- a/src/hotspot/share/memory/metaspace/chunkManager.hpp +++ b/src/hotspot/share/memory/metaspace/chunkManager.hpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,173 +27,162 @@ #define SHARE_MEMORY_METASPACE_CHUNKMANAGER_HPP #include "memory/allocation.hpp" -#include "memory/binaryTreeDictionary.hpp" -#include "memory/freeList.hpp" +#include "memory/metaspace/chunklevel.hpp" +#include "memory/metaspace/counters.hpp" +#include "memory/metaspace/freeChunkList.hpp" #include "memory/metaspace/metachunk.hpp" -#include "memory/metaspace/metaspaceStatistics.hpp" -#include "memory/metaspaceChunkFreeListSummary.hpp" -#include "utilities/globalDefinitions.hpp" - -class ChunkManagerTestAccessor; namespace metaspace { -typedef class FreeList ChunkList; -typedef BinaryTreeDictionary > ChunkTreeDictionary; - -// Manages the global free lists of chunks. -class ChunkManager : public CHeapObj { - friend class ::ChunkManagerTestAccessor; - - // Free list of chunks of different sizes. - // SpecializedChunk - // SmallChunk - // MediumChunk - ChunkList _free_chunks[NumberOfFreeLists]; - - // Whether or not this is the class chunkmanager. - const bool _is_class; - - // Return non-humongous chunk list by its index. - ChunkList* free_chunks(ChunkIndex index); - - // Returns non-humongous chunk list for the given chunk word size. - ChunkList* find_free_chunks_list(size_t word_size); - - // HumongousChunk - ChunkTreeDictionary _humongous_dictionary; - - // Returns the humongous chunk dictionary. - ChunkTreeDictionary* humongous_dictionary() { return &_humongous_dictionary; } - const ChunkTreeDictionary* humongous_dictionary() const { return &_humongous_dictionary; } - - // Size, in metaspace words, of all chunks managed by this ChunkManager - size_t _free_chunks_total; - // Number of chunks in this ChunkManager - size_t _free_chunks_count; - - // Update counters after a chunk was added or removed removed. - void account_for_added_chunk(const Metachunk* c); - void account_for_removed_chunk(const Metachunk* c); - - // Given a pointer to a chunk, attempts to merge it with neighboring - // free chunks to form a bigger chunk. Returns true if successful. - bool attempt_to_coalesce_around_chunk(Metachunk* chunk, ChunkIndex target_chunk_type); - - // Helper for chunk merging: - // Given an address range with 1-n chunks which are all supposed to be - // free and hence currently managed by this ChunkManager, remove them - // from this ChunkManager and mark them as invalid. - // - This does not correct the occupancy map. - // - This does not adjust the counters in ChunkManager. - // - Does not adjust container count counter in containing VirtualSpaceNode. - // Returns number of chunks removed. - int remove_chunks_in_area(MetaWord* p, size_t word_size); - - // Helper for chunk splitting: given a target chunk size and a larger free chunk, - // split up the larger chunk into n smaller chunks, at least one of which should be - // the target chunk of target chunk size. The smaller chunks, including the target - // chunk, are returned to the freelist. The pointer to the target chunk is returned. - // Note that this chunk is supposed to be removed from the freelist right away. - Metachunk* split_chunk(size_t target_chunk_word_size, Metachunk* chunk); - - public: - - ChunkManager(bool is_class); - - // Add or delete (return) a chunk to the global freelist. - Metachunk* chunk_freelist_allocate(size_t word_size); - - // Map a size to a list index assuming that there are lists - // for special, small, medium, and humongous chunks. - ChunkIndex list_index(size_t size); - - // Map a given index to the chunk size. - size_t size_by_index(ChunkIndex index) const; - - bool is_class() const { return _is_class; } - - // Convenience accessors. - size_t medium_chunk_word_size() const { return size_by_index(MediumIndex); } - size_t small_chunk_word_size() const { return size_by_index(SmallIndex); } - size_t specialized_chunk_word_size() const { return size_by_index(SpecializedIndex); } - - // Take a chunk from the ChunkManager. The chunk is expected to be in - // the chunk manager (the freelist if non-humongous, the dictionary if - // humongous). - void remove_chunk(Metachunk* chunk); - - // Return a single chunk of type index to the ChunkManager. - void return_single_chunk(Metachunk* chunk); - - // Add the simple linked list of chunks to the freelist of chunks - // of type index. - void return_chunk_list(Metachunk* chunk); - - // Total of the space in the free chunks list - size_t free_chunks_total_words() const { return _free_chunks_total; } - size_t free_chunks_total_bytes() const { return free_chunks_total_words() * BytesPerWord; } - - // Number of chunks in the free chunks list - size_t free_chunks_count() const { return _free_chunks_count; } - - // Remove from a list by size. Selects list based on size of chunk. - Metachunk* free_chunks_get(size_t chunk_word_size); - -#define index_bounds_check(index) \ - assert(is_valid_chunktype(index), "Bad index: %d", (int) index) - - size_t num_free_chunks(ChunkIndex index) const { - index_bounds_check(index); - - if (index == HumongousIndex) { - return _humongous_dictionary.total_free_blocks(); - } - - ssize_t count = _free_chunks[index].count(); - return count == -1 ? 0 : (size_t) count; - } - - size_t size_free_chunks_in_bytes(ChunkIndex index) const { - index_bounds_check(index); - - size_t word_size = 0; - if (index == HumongousIndex) { - word_size = _humongous_dictionary.total_size(); - } else { - const size_t size_per_chunk_in_words = _free_chunks[index].size(); - word_size = size_per_chunk_in_words * num_free_chunks(index); - } - - return word_size * BytesPerWord; - } - - MetaspaceChunkFreeListSummary chunk_free_list_summary() const { - return MetaspaceChunkFreeListSummary(num_free_chunks(SpecializedIndex), - num_free_chunks(SmallIndex), - num_free_chunks(MediumIndex), - num_free_chunks(HumongousIndex), - size_free_chunks_in_bytes(SpecializedIndex), - size_free_chunks_in_bytes(SmallIndex), - size_free_chunks_in_bytes(MediumIndex), - size_free_chunks_in_bytes(HumongousIndex)); - } - -#ifdef ASSERT - // Debug support - // Verify free list integrity. slow=true: verify chunk-internal integrity too. - void verify(bool slow) const; - void locked_verify(bool slow) const; -#endif - - void locked_print_free_chunks(outputStream* st); - - // Fill in current statistic values to the given statistics object. - void collect_statistics(ChunkManagerStatistics* out) const; +class VirtualSpaceList; +struct ChunkManagerStats; + +// ChunkManager has a somewhat central role. + +// Arenas request chunks from it and, on death, return chunks back to it. +// It keeps freelists for chunks, one per chunk level, sorted by chunk +// commit state. +// To feed the freelists, it allocates root chunks from the associated +// VirtualSpace below it. +// +// ChunkManager directs splitting chunks, if a chunk request cannot be +// fulfilled directly. It also takes care of merging when chunks are +// returned to it, before they are added to the freelist. +// +// The freelists are double linked double headed; fully committed chunks +// are added to the front, others to the back. +// +// Level +// +--------------------+ +--------------------+ +// 0 +----| free root chunk |---| free root chunk |---... +// | +--------------------+ +--------------------+ +// | +// | +----------+ +----------+ +// 1 +----| |---| |---... +// | +----------+ +----------+ +// | +// . +// . +// . +// +// | +-+ +-+ +// 12 +----| |---| |---... +// +-+ +-+ + +class ChunkManager : public CHeapObj { + + // A chunk manager is connected to a virtual space list which is used + // to allocate new root chunks when no free chunks are found. + VirtualSpaceList* const _vslist; + + // Name + const char* const _name; + + // Freelists + FreeChunkListVector _chunks; + + // Returns true if this manager contains the given chunk. Slow (walks free lists) and + // only needed for verifications. + DEBUG_ONLY(bool contains_chunk(Metachunk* c) const;) + + // Given a chunk, split it into a target chunk of a smaller size (target level) + // at least one, possible more splinter chunks. Splinter chunks are added to the + // freelist. + // The original chunk must be outside of the freelist and its state must be free. + // The resulting target chunk will be located at the same address as the original + // chunk, but it will of course be smaller (of a higher level). + // The committed areas within the original chunk carry over to the resulting + // chunks. + void split_chunk_and_add_splinters(Metachunk* c, chunklevel_t target_level); + + // See get_chunk(s,s,s) + Metachunk* get_chunk_locked(size_t preferred_word_size, size_t min_word_size, size_t min_committed_words); + + // Uncommit all chunks equal or below the given level. + void uncommit_free_chunks(chunklevel_t max_level); + + // Return a single chunk to the freelist without doing any merging, and adjust accounting. + void return_chunk_simple_locked(Metachunk* c); + + // See return_chunk(). + void return_chunk_locked(Metachunk* c); + +public: + + // Creates a chunk manager with a given name (which is for debug purposes only) + // and an associated space list which will be used to request new chunks from + // (see get_chunk()) + ChunkManager(const char* name, VirtualSpaceList* space_list); + + // On success, returns a chunk of level of , but at most . + // The first of the chunk are guaranteed to be committed. + // On error, will return NULL. + // + // This function may fail for two reasons: + // - Either we are unable to reserve space for a new chunk (if the underlying VirtualSpaceList + // is non-expandable but needs expanding - aka out of compressed class space). + // - Or, if the necessary space cannot be committed because we hit a commit limit. + // This may be either the GC threshold or MaxMetaspaceSize. + Metachunk* get_chunk(chunklevel_t preferred_level, chunklevel_t max_level, size_t min_committed_words); + + // Convenience function - get a chunk of a given level, uncommitted. + Metachunk* get_chunk(chunklevel_t lvl) { return get_chunk(lvl, lvl, 0); } + + // Return a single chunk to the ChunkManager and adjust accounting. May merge chunk + // with neighbors. + // Happens after a Classloader was unloaded and releases its metaspace chunks. + // !! Notes: + // 1) After this method returns, c may not be valid anymore. ** Do not access c after this function returns **. + // 2) This function will not remove c from its current chunk list. This has to be done by the caller prior to + // calling this method. + void return_chunk(Metachunk* c); + + // Given a chunk c, which must be "in use" and must not be a root chunk, attempt to + // enlarge it in place by claiming its trailing buddy. + // + // This will only work if c is the leader of the buddy pair and the trailing buddy is free. + // + // If successful, the follower chunk will be removed from the freelists, the leader chunk c will + // double in size (level decreased by one). + // + // On success, true is returned, false otherwise. + bool attempt_enlarge_chunk(Metachunk* c); + + // Attempt to reclaim free areas in metaspace wholesale: + // - first, attempt to purge nodes of the backing virtual space list: nodes which are completely + // unused get unmapped and deleted completely. + // - second, it will uncommit free chunks depending on commit granule size. + void purge(); + + // Run verifications. slow=true: verify chunk-internal integrity too. + DEBUG_ONLY(void verify() const;) + DEBUG_ONLY(void verify_locked() const;) + + // Returns the name of this chunk manager. + const char* name() const { return _name; } + + // Returns total number of chunks + int total_num_chunks() const { return _chunks.num_chunks(); } + + // Returns number of words in all free chunks (regardless of commit state). + size_t total_word_size() const { return _chunks.word_size(); } + + // Returns number of committed words in all free chunks. + size_t total_committed_word_size() const { return _chunks.committed_word_size(); } + + // Update statistics. + void add_to_statistics(ChunkManagerStats* out) const; + + void print_on(outputStream* st) const; + void print_on_locked(outputStream* st) const; + + // Convenience methods to return the global class-space chunkmanager + // and non-class chunkmanager, respectively. + static ChunkManager* chunkmanager_class(); + static ChunkManager* chunkmanager_nonclass(); }; } // namespace metaspace - #endif // SHARE_MEMORY_METASPACE_CHUNKMANAGER_HPP diff --git a/src/hotspot/share/memory/metaspace/metaDebug.cpp b/src/hotspot/share/memory/metaspace/chunklevel.cpp similarity index 53% rename from src/hotspot/share/memory/metaspace/metaDebug.cpp rename to src/hotspot/share/memory/metaspace/chunklevel.cpp index 51ec50bed40..c8bb19373d6 100644 --- a/src/hotspot/share/memory/metaspace/metaDebug.cpp +++ b/src/hotspot/share/memory/metaspace/chunklevel.cpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,41 +24,39 @@ */ #include "precompiled.hpp" -#include "logging/log.hpp" -#include "memory/metaspace/metaDebug.hpp" -#include "runtime/os.hpp" -#include "runtime/thread.hpp" +#include "memory/metaspace/chunklevel.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" +#include "utilities/powerOfTwo.hpp" namespace metaspace { -int Metadebug::_allocation_fail_alot_count = 0; +using namespace chunklevel; -void Metadebug::init_allocation_fail_alot_count() { - if (MetadataAllocationFailALot) { - _allocation_fail_alot_count = - 1+(long)((double)MetadataAllocationFailALotInterval*os::random()/(max_jint+1.0)); +chunklevel_t chunklevel::level_fitting_word_size(size_t word_size) { + assert(MAX_CHUNK_WORD_SIZE >= word_size, + SIZE_FORMAT " - too large allocation size.", word_size * BytesPerWord); + if (word_size <= MIN_CHUNK_WORD_SIZE) { + return HIGHEST_CHUNK_LEVEL; } + const size_t v_pow2 = round_up_power_of_2(word_size); + const chunklevel_t lvl = (chunklevel_t)(exact_log2(MAX_CHUNK_WORD_SIZE) - exact_log2(v_pow2)); + return lvl; } -#ifdef ASSERT -bool Metadebug::test_metadata_failure() { - if (MetadataAllocationFailALot && - Threads::is_vm_complete()) { - if (_allocation_fail_alot_count > 0) { - _allocation_fail_alot_count--; +void chunklevel::print_chunk_size(outputStream* st, chunklevel_t lvl) { + if (chunklevel::is_valid_level(lvl)) { + const size_t s = chunklevel::word_size_for_level(lvl) * BytesPerWord; + if (s < 1 * M) { + st->print("%3uk", (unsigned)(s / K)); } else { - log_trace(gc, metaspace, freelist)("Metadata allocation failing for MetadataAllocationFailALot"); - init_allocation_fail_alot_count(); - return true; + st->print("%3um", (unsigned)(s / M)); } + } else { + st->print("?-?"); } - return false; } -#endif - } // namespace metaspace - diff --git a/src/hotspot/share/memory/metaspace/chunklevel.hpp b/src/hotspot/share/memory/metaspace/chunklevel.hpp new file mode 100644 index 00000000000..8dbc2467fd7 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/chunklevel.hpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_CHUNKLEVEL_HPP +#define SHARE_MEMORY_METASPACE_CHUNKLEVEL_HPP + +#include "utilities/globalDefinitions.hpp" + +// Constants for the chunk levels and some utility functions. + +class outputStream; + +namespace metaspace { + +// Chunks are managed by a binary buddy allocator. + +// Chunk sizes range from 1K to 4MB (64bit). +// + +// Each chunk has a level; the level corresponds to its position in the tree +// and describes its size. +// +// The largest chunks are called root chunks, of 4MB in size, and have level 0. +// From there on it goes: +// +// size level +// 4MB 0 +// 2MB 1 +// 1MB 2 +// 512K 3 +// 256K 4 +// 128K 5 +// 64K 6 +// 32K 7 +// 16K 8 +// 8K 9 +// 4K 10 +// 2K 11 +// 1K 12 + +// Metachunk level (must be signed) +typedef signed char chunklevel_t; + +#define CHKLVL_FORMAT "lv%.2d" + +namespace chunklevel { + +static const size_t MAX_CHUNK_BYTE_SIZE = 4 * M; +static const int NUM_CHUNK_LEVELS = 13; +static const size_t MIN_CHUNK_BYTE_SIZE = (MAX_CHUNK_BYTE_SIZE >> ((size_t)NUM_CHUNK_LEVELS - 1)); + +static const size_t MIN_CHUNK_WORD_SIZE = MIN_CHUNK_BYTE_SIZE / sizeof(MetaWord); +static const size_t MAX_CHUNK_WORD_SIZE = MAX_CHUNK_BYTE_SIZE / sizeof(MetaWord); + +static const chunklevel_t ROOT_CHUNK_LEVEL = 0; + +static const chunklevel_t HIGHEST_CHUNK_LEVEL = NUM_CHUNK_LEVELS - 1; +static const chunklevel_t LOWEST_CHUNK_LEVEL = 0; + +static const chunklevel_t INVALID_CHUNK_LEVEL = (chunklevel_t) -1; + +inline bool is_valid_level(chunklevel_t level) { + return level >= LOWEST_CHUNK_LEVEL && + level <= HIGHEST_CHUNK_LEVEL; +} + +inline void check_valid_level(chunklevel_t lvl) { + assert(is_valid_level(lvl), "invalid level (%d)", (int)lvl); +} + +// Given a level return the chunk size, in words. +inline size_t word_size_for_level(chunklevel_t level) { + return (MAX_CHUNK_BYTE_SIZE >> level) / BytesPerWord; +} + +// Given an arbitrary word size smaller than the highest chunk size, +// return the highest chunk level able to hold this size. +// Returns INVALID_CHUNK_LEVEL if no fitting level can be found. +chunklevel_t level_fitting_word_size(size_t word_size); + +// Shorthands to refer to exact sizes +static const chunklevel_t CHUNK_LEVEL_4M = ROOT_CHUNK_LEVEL; +static const chunklevel_t CHUNK_LEVEL_2M = (ROOT_CHUNK_LEVEL + 1); +static const chunklevel_t CHUNK_LEVEL_1M = (ROOT_CHUNK_LEVEL + 2); +static const chunklevel_t CHUNK_LEVEL_512K = (ROOT_CHUNK_LEVEL + 3); +static const chunklevel_t CHUNK_LEVEL_256K = (ROOT_CHUNK_LEVEL + 4); +static const chunklevel_t CHUNK_LEVEL_128K = (ROOT_CHUNK_LEVEL + 5); +static const chunklevel_t CHUNK_LEVEL_64K = (ROOT_CHUNK_LEVEL + 6); +static const chunklevel_t CHUNK_LEVEL_32K = (ROOT_CHUNK_LEVEL + 7); +static const chunklevel_t CHUNK_LEVEL_16K = (ROOT_CHUNK_LEVEL + 8); +static const chunklevel_t CHUNK_LEVEL_8K = (ROOT_CHUNK_LEVEL + 9); +static const chunklevel_t CHUNK_LEVEL_4K = (ROOT_CHUNK_LEVEL + 10); +static const chunklevel_t CHUNK_LEVEL_2K = (ROOT_CHUNK_LEVEL + 11); +static const chunklevel_t CHUNK_LEVEL_1K = (ROOT_CHUNK_LEVEL + 12); + +STATIC_ASSERT(CHUNK_LEVEL_1K == HIGHEST_CHUNK_LEVEL); +STATIC_ASSERT(CHUNK_LEVEL_4M == LOWEST_CHUNK_LEVEL); +STATIC_ASSERT(ROOT_CHUNK_LEVEL == LOWEST_CHUNK_LEVEL); + +///////////////////////////////////////////////////////// +// print helpers +void print_chunk_size(outputStream* st, chunklevel_t lvl); + +} // namespace chunklevel + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_CHUNKLEVEL_HPP diff --git a/src/hotspot/share/memory/metaspace/smallBlocks.cpp b/src/hotspot/share/memory/metaspace/commitLimiter.cpp similarity index 50% rename from src/hotspot/share/memory/metaspace/smallBlocks.cpp rename to src/hotspot/share/memory/metaspace/commitLimiter.cpp index 601ae175052..94bffb0492a 100644 --- a/src/hotspot/share/memory/metaspace/smallBlocks.cpp +++ b/src/hotspot/share/memory/metaspace/commitLimiter.cpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,41 +22,34 @@ * questions. * */ -#include "precompiled.hpp" -#include "memory/metaspace/smallBlocks.hpp" +#include "precompiled.hpp" +#include "memory/metaspace.hpp" +#include "memory/metaspace/commitLimiter.hpp" +#include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/ostream.hpp" namespace metaspace { -void SmallBlocks::print_on(outputStream* st) const { - st->print_cr("SmallBlocks:"); - for (uint i = _small_block_min_size; i < _small_block_max_size; i++) { - uint k = i - _small_block_min_size; - st->print_cr("small_lists size " SIZE_FORMAT " count " SIZE_FORMAT, _small_lists[k].size(), _small_lists[k].count()); +// Returns the size, in words, by which we may expand the metaspace committed area without: +// - _cap == 0: hitting GC threshold or the MaxMetaspaceSize +// - _cap > 0: hitting cap (this is just for testing purposes) +size_t CommitLimiter::possible_expansion_words() const { + if (_cap > 0) { // Testing. + assert(_cnt.get() <= _cap, "Beyond limit?"); + return _cap - _cnt.get(); } + assert(_cnt.get() * BytesPerWord <= MaxMetaspaceSize, "Beyond limit?"); + const size_t words_left_below_max = MaxMetaspaceSize / BytesPerWord - _cnt.get(); + const size_t words_left_below_gc_threshold = MetaspaceGC::allowed_expansion(); + return MIN2(words_left_below_max, words_left_below_gc_threshold); } +static CommitLimiter g_global_limiter(0); -// Returns the total size, in words, of all blocks, across all block sizes. -size_t SmallBlocks::total_size() const { - size_t result = 0; - for (uint i = _small_block_min_size; i < _small_block_max_size; i++) { - uint k = i - _small_block_min_size; - result = result + _small_lists[k].count() * _small_lists[k].size(); - } - return result; -} - -// Returns the total number of all blocks across all block sizes. -uintx SmallBlocks::total_num_blocks() const { - uintx result = 0; - for (uint i = _small_block_min_size; i < _small_block_max_size; i++) { - uint k = i - _small_block_min_size; - result = result + _small_lists[k].count(); - } - return result; +// Returns the global metaspace commit counter +CommitLimiter* CommitLimiter::globalLimiter() { + return &g_global_limiter; } } // namespace metaspace diff --git a/src/hotspot/share/memory/metaspace/commitLimiter.hpp b/src/hotspot/share/memory/metaspace/commitLimiter.hpp new file mode 100644 index 00000000000..f9b6598127c --- /dev/null +++ b/src/hotspot/share/memory/metaspace/commitLimiter.hpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_COMMITLIMITER_HPP +#define SHARE_MEMORY_METASPACE_COMMITLIMITER_HPP + +#include "memory/allocation.hpp" +#include "memory/metaspace/counters.hpp" + +namespace metaspace { + +// The CommitLimiter encapsulates a limit we may want to impose on how much +// memory can be committed. This is a matter of separation of concerns: +// +// In metaspace, we have two limits to committing memory: the absolute limit, +// MaxMetaspaceSize; and the GC threshold. In both cases an allocation should +// fail if it would require committing memory and hit one of these limits. +// +// However, the actual Metaspace allocator is a generic one and this +// GC- and classloading specific logic should be kept separate. Therefore +// it is hidden inside this interface. +// +// This allows us to: +// - more easily write tests for metaspace, by providing a different implementation +// of the commit limiter, thus keeping test logic separate from VM state. +// - (potentially) use the metaspace for things other than class metadata, +// where different commit rules would apply. +// +class CommitLimiter : public CHeapObj { + + // Counts total words committed for metaspace + SizeCounter _cnt; + + // Purely for testing purposes: cap, in words. + const size_t _cap; + +public: + + // Create a commit limiter. This is only useful for testing, with a cap != 0, + // since normal code should use the global commit limiter. + // If cap != 0 (word size), the cap replaces the internal logic of limiting. + CommitLimiter(size_t cap = 0) : _cnt(), _cap(cap) {} + + // Returns the size, in words, by which we may expand the metaspace committed area without: + // - _cap == 0: hitting GC threshold or the MaxMetaspaceSize + // - _cap > 0: hitting cap (this is just for testing purposes) + size_t possible_expansion_words() const; + + void increase_committed(size_t word_size) { _cnt.increment_by(word_size); } + void decrease_committed(size_t word_size) { _cnt.decrement_by(word_size); } + + size_t committed_words() const { return _cnt.get(); } + size_t cap() const { return _cap; } + + // Returns the global metaspace commit counter + static CommitLimiter* globalLimiter(); + +}; + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_COMMITLIMITER_HPP diff --git a/src/hotspot/share/memory/metaspace/commitMask.cpp b/src/hotspot/share/memory/metaspace/commitMask.cpp new file mode 100644 index 00000000000..833375e53ae --- /dev/null +++ b/src/hotspot/share/memory/metaspace/commitMask.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "memory/metaspace/commitMask.hpp" +#include "memory/metaspace/metaspaceCommon.hpp" +#include "memory/metaspace/metaspaceSettings.hpp" +#include "runtime/stubRoutines.hpp" +#include "utilities/align.hpp" +#include "utilities/debug.hpp" + +namespace metaspace { + +CommitMask::CommitMask(const MetaWord* start, size_t word_size) : + CHeapBitMap(mask_size(word_size, Settings::commit_granule_words())), + _base(start), + _word_size(word_size), + _words_per_bit(Settings::commit_granule_words()) +{ + assert(_word_size > 0 && _words_per_bit > 0 && + is_aligned(_word_size, _words_per_bit), "Sanity"); +} + +#ifdef ASSERT + +// Given a pointer, check if it points into the range this bitmap covers. +bool CommitMask::is_pointer_valid(const MetaWord* p) const { + return p >= _base && p < _base + _word_size; +} + +// Given a pointer, check if it points into the range this bitmap covers. +void CommitMask::check_pointer(const MetaWord* p) const { + assert(is_pointer_valid(p), + "Pointer " PTR_FORMAT " not in range of this bitmap [" PTR_FORMAT ", " PTR_FORMAT ").", + p2i(p), p2i(_base), p2i(_base + _word_size)); +} +// Given a pointer, check if it points into the range this bitmap covers, +// and if it is aligned to commit granule border. +void CommitMask::check_pointer_aligned(const MetaWord* p) const { + check_pointer(p); + assert(is_aligned(p, _words_per_bit * BytesPerWord), + "Pointer " PTR_FORMAT " should be aligned to commit granule size " SIZE_FORMAT ".", + p2i(p), _words_per_bit * BytesPerWord); +} +// Given a range, check if it points into the range this bitmap covers, +// and if its borders are aligned to commit granule border. +void CommitMask::check_range(const MetaWord* start, size_t word_size) const { + check_pointer_aligned(start); + assert(is_aligned(word_size, _words_per_bit), + "Range " SIZE_FORMAT " should be aligned to commit granule size " SIZE_FORMAT ".", + word_size, _words_per_bit); + check_pointer(start + word_size - 1); +} + +void CommitMask::verify() const { + // Walk the whole commit mask. + // For each 1 bit, check if the associated granule is accessible. + // For each 0 bit, check if the associated granule is not accessible. Slow mode only. + assert(_base != NULL && _word_size > 0 && _words_per_bit > 0, "Sanity"); + assert_is_aligned(_base, _words_per_bit * BytesPerWord); + assert_is_aligned(_word_size, _words_per_bit); +} + +#endif // ASSERT + +void CommitMask::print_on(outputStream* st) const { + st->print("commit mask, base " PTR_FORMAT ":", p2i(base())); + for (idx_t i = 0; i < size(); i++) { + st->print("%c", at(i) ? 'X' : '-'); + } + st->cr(); +} + +} // namespace metaspace + diff --git a/src/hotspot/share/memory/metaspace/commitMask.hpp b/src/hotspot/share/memory/metaspace/commitMask.hpp new file mode 100644 index 00000000000..d1edc0282dd --- /dev/null +++ b/src/hotspot/share/memory/metaspace/commitMask.hpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_COMMITMASK_HPP +#define SHARE_MEMORY_METASPACE_COMMITMASK_HPP + +#include "utilities/bitMap.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" + +class outputStream; + +namespace metaspace { + +// The CommitMask is a bitmask used to store the commit state of commit granules. +// It keeps one bit per granule; 1 means committed, 0 means uncommitted. + +class CommitMask : public CHeapBitMap { + + const MetaWord* const _base; + const size_t _word_size; + const size_t _words_per_bit; + + // Given an offset, in words, into the area, return the number of the bit + // covering it. + static idx_t bitno_for_word_offset(size_t offset, size_t words_per_bit) { + return offset / words_per_bit; + } + + idx_t bitno_for_address(const MetaWord* p) const { + // Note: we allow one-beyond since this is a typical need. + assert(p >= _base && p <= _base + _word_size, "Invalid address"); + const size_t off = p - _base; + return bitno_for_word_offset(off, _words_per_bit); + } + + static idx_t mask_size(size_t word_size, size_t words_per_bit) { + return bitno_for_word_offset(word_size, words_per_bit); + } + + // Marks a single commit granule as committed (value == true) + // or uncomitted (value == false) and returns + // its prior state. + bool mark_granule(idx_t bitno, bool value) { + bool b = at(bitno); + at_put(bitno, value); + return b; + } + +#ifdef ASSERT + + // Given a pointer, check if it points into the range this bitmap covers. + bool is_pointer_valid(const MetaWord* p) const; + + // Given a pointer, check if it points into the range this bitmap covers. + void check_pointer(const MetaWord* p) const; + + // Given a pointer, check if it points into the range this bitmap covers, + // and if it is aligned to commit granule border. + void check_pointer_aligned(const MetaWord* p) const; + + // Given a range, check if it points into the range this bitmap covers, + // and if its borders are aligned to commit granule border. + void check_range(const MetaWord* start, size_t word_size) const; + +#endif // ASSERT + +public: + + // Create a commit mask covering a range [start, start + word_size). + CommitMask(const MetaWord* start, size_t word_size); + + const MetaWord* base() const { return _base; } + size_t word_size() const { return _word_size; } + const MetaWord* end() const { return _base + word_size(); } + + // Given an address, returns true if the address is committed, false if not. + bool is_committed_address(const MetaWord* p) const { + DEBUG_ONLY(check_pointer(p)); + const idx_t bitno = bitno_for_address(p); + return at(bitno); + } + + // Given an address range, return size, in number of words, of committed area within that range. + size_t get_committed_size_in_range(const MetaWord* start, size_t word_size) const { + DEBUG_ONLY(check_range(start, word_size)); + assert(word_size > 0, "zero range"); + const idx_t b1 = bitno_for_address(start); + const idx_t b2 = bitno_for_address(start + word_size); + const idx_t num_bits = count_one_bits(b1, b2); + return num_bits * _words_per_bit; + } + + // Return total committed size, in number of words. + size_t get_committed_size() const { + return count_one_bits() * _words_per_bit; + } + + // Mark a whole address range [start, end) as committed. + // Return the number of words which had already been committed before this operation. + size_t mark_range_as_committed(const MetaWord* start, size_t word_size) { + DEBUG_ONLY(check_range(start, word_size)); + assert(word_size > 0, "zero range"); + const idx_t b1 = bitno_for_address(start); + const idx_t b2 = bitno_for_address(start + word_size); + if (b1 == b2) { // Simple case, 1 granule + bool was_committed = mark_granule(b1, true); + return was_committed ? _words_per_bit : 0; + } + const idx_t one_bits_in_range_before = count_one_bits(b1, b2); + set_range(b1, b2); + return one_bits_in_range_before * _words_per_bit; + } + + // Mark a whole address range [start, end) as uncommitted. + // Return the number of words which had already been uncommitted before this operation. + size_t mark_range_as_uncommitted(const MetaWord* start, size_t word_size) { + DEBUG_ONLY(check_range(start, word_size)); + assert(word_size > 0, "zero range"); + const idx_t b1 = bitno_for_address(start); + const idx_t b2 = bitno_for_address(start + word_size); + if (b1 == b2) { // Simple case, 1 granule + bool was_committed = mark_granule(b1, false); + return was_committed ? 0 : _words_per_bit; + } + const idx_t zero_bits_in_range_before = + (b2 - b1) - count_one_bits(b1, b2); + clear_range(b1, b2); + return zero_bits_in_range_before * _words_per_bit; + } + + //// Debug stuff //// + + // Verify internals. + DEBUG_ONLY(void verify() const;) + + void print_on(outputStream* st) const; + +}; + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_COMMITMASK_HPP diff --git a/src/hotspot/share/memory/metaspace/counters.hpp b/src/hotspot/share/memory/metaspace/counters.hpp new file mode 100644 index 00000000000..066d36279a6 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/counters.hpp @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_COUNTERS_HPP +#define SHARE_MEMORY_METASPACE_COUNTERS_HPP + +#include "metaprogramming/isSigned.hpp" +#include "runtime/atomic.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" + +namespace metaspace { + +// We seem to be counting a lot of things which makes it worthwhile to +// make helper classes for all that boilerplate coding. + +// AbstractCounter counts something and asserts overflow and underflow. +template +class AbstractCounter { + + T _c; + + // Only allow unsigned values for now + STATIC_ASSERT(IsSigned::value == false); + +public: + + AbstractCounter() : _c(0) {} + + T get() const { return _c; } + + void increment() { increment_by(1); } + void decrement() { decrement_by(1); } + + void increment_by(T v) { +#ifdef ASSERT + T old = _c; + assert(old + v >= old, + "overflow (" UINT64_FORMAT "+" UINT64_FORMAT ")", (uint64_t)old, (uint64_t)v); +#endif + _c += v; + } + + void decrement_by(T v) { + assert(_c >= v, + "underflow (" UINT64_FORMAT "-" UINT64_FORMAT ")", + (uint64_t)_c, (uint64_t)v); + _c -= v; + } + + void reset() { _c = 0; } + +#ifdef ASSERT + void check(T expected) const { + assert(_c == expected, "Counter mismatch: %d, expected: %d.", + (int)_c, (int)expected); + } +#endif + +}; + +// Atomic variant of AbstractCounter. +template +class AbstractAtomicCounter { + + volatile T _c; + + // Only allow unsigned values for now + STATIC_ASSERT(IsSigned::value == false); + +public: + + AbstractAtomicCounter() : _c(0) {} + + T get() const { return _c; } + + void increment() { + Atomic::inc(&_c); + } + + void decrement() { + Atomic::dec(&_c); + } + + void increment_by(T v) { + Atomic::add(&_c, v); + } + + void decrement_by(T v) { + Atomic::sub(&_c, v); + } + +#ifdef ASSERT + void check(T expected) const { + assert(_c == expected, "Counter mismatch: %d, expected: %d.", + (int)_c, (int)expected); + } +#endif + +}; + +typedef AbstractCounter SizeCounter; +typedef AbstractCounter IntCounter; + +typedef AbstractAtomicCounter SizeAtomicCounter; + +// We often count memory ranges (blocks, chunks etc). +// Make a helper class for that. +template +class AbstractMemoryRangeCounter { + + AbstractCounter _count; + AbstractCounter _total_size; + +public: + + void add(T_size s) { + if(s > 0) { + _count.increment(); + _total_size.increment_by(s); + } + } + + void sub(T_size s) { + if(s > 0) { + _count.decrement(); + _total_size.decrement_by(s); + } + } + + T_num count() const { return _count.get(); } + T_size total_size() const { return _total_size.get(); } + +#ifdef ASSERT + void check(T_num expected_count, T_size expected_size) const { + _count.check(expected_count); + _total_size.check(expected_size); + } + void check(const AbstractMemoryRangeCounter& other) const { + check(other.count(), other.total_size()); + } +#endif + +}; + +typedef AbstractMemoryRangeCounter MemRangeCounter; + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_COUNTERS_HPP + diff --git a/src/hotspot/share/memory/metaspace/freeBlocks.cpp b/src/hotspot/share/memory/metaspace/freeBlocks.cpp new file mode 100644 index 00000000000..d6901cb7608 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/freeBlocks.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "memory/metaspace/freeBlocks.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" + +namespace metaspace { + +void FreeBlocks::add_block(MetaWord* p, size_t word_size) { + assert(word_size >= MinWordSize, "sanity (" SIZE_FORMAT ")", word_size); + if (word_size > MaxSmallBlocksWordSize) { + _tree.add_block(p, word_size); + } else { + _small_blocks.add_block(p, word_size); + } +} + +MetaWord* FreeBlocks::remove_block(size_t requested_word_size) { + assert(requested_word_size >= MinWordSize, + "requested_word_size too small (" SIZE_FORMAT ")", requested_word_size); + size_t real_size = 0; + MetaWord* p = NULL; + if (requested_word_size > MaxSmallBlocksWordSize) { + p = _tree.remove_block(requested_word_size, &real_size); + } else { + p = _small_blocks.remove_block(requested_word_size, &real_size); + } + if (p != NULL) { + // Blocks which are larger than a certain threshold are split and + // the remainder is handed back to the manager. + const size_t waste = real_size - requested_word_size; + if (waste > MinWordSize) { + add_block(p + requested_word_size, waste); + } + } + return p; +} + +} // namespace metaspace + diff --git a/src/hotspot/share/memory/metaspace/freeBlocks.hpp b/src/hotspot/share/memory/metaspace/freeBlocks.hpp new file mode 100644 index 00000000000..acb44387615 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/freeBlocks.hpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_FREEBLOCKS_HPP +#define SHARE_MEMORY_METASPACE_FREEBLOCKS_HPP + +#include "memory/allocation.hpp" +#include "memory/metaspace/binList.hpp" +#include "memory/metaspace/blockTree.hpp" +#include "memory/metaspace/counters.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" + +class outputStream; + +namespace metaspace { + +// Class FreeBlocks manages deallocated blocks in Metaspace. +// +// In Metaspace, allocated memory blocks may be release prematurely. This is +// uncommon (otherwise an arena-based allocation scheme would not make sense). +// It can happen e.g. when class loading fails or when bytecode gets rewritten. +// +// All these released blocks should be reused, so they are collected. Since these +// blocks are embedded into chunks which are still in use by a live arena, +// we cannot just give these blocks to anyone; only the owner of this arena can +// reuse these blocks. Therefore these blocks are kept at arena-level. +// +// The structure to manage these released blocks at arena level is class FreeBlocks. +// +// FreeBlocks is optimized toward the typical size and number of deallocated +// blocks. The vast majority of them (about 90%) are below 16 words in size, +// but there is a significant portion of memory blocks much larger than that, +// leftover space from retired chunks, see MetaspaceArena::retire_current_chunk(). +// +// Since the vast majority of blocks are small or very small, FreeBlocks consists +// internally of two separate structures to keep very small blocks and other blocks. +// Very small blocks are kept in a bin list (see binlist.hpp) and larger blocks in +// a BST (see blocktree.hpp). + +class FreeBlocks : public CHeapObj { + + // _small_blocks takes care of small to very small blocks. + BinList32 _small_blocks; + + // A BST for larger blocks, only for blocks which are too large + // to fit into _smallblocks. + BlockTree _tree; + + // Cutoff point: blocks larger than this size are kept in the + // tree, blocks smaller than or equal to this size in the bin list. + const size_t MaxSmallBlocksWordSize = BinList32::MaxWordSize; + +public: + + // Smallest blocks we can keep in this structure. + const static size_t MinWordSize = BinList32::MinWordSize; + + // Add a block to the deallocation management. + void add_block(MetaWord* p, size_t word_size); + + // Retrieve a block of at least requested_word_size. + MetaWord* remove_block(size_t requested_word_size); + +#ifdef ASSERT + void verify() const { + _tree.verify(); + _small_blocks.verify(); + }; +#endif + + // Returns number of blocks. + int count() const { + return _small_blocks.count() + _tree.count(); + } + + // Returns total size, in words, of all elements. + size_t total_size() const { + return _small_blocks.total_size() + _tree.total_size(); + } + + // Returns true if empty. + bool is_empty() const { + return _small_blocks.is_empty() && _tree.is_empty(); + } + +}; + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_FREEBLOCKS_HPP diff --git a/src/hotspot/share/memory/metaspace/freeChunkList.cpp b/src/hotspot/share/memory/metaspace/freeChunkList.cpp new file mode 100644 index 00000000000..90dec13c16f --- /dev/null +++ b/src/hotspot/share/memory/metaspace/freeChunkList.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "memory/metaspace/freeChunkList.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" + +namespace metaspace { + +void FreeChunkList::print_on(outputStream* st) const { + if (_num_chunks.get() > 0) { + for (const Metachunk* c = _first; c != NULL; c = c->next()) { + st->print(" - <"); + c->print_on(st); + st->print(">"); + } + st->print(" - total : %d chunks.", _num_chunks.get()); + } else { + st->print("empty"); + } +} + +#ifdef ASSERT + +bool FreeChunkList::contains(const Metachunk* c) const { + for (Metachunk* c2 = _first; c2 != NULL; c2 = c2->next()) { + if (c2 == c) { + return true; + } + } + return false; +} + +void FreeChunkList::verify() const { + if (_first == NULL) { + assert(_last == NULL, "Sanity"); + } else { + assert(_last != NULL, "Sanity"); + size_t committed = 0; + int num = 0; + bool uncommitted = (_first->committed_words() == 0); + for (Metachunk* c = _first; c != NULL; c = c->next()) { + assert(c->is_free(), "Chunks in freelist should be free"); + assert(c->used_words() == 0, "Chunk in freelist should have not used words."); + assert(c->level() == _first->level(), "wrong level"); + assert(c->next() == NULL || c->next()->prev() == c, "front link broken"); + assert(c->prev() == NULL || c->prev()->next() == c, "back link broken"); + assert(c != c->prev() && c != c->next(), "circle"); + c->verify(); + committed += c->committed_words(); + num++; + } + _num_chunks.check(num); + _committed_word_size.check(committed); + } +} + +#endif // ASSERT + +// Returns total size in all lists (regardless of commit state of underlying memory) +size_t FreeChunkListVector::word_size() const { + size_t sum = 0; + for (chunklevel_t l = chunklevel::LOWEST_CHUNK_LEVEL; l <= chunklevel::HIGHEST_CHUNK_LEVEL; l++) { + sum += list_for_level(l)->num_chunks() * chunklevel::word_size_for_level(l); + } + return sum; +} + +// Returns total committed size in all lists +size_t FreeChunkListVector::committed_word_size() const { + size_t sum = 0; + for (chunklevel_t l = chunklevel::LOWEST_CHUNK_LEVEL; l <= chunklevel::HIGHEST_CHUNK_LEVEL; l++) { + sum += list_for_level(l)->committed_word_size(); + } + return sum; +} + +// Returns total committed size in all lists +int FreeChunkListVector::num_chunks() const { + int n = 0; + for (chunklevel_t l = chunklevel::LOWEST_CHUNK_LEVEL; l <= chunklevel::HIGHEST_CHUNK_LEVEL; l++) { + n += list_for_level(l)->num_chunks(); + } + return n; +} + +// Look for a chunk: starting at level, up to and including max_level, +// return the first chunk whose committed words >= min_committed_words. +// Return NULL if no such chunk was found. +Metachunk* FreeChunkListVector::search_chunk_ascending(chunklevel_t level, chunklevel_t max_level, size_t min_committed_words) { + assert(min_committed_words <= chunklevel::word_size_for_level(max_level), + "min chunk size too small to hold min_committed_words"); + for (chunklevel_t l = level; l <= max_level; l++) { + FreeChunkList* list = list_for_level(l); + Metachunk* c = list->first_minimally_committed(min_committed_words); + if (c != NULL) { + list->remove(c); + return c; + } + } + return NULL; +} + +// Look for a chunk: starting at level, down to (including) the root chunk level, +// return the first chunk whose committed words >= min_committed_words. +// Return NULL if no such chunk was found. +Metachunk* FreeChunkListVector::search_chunk_descending(chunklevel_t level, size_t min_committed_words) { + for (chunklevel_t l = level; l >= chunklevel::LOWEST_CHUNK_LEVEL; l --) { + FreeChunkList* list = list_for_level(l); + Metachunk* c = list->first_minimally_committed(min_committed_words); + if (c != NULL) { + list->remove(c); + return c; + } + } + return NULL; +} + +void FreeChunkListVector::print_on(outputStream* st) const { + for (chunklevel_t l = chunklevel::LOWEST_CHUNK_LEVEL; l <= chunklevel::HIGHEST_CHUNK_LEVEL; l++) { + st->print("-- List[" CHKLVL_FORMAT "]: ", l); + list_for_level(l)->print_on(st); + st->cr(); + } + st->print_cr("total chunks: %d, total word size: " SIZE_FORMAT ", committed word size: " SIZE_FORMAT ".", + num_chunks(), word_size(), committed_word_size()); +} + +#ifdef ASSERT + +void FreeChunkListVector::verify() const { + for (chunklevel_t l = chunklevel::LOWEST_CHUNK_LEVEL; l <= chunklevel::HIGHEST_CHUNK_LEVEL; l++) { + list_for_level(l)->verify(); + } +} + +bool FreeChunkListVector::contains(const Metachunk* c) const { + for (chunklevel_t l = chunklevel::LOWEST_CHUNK_LEVEL; l <= chunklevel::HIGHEST_CHUNK_LEVEL; l++) { + if (list_for_level(l)->contains(c)) { + return true; + } + } + return false; +} + +#endif // ASSERT + +} // namespace metaspace + diff --git a/src/hotspot/share/memory/metaspace/freeChunkList.hpp b/src/hotspot/share/memory/metaspace/freeChunkList.hpp new file mode 100644 index 00000000000..ed27a1414c0 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/freeChunkList.hpp @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_FREECHUNKLIST_HPP +#define SHARE_MEMORY_METASPACE_FREECHUNKLIST_HPP + +#include "memory/allocation.hpp" +#include "memory/metaspace/chunklevel.hpp" +#include "memory/metaspace/counters.hpp" +#include "memory/metaspace/metachunk.hpp" +#include "memory/metaspace/metachunkList.hpp" + +class outputStream; + +namespace metaspace { + +// This is the free list underlying the ChunkManager. +// +// Chunks are kept in a vector of double-linked double-headed lists +// (using Metachunk::prev/next). One list per chunk level exists. +// +// Chunks in these lists are roughly ordered: uncommitted chunks +// are added to the back of the list, fully or partially committed +// chunks to the front. We do not use a more elaborate sorting on +// insert since that path is used during class unloading, hence timing +// sensitive. +// +// During retrieval (at class loading), we search the list for a chunk +// of at least n committed words to satisfy the caller requested +// committed word size. We stop searching at the first fully uncommitted +// chunk. +// +// Note that even though this is an O(n) search, partially committed chunks are +// very rare. A partially committed chunk is one spanning multiple commit +// granules, of which some are committed and some are not. +// If metaspace reclamation is on (MetaspaceReclaimPolicy=balanced|aggressive), these +// chunks will become uncommitted after they are returned to the ChunkManager. +// If metaspace reclamation is off (MetaspaceReclaimPolicy=none) they are fully +// committed when handed out and will not be uncommitted when returned to the +// ChunkManager. +// +// Therefore in all likelihood the chunk lists only contain fully committed or +// fully uncommitted chunks; either way search will stop at the first chunk. + +class FreeChunkList { + + Metachunk* _first; + Metachunk* _last; + + IntCounter _num_chunks; + SizeCounter _committed_word_size; + + void add_front(Metachunk* c) { + if (_first == NULL) { + assert(_last == NULL, "Sanity"); + _first = _last = c; + c->set_prev(NULL); + c->set_next(NULL); + } else { + assert(_last != NULL, "Sanity"); + c->set_next(_first); + c->set_prev(NULL); + _first->set_prev(c); + _first = c; + } + } + + // Add chunk to the back of the list. + void add_back(Metachunk* c) { + if (_last == NULL) { + assert(_first == NULL, "Sanity"); + _last = _first = c; + c->set_prev(NULL); + c->set_next(NULL); + } else { + assert(_first != NULL, "Sanity"); + c->set_next(NULL); + c->set_prev(_last); + _last->set_next(c); + _last = c; + } + } + +public: + + FreeChunkList() : + _first(NULL), + _last(NULL) + {} + + // Remove given chunk from anywhere in the list. + Metachunk* remove(Metachunk* c) { + assert(contains(c), "Must be contained here"); + Metachunk* pred = c->prev(); + Metachunk* succ = c->next(); + if (pred) { + pred->set_next(succ); + } + if (succ) { + succ->set_prev(pred); + } + if (_first == c) { + _first = succ; + } + if (_last == c) { + _last = pred; + } + c->set_next(NULL); + c->set_prev(NULL); + _committed_word_size.decrement_by(c->committed_words()); + _num_chunks.decrement(); + return c; + } + + void add(Metachunk* c) { + assert(contains(c) == false, "Chunk already in freelist"); + assert(_first == NULL || _first->level() == c->level(), + "List should only contains chunks of the same level."); + // Uncomitted chunks go to the back, fully or partially committed to the front. + if (c->committed_words() == 0) { + add_back(c); + } else { + add_front(c); + } + _committed_word_size.increment_by(c->committed_words()); + _num_chunks.increment(); + } + + // Removes the first chunk from the list and returns it. Returns NULL if list is empty. + Metachunk* remove_first() { + Metachunk* c = _first; + if (c != NULL) { + remove(c); + } + return c; + } + + // Returns reference to the first chunk in the list, or NULL + Metachunk* first() const { return _first; } + + // Returns reference to the fist chunk in the list with a committed word + // level >= min_committed_words, or NULL. + Metachunk* first_minimally_committed(size_t min_committed_words) const { + // Since uncommitted chunks are added to the back we can stop looking once + // we encounter a fully uncommitted chunk. + Metachunk* c = first(); + while (c != NULL && + c->committed_words() < min_committed_words && + c->committed_words() > 0) { + c = c->next(); + } + if (c != NULL && + c->committed_words() >= min_committed_words) { + return c; + } + return NULL; + } + +#ifdef ASSERT + bool contains(const Metachunk* c) const; + void verify() const; +#endif + + // Returns number of chunks + int num_chunks() const { return _num_chunks.get(); } + + // Returns total committed word size + size_t committed_word_size() const { return _committed_word_size.get(); } + + void print_on(outputStream* st) const; + +}; + +// A vector of free chunk lists, one per chunk level +class FreeChunkListVector { + + FreeChunkList _lists[chunklevel::NUM_CHUNK_LEVELS]; + + const FreeChunkList* list_for_level(chunklevel_t lvl) const { DEBUG_ONLY(chunklevel::check_valid_level(lvl)); return _lists + lvl; } + FreeChunkList* list_for_level(chunklevel_t lvl) { DEBUG_ONLY(chunklevel::check_valid_level(lvl)); return _lists + lvl; } + + const FreeChunkList* list_for_chunk(const Metachunk* c) const { return list_for_level(c->level()); } + FreeChunkList* list_for_chunk(const Metachunk* c) { return list_for_level(c->level()); } + +public: + + // Remove given chunk from its list. List must contain that chunk. + void remove(Metachunk* c) { + list_for_chunk(c)->remove(c); + } + + // Remove first node unless empty. Returns node or NULL. + Metachunk* remove_first(chunklevel_t lvl) { + Metachunk* c = list_for_level(lvl)->remove_first(); + return c; + } + + void add(Metachunk* c) { + list_for_chunk(c)->add(c); + } + + // Returns number of chunks for a given level. + int num_chunks_at_level(chunklevel_t lvl) const { + return list_for_level(lvl)->num_chunks(); + } + + // Returns number of chunks for a given level. + size_t committed_word_size_at_level(chunklevel_t lvl) const { + return list_for_level(lvl)->committed_word_size(); + } + + // Returns reference to first chunk at this level, or NULL if sublist is empty. + Metachunk* first_at_level(chunklevel_t lvl) const { + return list_for_level(lvl)->first(); + } + + // Look for a chunk: starting at level, up to and including max_level, + // return the first chunk whose committed words >= min_committed_words. + // Return NULL if no such chunk was found. + Metachunk* search_chunk_ascending(chunklevel_t level, chunklevel_t max_level, + size_t min_committed_words); + + // Look for a chunk: starting at level, down to (including) the root chunk level, + // return the first chunk whose committed words >= min_committed_words. + // Return NULL if no such chunk was found. + Metachunk* search_chunk_descending(chunklevel_t level, size_t min_committed_words); + + // Returns total size in all lists (regardless of commit state of underlying memory) + size_t word_size() const; + + // Returns total committed size in all lists + size_t committed_word_size() const; + + // Returns number of chunks in all lists + int num_chunks() const; + +#ifdef ASSERT + bool contains(const Metachunk* c) const; + void verify() const; +#endif + + void print_on(outputStream* st) const; + +}; + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_FREECHUNKLIST_HPP diff --git a/src/hotspot/share/memory/metaspace/metablock.hpp b/src/hotspot/share/memory/metaspace/internalStats.cpp similarity index 56% rename from src/hotspot/share/memory/metaspace/metablock.hpp rename to src/hotspot/share/memory/metaspace/internalStats.cpp index 033bf29590a..d7b0e449505 100644 --- a/src/hotspot/share/memory/metaspace/metablock.hpp +++ b/src/hotspot/share/memory/metaspace/internalStats.cpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,29 +23,29 @@ * */ -#ifndef SHARE_MEMORY_METASPACE_METABLOCK_HPP -#define SHARE_MEMORY_METASPACE_METABLOCK_HPP - -#include "memory/metaspace/metabase.hpp" +#include "precompiled.hpp" +#include "memory/metaspace/internalStats.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" namespace metaspace { -// Metablock is the unit of allocation from a Chunk. -// -// A Metablock may be reused by its SpaceManager but are never moved between -// SpaceManagers. There is no explicit link to the Metachunk -// from which it was allocated. Metablock may be deallocated and -// put on a freelist but the space is never freed, rather -// the Metachunk it is a part of will be deallocated when it's -// associated class loader is collected. - -class Metablock : public Metabase { - friend class VMStructs; - public: - Metablock(size_t word_size) : Metabase(word_size) {} -}; +#define MATERIALIZE_COUNTER(name) uintx InternalStats::_##name; +#define MATERIALIZE_ATOMIC_COUNTER(name) volatile uintx InternalStats::_##name; + ALL_MY_COUNTERS(MATERIALIZE_COUNTER, MATERIALIZE_ATOMIC_COUNTER) +#undef MATERIALIZE_COUNTER +#undef MATERIALIZE_ATOMIC_COUNTER + +void InternalStats::print_on(outputStream* st) { + +#define xstr(s) str(s) +#define str(s) #s + +#define PRINT_COUNTER(name) st->print_cr("%s: " UINTX_FORMAT ".", xstr(name), _##name); + ALL_MY_COUNTERS(PRINT_COUNTER, PRINT_COUNTER) +#undef PRINT_COUNTER + +} } // namespace metaspace -#endif // SHARE_MEMORY_METASPACE_METABLOCK_HPP diff --git a/src/hotspot/share/memory/metaspace/internalStats.hpp b/src/hotspot/share/memory/metaspace/internalStats.hpp new file mode 100644 index 00000000000..272b784dde4 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/internalStats.hpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_INTERNALSTATS_HPP +#define SHARE_MEMORY_METASPACE_INTERNALSTATS_HPP + +#include "memory/allocation.hpp" +#include "runtime/atomic.hpp" +#include "utilities/globalDefinitions.hpp" + +class outputStream; + +namespace metaspace { + +// These are some counters useful for debugging and analyzing Metaspace problems. +// They get printed as part of the Metaspace report (e.g. via jcmd VM.metaspace) + +class InternalStats : public AllStatic { + + // Note: all counters which are modified on the classloader local allocation path + // (not under ExpandLock protection) have to be atomic. + +#define ALL_MY_COUNTERS(x, x_atomic) \ + \ + /* Number of allocations. */ \ + DEBUG_ONLY(x_atomic(num_allocs)) \ + \ + /* Number of external deallocations */ \ + /* (excluding retired chunk remains) */ \ + DEBUG_ONLY(x_atomic(num_deallocs)) \ + \ + /* Number of times an allocation was satisfied */ \ + /* from deallocated blocks. */ \ + DEBUG_ONLY(x_atomic(num_allocs_from_deallocated_blocks)) \ + \ + /* Number of times an arena retired a chunk */ \ + DEBUG_ONLY(x_atomic(num_chunks_retired)) \ + \ + /* Number of times an allocation failed */ \ + /* because we hit a limit. */ \ + x_atomic(num_allocs_failed_limit) \ + \ + /* Number of times an arena was born ... */ \ + x_atomic(num_arena_births) \ + /* ... and died. */ \ + x_atomic(num_arena_deaths) \ + \ + /* Number of times VirtualSpaceNode were */ \ + /* born... */ \ + x(num_vsnodes_births) \ + /* ... and died. */ \ + x(num_vsnodes_deaths) \ + \ + /* Number of times we committed space. */ \ + x(num_space_committed) \ + /* Number of times we uncommitted space. */ \ + x(num_space_uncommitted) \ + \ + /* Number of times a chunk was returned to the */ \ + /* freelist (external only). */ \ + x(num_chunks_returned_to_freelist) \ + /* Number of times a chunk was taken from */ \ + /* freelist (external only) */ \ + x(num_chunks_taken_from_freelist) \ + \ + /* Number of successful chunk merges */ \ + x(num_chunk_merges) \ + /* Number of chunk splits */ \ + x(num_chunk_splits) \ + /* Number of chunk in place enlargements */ \ + x(num_chunks_enlarged) \ + \ + /* Number of times we did a purge */ \ + x(num_purges) \ + +// Note: We use uintx since 32bit platforms lack 64bit atomic add; this increases +// the possibility of counter overflows but the probability is very low for any counter +// but num_allocs; note that these counters are for human eyes only. +#define DEFINE_COUNTER(name) static uintx _##name; +#define DEFINE_ATOMIC_COUNTER(name) static volatile uintx _##name; + ALL_MY_COUNTERS(DEFINE_COUNTER, DEFINE_ATOMIC_COUNTER) +#undef DEFINE_COUNTER +#undef DEFINE_ATOMIC_COUNTER + +public: + +// incrementors +#define INCREMENTOR(name) static void inc_##name() { _##name++; } +#define INCREMENTOR_ATOMIC(name) static void inc_##name() { Atomic::inc(&_##name); } + ALL_MY_COUNTERS(INCREMENTOR, INCREMENTOR_ATOMIC) +#undef INCREMENTOR +#undef INCREMENTOR_ATOMIC + +// getters +#define GETTER(name) static uint64_t name() { return _##name; } + ALL_MY_COUNTERS(GETTER, GETTER) +#undef GETTER + + static void print_on(outputStream* st); + +}; + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_INTERNALSTATS_HPP diff --git a/src/hotspot/share/memory/metaspace/metabase.hpp b/src/hotspot/share/memory/metaspace/metabase.hpp deleted file mode 100644 index 0f9f32021f8..00000000000 --- a/src/hotspot/share/memory/metaspace/metabase.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_MEMORY_METASPACE_METABASE_HPP -#define SHARE_MEMORY_METASPACE_METABASE_HPP - -#include "utilities/globalDefinitions.hpp" - -namespace metaspace { - -// Super class of Metablock and Metachunk to allow them to -// be put on the FreeList and in the BinaryTreeDictionary. -template -class Metabase { - size_t _word_size; - T* _next; - T* _prev; - - protected: - Metabase(size_t word_size) : _word_size(word_size), _next(NULL), _prev(NULL) {} - - public: - T* next() const { return _next; } - T* prev() const { return _prev; } - void set_next(T* v) { _next = v; assert(v != this, "Boom");} - void set_prev(T* v) { _prev = v; assert(v != this, "Boom");} - void clear_next() { set_next(NULL); } - void clear_prev() { set_prev(NULL); } - - size_t size() const { return _word_size; } - - void link_next(T* ptr) { set_next(ptr); } - void link_prev(T* ptr) { set_prev(ptr); } - void link_after(T* ptr) { - link_next(ptr); - if (ptr != NULL) ptr->link_prev((T*)this); - } - - uintptr_t* end() const { return ((uintptr_t*) this) + size(); } - - bool cantCoalesce() const { return false; } - - // Debug support -#ifdef ASSERT - void* prev_addr() const { return (void*)&_prev; } - void* next_addr() const { return (void*)&_next; } - void* size_addr() const { return (void*)&_word_size; } -#endif - bool verify_chunk_in_free_list(T* tc) const { return true; } - bool verify_par_locked() { return true; } - - void assert_is_mangled() const {/* Don't check "\*/} - - bool is_free() { return true; } -}; - -} // namespace metaspace - -#endif // SHARE_MEMORY_METASPACE_METABASE_HPP diff --git a/src/hotspot/share/memory/metaspace/metachunk.cpp b/src/hotspot/share/memory/metaspace/metachunk.cpp index 0708387cb8f..93862d90a8f 100644 --- a/src/hotspot/share/memory/metaspace/metachunk.cpp +++ b/src/hotspot/share/memory/metaspace/metachunk.cpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,149 +24,285 @@ */ #include "precompiled.hpp" -#include "memory/allocation.hpp" +#include "logging/log.hpp" #include "memory/metaspace/metachunk.hpp" -#include "memory/metaspace/occupancyMap.hpp" +#include "memory/metaspace/metaspaceCommon.hpp" +#include "memory/metaspace/metaspaceSettings.hpp" #include "memory/metaspace/virtualSpaceNode.hpp" +#include "runtime/mutexLocker.hpp" #include "utilities/align.hpp" #include "utilities/copy.hpp" #include "utilities/debug.hpp" namespace metaspace { -size_t Metachunk::object_alignment() { - // Must align pointers and sizes to 8, - // so that 64 bit types get correctly aligned. - const size_t alignment = 8; - - // Make sure that the Klass alignment also agree. - STATIC_ASSERT(alignment == (size_t)KlassAlignmentInBytes); - - return alignment; +// Return a single char presentation of the state ('f', 'u', 'd') +char Metachunk::get_state_char() const { + switch (_state) { + case State::Free: return 'f'; + case State::InUse: return 'u'; + case State::Dead: return 'd'; + } + return '?'; } -size_t Metachunk::overhead() { - return align_up(sizeof(Metachunk), object_alignment()) / BytesPerWord; +#ifdef ASSERT +void Metachunk::assert_have_expand_lock() { + assert_lock_strong(MetaspaceExpand_lock); } +#endif + +// Commit uncommitted section of the chunk. +// Fails if we hit a commit limit. +bool Metachunk::commit_up_to(size_t new_committed_words) { + // Please note: + // + // VirtualSpaceNode::ensure_range_is_committed(), when called over a range containing both committed and uncommitted parts, + // will replace the whole range with a new mapping, thus erasing the existing content in the committed parts. Therefore + // we must make sure never to call VirtualSpaceNode::ensure_range_is_committed() over a range containing live data. + // + // Luckily, this cannot happen by design. We have two cases: + // + // 1) chunks equal or larger than a commit granule. + // In this case, due to chunk geometry, the chunk should cover whole commit granules (in other words, a chunk equal or larger than + // a commit granule will never share a granule with a neighbor). That means whatever we commit or uncommit here does not affect + // neighboring chunks. We only have to take care not to re-commit used parts of ourself. We do this by moving the committed_words + // limit in multiple of commit granules. + // + // 2) chunks smaller than a commit granule. + // In this case, a chunk shares a single commit granule with its neighbors. But this never can be a problem: + // - Either the commit granule is already committed (and maybe the neighbors contain live data). In that case calling + // ensure_range_is_committed() will do nothing. + // - Or the commit granule is not committed, but in this case, the neighbors are uncommitted too and cannot contain live data. -// Metachunk methods - -Metachunk::Metachunk(ChunkIndex chunktype, bool is_class, size_t word_size, - VirtualSpaceNode* container) - : Metabase(word_size), - _container(container), - _top(NULL), - _sentinel(CHUNK_SENTINEL), - _chunk_type(chunktype), - _is_class(is_class), - _origin(origin_normal), - _use_count(0) -{ - _top = initial_top(); - set_is_tagged_free(false); #ifdef ASSERT - mangle(uninitMetaWordVal); - verify(); + if (word_size() >= Settings::commit_granule_words()) { + // case (1) + assert(is_aligned(base(), Settings::commit_granule_bytes()) && + is_aligned(end(), Settings::commit_granule_bytes()), + "Chunks larger than a commit granule must cover whole granules."); + assert(is_aligned(_committed_words, Settings::commit_granule_words()), + "The commit boundary must be aligned to commit granule size"); + assert(_used_words <= _committed_words, "Sanity"); + } else { + // case (2) + assert(_committed_words == 0 || _committed_words == word_size(), "Sanity"); + } #endif + + // We should hold the expand lock at this point. + assert_lock_strong(MetaspaceExpand_lock); + + const size_t commit_from = _committed_words; + const size_t commit_to = MIN2(align_up(new_committed_words, Settings::commit_granule_words()), word_size()); + assert(commit_from >= used_words(), "Sanity"); + assert(commit_to <= word_size(), "Sanity"); + if (commit_to > commit_from) { + log_debug(metaspace)("Chunk " METACHUNK_FORMAT ": attempting to move commit line to " + SIZE_FORMAT " words.", METACHUNK_FORMAT_ARGS(this), commit_to); + if (!_vsnode->ensure_range_is_committed(base() + commit_from, commit_to - commit_from)) { + DEBUG_ONLY(verify();) + return false; + } + } + + // Remember how far we have committed. + _committed_words = commit_to; + DEBUG_ONLY(verify();) + return true; } -MetaWord* Metachunk::allocate(size_t word_size) { - MetaWord* result = NULL; - // If available, bump the pointer to allocate. - if (free_word_size() >= word_size) { - result = _top; - _top = _top + word_size; +// Ensure that chunk is committed up to at least new_committed_words words. +// Fails if we hit a commit limit. +bool Metachunk::ensure_committed(size_t new_committed_words) { + bool rc = true; + if (new_committed_words > committed_words()) { + MutexLocker cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); + rc = commit_up_to(new_committed_words); } - return result; + return rc; } -// _bottom points to the start of the chunk including the overhead. -size_t Metachunk::used_word_size() const { - return pointer_delta(_top, bottom(), sizeof(MetaWord)); +bool Metachunk::ensure_committed_locked(size_t new_committed_words) { + // the .._locked() variant should be called if we own the lock already. + assert_lock_strong(MetaspaceExpand_lock); + bool rc = true; + if (new_committed_words > committed_words()) { + rc = commit_up_to(new_committed_words); + } + return rc; } -size_t Metachunk::free_word_size() const { - return pointer_delta(end(), _top, sizeof(MetaWord)); +// Uncommit chunk area. The area must be a common multiple of the +// commit granule size (in other words, we cannot uncommit chunks smaller than +// a commit granule size). +void Metachunk::uncommit() { + MutexLocker cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); + uncommit_locked(); } -void Metachunk::print_on(outputStream* st) const { - st->print_cr("Metachunk:" - " bottom " PTR_FORMAT " top " PTR_FORMAT - " end " PTR_FORMAT " size " SIZE_FORMAT " (%s)", - p2i(bottom()), p2i(_top), p2i(end()), word_size(), - chunk_size_name(get_chunk_type())); - if (Verbose) { - st->print_cr(" used " SIZE_FORMAT " free " SIZE_FORMAT, - used_word_size(), free_word_size()); +void Metachunk::uncommit_locked() { + // Only uncommit chunks which are free, have no used words set (extra precaution) and are equal or larger in size than a single commit granule. + assert_lock_strong(MetaspaceExpand_lock); + assert(_state == State::Free && _used_words == 0 && word_size() >= Settings::commit_granule_words(), + "Only free chunks equal or larger than commit granule size can be uncommitted " + "(chunk " METACHUNK_FULL_FORMAT ").", METACHUNK_FULL_FORMAT_ARGS(this)); + if (word_size() >= Settings::commit_granule_words()) { + _vsnode->uncommit_range(base(), word_size()); + _committed_words = 0; } } +void Metachunk::set_committed_words(size_t v) { + // Set committed words. Since we know that we only commit whole commit granules, we can round up v here. + v = MIN2(align_up(v, Settings::commit_granule_words()), word_size()); + _committed_words = v; +} + +// Allocate word_size words from this chunk (word_size must be aligned to +// allocation_alignment_words). +// +// Caller must make sure the chunk is both large enough and committed far enough +// to hold the allocation. Will always work. +// +MetaWord* Metachunk::allocate(size_t request_word_size) { + // Caller must have made sure this works + assert(free_words() >= request_word_size, "Chunk too small."); + assert(free_below_committed_words() >= request_word_size, "Chunk not committed."); + MetaWord* const p = top(); + _used_words += request_word_size; + SOMETIMES(verify();) + return p; +} #ifdef ASSERT -void Metachunk::mangle(juint word_value) { - // Overwrite the payload of the chunk and not the links that - // maintain list of chunks. - HeapWord* start = (HeapWord*)initial_top(); - size_t size = word_size() - overhead(); - Copy::fill_to_words(start, size, word_value); + +// Zap this structure. +void Metachunk::zap_header(uint8_t c) { + memset(this, c, sizeof(Metachunk)); } -void Metachunk::verify() const { - assert(is_valid_sentinel(), "Chunk " PTR_FORMAT ": sentinel invalid", p2i(this)); - const ChunkIndex chunk_type = get_chunk_type(); - assert(is_valid_chunktype(chunk_type), "Chunk " PTR_FORMAT ": Invalid chunk type.", p2i(this)); - if (chunk_type != HumongousIndex) { - assert(word_size() == get_size_for_nonhumongous_chunktype(chunk_type, is_class()), - "Chunk " PTR_FORMAT ": wordsize " SIZE_FORMAT " does not fit chunk type %s.", - p2i(this), word_size(), chunk_size_name(chunk_type)); +// Verifies linking with neighbors in virtual space. +// Can only be done under expand lock protection. +void Metachunk::verify_neighborhood() const { + assert_lock_strong(MetaspaceExpand_lock); + assert(!is_dead(), "Do not call on dead chunks."); + if (is_root_chunk()) { + // Root chunks are all alone in the world. + assert(next_in_vs() == NULL || prev_in_vs() == NULL, "Root chunks should have no neighbors"); + } else { + // Non-root chunks have neighbors, at least one, possibly two. + assert(next_in_vs() != NULL || prev_in_vs() != NULL, + "A non-root chunk should have neighbors (chunk @" PTR_FORMAT + ", base " PTR_FORMAT ", level " CHKLVL_FORMAT ".", + p2i(this), p2i(base()), level()); + if (prev_in_vs() != NULL) { + assert(prev_in_vs()->end() == base(), + "Chunk " METACHUNK_FULL_FORMAT ": should be adjacent to predecessor: " METACHUNK_FULL_FORMAT ".", + METACHUNK_FULL_FORMAT_ARGS(this), METACHUNK_FULL_FORMAT_ARGS(prev_in_vs())); + assert(prev_in_vs()->next_in_vs() == this, + "Chunk " METACHUNK_FULL_FORMAT ": broken link to left neighbor: " METACHUNK_FULL_FORMAT " (" PTR_FORMAT ").", + METACHUNK_FULL_FORMAT_ARGS(this), METACHUNK_FULL_FORMAT_ARGS(prev_in_vs()), p2i(prev_in_vs()->next_in_vs())); + } + if (next_in_vs() != NULL) { + assert(end() == next_in_vs()->base(), + "Chunk " METACHUNK_FULL_FORMAT ": should be adjacent to successor: " METACHUNK_FULL_FORMAT ".", + METACHUNK_FULL_FORMAT_ARGS(this), METACHUNK_FULL_FORMAT_ARGS(next_in_vs())); + assert(next_in_vs()->prev_in_vs() == this, + "Chunk " METACHUNK_FULL_FORMAT ": broken link to right neighbor: " METACHUNK_FULL_FORMAT " (" PTR_FORMAT ").", + METACHUNK_FULL_FORMAT_ARGS(this), METACHUNK_FULL_FORMAT_ARGS(next_in_vs()), p2i(next_in_vs()->prev_in_vs())); + } + + // One of the neighbors must be the buddy. It can be whole or splintered. + + // The chunk following us or preceeding us may be our buddy or a splintered part of it. + Metachunk* buddy = is_leader() ? next_in_vs() : prev_in_vs(); + assert(buddy != NULL, "Missing neighbor."); + assert(!buddy->is_dead(), "Invalid buddy state."); + + // This neighbor is either or buddy (same level) or a splinter of our buddy - hence + // the level can never be smaller (aka the chunk size cannot be larger). + assert(buddy->level() >= level(), "Wrong level."); + + if (buddy->level() == level()) { + // If the buddy is of the same size as us, it is unsplintered. + assert(buddy->is_leader() == !is_leader(), + "Only one chunk can be leader in a pair"); + + // When direct buddies are neighbors, one or both should be in use, otherwise they should + // have been merged. + // But since we call this verification function from internal functions where we are about to merge or just did split, + // do not test this. We have RootChunkArea::verify_area_is_ideally_merged() for testing that. + if (is_leader()) { + assert(buddy->base() == end(), "Sanity"); + assert(is_aligned(base(), word_size() * 2 * BytesPerWord), "Sanity"); + } else { + assert(buddy->end() == base(), "Sanity"); + assert(is_aligned(buddy->base(), word_size() * 2 * BytesPerWord), "Sanity"); + } + } else { + // Buddy, but splintered, and this is a part of it. + if (is_leader()) { + assert(buddy->base() == end(), "Sanity"); + } else { + assert(buddy->end() > (base() - word_size()), "Sanity"); + } + } } - assert(is_valid_chunkorigin(get_origin()), "Chunk " PTR_FORMAT ": Invalid chunk origin.", p2i(this)); - assert(bottom() <= _top && _top <= (MetaWord*)end(), - "Chunk " PTR_FORMAT ": Chunk top out of chunk bounds.", p2i(this)); - - // For non-humongous chunks, starting address shall be aligned - // to its chunk size. Humongous chunks start address is - // aligned to specialized chunk size. - const size_t required_alignment = - (chunk_type != HumongousIndex ? word_size() : get_size_for_nonhumongous_chunktype(SpecializedIndex, is_class())) * sizeof(MetaWord); - assert(is_aligned((address)this, required_alignment), - "Chunk " PTR_FORMAT ": (size " SIZE_FORMAT ") not aligned to " SIZE_FORMAT ".", - p2i(this), word_size() * sizeof(MetaWord), required_alignment); } -#endif // ASSERT +volatile MetaWord dummy = 0; + +void Metachunk::verify() const { + // Note. This should be called under CLD lock protection. -// Helper, returns a descriptive name for the given index. -const char* chunk_size_name(ChunkIndex index) { - switch (index) { - case SpecializedIndex: - return "specialized"; - case SmallIndex: - return "small"; - case MediumIndex: - return "medium"; - case HumongousIndex: - return "humongous"; - default: - return "Invalid index"; + // We can verify everything except the _prev_in_vs/_next_in_vs pair. + // This is because neighbor chunks may be added concurrently, so we cannot rely + // on the content of _next_in_vs/_prev_in_vs unless we have the expand lock. + assert(!is_dead(), "Do not call on dead chunks."); + if (is_free()) { + assert(used_words() == 0, "free chunks are not used."); } -} -#ifdef ASSERT -void do_verify_chunk(Metachunk* chunk) { - guarantee(chunk != NULL, "Sanity"); - // Verify chunk itself; then verify that it is consistent with the - // occupany map of its containing node. - chunk->verify(); - VirtualSpaceNode* const vsn = chunk->container(); - OccupancyMap* const ocmap = vsn->occupancy_map(); - ocmap->verify_for_chunk(chunk); + // Note: only call this on a life Metachunk. + chunklevel::check_valid_level(level()); + + assert(base() != NULL, "No base ptr"); + assert(committed_words() >= used_words(), + "mismatch: committed: " SIZE_FORMAT ", used: " SIZE_FORMAT ".", + committed_words(), used_words()); + assert(word_size() >= committed_words(), + "mismatch: word_size: " SIZE_FORMAT ", committed: " SIZE_FORMAT ".", + word_size(), committed_words()); + + // Test base pointer + assert(base() != NULL, "Base pointer NULL"); + assert(vsnode() != NULL, "No space"); + vsnode()->check_pointer(base()); + + // Starting address shall be aligned to chunk size. + const size_t required_alignment = word_size() * sizeof(MetaWord); + assert_is_aligned(base(), required_alignment); + + // Test accessing the committed area. + SOMETIMES( + if (_committed_words > 0) { + for (const MetaWord* p = _base; p < _base + _committed_words; p += os::vm_page_size()) { + dummy = *p; + } + dummy = *(_base + _committed_words - 1); + } + ) } -#endif +#endif // ASSERT -void do_update_in_use_info_for_chunk(Metachunk* chunk, bool inuse) { - chunk->set_is_tagged_free(!inuse); - OccupancyMap* const ocmap = chunk->container()->occupancy_map(); - ocmap->set_region_in_use((MetaWord*)chunk, chunk->word_size(), inuse); +void Metachunk::print_on(outputStream* st) const { + // Note: must also work with invalid/random data. (e.g. do not call word_size()) + st->print("Chunk @" PTR_FORMAT ", state %c, base " PTR_FORMAT ", " + "level " CHKLVL_FORMAT " (" SIZE_FORMAT " words), " + "used " SIZE_FORMAT " words, committed " SIZE_FORMAT " words.", + p2i(this), get_state_char(), p2i(base()), level(), + (chunklevel::is_valid_level(level()) ? chunklevel::word_size_for_level(level()) : (size_t)-1), + used_words(), committed_words()); } } // namespace metaspace diff --git a/src/hotspot/share/memory/metaspace/metachunk.hpp b/src/hotspot/share/memory/metaspace/metachunk.hpp index c11ff7090fb..4f48346b78d 100644 --- a/src/hotspot/share/memory/metaspace/metachunk.hpp +++ b/src/hotspot/share/memory/metaspace/metachunk.hpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,152 +22,348 @@ * questions. * */ + #ifndef SHARE_MEMORY_METASPACE_METACHUNK_HPP #define SHARE_MEMORY_METASPACE_METACHUNK_HPP -#include "memory/metaspace/metabase.hpp" -#include "memory/metaspace/metaspaceCommon.hpp" +#include "memory/metaspace/chunklevel.hpp" +#include "memory/metaspace/counters.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" -class MetachunkTest; +class outputStream; namespace metaspace { class VirtualSpaceNode; -// Metachunk - Quantum of allocation from a Virtualspace -// Metachunks are reused (when freed are put on a global freelist) and -// have no permanent association to a SpaceManager. - -// +--------------+ <- end --+ --+ -// | | | | -// | | | free | -// | | | | -// | | | | size | capacity -// | | | | -// | | <- top -- + | -// | | | | -// | | | used | -// | | | | -// | | | | -// +--------------+ <- bottom --+ --+ - -enum ChunkOrigin { - // Chunk normally born (via take_from_committed) - origin_normal = 1, - // Chunk was born as padding chunk - origin_pad = 2, - // Chunk was born as leftover chunk in VirtualSpaceNode::retire - origin_leftover = 3, - // Chunk was born as result of a merge of smaller chunks - origin_merge = 4, - // Chunk was born as result of a split of a larger chunk - origin_split = 5, - - origin_minimum = origin_normal, - origin_maximum = origin_split, - origins_count = origin_maximum + 1 -}; - -inline bool is_valid_chunkorigin(ChunkOrigin origin) { - return origin == origin_normal || - origin == origin_pad || - origin == origin_leftover || - origin == origin_merge || - origin == origin_split; -} - -class Metachunk : public Metabase { - - friend class ::MetachunkTest; - - // The VirtualSpaceNode containing this chunk. - VirtualSpaceNode* const _container; - - // Current allocation top. - MetaWord* _top; - - // A 32bit sentinel for debugging purposes. - enum { CHUNK_SENTINEL = 0x4d4554EF, // "MET" - CHUNK_SENTINEL_INVALID = 0xFEEEEEEF +// A Metachunk is a contiguous metaspace memory region. It is used by +// a MetaspaceArena to allocate from via pointer bump (somewhat similar +// to a TLAB in java heap. +// +// The Metachunk object itself (the "chunk header") is separated from +// the memory region (the chunk payload) it describes. It also can have +// no payload (a "dead" chunk). In itself it lives in C-heap, managed +// as part of a pool of Metachunk headers (ChunkHeaderPool). +// +// +// +---------+ +---------+ +---------+ +// |MetaChunk| <--next/prev--> |MetaChunk| <--next/prev--> |MetaChunk| Chunk headers +// +---------+ +---------+ +---------+ in C-heap +// | | | +// base base base +// | / | +// / --------------- / +// / / ---------------------------- +// | | / +// v v v +// +---------+ +---------+ +-------------------+ +// | | | | | | +// | chunk | | chunk | | chunk | The real chunks ("payload") +// | | | | | | live in Metaspace +// +---------+ +---------+ +-------------------+ +// +// +// -- Metachunk state -- +// +// A Metachunk is "in-use" if it is part of a MetaspaceArena. That means +// its memory is used - or will be used shortly - to hold VM metadata +// on behalf of a class loader. +// +// A Metachunk is "free" if its payload is currently unused. In that +// case it is managed by a chunk freelist (the ChunkManager). +// +// A Metachunk is "dead" if it does not have a corresponding payload. +// In that case it lives as part of a freelist-of-dead-chunk-headers +// in the ChunkHeaderPool. +// +// A Metachunk is always part of a linked list. In-use chunks are part of +// the chunk list of a MetaspaceArena. Free chunks are in a freelist in +// the ChunkManager. Dead chunk headers are in a linked list as part +// of the ChunkHeaderPool. +// +// +// -- Level -- +// +// Metachunks are managed as part of a buddy style allocation scheme. +// Sized always in steps of power-of-2, ranging from the smallest chunk size +// (1Kb) to the largest (4Mb) (see chunklevel.hpp). +// Its size is encoded as level, with level 0 being the largest chunk +// size ("root chunk"). +// +// +// -- Payload commit state -- +// +// A Metachunk payload (the "real chunk") may be committed, partly committed +// or completely uncommitted. Technically, a payload may be committed +// "checkered" - i.e. committed and uncommitted parts may interleave - but the +// important part is how much contiguous space is committed starting +// at the base of the payload (since that's where we allocate). +// +// The Metachunk keeps track of how much space is committed starting +// at the base of the payload - which is a performace optimization - +// while underlying layers (VirtualSpaceNode->commitmask) keep track +// of the "real" commit state, aka which granules are committed, +// independent on what chunks reside above those granules. + +// +--------------+ <- end -----------+ ----------+ +// | | | | +// | | | | +// | | | | +// | | | | +// | | | | +// | ----------- | <- committed_top -- + | +// | | | | +// | | | "free" | +// | | | | size +// | | "free_below_ | | +// | | committed" | | +// | | | | +// | | | | +// | ----------- | <- top --------- + -------- | +// | | | | +// | | "used" | | +// | | | | +// +--------------+ <- start ----------+ ----------+ +// +// +// -- Relationships -- +// +// Chunks are managed by a binary buddy style allocator +// (see https://en.wikipedia.org/wiki/Buddy_memory_allocation). +// Chunks which are not a root chunk always have an adjoining buddy. +// The first chunk in a buddy pair is called the leader, the second +// one the follower. +// +// +----------+----------+ +// | leader | follower | +// +----------+----------+ +// +// +// -- Layout in address space -- +// +// In order to implement buddy style allocation, we need an easy way to get +// from one chunk to the Metachunk representing the neighboring chunks +// (preceding resp. following it in memory). +// But Metachunk headers and chunks are physically separated, and it is not +// possible to get the Metachunk* from the start of the chunk. Therefore +// Metachunk headers are part of a second linked list, describing the order +// in which their payload appears in memory: +// +// +---------+ +---------+ +---------+ +// |MetaChunk| <--next/prev_in_vs--> |MetaChunk| <--next/prev_in_vs--> |MetaChunk| +// +---------+ +---------+ +---------+ +// | | | +// base base base +// | / | +// / -------------------------- / +// / / -------------------------------------------------- +// | | / +// v v v +// +---------+---------+-------------------+ +// | chunk | chunk | chunk | +// +---------+---------+-------------------+ +// + +class Metachunk { + + // start of chunk memory; NULL if dead. + MetaWord* _base; + + // Used words. + size_t _used_words; + + // Size of the region, starting from base, which is guaranteed to be committed. In words. + // The actual size of committed regions may actually be larger. + // + // (This is a performance optimization. The underlying VirtualSpaceNode knows + // which granules are committed; but we want to avoid having to ask.) + size_t _committed_words; + + chunklevel_t _level; // aka size. + + // state_free: free, owned by a ChunkManager + // state_in_use: in-use, owned by a MetaspaceArena + // dead: just a hollow chunk header without associated memory, owned + // by chunk header pool. + enum class State : uint8_t { + Free = 0, + InUse = 1, + Dead = 2 }; - - uint32_t _sentinel; - - const ChunkIndex _chunk_type; - const bool _is_class; - // Whether the chunk is free (in freelist) or in use by some class loader. - bool _is_tagged_free; - - ChunkOrigin _origin; - int _use_count; - - MetaWord* initial_top() const { return (MetaWord*)this + overhead(); } - MetaWord* top() const { return _top; } - - public: - // Metachunks are allocated out of a MetadataVirtualSpace and - // and use some of its space to describe itself (plus alignment - // considerations). Metadata is allocated in the rest of the chunk. - // This size is the overhead of maintaining the Metachunk within - // the space. - - // Alignment of each allocation in the chunks. - static size_t object_alignment(); - - // Size of the Metachunk header, in words, including alignment. - static size_t overhead(); - - Metachunk(ChunkIndex chunktype, bool is_class, size_t word_size, VirtualSpaceNode* container); - - MetaWord* allocate(size_t word_size); - - VirtualSpaceNode* container() const { return _container; } - - MetaWord* bottom() const { return (MetaWord*) this; } - - // Reset top to bottom so chunk can be reused. - void reset_empty() { _top = initial_top(); clear_next(); clear_prev(); } - bool is_empty() { return _top == initial_top(); } - - // used (has been allocated) - // free (available for future allocations) - size_t word_size() const { return size(); } - size_t used_word_size() const; - size_t free_word_size() const; - - bool is_tagged_free() { return _is_tagged_free; } - void set_is_tagged_free(bool v) { _is_tagged_free = v; } - - bool contains(const void* ptr) { return bottom() <= ptr && ptr < _top; } + State _state; + + // We need unfortunately a back link to the virtual space node + // for splitting and merging nodes. + VirtualSpaceNode* _vsnode; + + // A chunk header is kept in a list: + // 1 in the list of used chunks inside a MetaspaceArena, if it is in use + // 2 in the list of free chunks inside a ChunkManager, if it is free + // 3 in the freelist of unused headers inside the ChunkHeaderPool, + // if it is unused (e.g. result of chunk merging) and has no associated + // memory area. + Metachunk* _prev; + Metachunk* _next; + + // Furthermore, we keep, per chunk, information about the neighboring chunks. + // This is needed to split and merge chunks. + // + // Note: These members can be modified concurrently while a chunk is alive and in use. + // This can happen if a neighboring chunk is added or removed. + // This means only read or modify these members under expand lock protection. + Metachunk* _prev_in_vs; + Metachunk* _next_in_vs; + + // Commit uncommitted section of the chunk. + // Fails if we hit a commit limit. + bool commit_up_to(size_t new_committed_words); + + DEBUG_ONLY(static void assert_have_expand_lock();) + +public: + + Metachunk() : + _base(NULL), + _used_words(0), + _committed_words(0), + _level(chunklevel::ROOT_CHUNK_LEVEL), + _state(State::Free), + _vsnode(NULL), + _prev(NULL), _next(NULL), + _prev_in_vs(NULL), + _next_in_vs(NULL) + {} + + void clear() { + _base = NULL; + _used_words = 0; _committed_words = 0; + _level = chunklevel::ROOT_CHUNK_LEVEL; + _state = State::Free; + _vsnode = NULL; + _prev = NULL; _next = NULL; + _prev_in_vs = NULL; _next_in_vs = NULL; + } + + size_t word_size() const { return chunklevel::word_size_for_level(_level); } + + MetaWord* base() const { return _base; } + MetaWord* top() const { return base() + _used_words; } + MetaWord* committed_top() const { return base() + _committed_words; } + MetaWord* end() const { return base() + word_size(); } + + // Chunk list wiring + void set_prev(Metachunk* c) { _prev = c; } + Metachunk* prev() const { return _prev; } + void set_next(Metachunk* c) { _next = c; } + Metachunk* next() const { return _next; } + + DEBUG_ONLY(bool in_list() const { return _prev != NULL || _next != NULL; }) + + // Physical neighbors wiring + void set_prev_in_vs(Metachunk* c) { DEBUG_ONLY(assert_have_expand_lock()); _prev_in_vs = c; } + Metachunk* prev_in_vs() const { DEBUG_ONLY(assert_have_expand_lock()); return _prev_in_vs; } + void set_next_in_vs(Metachunk* c) { DEBUG_ONLY(assert_have_expand_lock()); _next_in_vs = c; } + Metachunk* next_in_vs() const { DEBUG_ONLY(assert_have_expand_lock()); return _next_in_vs; } + + bool is_free() const { return _state == State::Free; } + bool is_in_use() const { return _state == State::InUse; } + bool is_dead() const { return _state == State::Dead; } + void set_free() { _state = State::Free; } + void set_in_use() { _state = State::InUse; } + void set_dead() { _state = State::Dead; } + + // Return a single char presentation of the state ('f', 'u', 'd') + char get_state_char() const; + + void inc_level() { _level++; DEBUG_ONLY(chunklevel::is_valid_level(_level);) } + void dec_level() { _level --; DEBUG_ONLY(chunklevel::is_valid_level(_level);) } + chunklevel_t level() const { return _level; } + + // Convenience functions for extreme levels. + bool is_root_chunk() const { return chunklevel::ROOT_CHUNK_LEVEL == _level; } + bool is_leaf_chunk() const { return chunklevel::HIGHEST_CHUNK_LEVEL == _level; } + + VirtualSpaceNode* vsnode() const { return _vsnode; } + + size_t used_words() const { return _used_words; } + size_t free_words() const { return word_size() - used_words(); } + size_t free_below_committed_words() const { return committed_words() - used_words(); } + void reset_used_words() { _used_words = 0; } + + size_t committed_words() const { return _committed_words; } + void set_committed_words(size_t v); + bool is_fully_committed() const { return committed_words() == word_size(); } + bool is_fully_uncommitted() const { return committed_words() == 0; } + + // Ensure that chunk is committed up to at least new_committed_words words. + // Fails if we hit a commit limit. + bool ensure_committed(size_t new_committed_words); + bool ensure_committed_locked(size_t new_committed_words); + + // Ensure that the chunk is committed far enough to serve an additional allocation of word_size. + bool ensure_committed_additional(size_t additional_word_size) { + return ensure_committed(used_words() + additional_word_size); + } + + // Uncommit chunk area. The area must be a common multiple of the + // commit granule size (in other words, we cannot uncommit chunks smaller than + // a commit granule size). + void uncommit(); + void uncommit_locked(); + + // Allocation from a chunk + + // Allocate word_size words from this chunk (word_size must be aligned to + // allocation_alignment_words). + // + // Caller must make sure the chunk is both large enough and committed far enough + // to hold the allocation. Will always work. + // + MetaWord* allocate(size_t request_word_size); + + // Initialize structure for reuse. + void initialize(VirtualSpaceNode* node, MetaWord* base, chunklevel_t lvl) { + clear(); + _vsnode = node; _base = base; _level = lvl; + } + + // Returns true if this chunk is the leader in its buddy pair, false if not. + // Do not call for root chunks. + bool is_leader() const { + assert(!is_root_chunk(), "Root chunks have no buddy."); // Bit harsh? + return is_aligned(base(), chunklevel::word_size_for_level(level() - 1) * BytesPerWord); + } + + //// Debug stuff //// +#ifdef ASSERT + void verify() const; + // Verifies linking with neighbors in virtual space. Needs expand lock protection. + void verify_neighborhood() const; + void zap_header(uint8_t c = 0x17); + + // Returns true if given pointer points into the payload area of this chunk. + bool is_valid_pointer(const MetaWord* p) const { + return base() <= p && p < top(); + } + + // Returns true if given pointer points into the commmitted payload area of this chunk. + bool is_valid_committed_pointer(const MetaWord* p) const { + return base() <= p && p < committed_top(); + } + +#endif // ASSERT void print_on(outputStream* st) const; - bool is_valid_sentinel() const { return _sentinel == CHUNK_SENTINEL; } - void remove_sentinel() { _sentinel = CHUNK_SENTINEL_INVALID; } - - int get_use_count() const { return _use_count; } - void inc_use_count() { _use_count ++; } - - ChunkOrigin get_origin() const { return _origin; } - void set_origin(ChunkOrigin orig) { _origin = orig; } - - ChunkIndex get_chunk_type() const { return _chunk_type; } - bool is_class() const { return _is_class; } - - DEBUG_ONLY(void mangle(juint word_value);) - DEBUG_ONLY(void verify() const;) - }; +// Little print helpers: since we often print out chunks, here some convenience macros +#define METACHUNK_FORMAT "@" PTR_FORMAT ", %c, base " PTR_FORMAT ", level " CHKLVL_FORMAT +#define METACHUNK_FORMAT_ARGS(chunk) p2i(chunk), chunk->get_state_char(), p2i(chunk->base()), chunk->level() -// Helper function that does a bunch of checks for a chunk. -DEBUG_ONLY(void do_verify_chunk(Metachunk* chunk);) - -// Given a Metachunk, update its in-use information (both in the -// chunk and the occupancy map). -void do_update_in_use_info_for_chunk(Metachunk* chunk, bool inuse); +#define METACHUNK_FULL_FORMAT "@" PTR_FORMAT ", %c, base " PTR_FORMAT ", level " CHKLVL_FORMAT " (" SIZE_FORMAT "), used: " SIZE_FORMAT ", committed: " SIZE_FORMAT ", committed-free: " SIZE_FORMAT +#define METACHUNK_FULL_FORMAT_ARGS(chunk) p2i(chunk), chunk->get_state_char(), p2i(chunk->base()), chunk->level(), chunk->word_size(), chunk->used_words(), chunk->committed_words(), chunk->free_below_committed_words() } // namespace metaspace diff --git a/src/hotspot/share/memory/metaspace/metachunkList.cpp b/src/hotspot/share/memory/metaspace/metachunkList.cpp new file mode 100644 index 00000000000..65c296b634b --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metachunkList.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "memory/metaspace/metachunkList.hpp" +#include "memory/metaspace/metaspaceCommon.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" + +namespace metaspace { + +#ifdef ASSERT + +void MetachunkList::verify_does_not_contain(const Metachunk* c) const { + SOMETIMES(assert(contains(c) == false, "List contains this chunk.");) +} + +bool MetachunkList::contains(const Metachunk* c) const { + for (Metachunk* c2 = _first; c2 != NULL; c2 = c2->next()) { + if (c == c2) { + return true; + } + } + return false; +} + +void MetachunkList::verify() const { + int num = 0; + const Metachunk* last_c = NULL; + for (const Metachunk* c = _first; c != NULL; c = c->next()) { + num++; + assert(c->prev() != c && c->next() != c, "circularity"); + assert(c->prev() == last_c, + "Broken link to predecessor. Chunk " METACHUNK_FULL_FORMAT ".", + METACHUNK_FULL_FORMAT_ARGS(c)); + c->verify(); + last_c = c; + } + _num_chunks.check(num); +} + +#endif // ASSERT + +size_t MetachunkList::calc_committed_word_size() const { + if (_first != NULL && _first->is_dead()) { + // list used for chunk header pool; dead chunks have no size. + return 0; + } + size_t s = 0; + for (Metachunk* c = _first; c != NULL; c = c->next()) { + assert(c->is_dead() == false, "Sanity"); + s += c->committed_words(); + } + return s; +} + +size_t MetachunkList::calc_word_size() const { + if (_first != NULL && _first->is_dead()) { + // list used for chunk header pool; dead chunks have no size. + return 0; + } + size_t s = 0; + for (Metachunk* c = _first; c != NULL; c = c->next()) { + assert(c->is_dead() == false, "Sanity"); + s += c->committed_words(); + } + return s; +} + +void MetachunkList::print_on(outputStream* st) const { + if (_num_chunks.get() > 0) { + for (const Metachunk* c = _first; c != NULL; c = c->next()) { + st->print(" - <"); + c->print_on(st); + st->print(">"); + } + st->print(" - total : %d chunks.", _num_chunks.get()); + } else { + st->print("empty"); + } +} + +} // namespace metaspace + diff --git a/src/hotspot/share/memory/metaspace/metachunkList.hpp b/src/hotspot/share/memory/metaspace/metachunkList.hpp new file mode 100644 index 00000000000..7137adabb8d --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metachunkList.hpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_METACHUNKLIST_HPP +#define SHARE_MEMORY_METASPACE_METACHUNKLIST_HPP + +#include "memory/metaspace/counters.hpp" +#include "memory/metaspace/metachunk.hpp" +#include "utilities/globalDefinitions.hpp" + +class outputStream; + +namespace metaspace { + +// A simple single-linked list of chunks, used in MetaspaceArena to keep +// a list of retired chunks, as well as in the ChunkHeaderPool to keep +// a cache of unused chunk headers. + +class MetachunkList { + + Metachunk* _first; + IntCounter _num_chunks; + + // Note: The chunks inside this list may be dead (->chunk header pool). + // So, do not call c->word size on them or anything else which may not + // work with dead chunks. + + // Check that list does not contain the given chunk; Note that since that check + // is expensive, it is subject to VerifyMetaspaceInterval. + DEBUG_ONLY(void verify_does_not_contain(const Metachunk* c) const;) + +public: + + MetachunkList() : _first(NULL), _num_chunks() {} + + int count() const { return _num_chunks.get(); } + + void add(Metachunk* c) { + DEBUG_ONLY(verify_does_not_contain(c);) + c->set_next(_first); + if (_first) { + _first->set_prev(c); + } + _first = c; + _num_chunks.increment(); + } + + Metachunk* remove_first() { + if (_first) { + Metachunk* c = _first; + _first = _first->next(); + if (_first) { + _first->set_prev(NULL); + } + _num_chunks.decrement(); + c->set_prev(NULL); + c->set_next(NULL); + return c; + } + return NULL; + } + + Metachunk* first() { return _first; } + const Metachunk* first() const { return _first; } + +#ifdef ASSERT + // Note: linear search + bool contains(const Metachunk* c) const; + void verify() const; +#endif + + size_t calc_committed_word_size() const; + size_t calc_word_size() const; + + void print_on(outputStream* st) const; + +}; + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_METACHUNKLIST_HPP diff --git a/src/hotspot/share/memory/metaspace/metaspaceArena.cpp b/src/hotspot/share/memory/metaspace/metaspaceArena.cpp new file mode 100644 index 00000000000..ae058fe2fdf --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metaspaceArena.cpp @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "logging/log.hpp" +#include "logging/logStream.hpp" +#include "memory/metaspace/allocationGuard.hpp" +#include "memory/metaspace/chunkManager.hpp" +#include "memory/metaspace/counters.hpp" +#include "memory/metaspace/freeBlocks.hpp" +#include "memory/metaspace/internalStats.hpp" +#include "memory/metaspace/metachunk.hpp" +#include "memory/metaspace/metaspaceArena.hpp" +#include "memory/metaspace/metaspaceArenaGrowthPolicy.hpp" +#include "memory/metaspace/metaspaceCommon.hpp" +#include "memory/metaspace/metaspaceSettings.hpp" +#include "memory/metaspace/metaspaceStatistics.hpp" +#include "memory/metaspace/virtualSpaceList.hpp" +#include "runtime/atomic.hpp" +#include "runtime/init.hpp" +#include "runtime/mutexLocker.hpp" +#include "services/memoryService.hpp" +#include "utilities/align.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" + +namespace metaspace { + +#define LOGFMT "Arena @" PTR_FORMAT " (%s)" +#define LOGFMT_ARGS p2i(this), this->_name + +// Returns the level of the next chunk to be added, acc to growth policy. +chunklevel_t MetaspaceArena::next_chunk_level() const { + const int growth_step = _chunks.count(); + return _growth_policy->get_level_at_step(growth_step); +} + +// Given a chunk, add its remaining free committed space to the free block list. +void MetaspaceArena::salvage_chunk(Metachunk* c) { + if (Settings::handle_deallocations() == false) { + return; + } + + assert_lock_strong(lock()); + size_t remaining_words = c->free_below_committed_words(); + if (remaining_words > FreeBlocks::MinWordSize) { + + UL2(trace, "salvaging chunk " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(c)); + + MetaWord* ptr = c->allocate(remaining_words); + assert(ptr != NULL, "Should have worked"); + _total_used_words_counter->increment_by(remaining_words); + + add_allocation_to_fbl(ptr, remaining_words); + + // After this operation: the chunk should have no free committed space left. + assert(c->free_below_committed_words() == 0, + "Salvaging chunk failed (chunk " METACHUNK_FULL_FORMAT ").", + METACHUNK_FULL_FORMAT_ARGS(c)); + } +} + +// Allocate a new chunk from the underlying chunk manager able to hold at least +// requested word size. +Metachunk* MetaspaceArena::allocate_new_chunk(size_t requested_word_size) { + assert_lock_strong(lock()); + + // Should this ever happen, we need to increase the maximum possible chunk size. + guarantee(requested_word_size <= chunklevel::MAX_CHUNK_WORD_SIZE, + "Requested size too large (" SIZE_FORMAT ") - max allowed size per allocation is " SIZE_FORMAT ".", + requested_word_size, chunklevel::MAX_CHUNK_WORD_SIZE); + + const chunklevel_t max_level = chunklevel::level_fitting_word_size(requested_word_size); + const chunklevel_t preferred_level = MIN2(max_level, next_chunk_level()); + + Metachunk* c = _chunk_manager->get_chunk(preferred_level, max_level, requested_word_size); + if (c == NULL) { + return NULL; + } + + assert(c->is_in_use(), "Wrong chunk state."); + assert(c->free_below_committed_words() >= requested_word_size, "Chunk not committed"); + return c; +} + +void MetaspaceArena::add_allocation_to_fbl(MetaWord* p, size_t word_size) { + assert(Settings::handle_deallocations(), "Sanity"); + if (_fbl == NULL) { + _fbl = new FreeBlocks(); // Create only on demand + } + _fbl->add_block(p, word_size); +} + +MetaspaceArena::MetaspaceArena(ChunkManager* chunk_manager, const ArenaGrowthPolicy* growth_policy, + Mutex* lock, SizeAtomicCounter* total_used_words_counter, + const char* name) : + _lock(lock), + _chunk_manager(chunk_manager), + _growth_policy(growth_policy), + _chunks(), + _fbl(NULL), + _total_used_words_counter(total_used_words_counter), + _name(name) +{ + UL(debug, ": born."); + + // Update statistics + InternalStats::inc_num_arena_births(); +} + +MetaspaceArena::~MetaspaceArena() { +#ifdef ASSERT + verify(); + if (Settings::use_allocation_guard()) { + verify_allocation_guards(); + } +#endif + + MutexLocker fcl(lock(), Mutex::_no_safepoint_check_flag); + MemRangeCounter return_counter; + + Metachunk* c = _chunks.first(); + Metachunk* c2 = NULL; + + while (c) { + c2 = c->next(); + return_counter.add(c->used_words()); + DEBUG_ONLY(c->set_prev(NULL);) + DEBUG_ONLY(c->set_next(NULL);) + UL2(debug, "return chunk: " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(c)); + _chunk_manager->return_chunk(c); + // c may be invalid after return_chunk(c) was called. Don't access anymore. + c = c2; + } + + UL2(info, "returned %d chunks, total capacity " SIZE_FORMAT " words.", + return_counter.count(), return_counter.total_size()); + + _total_used_words_counter->decrement_by(return_counter.total_size()); + DEBUG_ONLY(chunk_manager()->verify();) + delete _fbl; + UL(debug, ": dies."); + + // Update statistics + InternalStats::inc_num_arena_deaths(); +} + +// Attempt to enlarge the current chunk to make it large enough to hold at least +// requested_word_size additional words. +// +// On success, true is returned, false otherwise. +bool MetaspaceArena::attempt_enlarge_current_chunk(size_t requested_word_size) { + assert_lock_strong(lock()); + + Metachunk* c = current_chunk(); + assert(c->free_words() < requested_word_size, "Sanity"); + + // Not if chunk enlargment is switched off... + if (Settings::enlarge_chunks_in_place() == false) { + return false; + } + // ... nor if we are already a root chunk ... + if (c->is_root_chunk()) { + return false; + } + // ... nor if the combined size of chunk content and new content would bring us above the size of a root chunk ... + if ((c->used_words() + requested_word_size) > metaspace::chunklevel::MAX_CHUNK_WORD_SIZE) { + return false; + } + + const chunklevel_t new_level = + chunklevel::level_fitting_word_size(c->used_words() + requested_word_size); + assert(new_level < c->level(), "Sanity"); + + // Atm we only enlarge by one level (so, doubling the chunk in size). So, if the requested enlargement + // would require the chunk to more than double in size, we bail. But this covers about 99% of all cases, + // so this is good enough. + if (new_level < c->level() - 1) { + return false; + } + // This only works if chunk is the leader of its buddy pair (and also if buddy + // is free and unsplit, but that we cannot check outside of metaspace lock). + if (!c->is_leader()) { + return false; + } + // If the size added to the chunk would be larger than allowed for the next growth step + // dont enlarge. + if (next_chunk_level() > c->level()) { + return false; + } + + bool success = _chunk_manager->attempt_enlarge_chunk(c); + assert(success == false || c->free_words() >= requested_word_size, "Sanity"); + return success; +} + +// Allocate memory from Metaspace. +// 1) Attempt to allocate from the free block list. +// 2) Attempt to allocate from the current chunk. +// 3) Attempt to enlarge the current chunk in place if it is too small. +// 4) Attempt to get a new chunk and allocate from that chunk. +// At any point, if we hit a commit limit, we return NULL. +MetaWord* MetaspaceArena::allocate(size_t requested_word_size) { + MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag); + UL2(trace, "requested " SIZE_FORMAT " words.", requested_word_size); + + MetaWord* p = NULL; + const size_t raw_word_size = get_raw_word_size_for_requested_word_size(requested_word_size); + + // 1) Attempt to allocate from the free blocks list + // (Note: to reduce complexity, deallocation handling is disabled if allocation guards + // are enabled, see Settings::ergo_initialize()) + if (Settings::handle_deallocations() && _fbl != NULL && !_fbl->is_empty()) { + p = _fbl->remove_block(raw_word_size); + if (p != NULL) { + DEBUG_ONLY(InternalStats::inc_num_allocs_from_deallocated_blocks();) + UL2(trace, "taken from fbl (now: %d, " SIZE_FORMAT ").", + _fbl->count(), _fbl->total_size()); + // Note: Space which is kept in the freeblock dictionary still counts as used as far + // as statistics go; therefore we skip the epilogue in this function to avoid double + // accounting. + return p; + } + } + + bool current_chunk_too_small = false; + bool commit_failure = false; + + if (current_chunk() != NULL) { + + // 2) Attempt to satisfy the allocation from the current chunk. + + // If the current chunk is too small to hold the requested size, attempt to enlarge it. + // If that fails, retire the chunk. + if (current_chunk()->free_words() < raw_word_size) { + if (!attempt_enlarge_current_chunk(raw_word_size)) { + current_chunk_too_small = true; + } else { + DEBUG_ONLY(InternalStats::inc_num_chunks_enlarged();) + UL(debug, "enlarged chunk."); + } + } + + // Commit the chunk far enough to hold the requested word size. If that fails, we + // hit a limit (either GC threshold or MaxMetaspaceSize). In that case retire the + // chunk. + if (!current_chunk_too_small) { + if (!current_chunk()->ensure_committed_additional(raw_word_size)) { + UL2(info, "commit failure (requested size: " SIZE_FORMAT ")", raw_word_size); + commit_failure = true; + } + } + + // Allocate from the current chunk. This should work now. + if (!current_chunk_too_small && !commit_failure) { + p = current_chunk()->allocate(raw_word_size); + assert(p != NULL, "Allocation from chunk failed."); + } + } + + if (p == NULL) { + // If we are here, we either had no current chunk to begin with or it was deemed insufficient. + assert(current_chunk() == NULL || + current_chunk_too_small || commit_failure, "Sanity"); + + Metachunk* new_chunk = allocate_new_chunk(raw_word_size); + if (new_chunk != NULL) { + UL2(debug, "allocated new chunk " METACHUNK_FORMAT " for requested word size " SIZE_FORMAT ".", + METACHUNK_FORMAT_ARGS(new_chunk), requested_word_size); + + assert(new_chunk->free_below_committed_words() >= raw_word_size, "Sanity"); + if (Settings::new_chunks_are_fully_committed()) { + assert(new_chunk->is_fully_committed(), "Chunk should be fully committed."); + } + + // We have a new chunk. Before making it the current chunk, retire the old one. + if (current_chunk() != NULL) { + salvage_chunk(current_chunk()); + DEBUG_ONLY(InternalStats::inc_num_chunks_retired();) + } + + _chunks.add(new_chunk); + + // Now, allocate from that chunk. That should work. + p = current_chunk()->allocate(raw_word_size); + assert(p != NULL, "Allocation from chunk failed."); + } else { + UL2(info, "failed to allocate new chunk for requested word size " SIZE_FORMAT ".", requested_word_size); + } + } + +#ifdef ASSERT + // When using allocation guards, establish a prefix. + if (p != NULL && Settings::use_allocation_guard()) { + p = establish_prefix(p, raw_word_size); + } +#endif + + if (p == NULL) { + InternalStats::inc_num_allocs_failed_limit(); + } else { + DEBUG_ONLY(InternalStats::inc_num_allocs();) + _total_used_words_counter->increment_by(raw_word_size); + } + + SOMETIMES(verify_locked();) + + if (p == NULL) { + UL(info, "allocation failed, returned NULL."); + } else { + UL2(trace, "after allocation: %u chunk(s), current:" METACHUNK_FULL_FORMAT, + _chunks.count(), METACHUNK_FULL_FORMAT_ARGS(current_chunk())); + UL2(trace, "returning " PTR_FORMAT ".", p2i(p)); + } + return p; +} + +// Prematurely returns a metaspace allocation to the _block_freelists +// because it is not needed anymore (requires CLD lock to be active). +void MetaspaceArena::deallocate_locked(MetaWord* p, size_t word_size) { + if (Settings::handle_deallocations() == false) { + return; + } + + assert_lock_strong(lock()); + // At this point a current chunk must exist since we only deallocate if we did allocate before. + assert(current_chunk() != NULL, "stray deallocation?"); + assert(is_valid_area(p, word_size), + "Pointer range not part of this Arena and cannot be deallocated: (" PTR_FORMAT ".." PTR_FORMAT ").", + p2i(p), p2i(p + word_size)); + + UL2(trace, "deallocating " PTR_FORMAT ", word size: " SIZE_FORMAT ".", + p2i(p), word_size); + + size_t raw_word_size = get_raw_word_size_for_requested_word_size(word_size); + add_allocation_to_fbl(p, raw_word_size); + + DEBUG_ONLY(verify_locked();) +} + +// Prematurely returns a metaspace allocation to the _block_freelists because it is not +// needed anymore. +void MetaspaceArena::deallocate(MetaWord* p, size_t word_size) { + MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag); + deallocate_locked(p, word_size); +} + +// Update statistics. This walks all in-use chunks. +void MetaspaceArena::add_to_statistics(ArenaStats* out) const { + MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag); + + for (const Metachunk* c = _chunks.first(); c != NULL; c = c->next()) { + InUseChunkStats& ucs = out->_stats[c->level()]; + ucs._num++; + ucs._word_size += c->word_size(); + ucs._committed_words += c->committed_words(); + ucs._used_words += c->used_words(); + // Note: for free and waste, we only count what's committed. + if (c == current_chunk()) { + ucs._free_words += c->free_below_committed_words(); + } else { + ucs._waste_words += c->free_below_committed_words(); + } + } + + if (_fbl != NULL) { + out->_free_blocks_num += _fbl->count(); + out->_free_blocks_word_size += _fbl->total_size(); + } + + SOMETIMES(out->verify();) +} + +// Convenience method to get the most important usage statistics. +// For deeper analysis use add_to_statistics(). +void MetaspaceArena::usage_numbers(size_t* p_used_words, size_t* p_committed_words, size_t* p_capacity_words) const { + MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag); + size_t used = 0, comm = 0, cap = 0; + for (const Metachunk* c = _chunks.first(); c != NULL; c = c->next()) { + used += c->used_words(); + comm += c->committed_words(); + cap += c->word_size(); + } + if (p_used_words != NULL) { + *p_used_words = used; + } + if (p_committed_words != NULL) { + *p_committed_words = comm; + } + if (p_capacity_words != NULL) { + *p_capacity_words = cap; + } +} + +#ifdef ASSERT + +void MetaspaceArena::verify_locked() const { + assert_lock_strong(lock()); + assert(_growth_policy != NULL && _chunk_manager != NULL, "Sanity"); + _chunks.verify(); + if (_fbl != NULL) { + _fbl->verify(); + } +} + +void MetaspaceArena::verify_allocation_guards() const { + assert(Settings::use_allocation_guard(), "Don't call with guards disabled."); + + // Verify canaries of all allocations. + // (We can walk all allocations since at the start of a chunk an allocation + // must be present, and the allocation header contains its size, so we can + // find the next one). + for (const Metachunk* c = _chunks.first(); c != NULL; c = c->next()) { + const Prefix* first_broken_block = NULL; + int num_broken_blocks = 0; + const MetaWord* p = c->base(); + while (p < c->top()) { + const Prefix* pp = (const Prefix*)p; + if (!pp->is_valid()) { + UL2(error, "Corrupt block at " PTR_FORMAT " (chunk: " METACHUNK_FORMAT ").", + p2i(pp), METACHUNK_FORMAT_ARGS(c)); + if (first_broken_block == NULL) { + first_broken_block = pp; + } + num_broken_blocks ++; + } + p += pp->_word_size; + } + // After examining all blocks in a chunk, assert if any of those blocks + // was found to be corrupted. + if (first_broken_block != NULL) { + assert(false, "Corrupt block: found at least %d corrupt metaspace block(s) - " + "first corrupted block at " PTR_FORMAT ".", + num_broken_blocks, p2i(first_broken_block)); + } + } +} + +void MetaspaceArena::verify() const { + MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag); + verify_locked(); +} + +// Returns true if the area indicated by pointer and size have actually been allocated +// from this arena. +bool MetaspaceArena::is_valid_area(MetaWord* p, size_t word_size) const { + assert(p != NULL && word_size > 0, "Sanity"); + bool found = false; + for (const Metachunk* c = _chunks.first(); c != NULL && !found; c = c->next()) { + assert(c->is_valid_committed_pointer(p) == + c->is_valid_committed_pointer(p + word_size - 1), "range intersects"); + found = c->is_valid_committed_pointer(p); + } + return found; +} + +#endif // ASSERT + +void MetaspaceArena::print_on(outputStream* st) const { + MutexLocker fcl(_lock, Mutex::_no_safepoint_check_flag); + print_on_locked(st); +} + +void MetaspaceArena::print_on_locked(outputStream* st) const { + assert_lock_strong(_lock); + st->print_cr("sm %s: %d chunks, total word size: " SIZE_FORMAT ", committed word size: " SIZE_FORMAT, _name, + _chunks.count(), _chunks.calc_word_size(), _chunks.calc_committed_word_size()); + _chunks.print_on(st); + st->cr(); + st->print_cr("growth-policy " PTR_FORMAT ", lock " PTR_FORMAT ", cm " PTR_FORMAT ", fbl " PTR_FORMAT, + p2i(_growth_policy), p2i(_lock), p2i(_chunk_manager), p2i(_fbl)); +} + +} // namespace metaspace + diff --git a/src/hotspot/share/memory/metaspace/metaspaceArena.hpp b/src/hotspot/share/memory/metaspace/metaspaceArena.hpp new file mode 100644 index 00000000000..1edbc8997b9 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metaspaceArena.hpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_METASPACEARENA_HPP +#define SHARE_MEMORY_METASPACE_METASPACEARENA_HPP + +#include "memory/allocation.hpp" +#include "memory/metaspace.hpp" +#include "memory/metaspace/chunkManager.hpp" +#include "memory/metaspace/counters.hpp" +#include "memory/metaspace/metachunk.hpp" +#include "memory/metaspace/metachunkList.hpp" +#include "memory/metaspace/metaspaceCommon.hpp" + +class outputStream; +class Mutex; + +namespace metaspace { + +class ArenaGrowthPolicy; +class FreeBlocks; + +struct ArenaStats; + +// The MetaspaceArena is a growable metaspace memory pool belonging to a CLD; +// internally it consists of a list of metaspace chunks, of which the head chunk +// is the current chunk from which we allocate via pointer bump. +// +// +---------------+ +// | Arena | +// +---------------+ +// | +// | _chunks commit top +// | v +// +----------+ +----------+ +----------+ +----------+ +// | retired | ---> | retired | ---> | retired | ---> | current | +// | chunk | | chunk | | chunk | | chunk | +// +----------+ +----------+ +----------+ +----------+ +// ^ +// used top +// +// +------------+ +// | FreeBlocks | --> O -> O -> O -> O +// +------------+ +// +// + +// When the current chunk is used up, MetaspaceArena requestes a new chunk from +// the associated ChunkManager. +// +// MetaspaceArena also keeps a FreeBlocks structure to manage memory blocks which +// had been deallocated prematurely. +// + +class MetaspaceArena : public CHeapObj { + + // Reference to an outside lock to use for synchronizing access to this arena. + // This lock is normally owned by the CLD which owns the ClassLoaderMetaspace which + // owns this arena. + // Todo: This should be changed. Either the CLD should synchronize access to the + // CLMS and its arenas itself, or the arena should have an own lock. The latter + // would allow for more fine granular locking since it would allow access to + // both class- and non-class arena in the CLMS independently. + Mutex* const _lock; + + // Reference to the chunk manager to allocate chunks from. + ChunkManager* const _chunk_manager; + + // Reference to the growth policy to use. + const ArenaGrowthPolicy* const _growth_policy; + + // List of chunks. Head of the list is the current chunk. + MetachunkList _chunks; + + // Structure to take care of leftover/deallocated space in used chunks. + // Owned by the Arena. Gets allocated on demand only. + FreeBlocks* _fbl; + + Metachunk* current_chunk() { return _chunks.first(); } + const Metachunk* current_chunk() const { return _chunks.first(); } + + // Reference to an outside counter to keep track of used space. + SizeAtomicCounter* const _total_used_words_counter; + + // A name for purely debugging/logging purposes. + const char* const _name; + + Mutex* lock() const { return _lock; } + ChunkManager* chunk_manager() const { return _chunk_manager; } + + // free block list + FreeBlocks* fbl() const { return _fbl; } + void add_allocation_to_fbl(MetaWord* p, size_t word_size); + + // Given a chunk, add its remaining free committed space to the free block list. + void salvage_chunk(Metachunk* c); + + // Allocate a new chunk from the underlying chunk manager able to hold at least + // requested word size. + Metachunk* allocate_new_chunk(size_t requested_word_size); + + // Returns the level of the next chunk to be added, acc to growth policy. + chunklevel_t next_chunk_level() const; + + // Attempt to enlarge the current chunk to make it large enough to hold at least + // requested_word_size additional words. + // + // On success, true is returned, false otherwise. + bool attempt_enlarge_current_chunk(size_t requested_word_size); + + // Prematurely returns a metaspace allocation to the _block_freelists + // because it is not needed anymore (requires CLD lock to be active). + void deallocate_locked(MetaWord* p, size_t word_size); + + // Returns true if the area indicated by pointer and size have actually been allocated + // from this arena. + DEBUG_ONLY(bool is_valid_area(MetaWord* p, size_t word_size) const;) + +public: + + MetaspaceArena(ChunkManager* chunk_manager, const ArenaGrowthPolicy* growth_policy, + Mutex* lock, SizeAtomicCounter* total_used_words_counter, + const char* name); + + ~MetaspaceArena(); + + // Allocate memory from Metaspace. + // 1) Attempt to allocate from the dictionary of deallocated blocks. + // 2) Attempt to allocate from the current chunk. + // 3) Attempt to enlarge the current chunk in place if it is too small. + // 4) Attempt to get a new chunk and allocate from that chunk. + // At any point, if we hit a commit limit, we return NULL. + MetaWord* allocate(size_t word_size); + + // Prematurely returns a metaspace allocation to the _block_freelists because it is not + // needed anymore. + void deallocate(MetaWord* p, size_t word_size); + + // Update statistics. This walks all in-use chunks. + void add_to_statistics(ArenaStats* out) const; + + // Convenience method to get the most important usage statistics. + // For deeper analysis use add_to_statistics(). + void usage_numbers(size_t* p_used_words, size_t* p_committed_words, size_t* p_capacity_words) const; + + DEBUG_ONLY(void verify() const;) + DEBUG_ONLY(void verify_locked() const;) + DEBUG_ONLY(void verify_allocation_guards() const;) + + void print_on(outputStream* st) const; + void print_on_locked(outputStream* st) const; + +}; + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_METASPACEARENA_HPP + diff --git a/src/hotspot/share/memory/metaspace/metaspaceArenaGrowthPolicy.cpp b/src/hotspot/share/memory/metaspace/metaspaceArenaGrowthPolicy.cpp new file mode 100644 index 00000000000..d8dbf3bbcfb --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metaspaceArenaGrowthPolicy.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "memory/metaspace/metaspaceArenaGrowthPolicy.hpp" +#include "utilities/globalDefinitions.hpp" + +namespace metaspace { + +// hard-coded chunk allocation sequences for various space types +// (Note: when modifying this, don't add jumps of more than double the +// last chunk size. There is a gtest testing this, see test_arenagrowthpolicy.cpp) + +static const chunklevel_t g_sequ_standard_non_class[] = { + chunklevel::CHUNK_LEVEL_4K, + chunklevel::CHUNK_LEVEL_4K, + chunklevel::CHUNK_LEVEL_4K, + chunklevel::CHUNK_LEVEL_8K, + chunklevel::CHUNK_LEVEL_16K + // .. repeat last +}; + +static const chunklevel_t g_sequ_standard_class[] = { + chunklevel::CHUNK_LEVEL_2K, + chunklevel::CHUNK_LEVEL_2K, + chunklevel::CHUNK_LEVEL_4K, + chunklevel::CHUNK_LEVEL_8K, + chunklevel::CHUNK_LEVEL_16K + // .. repeat last +}; + +static const chunklevel_t g_sequ_anon_non_class[] = { + chunklevel::CHUNK_LEVEL_1K, + // .. repeat last +}; + +static const chunklevel_t g_sequ_anon_class[] = { + chunklevel::CHUNK_LEVEL_1K, + // .. repeat last +}; + +static const chunklevel_t g_sequ_refl_non_class[] = { + chunklevel::CHUNK_LEVEL_2K, + chunklevel::CHUNK_LEVEL_1K + // .. repeat last +}; + +static const chunklevel_t g_sequ_refl_class[] = { + chunklevel::CHUNK_LEVEL_1K, + // .. repeat last +}; + +// Boot class loader: give it large chunks: beyond commit granule size +// (typically 64K) the costs for large chunks largely diminishes since +// they are committed on the fly. +static const chunklevel_t g_sequ_boot_non_class[] = { + chunklevel::CHUNK_LEVEL_4M, + chunklevel::CHUNK_LEVEL_1M + // .. repeat last +}; + +static const chunklevel_t g_sequ_boot_class[] = { + chunklevel::CHUNK_LEVEL_256K + // .. repeat last +}; + +const ArenaGrowthPolicy* ArenaGrowthPolicy::policy_for_space_type(Metaspace::MetaspaceType space_type, bool is_class) { + +#define DEFINE_CLASS_FOR_ARRAY(what) \ + static ArenaGrowthPolicy chunk_alloc_sequence_##what (g_sequ_##what, sizeof(g_sequ_##what)/sizeof(chunklevel_t)); + + DEFINE_CLASS_FOR_ARRAY(standard_non_class) + DEFINE_CLASS_FOR_ARRAY(standard_class) + DEFINE_CLASS_FOR_ARRAY(anon_non_class) + DEFINE_CLASS_FOR_ARRAY(anon_class) + DEFINE_CLASS_FOR_ARRAY(refl_non_class) + DEFINE_CLASS_FOR_ARRAY(refl_class) + DEFINE_CLASS_FOR_ARRAY(boot_non_class) + DEFINE_CLASS_FOR_ARRAY(boot_class) + + if (is_class) { + switch(space_type) { + case Metaspace::StandardMetaspaceType: return &chunk_alloc_sequence_standard_class; + case Metaspace::ReflectionMetaspaceType: return &chunk_alloc_sequence_refl_class; + case Metaspace::ClassMirrorHolderMetaspaceType: return &chunk_alloc_sequence_anon_class; + case Metaspace::BootMetaspaceType: return &chunk_alloc_sequence_boot_class; + default: ShouldNotReachHere(); + } + } else { + switch(space_type) { + case Metaspace::StandardMetaspaceType: return &chunk_alloc_sequence_standard_non_class; + case Metaspace::ReflectionMetaspaceType: return &chunk_alloc_sequence_refl_non_class; + case Metaspace::ClassMirrorHolderMetaspaceType: return &chunk_alloc_sequence_anon_non_class; + case Metaspace::BootMetaspaceType: return &chunk_alloc_sequence_boot_non_class; + default: ShouldNotReachHere(); + } + } + + return NULL; + +} + +} // namespace + diff --git a/src/hotspot/share/memory/metaspace/metaspaceArenaGrowthPolicy.hpp b/src/hotspot/share/memory/metaspace/metaspaceArenaGrowthPolicy.hpp new file mode 100644 index 00000000000..fe5c6938b48 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metaspaceArenaGrowthPolicy.hpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_METASPACEARENAGROWTHPOLICY_HPP +#define SHARE_MEMORY_METASPACE_METASPACEARENAGROWTHPOLICY_HPP + +#include "memory/metaspace.hpp" // For Metaspace::MetaspaceType +#include "memory/metaspace/chunklevel.hpp" +#include "utilities/debug.hpp" + +namespace metaspace { + +// ArenaGrowthPolicy encodes the growth policy of a MetaspaceArena. +// +// These arenas grow in steps (by allocating new chunks). The coarseness of growth +// (chunk size, level) depends on what the arena is used for. Used for a class loader +// which is expected to load only one or very few classes should grow in tiny steps. +// For normal classloaders, it can grow in coarser steps, and arenas used by +// the boot loader will grow in even larger steps since we expect it to load a lot of +// classes. +// Note that when growing in large steps (in steps larger than a commit granule, +// by default 64K), costs diminish somewhat since we do not commit the whole space +// immediately. + +class ArenaGrowthPolicy { + + // const array specifying chunk level allocation progression (growth steps). Last + // chunk is to be an endlessly repeated allocation. + const chunklevel_t* const _entries; + const int _num_entries; + +public: + + ArenaGrowthPolicy(const chunklevel_t* array, int num_entries) : + _entries(array), + _num_entries(num_entries) + { + assert(_num_entries > 0, "must not be empty."); + } + + chunklevel_t get_level_at_step(int num_allocated) const { + if (num_allocated >= _num_entries) { + // Caller shall repeat last allocation + return _entries[_num_entries - 1]; + } + return _entries[num_allocated]; + } + + // Given a space type, return the correct policy to use. + // The returned object is static and read only. + static const ArenaGrowthPolicy* policy_for_space_type(Metaspace::MetaspaceType space_type, bool is_class); + +}; + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_METASPACEARENAGROWTHPOLICY_HPP diff --git a/src/hotspot/share/memory/metaspace/metaspaceCommon.cpp b/src/hotspot/share/memory/metaspace/metaspaceCommon.cpp index 85e2b69a809..96673621bb2 100644 --- a/src/hotspot/share/memory/metaspace/metaspaceCommon.cpp +++ b/src/hotspot/share/memory/metaspace/metaspaceCommon.cpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,17 +24,18 @@ */ #include "precompiled.hpp" - +#include "memory/metaspace/allocationGuard.hpp" +#include "memory/metaspace/freeBlocks.hpp" #include "memory/metaspace/metaspaceCommon.hpp" +#include "memory/metaspace/metaspaceSettings.hpp" #include "memory/metaspace/virtualSpaceNode.hpp" +#include "utilities/align.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" namespace metaspace { -DEBUG_ONLY(internal_statistics_t g_internal_statistics;) - // Print a size, in words, scaled. void print_scaled_words(outputStream* st, size_t word_size, size_t scale, int width) { print_human_readable_size(st, word_size * sizeof(MetaWord), scale, width); @@ -47,6 +49,19 @@ void print_scaled_words_and_percentage(outputStream* st, size_t word_size, size_ st->print(")"); } +static const char* display_unit_for_scale(size_t scale) { + const char* s = NULL; + switch(scale) { + case 1: s = "bytes"; break; + case BytesPerWord: s = "words"; break; + case K: s = "KB"; break; + case M: s = "MB"; break; + case G: s = "GB"; break; + default: + ShouldNotReachHere(); + } + return s; +} // Print a human readable size. // byte_size: size, in bytes, to be printed. @@ -74,36 +89,45 @@ void print_human_readable_size(outputStream* st, size_t byte_size, size_t scale, } #ifdef ASSERT - assert(scale == 1 || scale == BytesPerWord || scale == K || scale == M || scale == G, "Invalid scale"); + assert(scale == 1 || scale == BytesPerWord || + scale == K || scale == M || scale == G, "Invalid scale"); // Special case: printing wordsize should only be done with word-sized values if (scale == BytesPerWord) { assert(byte_size % BytesPerWord == 0, "not word sized"); } #endif - if (scale == 1) { - st->print("%*" PRIuPTR " bytes", width, byte_size); - } else if (scale == BytesPerWord) { - st->print("%*" PRIuPTR " words", width, byte_size / BytesPerWord); - } else { - const char* display_unit = ""; - switch(scale) { - case 1: display_unit = "bytes"; break; - case BytesPerWord: display_unit = "words"; break; - case K: display_unit = "KB"; break; - case M: display_unit = "MB"; break; - case G: display_unit = "GB"; break; - default: - ShouldNotReachHere(); + if (width == -1) { + if (scale == 1) { + st->print(SIZE_FORMAT " bytes", byte_size); + } else if (scale == BytesPerWord) { + st->print(SIZE_FORMAT " words", byte_size / BytesPerWord); + } else { + const char* display_unit = display_unit_for_scale(scale); + float display_value = (float) byte_size / scale; + // Prevent very small but non-null values showing up as 0.00. + if (byte_size > 0 && display_value < 0.01f) { + st->print("<0.01 %s", display_unit); + } else { + st->print("%.2f %s", display_value, display_unit); + } } - float display_value = (float) byte_size / scale; - // Since we use width to display a number with two trailing digits, increase it a bit. - width += 3; - // Prevent very small but non-null values showing up as 0.00. - if (byte_size > 0 && display_value < 0.01f) { - st->print("%*s %s", width, "<0.01", display_unit); + } else { + if (scale == 1) { + st->print("%*" PRIuPTR " bytes", width, byte_size); + } else if (scale == BytesPerWord) { + st->print("%*" PRIuPTR " words", width, byte_size / BytesPerWord); } else { - st->print("%*.2f %s", width, display_value, display_unit); + const char* display_unit = display_unit_for_scale(scale); + float display_value = (float) byte_size / scale; + // Since we use width to display a number with two trailing digits, increase it a bit. + width += 3; + // Prevent very small but non-null values showing up as 0.00. + if (byte_size > 0 && display_value < 0.01f) { + st->print("%*s %s", width, "<0.01", display_unit); + } else { + st->print("%*.2f %s", width, display_value, display_unit); + } } } } @@ -130,70 +154,6 @@ void print_percentage(outputStream* st, size_t total, size_t part) { } } -// Returns size of this chunk type. -size_t get_size_for_nonhumongous_chunktype(ChunkIndex chunktype, bool is_class) { - assert(is_valid_nonhumongous_chunktype(chunktype), "invalid chunk type."); - size_t size = 0; - if (is_class) { - switch(chunktype) { - case SpecializedIndex: size = ClassSpecializedChunk; break; - case SmallIndex: size = ClassSmallChunk; break; - case MediumIndex: size = ClassMediumChunk; break; - default: - ShouldNotReachHere(); - } - } else { - switch(chunktype) { - case SpecializedIndex: size = SpecializedChunk; break; - case SmallIndex: size = SmallChunk; break; - case MediumIndex: size = MediumChunk; break; - default: - ShouldNotReachHere(); - } - } - return size; -} - -ChunkIndex get_chunk_type_by_size(size_t size, bool is_class) { - if (is_class) { - if (size == ClassSpecializedChunk) { - return SpecializedIndex; - } else if (size == ClassSmallChunk) { - return SmallIndex; - } else if (size == ClassMediumChunk) { - return MediumIndex; - } else if (size > ClassMediumChunk) { - // A valid humongous chunk size is a multiple of the smallest chunk size. - assert(is_aligned(size, ClassSpecializedChunk), "Invalid chunk size"); - return HumongousIndex; - } - } else { - if (size == SpecializedChunk) { - return SpecializedIndex; - } else if (size == SmallChunk) { - return SmallIndex; - } else if (size == MediumChunk) { - return MediumIndex; - } else if (size > MediumChunk) { - // A valid humongous chunk size is a multiple of the smallest chunk size. - assert(is_aligned(size, SpecializedChunk), "Invalid chunk size"); - return HumongousIndex; - } - } - ShouldNotReachHere(); - return (ChunkIndex)-1; -} - -ChunkIndex next_chunk_index(ChunkIndex i) { - assert(i < NumberOfInUseLists, "Out of bound"); - return (ChunkIndex) (i+1); -} - -ChunkIndex prev_chunk_index(ChunkIndex i) { - assert(i > ZeroIndex, "Out of bound"); - return (ChunkIndex) (i-1); -} - const char* loaders_plural(uintx num) { return num == 1 ? "loader" : "loaders"; } @@ -209,5 +169,29 @@ void print_number_of_classes(outputStream* out, uintx classes, uintx classes_sha } } +// Given a net allocation word size, return the raw word size we actually allocate. +// Note: externally visible for gtests. +//static +size_t get_raw_word_size_for_requested_word_size(size_t word_size) { + size_t byte_size = word_size * BytesPerWord; + + // Deallocated metablocks are kept in a binlist which limits their minimal + // size to at least the size of a binlist item (2 words). + byte_size = MAX2(byte_size, FreeBlocks::MinWordSize * BytesPerWord); + + // Metaspace allocations are aligned to word size. + byte_size = align_up(byte_size, AllocationAlignmentByteSize); + + // If we guard allocations, we need additional space for a prefix. +#ifdef ASSERT + if (Settings::use_allocation_guard()) { + byte_size += align_up(prefix_size(), AllocationAlignmentByteSize); + } +#endif + size_t raw_word_size = byte_size / BytesPerWord; + assert(raw_word_size * BytesPerWord == byte_size, "Sanity"); + return raw_word_size; +} + } // namespace metaspace diff --git a/src/hotspot/share/memory/metaspace/metaspaceCommon.hpp b/src/hotspot/share/memory/metaspace/metaspaceCommon.hpp index 15af345b3c4..6ce10199e42 100644 --- a/src/hotspot/share/memory/metaspace/metaspaceCommon.hpp +++ b/src/hotspot/share/memory/metaspace/metaspaceCommon.hpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +26,7 @@ #ifndef SHARE_MEMORY_METASPACE_METASPACECOMMON_HPP #define SHARE_MEMORY_METASPACE_METASPACECOMMON_HPP +#include "runtime/globals.hpp" #include "utilities/align.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -33,14 +35,28 @@ class outputStream; namespace metaspace { -enum ChunkSizes { // in words. - ClassSpecializedChunk = 128, - SpecializedChunk = 128, - ClassSmallChunk = 256, - SmallChunk = 512, - ClassMediumChunk = 4 * K, - MediumChunk = 8 * K -}; +// Metaspace allocation alignment: + +// 1) Metaspace allocations have to be aligned such that 64bit values are aligned +// correctly. +// +// 2) Klass* structures allocated from Metaspace have to be aligned to KlassAlignmentInBytes. +// +// At the moment LogKlassAlignmentInBytes is 3, so KlassAlignmentInBytes == 8, +// so (1) and (2) can both be fulfilled with an alignment of 8. Should we increase +// KlassAlignmentInBytes at any time this will increase the necessary alignment as well. In +// that case we may think about introducing a separate alignment just for the class space +// since that alignment would only be needed for Klass structures. + +static const size_t AllocationAlignmentByteSize = 8; +STATIC_ASSERT(AllocationAlignmentByteSize == (size_t)KlassAlignmentInBytes); + +static const size_t AllocationAlignmentWordSize = AllocationAlignmentByteSize / BytesPerWord; + +// Returns the raw word size allocated for a given net allocation +size_t get_raw_word_size_for_requested_word_size(size_t word_size); + +// Utility functions // Print a size, in words, scaled. void print_scaled_words(outputStream* st, size_t word_size, size_t scale = 0, int width = -1); @@ -59,92 +75,72 @@ void print_human_readable_size(outputStream* st, size_t byte_size, size_t scale // larger than 99% but not 100% are displayed as ">100%". void print_percentage(outputStream* st, size_t total, size_t part); - +#ifdef ASSERT #define assert_is_aligned(value, alignment) \ assert(is_aligned((value), (alignment)), \ SIZE_FORMAT_HEX " is not aligned to " \ - SIZE_FORMAT, (size_t)(uintptr_t)value, (alignment)) - -// Internal statistics. -#ifdef ASSERT -struct internal_statistics_t { - // Number of allocations. - uintx num_allocs; - // Number of times a ClassLoaderMetaspace was born... - uintx num_metaspace_births; - // ... and died. - uintx num_metaspace_deaths; - // Number of times VirtualSpaceListNodes were created... - uintx num_vsnodes_created; - // ... and purged. - uintx num_vsnodes_purged; - // Number of times we expanded the committed section of the space. - uintx num_committed_space_expanded; - // Number of deallocations - uintx num_deallocs; - // Number of deallocations triggered from outside ("real" deallocations). - uintx num_external_deallocs; - // Number of times an allocation was satisfied from deallocated blocks. - uintx num_allocs_from_deallocated_blocks; - // Number of times a chunk was added to the freelist - uintx num_chunks_added_to_freelist; - // Number of times a chunk was removed from the freelist - uintx num_chunks_removed_from_freelist; - // Number of chunk merges - uintx num_chunk_merges; - // Number of chunk splits - uintx num_chunk_splits; -}; -extern internal_statistics_t g_internal_statistics; + SIZE_FORMAT_HEX, (size_t)(uintptr_t)value, (size_t)(alignment)) +#else +#define assert_is_aligned(value, alignment) #endif -// ChunkIndex defines the type of chunk. -// Chunk types differ by size: specialized < small < medium, chunks -// larger than medium are humongous chunks of varying size. -enum ChunkIndex { - ZeroIndex = 0, - SpecializedIndex = ZeroIndex, - SmallIndex = SpecializedIndex + 1, - MediumIndex = SmallIndex + 1, - HumongousIndex = MediumIndex + 1, - NumberOfFreeLists = 3, - NumberOfInUseLists = 4 -}; - -// Utility functions. -size_t get_size_for_nonhumongous_chunktype(ChunkIndex chunk_type, bool is_class); -ChunkIndex get_chunk_type_by_size(size_t size, bool is_class); - -ChunkIndex next_chunk_index(ChunkIndex i); -ChunkIndex prev_chunk_index(ChunkIndex i); -// Returns a descriptive name for a chunk type. -const char* chunk_size_name(ChunkIndex index); - -// Verify chunk sizes. -inline bool is_valid_chunksize(bool is_class, size_t size) { - const size_t reasonable_maximum_humongous_chunk_size = 1 * G; - return is_aligned(size, sizeof(MetaWord)) && - size < reasonable_maximum_humongous_chunk_size && - is_class ? - (size == ClassSpecializedChunk || size == ClassSmallChunk || size >= ClassMediumChunk) : - (size == SpecializedChunk || size == SmallChunk || size >= MediumChunk); -} - -// Verify chunk type. -inline bool is_valid_chunktype(ChunkIndex index) { - return index == SpecializedIndex || index == SmallIndex || - index == MediumIndex || index == HumongousIndex; -} - -inline bool is_valid_nonhumongous_chunktype(ChunkIndex index) { - return is_valid_chunktype(index) && index != HumongousIndex; -} - // Pretty printing helpers const char* classes_plural(uintx num); const char* loaders_plural(uintx num); void print_number_of_classes(outputStream* out, uintx classes, uintx classes_shared); +// Since Metaspace verifications are expensive, we want to do them at a reduced rate, +// but not completely avoiding them. +// For that we introduce the macros SOMETIMES() and ASSERT_SOMETIMES() which will +// execute code or assert at intervals controlled via VerifyMetaspaceInterval. +#ifdef ASSERT + +#define EVERY_NTH(n) \ +{ static int counter_ = 0; \ + if (n > 0) { \ + counter_++; \ + if (counter_ >= n) { \ + counter_ = 0; \ + +#define END_EVERY_NTH } } } + +#define SOMETIMES(code) \ + EVERY_NTH(VerifyMetaspaceInterval) \ + { code } \ + END_EVERY_NTH + +#define ASSERT_SOMETIMES(condition, ...) \ + EVERY_NTH(VerifyMetaspaceInterval) \ + assert( (condition), __VA_ARGS__); \ + END_EVERY_NTH + +#else + +#define SOMETIMES(code) +#define ASSERT_SOMETIMES(condition, ...) + +#endif // ASSERT + +///////// Logging ////////////// + +// What we log at which levels: + +// "info" : metaspace failed allocation, commit failure, reserve failure, metaspace oom, metaspace gc threshold changed, Arena created, destroyed, metaspace purged + +// "debug" : "info" + vslist extended, memory committed/uncommitted, chunk created/split/merged/enlarged, chunk returned + +// "trace" : "debug" + every single allocation and deallocation, internals + +#define HAVE_UL + +#ifdef HAVE_UL +#define UL(level, message) log_##level(metaspace)(LOGFMT ": " message, LOGFMT_ARGS); +#define UL2(level, message, ...) log_##level(metaspace)(LOGFMT ": " message, LOGFMT_ARGS, __VA_ARGS__); +#else +#define UL(level, ...) +#define UL2(level, ...) +#endif + } // namespace metaspace #endif // SHARE_MEMORY_METASPACE_METASPACECOMMON_HPP diff --git a/src/hotspot/share/memory/metaspace/metaspaceContext.cpp b/src/hotspot/share/memory/metaspace/metaspaceContext.cpp new file mode 100644 index 00000000000..c927720a739 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metaspaceContext.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "memory/metaspace/chunkManager.hpp" +#include "memory/metaspace/commitLimiter.hpp" +#include "memory/metaspace/metaspaceContext.hpp" +#include "memory/metaspace/virtualSpaceList.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" + +namespace metaspace { + +MetaspaceContext* MetaspaceContext::_class_space_context = NULL; +MetaspaceContext* MetaspaceContext::_nonclass_space_context = NULL; + +// Destroys the context: deletes chunkmanager and virtualspacelist. +// If this is a non-expandable context over an existing space, that space remains +// untouched, otherwise all memory is unmapped. +// Note: the standard metaspace contexts (non-class context and class context) are +// never deleted. This code only exists for the sake of tests and for future reuse +// of metaspace contexts in different scenarios. +MetaspaceContext::~MetaspaceContext() { + delete _cm; + delete _vslist; +} + +// Create a new, empty, expandable metaspace context. +MetaspaceContext* MetaspaceContext::create_expandable_context(const char* name, CommitLimiter* commit_limiter) { + VirtualSpaceList* vsl = new VirtualSpaceList(name, commit_limiter); + ChunkManager* cm = new ChunkManager(name, vsl); + return new MetaspaceContext(name, vsl, cm); +} + +// Create a new, empty, non-expandable metaspace context atop of an externally provided space. +MetaspaceContext* MetaspaceContext::create_nonexpandable_context(const char* name, ReservedSpace rs, CommitLimiter* commit_limiter) { + VirtualSpaceList* vsl = new VirtualSpaceList(name, rs, commit_limiter); + ChunkManager* cm = new ChunkManager(name, vsl); + return new MetaspaceContext(name, vsl, cm); +} + +void MetaspaceContext::initialize_class_space_context(ReservedSpace rs) { + _class_space_context = create_nonexpandable_context("class-space", rs, CommitLimiter::globalLimiter()); +} + +void MetaspaceContext::initialize_nonclass_space_context() { + _nonclass_space_context = create_expandable_context("non-class-space", CommitLimiter::globalLimiter()); +} + +void MetaspaceContext::print_on(outputStream* st) const { + _vslist->print_on(st); + _cm->print_on(st); +} + +#ifdef ASSERT +void MetaspaceContext::verify() const { + _vslist->verify(); + _cm->verify(); +} +#endif // ASSERT + +} // namespace metaspace + diff --git a/src/hotspot/share/memory/metaspace/metaspaceContext.hpp b/src/hotspot/share/memory/metaspace/metaspaceContext.hpp new file mode 100644 index 00000000000..752a360690f --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metaspaceContext.hpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_METASPACECONTEXT_HPP +#define SHARE_MEMORY_METASPACE_METASPACECONTEXT_HPP + +#include "memory/allocation.hpp" +#include "memory/virtualspace.hpp" +#include "utilities/debug.hpp" + +class outputStream; + +namespace metaspace { + +class ChunkManager; +class VirtualSpaceList; +class CommitLimiter; + +// MetaspaceContext is a convenience bracket around: +// +// - a VirtualSpaceList managing a memory area used for Metaspace +// - a ChunkManager sitting atop of that which manages chunk freelists +// +// In a normal VM only one or two of these contexts ever exist: one for the metaspace, and +// optionally another one for the compressed class space. +// +// For tests more contexts may be created, and this would also be a way to use Metaspace +// for things other than class metadata. We would have to work on the naming then. +// +// - (Future TODO): Context should own a lock to guard it. Currently this stuff is guarded +// by one global lock, the slightly misnamed Metaspace_expandlock, but that one +// should be split into one per context. +// - (Future TODO): Context can/should have its own allocation alignment. That way we +// can have different alignment between class space and non-class metaspace. That could +// help optimize compressed class pointer encoding, see discussion for JDK-8244943). + +class MetaspaceContext : public CHeapObj { + + const char* const _name; + VirtualSpaceList* const _vslist; + ChunkManager* const _cm; + + MetaspaceContext(const char* name, VirtualSpaceList* vslist, ChunkManager* cm) : + _name(name), + _vslist(vslist), + _cm(cm) + {} + + static MetaspaceContext* _nonclass_space_context; + static MetaspaceContext* _class_space_context; + +public: + + // Destroys the context: deletes chunkmanager and virtualspacelist. + // If this is a non-expandable context over an existing space, that space remains + // untouched, otherwise all memory is unmapped. + ~MetaspaceContext(); + + VirtualSpaceList* vslist() { return _vslist; } + ChunkManager* cm() { return _cm; } + + // Create a new, empty, expandable metaspace context. + static MetaspaceContext* create_expandable_context(const char* name, CommitLimiter* commit_limiter); + + // Create a new, empty, non-expandable metaspace context atop of an externally provided space. + static MetaspaceContext* create_nonexpandable_context(const char* name, ReservedSpace rs, CommitLimiter* commit_limiter); + + void print_on(outputStream* st) const; + + DEBUG_ONLY(void verify() const;) + + static void initialize_class_space_context(ReservedSpace rs); + static void initialize_nonclass_space_context(); + + // Returns pointer to the global metaspace context. + // If compressed class space is active, this contains the non-class-space allocations. + // If compressed class space is inactive, this contains all metaspace allocations. + static MetaspaceContext* context_nonclass() { return _nonclass_space_context; } + + // Returns pointer to the global class space context, if compressed class space is active, + // NULL otherwise. + static MetaspaceContext* context_class() { return _class_space_context; } + +}; + +} // end namespace + +#endif // SHARE_MEMORY_METASPACE_METASPACECONTEXT_HPP + diff --git a/src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp b/src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp index 6ce0960b82c..7e0e0969f7c 100644 --- a/src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp +++ b/src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018, SAP and/or its affiliates. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,27 +22,27 @@ * questions. * */ + #include "precompiled.hpp" -#include "memory/metaspace.hpp" #include "memory/metaspace/metaspaceDCmd.hpp" +#include "memory/metaspace/metaspaceReporter.hpp" #include "memory/resourceArea.hpp" #include "services/diagnosticCommand.hpp" #include "services/nmtCommon.hpp" namespace metaspace { -MetaspaceDCmd::MetaspaceDCmd(outputStream* output, bool heap) - : DCmdWithParser(output, heap) - , _basic("basic", "Prints a basic summary (does not need a safepoint).", "BOOLEAN", false, "false") - , _show_loaders("show-loaders", "Shows usage by class loader.", "BOOLEAN", false, "false") - , _by_spacetype("by-spacetype", "Break down numbers by loader type.", "BOOLEAN", false, "false") - , _by_chunktype("by-chunktype", "Break down numbers by chunk type.", "BOOLEAN", false, "false") - , _show_vslist("vslist", "Shows details about the underlying virtual space.", "BOOLEAN", false, "false") - , _show_vsmap("vsmap", "Shows chunk composition of the underlying virtual spaces", "BOOLEAN", false, "false") - , _scale("scale", "Memory usage in which to scale. Valid values are: 1, KB, MB or GB (fixed scale) " - "or \"dynamic\" for a dynamically choosen scale.", - "STRING", false, "dynamic") - , _show_classes("show-classes", "If show-loaders is set, shows loaded classes for each loader.", "BOOLEAN", false, "false") +MetaspaceDCmd::MetaspaceDCmd(outputStream* output, bool heap) : + DCmdWithParser(output, heap), + _basic("basic", "Prints a basic summary (does not need a safepoint).", "BOOLEAN", false, "false"), + _show_loaders("show-loaders", "Shows usage by class loader.", "BOOLEAN", false, "false"), + _by_spacetype("by-spacetype", "Break down numbers by loader type.", "BOOLEAN", false, "false"), + _by_chunktype("by-chunktype", "Break down numbers by chunk type.", "BOOLEAN", false, "false"), + _show_vslist("vslist", "Shows details about the underlying virtual space.", "BOOLEAN", false, "false"), + _scale("scale", "Memory usage in which to scale. Valid values are: 1, KB, MB or GB (fixed scale) " + "or \"dynamic\" for a dynamically choosen scale.", + "STRING", false, "dynamic"), + _show_classes("show-classes", "If show-loaders is set, shows loaded classes for each loader.", "BOOLEAN", false, "false") { _dcmdparser.add_dcmd_option(&_basic); _dcmdparser.add_dcmd_option(&_show_loaders); @@ -50,7 +50,6 @@ MetaspaceDCmd::MetaspaceDCmd(outputStream* output, bool heap) _dcmdparser.add_dcmd_option(&_by_chunktype); _dcmdparser.add_dcmd_option(&_by_spacetype); _dcmdparser.add_dcmd_option(&_show_vslist); - _dcmdparser.add_dcmd_option(&_show_vsmap); _dcmdparser.add_dcmd_option(&_scale); } @@ -81,7 +80,7 @@ void MetaspaceDCmd::execute(DCmdSource source, TRAPS) { } if (_basic.value() == true) { if (_show_loaders.value() || _by_chunktype.value() || _by_spacetype.value() || - _show_vslist.value() || _show_vsmap.value()) { + _show_vslist.value()) { // Basic mode. Just print essentials. Does not need to be at a safepoint. output()->print_cr("In basic mode, additional arguments are ignored."); } @@ -89,12 +88,11 @@ void MetaspaceDCmd::execute(DCmdSource source, TRAPS) { } else { // Full mode. Requires safepoint. int flags = 0; - if (_show_loaders.value()) flags |= MetaspaceUtils::rf_show_loaders; - if (_show_classes.value()) flags |= MetaspaceUtils::rf_show_classes; - if (_by_chunktype.value()) flags |= MetaspaceUtils::rf_break_down_by_chunktype; - if (_by_spacetype.value()) flags |= MetaspaceUtils::rf_break_down_by_spacetype; - if (_show_vslist.value()) flags |= MetaspaceUtils::rf_show_vslist; - if (_show_vsmap.value()) flags |= MetaspaceUtils::rf_show_vsmap; + if (_show_loaders.value()) flags |= (int)MetaspaceReporter::Option::ShowLoaders; + if (_show_classes.value()) flags |= (int)MetaspaceReporter::Option::ShowClasses; + if (_by_chunktype.value()) flags |= (int)MetaspaceReporter::Option::BreakDownByChunkType; + if (_by_spacetype.value()) flags |= (int)MetaspaceReporter::Option::BreakDownBySpaceType; + if (_show_vslist.value()) flags |= (int)MetaspaceReporter::Option::ShowVSList; VM_PrintMetadata op(output(), scale, flags); VMThread::execute(&op); } diff --git a/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp b/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp index 90bcd76caa6..0c0f49795d5 100644 --- a/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp +++ b/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018, SAP and/or its affiliates. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,7 +38,6 @@ class MetaspaceDCmd : public DCmdWithParser { DCmdArgument _by_spacetype; DCmdArgument _by_chunktype; DCmdArgument _show_vslist; - DCmdArgument _show_vsmap; DCmdArgument _scale; DCmdArgument _show_classes; public: diff --git a/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp b/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp new file mode 100644 index 00000000000..d772d071625 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "classfile/classLoaderData.hpp" +#include "classfile/classLoaderDataGraph.hpp" +#include "memory/metaspace.hpp" +#include "memory/metaspace/chunkHeaderPool.hpp" +#include "memory/metaspace/chunkManager.hpp" +#include "memory/metaspace/internalStats.hpp" +#include "memory/metaspace/metaspaceCommon.hpp" +#include "memory/metaspace/metaspaceReporter.hpp" +#include "memory/metaspace/metaspaceSettings.hpp" +#include "memory/metaspace/metaspaceStatistics.hpp" +#include "memory/metaspace/printCLDMetaspaceInfoClosure.hpp" +#include "memory/metaspace/runningCounters.hpp" +#include "memory/metaspace/virtualSpaceList.hpp" +#include "runtime/os.hpp" + +namespace metaspace { + +static const char* describe_spacetype(Metaspace::MetaspaceType st) { + const char* s = NULL; + switch (st) { + case Metaspace::StandardMetaspaceType: s = "Standard"; break; + case Metaspace::BootMetaspaceType: s = "Boot"; break; + case Metaspace::ClassMirrorHolderMetaspaceType: s = "ClassMirrorHolder"; break; + case Metaspace::ReflectionMetaspaceType: s = "Reflection"; break; + default: ShouldNotReachHere(); + } + return s; +} + +static void print_vs(outputStream* out, size_t scale) { + const size_t reserved_nc = RunningCounters::reserved_words_nonclass(); + const size_t committed_nc = RunningCounters::committed_words_nonclass(); + const int num_nodes_nc = VirtualSpaceList::vslist_nonclass()->num_nodes(); + + if (Metaspace::using_class_space()) { + const size_t reserved_c = RunningCounters::reserved_words_class(); + const size_t committed_c = RunningCounters::committed_words_class(); + const int num_nodes_c = VirtualSpaceList::vslist_class()->num_nodes(); + + out->print(" Non-class space: "); + print_scaled_words(out, reserved_nc, scale, 7); + out->print(" reserved, "); + print_scaled_words_and_percentage(out, committed_nc, reserved_nc, scale, 7); + out->print(" committed, "); + out->print(" %d nodes.", num_nodes_nc); + out->cr(); + out->print(" Class space: "); + print_scaled_words(out, reserved_c, scale, 7); + out->print(" reserved, "); + print_scaled_words_and_percentage(out, committed_c, reserved_c, scale, 7); + out->print(" committed, "); + out->print(" %d nodes.", num_nodes_c); + out->cr(); + out->print(" Both: "); + print_scaled_words(out, reserved_c + reserved_nc, scale, 7); + out->print(" reserved, "); + print_scaled_words_and_percentage(out, committed_c + committed_nc, reserved_c + reserved_nc, scale, 7); + out->print(" committed. "); + out->cr(); + } else { + print_scaled_words(out, reserved_nc, scale, 7); + out->print(" reserved, "); + print_scaled_words_and_percentage(out, committed_nc, reserved_nc, scale, 7); + out->print(" committed, "); + out->print(" %d nodes.", num_nodes_nc); + out->cr(); + } +} + +static void print_settings(outputStream* out, size_t scale) { + out->print("MaxMetaspaceSize: "); + if (MaxMetaspaceSize >= (max_uintx) - (2 * os::vm_page_size())) { + // aka "very big". Default is max_uintx, but due to rounding in arg parsing the real + // value is smaller. + out->print("unlimited"); + } else { + print_human_readable_size(out, MaxMetaspaceSize, scale); + } + out->cr(); + if (Metaspace::using_class_space()) { + out->print("CompressedClassSpaceSize: "); + print_human_readable_size(out, CompressedClassSpaceSize, scale); + } + out->cr(); + Settings::print_on(out); +} + +// This will print out a basic metaspace usage report but +// unlike print_report() is guaranteed not to lock or to walk the CLDG. +void MetaspaceReporter::print_basic_report(outputStream* out, size_t scale) { + if (!Metaspace::initialized()) { + out->print_cr("Metaspace not yet initialized."); + return; + } + out->cr(); + out->print_cr("Usage:"); + if (Metaspace::using_class_space()) { + out->print(" Non-class: "); + } + + // Note: since we want to purely rely on counters, without any locking or walking the CLDG, + // for Usage stats (statistics over in-use chunks) all we can print is the + // used words. We cannot print committed areas, or free/waste areas, of in-use chunks require + // walking. + const size_t used_nc = MetaspaceUtils::used_words(Metaspace::NonClassType); + + print_scaled_words(out, used_nc, scale, 5); + out->print(" used."); + out->cr(); + if (Metaspace::using_class_space()) { + const size_t used_c = MetaspaceUtils::used_words(Metaspace::ClassType); + out->print(" Class: "); + print_scaled_words(out, used_c, scale, 5); + out->print(" used."); + out->cr(); + out->print(" Both: "); + const size_t used = used_nc + used_c; + print_scaled_words(out, used, scale, 5); + out->print(" used."); + out->cr(); + } + out->cr(); + out->print_cr("Virtual space:"); + print_vs(out, scale); + out->cr(); + out->print_cr("Chunk freelists:"); + if (Metaspace::using_class_space()) { + out->print(" Non-Class: "); + } + print_scaled_words(out, ChunkManager::chunkmanager_nonclass()->total_word_size(), scale); + out->cr(); + if (Metaspace::using_class_space()) { + out->print(" Class: "); + print_scaled_words(out, ChunkManager::chunkmanager_class()->total_word_size(), scale); + out->cr(); + out->print(" Both: "); + print_scaled_words(out, ChunkManager::chunkmanager_nonclass()->total_word_size() + + ChunkManager::chunkmanager_class()->total_word_size(), scale); + out->cr(); + } + out->cr(); + + // Print basic settings + print_settings(out, scale); + out->cr(); + out->cr(); + out->print_cr("Internal statistics:"); + out->cr(); + InternalStats::print_on(out); + out->cr(); +} + +void MetaspaceReporter::print_report(outputStream* out, size_t scale, int flags) { + if (!Metaspace::initialized()) { + out->print_cr("Metaspace not yet initialized."); + return; + } + const bool print_loaders = (flags & (int)Option::ShowLoaders) > 0; + const bool print_classes = (flags & (int)Option::ShowClasses) > 0; + const bool print_by_chunktype = (flags & (int)Option::BreakDownByChunkType) > 0; + const bool print_by_spacetype = (flags & (int)Option::BreakDownBySpaceType) > 0; + + // Some report options require walking the class loader data graph. + metaspace::PrintCLDMetaspaceInfoClosure cl(out, scale, print_loaders, print_classes, print_by_chunktype); + if (print_loaders) { + out->cr(); + out->print_cr("Usage per loader:"); + out->cr(); + } + + ClassLoaderDataGraph::loaded_cld_do(&cl); // collect data and optionally print + + // Print totals, broken up by space type. + if (print_by_spacetype) { + out->cr(); + out->print_cr("Usage per space type:"); + out->cr(); + for (int space_type = (int)Metaspace::ZeroMetaspaceType; + space_type < (int)Metaspace::MetaspaceTypeCount; space_type++) + { + uintx num_loaders = cl._num_loaders_by_spacetype[space_type]; + uintx num_classes = cl._num_classes_by_spacetype[space_type]; + out->print("%s - " UINTX_FORMAT " %s", + describe_spacetype((Metaspace::MetaspaceType)space_type), + num_loaders, loaders_plural(num_loaders)); + if (num_classes > 0) { + out->print(", "); + + print_number_of_classes(out, num_classes, cl._num_classes_shared_by_spacetype[space_type]); + out->print(":"); + cl._stats_by_spacetype[space_type].print_on(out, scale, print_by_chunktype); + } else { + out->print("."); + out->cr(); + } + out->cr(); + } + } + + // Print totals for in-use data: + out->cr(); + { + uintx num_loaders = cl._num_loaders; + out->print("Total Usage - " UINTX_FORMAT " %s, ", + num_loaders, loaders_plural(num_loaders)); + print_number_of_classes(out, cl._num_classes, cl._num_classes_shared); + out->print(":"); + cl._stats_total.print_on(out, scale, print_by_chunktype); + out->cr(); + } + + ///////////////////////////////////////////////// + // -- Print Virtual space. + out->cr(); + out->print_cr("Virtual space:"); + + print_vs(out, scale); + + // -- Print VirtualSpaceList details. + if ((flags & (int)Option::ShowVSList) > 0) { + out->cr(); + out->print_cr("Virtual space list%s:", Metaspace::using_class_space() ? "s" : ""); + + if (Metaspace::using_class_space()) { + out->print_cr(" Non-Class:"); + } + VirtualSpaceList::vslist_nonclass()->print_on(out); + out->cr(); + if (Metaspace::using_class_space()) { + out->print_cr(" Class:"); + VirtualSpaceList::vslist_class()->print_on(out); + out->cr(); + } + } + out->cr(); + + //////////// Freelists (ChunkManager) section /////////////////////////// + + out->cr(); + out->print_cr("Chunk freelist%s:", Metaspace::using_class_space() ? "s" : ""); + + ChunkManagerStats non_class_cm_stat; + ChunkManagerStats class_cm_stat; + ChunkManagerStats total_cm_stat; + + ChunkManager::chunkmanager_nonclass()->add_to_statistics(&non_class_cm_stat); + if (Metaspace::using_class_space()) { + ChunkManager::chunkmanager_nonclass()->add_to_statistics(&non_class_cm_stat); + ChunkManager::chunkmanager_class()->add_to_statistics(&class_cm_stat); + total_cm_stat.add(non_class_cm_stat); + total_cm_stat.add(class_cm_stat); + + out->print_cr(" Non-Class:"); + non_class_cm_stat.print_on(out, scale); + out->cr(); + out->print_cr(" Class:"); + class_cm_stat.print_on(out, scale); + out->cr(); + out->print_cr(" Both:"); + total_cm_stat.print_on(out, scale); + out->cr(); + } else { + ChunkManager::chunkmanager_nonclass()->add_to_statistics(&non_class_cm_stat); + non_class_cm_stat.print_on(out, scale); + out->cr(); + } + + //////////// Waste section /////////////////////////// + // As a convenience, print a summary of common waste. + out->cr(); + out->print("Waste (unused committed space):"); + // For all wastages, print percentages from total. As total use the total size of memory committed for metaspace. + const size_t committed_words = RunningCounters::committed_words(); + + out->print("(percentages refer to total committed size "); + print_scaled_words(out, committed_words, scale); + out->print_cr("):"); + + // Print waste for in-use chunks. + InUseChunkStats ucs_nonclass = cl._stats_total._arena_stats_nonclass.totals(); + InUseChunkStats ucs_class = cl._stats_total._arena_stats_class.totals(); + const size_t waste_in_chunks_in_use = ucs_nonclass._waste_words + ucs_class._waste_words; + const size_t free_in_chunks_in_use = ucs_nonclass._free_words + ucs_class._free_words; + + out->print(" Waste in chunks in use: "); + print_scaled_words_and_percentage(out, waste_in_chunks_in_use, committed_words, scale, 6); + out->cr(); + out->print(" Free in chunks in use: "); + print_scaled_words_and_percentage(out, free_in_chunks_in_use, committed_words, scale, 6); + out->cr(); + + // Print waste in free chunks. + const size_t committed_in_free_chunks = total_cm_stat.total_committed_word_size(); + out->print(" In free chunks: "); + print_scaled_words_and_percentage(out, committed_in_free_chunks, committed_words, scale, 6); + out->cr(); + + // Print waste in deallocated blocks. + const uintx free_blocks_num = + cl._stats_total._arena_stats_nonclass._free_blocks_num + + cl._stats_total._arena_stats_class._free_blocks_num; + const size_t free_blocks_cap_words = + cl._stats_total._arena_stats_nonclass._free_blocks_word_size + + cl._stats_total._arena_stats_class._free_blocks_word_size; + out->print("Deallocated from chunks in use: "); + print_scaled_words_and_percentage(out, free_blocks_cap_words, committed_words, scale, 6); + out->print(" (" UINTX_FORMAT " blocks)", free_blocks_num); + out->cr(); + + // Print total waste. + const size_t total_waste = + waste_in_chunks_in_use + + free_in_chunks_in_use + + committed_in_free_chunks + + free_blocks_cap_words; + out->print(" -total-: "); + print_scaled_words_and_percentage(out, total_waste, committed_words, scale, 6); + out->cr(); + + // Also print chunk header pool size. + out->cr(); + out->print("chunk header pool: %u items, ", ChunkHeaderPool::pool()->used()); + print_scaled_words(out, ChunkHeaderPool::pool()->memory_footprint_words(), scale); + out->print("."); + out->cr(); + + // Print internal statistics + out->cr(); + out->print_cr("Internal statistics:"); + out->cr(); + InternalStats::print_on(out); + out->cr(); + + // Print some interesting settings + out->cr(); + out->print_cr("Settings:"); + print_settings(out, scale); + + out->cr(); + out->cr(); + + DEBUG_ONLY(MetaspaceUtils::verify();) +} // MetaspaceUtils::print_report() + +} // namespace metaspace + diff --git a/src/hotspot/share/memory/metaspace/metaspaceReporter.hpp b/src/hotspot/share/memory/metaspace/metaspaceReporter.hpp new file mode 100644 index 00000000000..45c76b69e9d --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metaspaceReporter.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_METASPACEREPORTER_HPP +#define SHARE_MEMORY_METASPACE_METASPACEREPORTER_HPP + +#include "memory/allocation.hpp" + +namespace metaspace { + +class MetaspaceReporter : public AllStatic { +public: + + // Flags for print_report(). + enum class Option { + // Show usage by class loader. + ShowLoaders = (1 << 0), + // Breaks report down by chunk type (small, medium, ...). + BreakDownByChunkType = (1 << 1), + // Breaks report down by space type (anonymous, reflection, ...). + BreakDownBySpaceType = (1 << 2), + // Print details about the underlying virtual spaces. + ShowVSList = (1 << 3), + // If show_loaders: show loaded classes for each loader. + ShowClasses = (1 << 4) + }; + + // This will print out a basic metaspace usage report but + // unlike print_report() is guaranteed not to lock or to walk the CLDG. + static void print_basic_report(outputStream* st, size_t scale); + + // Prints a report about the current metaspace state. + // Optional parts can be enabled via flags. + // Function will walk the CLDG and will lock the expand lock; if that is not + // convenient, use print_basic_report() instead. + static void print_report(outputStream* out, size_t scale = 0, int flags = 0); + +}; + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_METASPACEREPORTER_HPP diff --git a/src/hotspot/share/memory/metaspace/metaspaceSettings.cpp b/src/hotspot/share/memory/metaspace/metaspaceSettings.cpp new file mode 100644 index 00000000000..e11c2777e1d --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metaspaceSettings.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "logging/log.hpp" +#include "logging/logStream.hpp" +#include "memory/metaspace/metaspaceSettings.hpp" +#include "runtime/globals.hpp" +#include "runtime/java.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/powerOfTwo.hpp" + +namespace metaspace { + +size_t Settings::_commit_granule_bytes = 0; +size_t Settings::_commit_granule_words = 0; + +bool Settings::_new_chunks_are_fully_committed = false; +bool Settings::_uncommit_free_chunks = false; + +DEBUG_ONLY(bool Settings::_use_allocation_guard = false;) +DEBUG_ONLY(bool Settings::_handle_deallocations = true;) + +void Settings::ergo_initialize() { + if (strcmp(MetaspaceReclaimPolicy, "none") == 0) { + log_info(metaspace)("Initialized with strategy: no reclaim."); + _commit_granule_bytes = MAX2((size_t)os::vm_page_size(), 64 * K); + _commit_granule_words = _commit_granule_bytes / BytesPerWord; + // In "none" reclamation mode, we do not uncommit, and we commit new chunks fully; + // that very closely mimicks the behaviour of old Metaspace. + _new_chunks_are_fully_committed = true; + _uncommit_free_chunks = false; + } else if (strcmp(MetaspaceReclaimPolicy, "aggressive") == 0) { + log_info(metaspace)("Initialized with strategy: aggressive reclaim."); + // Set the granule size rather small; may increase + // mapping fragmentation but also increase chance to uncommit. + _commit_granule_bytes = MAX2((size_t)os::vm_page_size(), 16 * K); + _commit_granule_words = _commit_granule_bytes / BytesPerWord; + _new_chunks_are_fully_committed = false; + _uncommit_free_chunks = true; + } else if (strcmp(MetaspaceReclaimPolicy, "balanced") == 0) { + log_info(metaspace)("Initialized with strategy: balanced reclaim."); + _commit_granule_bytes = MAX2((size_t)os::vm_page_size(), 64 * K); + _commit_granule_words = _commit_granule_bytes / BytesPerWord; + _new_chunks_are_fully_committed = false; + _uncommit_free_chunks = true; + } else { + vm_exit_during_initialization("Invalid value for MetaspaceReclaimPolicy: \"%s\".", MetaspaceReclaimPolicy); + } + + // Sanity checks. + assert(commit_granule_words() <= chunklevel::MAX_CHUNK_WORD_SIZE, "Too large granule size"); + assert(is_power_of_2(commit_granule_words()), "granule size must be a power of 2"); + +#ifdef ASSERT + // Off for release builds, and by default for debug builds, but can be switched on manually to aid + // error analysis. + _use_allocation_guard = MetaspaceGuardAllocations; + + // Deallocations can be manually switched off to aid error analysis, since this removes one layer of complexity + // from allocation. + _handle_deallocations = MetaspaceHandleDeallocations; + + // We also switch it off automatically if we use allocation guards. This is to keep prefix handling in MetaspaceArena simple. + if (_use_allocation_guard) { + _handle_deallocations = false; + } +#endif + LogStream ls(Log(metaspace)::info()); + Settings::print_on(&ls); +} + +void Settings::print_on(outputStream* st) { + st->print_cr(" - commit_granule_bytes: " SIZE_FORMAT ".", commit_granule_bytes()); + st->print_cr(" - commit_granule_words: " SIZE_FORMAT ".", commit_granule_words()); + st->print_cr(" - virtual_space_node_default_size: " SIZE_FORMAT ".", virtual_space_node_default_word_size()); + st->print_cr(" - enlarge_chunks_in_place: %d.", (int)enlarge_chunks_in_place()); + st->print_cr(" - new_chunks_are_fully_committed: %d.", (int)new_chunks_are_fully_committed()); + st->print_cr(" - uncommit_free_chunks: %d.", (int)uncommit_free_chunks()); + st->print_cr(" - use_allocation_guard: %d.", (int)use_allocation_guard()); + st->print_cr(" - handle_deallocations: %d.", (int)handle_deallocations()); +} + +} // namespace metaspace + diff --git a/src/hotspot/share/memory/metaspace/metaspaceSettings.hpp b/src/hotspot/share/memory/metaspace/metaspaceSettings.hpp new file mode 100644 index 00000000000..47792fda422 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metaspaceSettings.hpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_METASPACESETTINGS_HPP +#define SHARE_MEMORY_METASPACE_METASPACESETTINGS_HPP + +#include "memory/allocation.hpp" +#include "memory/metaspace/chunklevel.hpp" +#include "utilities/globalDefinitions.hpp" + +namespace metaspace { + +class Settings : public AllStatic { + + // Granularity, in bytes, metaspace is committed with. + static size_t _commit_granule_bytes; + + // Granularity, in words, metaspace is committed with. + static size_t _commit_granule_words; + + // The default size of a VirtualSpaceNode, unless created with an explicitly specified size. + // Must be a multiple of the root chunk size. + // Increasing this value decreases the number of mappings used for metadata, + // at the cost of increased virtual size used for Metaspace (or, at least, + // coarser growth steps). Matters mostly for 32bit platforms due to limited + // address space. + // The default of two root chunks has been chosen on a whim but seems to work out okay + // (coming to a mapping size of 8m per node). + static const size_t _virtual_space_node_default_word_size = chunklevel::MAX_CHUNK_WORD_SIZE * 2; + + // Alignment of the base address of a virtual space node + static const size_t _virtual_space_node_reserve_alignment_words = chunklevel::MAX_CHUNK_WORD_SIZE; + + // When allocating from a chunk, if the remaining area in the chunk is too small to hold + // the requested size, we attempt to double the chunk size in place... + static const bool _enlarge_chunks_in_place = true; + + // Whether or not chunks handed out to an arena start out fully committed; + // if true, this deactivates committing-on-demand (regardless of whether + // we uncommit free chunks). + static bool _new_chunks_are_fully_committed; + + // If true, chunks equal or larger than a commit granule are uncommitted + // after being returned to the freelist. + static bool _uncommit_free_chunks; + + // If true, metablock allocations are guarded and periodically checked. + DEBUG_ONLY(static bool _use_allocation_guard;) + + // This enables or disables premature deallocation of metaspace allocated blocks. Using + // Metaspace::deallocate(), blocks can be returned prematurely (before the associated + // Arena dies, e.g. after class unloading) and can be reused by the arena. + // If disabled, those blocks will not be reused until the Arena dies. + // Note that premature deallocation is rare under normal circumstances. + // By default deallocation handling is enabled. + DEBUG_ONLY(static bool _handle_deallocations;) + +public: + + static size_t commit_granule_bytes() { return _commit_granule_bytes; } + static size_t commit_granule_words() { return _commit_granule_words; } + static bool new_chunks_are_fully_committed() { return _new_chunks_are_fully_committed; } + static size_t virtual_space_node_default_word_size() { return _virtual_space_node_default_word_size; } + static size_t virtual_space_node_reserve_alignment_words() { return _virtual_space_node_reserve_alignment_words; } + static bool enlarge_chunks_in_place() { return _enlarge_chunks_in_place; } + static bool uncommit_free_chunks() { return _uncommit_free_chunks; } + static bool use_allocation_guard() { return DEBUG_ONLY(_use_allocation_guard) NOT_DEBUG(false); } + static bool handle_deallocations() { return DEBUG_ONLY(_handle_deallocations) NOT_DEBUG(true); } + + static void ergo_initialize(); + + static void print_on(outputStream* st); + +}; + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_METASPACESETTINGS_HPP diff --git a/src/hotspot/share/memory/metaspace/metaspaceSizesSnapshot.cpp b/src/hotspot/share/memory/metaspace/metaspaceSizesSnapshot.cpp index dc1104d1bbd..26e07441504 100644 --- a/src/hotspot/share/memory/metaspace/metaspaceSizesSnapshot.cpp +++ b/src/hotspot/share/memory/metaspace/metaspaceSizesSnapshot.cpp @@ -24,18 +24,18 @@ */ #include "precompiled.hpp" - #include "memory/metaspace.hpp" #include "memory/metaspace/metaspaceSizesSnapshot.hpp" namespace metaspace { -MetaspaceSizesSnapshot::MetaspaceSizesSnapshot() - : _used(MetaspaceUtils::used_bytes()), - _committed(MetaspaceUtils::committed_bytes()), - _non_class_used(MetaspaceUtils::used_bytes(Metaspace::NonClassType)), - _non_class_committed(MetaspaceUtils::committed_bytes(Metaspace::NonClassType)), - _class_used(MetaspaceUtils::used_bytes(Metaspace::ClassType)), - _class_committed(MetaspaceUtils::committed_bytes(Metaspace::ClassType)) { } +MetaspaceSizesSnapshot::MetaspaceSizesSnapshot() : + _used(MetaspaceUtils::used_bytes()), + _committed(MetaspaceUtils::committed_bytes()), + _non_class_used(MetaspaceUtils::used_bytes(Metaspace::NonClassType)), + _non_class_committed(MetaspaceUtils::committed_bytes(Metaspace::NonClassType)), + _class_used(MetaspaceUtils::used_bytes(Metaspace::ClassType)), + _class_committed(MetaspaceUtils::committed_bytes(Metaspace::ClassType)) +{} } // namespace metaspace diff --git a/src/hotspot/share/memory/metaspace/metaspaceSizesSnapshot.hpp b/src/hotspot/share/memory/metaspace/metaspaceSizesSnapshot.hpp index b2ab8dac9df..8cf39e70591 100644 --- a/src/hotspot/share/memory/metaspace/metaspaceSizesSnapshot.hpp +++ b/src/hotspot/share/memory/metaspace/metaspaceSizesSnapshot.hpp @@ -26,8 +26,11 @@ #ifndef SHARE_MEMORY_METASPACE_METASPACESIZESSNAPSHOT_HPP #define SHARE_MEMORY_METASPACE_METASPACESIZESSNAPSHOT_HPP +#include "utilities/globalDefinitions.hpp" + namespace metaspace { +// Todo: clean up after jep387, see JDK-8251392 class MetaspaceSizesSnapshot { public: MetaspaceSizesSnapshot(); diff --git a/src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp b/src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp index 38194a4a61b..2c32cbe38b1 100644 --- a/src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp +++ b/src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018 SAP SE. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,9 +22,8 @@ * questions. * */ -#include "precompiled.hpp" -#include "memory/metaspace/metachunk.hpp" +#include "precompiled.hpp" #include "memory/metaspace/metaspaceCommon.hpp" #include "memory/metaspace/metaspaceStatistics.hpp" #include "utilities/debug.hpp" @@ -33,168 +32,139 @@ namespace metaspace { -// FreeChunksStatistics methods - -FreeChunksStatistics::FreeChunksStatistics() -: _num(0), _cap(0) -{} - -void FreeChunksStatistics::reset() { - _num = 0; _cap = 0; +// Returns total word size of all chunks in this manager. +void ChunkManagerStats::add(const ChunkManagerStats& other) { + for (chunklevel_t l = chunklevel::LOWEST_CHUNK_LEVEL; l <= chunklevel::HIGHEST_CHUNK_LEVEL; l++) { + _num_chunks[l] += other._num_chunks[l]; + _committed_word_size[l] += other._committed_word_size[l]; + } } -void FreeChunksStatistics::add(uintx n, size_t s) { - _num += n; _cap += s; +// Returns total word size of all chunks in this manager. +size_t ChunkManagerStats::total_word_size() const { + size_t s = 0; + for (chunklevel_t l = chunklevel::LOWEST_CHUNK_LEVEL; l <= chunklevel::HIGHEST_CHUNK_LEVEL; l++) { + s += _num_chunks[l] * chunklevel::word_size_for_level(l); + } + return s; } -void FreeChunksStatistics::add(const FreeChunksStatistics& other) { - _num += other._num; - _cap += other._cap; +// Returns total committed word size of all chunks in this manager. +size_t ChunkManagerStats::total_committed_word_size() const { + size_t s = 0; + for (chunklevel_t l = chunklevel::LOWEST_CHUNK_LEVEL; l <= chunklevel::HIGHEST_CHUNK_LEVEL; l++) { + s += _committed_word_size[l]; + } + return s; } -void FreeChunksStatistics::print_on(outputStream* st, size_t scale) const { - st->print(UINTX_FORMAT, _num); - st->print(" chunks, total capacity "); - print_scaled_words(st, _cap, scale); -} +void ChunkManagerStats::print_on(outputStream* st, size_t scale) const { + // Note: used as part of MetaspaceReport so formatting matters. + size_t total_size = 0; + size_t total_committed_size = 0; + for (chunklevel_t l = chunklevel::LOWEST_CHUNK_LEVEL; l <= chunklevel::HIGHEST_CHUNK_LEVEL; l++) { + st->cr(); + chunklevel::print_chunk_size(st, l); + st->print(": "); + if (_num_chunks[l] > 0) { + const size_t word_size = _num_chunks[l] * chunklevel::word_size_for_level(l); -// ChunkManagerStatistics methods + st->print("%4d, capacity=", _num_chunks[l]); + print_scaled_words(st, word_size, scale); -void ChunkManagerStatistics::reset() { - for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { - _chunk_stats[i].reset(); - } -} + st->print(", committed="); + print_scaled_words_and_percentage(st, _committed_word_size[l], word_size, scale); -size_t ChunkManagerStatistics::total_capacity() const { - return _chunk_stats[SpecializedIndex].cap() + - _chunk_stats[SmallIndex].cap() + - _chunk_stats[MediumIndex].cap() + - _chunk_stats[HumongousIndex].cap(); -} - -void ChunkManagerStatistics::print_on(outputStream* st, size_t scale) const { - FreeChunksStatistics totals; - for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { - st->cr(); - st->print("%12s chunks: ", chunk_size_name(i)); - if (_chunk_stats[i].num() > 0) { - st->print(UINTX_FORMAT_W(4) ", capacity ", _chunk_stats[i].num()); - print_scaled_words(st, _chunk_stats[i].cap(), scale); + total_size += word_size; + total_committed_size += _committed_word_size[l]; } else { st->print("(none)"); } - totals.add(_chunk_stats[i]); } st->cr(); - st->print("%19s: " UINTX_FORMAT_W(4) ", capacity=", "Total", totals.num()); - print_scaled_words(st, totals.cap(), scale); + st->print("Total word size: "); + print_scaled_words(st, total_size, scale); + st->print(", committed: "); + print_scaled_words_and_percentage(st, total_committed_size, total_size, scale); st->cr(); } -// UsedChunksStatistics methods - -UsedChunksStatistics::UsedChunksStatistics() -: _num(0), _cap(0), _used(0), _free(0), _waste(0), _overhead(0) -{} - -void UsedChunksStatistics::reset() { - _num = 0; - _cap = _overhead = _used = _free = _waste = 0; -} - -void UsedChunksStatistics::add(const UsedChunksStatistics& other) { - _num += other._num; - _cap += other._cap; - _used += other._used; - _free += other._free; - _waste += other._waste; - _overhead += other._overhead; - DEBUG_ONLY(check_sanity()); +#ifdef ASSERT +void ChunkManagerStats::verify() const { + assert(total_committed_word_size() <= total_word_size(), + "Sanity"); } +#endif -void UsedChunksStatistics::print_on(outputStream* st, size_t scale) const { +void InUseChunkStats::print_on(outputStream* st, size_t scale) const { int col = st->position(); - st->print(UINTX_FORMAT_W(4) " chunk%s, ", _num, _num != 1 ? "s" : ""); + st->print("%4d chunk%s, ", _num, _num != 1 ? "s" : ""); if (_num > 0) { col += 14; st->fill_to(col); - print_scaled_words(st, _cap, scale, 5); - st->print(" capacity, "); + print_scaled_words(st, _word_size, scale, 5); + st->print(" capacity,"); + + col += 20; st->fill_to(col); + print_scaled_words_and_percentage(st, _committed_words, _word_size, scale, 5); + st->print(" committed, "); col += 18; st->fill_to(col); - print_scaled_words_and_percentage(st, _used, _cap, scale, 5); + print_scaled_words_and_percentage(st, _used_words, _word_size, scale, 5); st->print(" used, "); col += 20; st->fill_to(col); - print_scaled_words_and_percentage(st, _free, _cap, scale, 5); + print_scaled_words_and_percentage(st, _free_words, _word_size, scale, 5); st->print(" free, "); col += 20; st->fill_to(col); - print_scaled_words_and_percentage(st, _waste, _cap, scale, 5); - st->print(" waste, "); + print_scaled_words_and_percentage(st, _waste_words, _word_size, scale, 5); + st->print(" waste "); - col += 20; st->fill_to(col); - print_scaled_words_and_percentage(st, _overhead, _cap, scale, 5); - st->print(" overhead"); } - DEBUG_ONLY(check_sanity()); } #ifdef ASSERT -void UsedChunksStatistics::check_sanity() const { - assert(_overhead == (Metachunk::overhead() * _num), "Sanity: Overhead."); - assert(_cap == _used + _free + _waste + _overhead, "Sanity: Capacity."); +void InUseChunkStats::verify() const { + assert(_word_size >= _committed_words && + _committed_words == _used_words + _free_words + _waste_words, + "Sanity: cap " SIZE_FORMAT ", committed " SIZE_FORMAT ", used " SIZE_FORMAT ", free " SIZE_FORMAT ", waste " SIZE_FORMAT ".", + _word_size, _committed_words, _used_words, _free_words, _waste_words); } #endif -// SpaceManagerStatistics methods - -SpaceManagerStatistics::SpaceManagerStatistics() { reset(); } - -void SpaceManagerStatistics::reset() { - for (int i = 0; i < NumberOfInUseLists; i ++) { - _chunk_stats[i].reset(); - _free_blocks_num = 0; _free_blocks_cap_words = 0; - } -} - -void SpaceManagerStatistics::add_free_blocks_info(uintx num, size_t cap) { - _free_blocks_num += num; - _free_blocks_cap_words += cap; -} - -void SpaceManagerStatistics::add(const SpaceManagerStatistics& other) { - for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { - _chunk_stats[i].add(other._chunk_stats[i]); +void ArenaStats::add(const ArenaStats& other) { + for (chunklevel_t l = chunklevel::LOWEST_CHUNK_LEVEL; l <= chunklevel::HIGHEST_CHUNK_LEVEL; l++) { + _stats[l].add(other._stats[l]); } _free_blocks_num += other._free_blocks_num; - _free_blocks_cap_words += other._free_blocks_cap_words; + _free_blocks_word_size += other._free_blocks_word_size; } // Returns total chunk statistics over all chunk types. -UsedChunksStatistics SpaceManagerStatistics::totals() const { - UsedChunksStatistics stat; - for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { - stat.add(_chunk_stats[i]); +InUseChunkStats ArenaStats::totals() const { + InUseChunkStats out; + for (chunklevel_t l = chunklevel::LOWEST_CHUNK_LEVEL; l <= chunklevel::HIGHEST_CHUNK_LEVEL; l++) { + out.add(_stats[l]); } - return stat; + return out; } -void SpaceManagerStatistics::print_on(outputStream* st, size_t scale, bool detailed) const { +void ArenaStats::print_on(outputStream* st, size_t scale, bool detailed) const { streamIndentor sti(st); if (detailed) { st->cr_indent(); - st->print("Usage by chunk type:"); + st->print("Usage by chunk level:"); { streamIndentor sti2(st); - for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { + for (chunklevel_t l = chunklevel::LOWEST_CHUNK_LEVEL; l <= chunklevel::HIGHEST_CHUNK_LEVEL; l++) { st->cr_indent(); - st->print("%15s: ", chunk_size_name(i)); - if (_chunk_stats[i].num() == 0) { + chunklevel::print_chunk_size(st, l); + st->print(" chunks: "); + if (_stats[l]._num == 0) { st->print(" (none)"); } else { - _chunk_stats[i].print_on(st, scale); + _stats[l].print_on(st, scale); } } @@ -205,58 +175,52 @@ void SpaceManagerStatistics::print_on(outputStream* st, size_t scale, bool deta if (_free_blocks_num > 0) { st->cr_indent(); st->print("deallocated: " UINTX_FORMAT " blocks with ", _free_blocks_num); - print_scaled_words(st, _free_blocks_cap_words, scale); + print_scaled_words(st, _free_blocks_word_size, scale); } } else { totals().print_on(st, scale); st->print(", "); st->print("deallocated: " UINTX_FORMAT " blocks with ", _free_blocks_num); - print_scaled_words(st, _free_blocks_cap_words, scale); + print_scaled_words(st, _free_blocks_word_size, scale); } } -// ClassLoaderMetaspaceStatistics methods - -ClassLoaderMetaspaceStatistics::ClassLoaderMetaspaceStatistics() { reset(); } - -void ClassLoaderMetaspaceStatistics::reset() { - nonclass_sm_stats().reset(); - if (Metaspace::using_class_space()) { - class_sm_stats().reset(); - } -} +#ifdef ASSERT -// Returns total space manager statistics for both class and non-class metaspace -SpaceManagerStatistics ClassLoaderMetaspaceStatistics::totals() const { - SpaceManagerStatistics stats; - stats.add(nonclass_sm_stats()); - if (Metaspace::using_class_space()) { - stats.add(class_sm_stats()); +void ArenaStats::verify() const { + size_t total_used = 0; + for (chunklevel_t l = chunklevel::LOWEST_CHUNK_LEVEL; l <= chunklevel::HIGHEST_CHUNK_LEVEL; l++) { + _stats[l].verify(); + total_used += _stats[l]._used_words; } - return stats; + // Deallocated allocations still count as used + assert(total_used >= _free_blocks_word_size, + "Sanity"); } +#endif -void ClassLoaderMetaspaceStatistics::add(const ClassLoaderMetaspaceStatistics& other) { - nonclass_sm_stats().add(other.nonclass_sm_stats()); - if (Metaspace::using_class_space()) { - class_sm_stats().add(other.class_sm_stats()); - } +// Returns total arena statistics for both class and non-class metaspace +ArenaStats ClmsStats::totals() const { + ArenaStats out; + out.add(_arena_stats_nonclass); + out.add(_arena_stats_class); + return out; } -void ClassLoaderMetaspaceStatistics::print_on(outputStream* st, size_t scale, bool detailed) const { +void ClmsStats::print_on(outputStream* st, size_t scale, bool detailed) const { streamIndentor sti(st); st->cr_indent(); if (Metaspace::using_class_space()) { st->print("Non-Class: "); } - nonclass_sm_stats().print_on(st, scale, detailed); + _arena_stats_nonclass.print_on(st, scale, detailed); if (detailed) { st->cr(); } if (Metaspace::using_class_space()) { st->cr_indent(); st->print(" Class: "); - class_sm_stats().print_on(st, scale, detailed); + _arena_stats_class.print_on(st, scale, detailed); if (detailed) { st->cr(); } @@ -270,7 +234,12 @@ void ClassLoaderMetaspaceStatistics::print_on(outputStream* st, size_t scale, bo st->cr(); } -} // end namespace metaspace - +#ifdef ASSERT +void ClmsStats::verify() const { + _arena_stats_nonclass.verify(); + _arena_stats_class.verify(); +} +#endif +} // end namespace metaspace diff --git a/src/hotspot/share/memory/metaspace/metaspaceStatistics.hpp b/src/hotspot/share/memory/metaspace/metaspaceStatistics.hpp index 3d62764ddb6..d9dd48ead17 100644 --- a/src/hotspot/share/memory/metaspace/metaspaceStatistics.hpp +++ b/src/hotspot/share/memory/metaspace/metaspaceStatistics.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018 SAP SE. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,162 +26,144 @@ #ifndef SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP #define SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP +#include "memory/metaspace.hpp" // for MetadataType enum +#include "memory/metaspace/chunklevel.hpp" #include "utilities/globalDefinitions.hpp" -#include "memory/metaspace.hpp" // for MetadataType enum -#include "memory/metaspace/metachunk.hpp" // for ChunkIndex enum class outputStream; namespace metaspace { -// Contains statistics for a number of free chunks. -class FreeChunksStatistics { - uintx _num; // Number of chunks - size_t _cap; // Total capacity, in words +// Contains a number of data output structures: +// +// - cm_stats_t +// - clms_stats_t -> arena_stats_t -> in_use_chunk_stats_t +// +// used for the various XXXX::add_to_statistic() methods in MetaspaceArena, ClassLoaderMetaspace +// and ChunkManager, respectively. -public: - FreeChunksStatistics(); +struct ChunkManagerStats { - void reset(); + // How many chunks per level are checked in. + int _num_chunks[chunklevel::NUM_CHUNK_LEVELS]; - uintx num() const { return _num; } - size_t cap() const { return _cap; } + // Size, in words, of the sum of all committed areas in this chunk manager, per level. + size_t _committed_word_size[chunklevel::NUM_CHUNK_LEVELS]; - void add(uintx n, size_t s); - void add(const FreeChunksStatistics& other); - void print_on(outputStream* st, size_t scale) const; - -}; // end: FreeChunksStatistics - - -// Contains statistics for a ChunkManager. -class ChunkManagerStatistics { + ChunkManagerStats() : _num_chunks(), _committed_word_size() {} - FreeChunksStatistics _chunk_stats[NumberOfInUseLists]; + void add(const ChunkManagerStats& other); -public: + // Returns total word size of all chunks in this manager. + size_t total_word_size() const; - // Free chunk statistics, by chunk index. - const FreeChunksStatistics& chunk_stats(ChunkIndex index) const { return _chunk_stats[index]; } - FreeChunksStatistics& chunk_stats(ChunkIndex index) { return _chunk_stats[index]; } - - void reset(); - size_t total_capacity() const; + // Returns total committed word size of all chunks in this manager. + size_t total_committed_word_size() const; void print_on(outputStream* st, size_t scale) const; -}; // ChunkManagerStatistics + DEBUG_ONLY(void verify() const;) -// Contains statistics for a number of chunks in use. -// Each chunk has a used and free portion; however, there are current chunks (serving -// potential future metaspace allocations) and non-current chunks. Unused portion of the -// former is counted as free, unused portion of the latter counts as waste. -class UsedChunksStatistics { - uintx _num; // Number of chunks - size_t _cap; // Total capacity in words. - size_t _used; // Total used area, in words - size_t _free; // Total free area (unused portions of current chunks), in words - size_t _waste; // Total waste area (unused portions of non-current chunks), in words - size_t _overhead; // Total sum of chunk overheads, in words. +}; -public: +// Contains statistics for one or multiple chunks in use. +struct InUseChunkStats { - UsedChunksStatistics(); + // Number of chunks + int _num; - void reset(); + // Note: + // capacity = committed + uncommitted + // committed = used + free + waste - uintx num() const { return _num; } + // Capacity (total sum of all chunk sizes) in words. + // May contain committed and uncommitted space. + size_t _word_size; - // Total capacity, in words - size_t cap() const { return _cap; } + // Total committed area, in words. + size_t _committed_words; - // Total used area, in words - size_t used() const { return _used; } + // Total used area, in words. + size_t _used_words; - // Total free area (unused portions of current chunks), in words - size_t free() const { return _free; } + // Total free committed area, in words. + size_t _free_words; - // Total waste area (unused portions of non-current chunks), in words - size_t waste() const { return _waste; } + // Total waste committed area, in words. + size_t _waste_words; - // Total area spent in overhead (chunk headers), in words - size_t overhead() const { return _overhead; } + InUseChunkStats() : + _num(0), + _word_size(0), + _committed_words(0), + _used_words(0), + _free_words(0), + _waste_words(0) + {} - void add_num(uintx n) { _num += n; } - void add_cap(size_t s) { _cap += s; } - void add_used(size_t s) { _used += s; } - void add_free(size_t s) { _free += s; } - void add_waste(size_t s) { _waste += s; } - void add_overhead(size_t s) { _overhead += s; } + void add(const InUseChunkStats& other) { + _num += other._num; + _word_size += other._word_size; + _committed_words += other._committed_words; + _used_words += other._used_words; + _free_words += other._free_words; + _waste_words += other._waste_words; - void add(const UsedChunksStatistics& other); + } void print_on(outputStream* st, size_t scale) const; -#ifdef ASSERT - void check_sanity() const; -#endif + DEBUG_ONLY(void verify() const;) -}; // UsedChunksStatistics +}; -// Class containing statistics for one or more space managers. -class SpaceManagerStatistics { +// Class containing statistics for one or more MetaspaceArena objects. +struct ArenaStats { - UsedChunksStatistics _chunk_stats[NumberOfInUseLists]; + // chunk statistics by chunk level + InUseChunkStats _stats[chunklevel::NUM_CHUNK_LEVELS]; uintx _free_blocks_num; - size_t _free_blocks_cap_words; - -public: - - SpaceManagerStatistics(); - - // Chunk statistics by chunk index - const UsedChunksStatistics& chunk_stats(ChunkIndex index) const { return _chunk_stats[index]; } - UsedChunksStatistics& chunk_stats(ChunkIndex index) { return _chunk_stats[index]; } + size_t _free_blocks_word_size; - uintx free_blocks_num () const { return _free_blocks_num; } - size_t free_blocks_cap_words () const { return _free_blocks_cap_words; } + ArenaStats() : + _stats(), + _free_blocks_num(0), + _free_blocks_word_size(0) + {} - void reset(); + void add(const ArenaStats& other); - void add_free_blocks_info(uintx num, size_t cap); + void print_on(outputStream* st, size_t scale = K, bool detailed = true) const; - // Returns total chunk statistics over all chunk types. - UsedChunksStatistics totals() const; + InUseChunkStats totals() const; - void add(const SpaceManagerStatistics& other); + DEBUG_ONLY(void verify() const;) - void print_on(outputStream* st, size_t scale, bool detailed) const; +}; -}; // SpaceManagerStatistics +// Statistics for one or multiple ClassLoaderMetaspace objects +struct ClmsStats { -class ClassLoaderMetaspaceStatistics { + ArenaStats _arena_stats_nonclass; + ArenaStats _arena_stats_class; - SpaceManagerStatistics _sm_stats[Metaspace::MetadataTypeCount]; + ClmsStats() : _arena_stats_nonclass(), _arena_stats_class() {} -public: + void add(const ClmsStats& other) { + _arena_stats_nonclass.add(other._arena_stats_nonclass); + _arena_stats_class.add(other._arena_stats_class); + } - ClassLoaderMetaspaceStatistics(); - - const SpaceManagerStatistics& sm_stats(Metaspace::MetadataType mdType) const { return _sm_stats[mdType]; } - SpaceManagerStatistics& sm_stats(Metaspace::MetadataType mdType) { return _sm_stats[mdType]; } - - const SpaceManagerStatistics& nonclass_sm_stats() const { return sm_stats(Metaspace::NonClassType); } - SpaceManagerStatistics& nonclass_sm_stats() { return sm_stats(Metaspace::NonClassType); } - const SpaceManagerStatistics& class_sm_stats() const { return sm_stats(Metaspace::ClassType); } - SpaceManagerStatistics& class_sm_stats() { return sm_stats(Metaspace::ClassType); } - - void reset(); - - void add(const ClassLoaderMetaspaceStatistics& other); + void print_on(outputStream* st, size_t scale, bool detailed) const; - // Returns total space manager statistics for both class and non-class metaspace - SpaceManagerStatistics totals() const; + // Returns total statistics for both class and non-class metaspace + ArenaStats totals() const; - void print_on(outputStream* st, size_t scale, bool detailed) const; + DEBUG_ONLY(void verify() const;) -}; // ClassLoaderMetaspaceStatistics +}; } // namespace metaspace #endif // SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP + diff --git a/src/hotspot/share/memory/metaspace/occupancyMap.cpp b/src/hotspot/share/memory/metaspace/occupancyMap.cpp deleted file mode 100644 index 504c968d26e..00000000000 --- a/src/hotspot/share/memory/metaspace/occupancyMap.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018 SAP SE. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include "precompiled.hpp" -#include "utilities/debug.hpp" -#include "utilities/globalDefinitions.hpp" -#include "memory/metaspace/metachunk.hpp" -#include "memory/metaspace/occupancyMap.hpp" -#include "runtime/os.hpp" - -namespace metaspace { - -OccupancyMap::OccupancyMap(const MetaWord* reference_address, size_t word_size, size_t smallest_chunk_word_size) : - _reference_address(reference_address), _word_size(word_size), - _smallest_chunk_word_size(smallest_chunk_word_size) -{ - assert(reference_address != NULL, "invalid reference address"); - assert(is_aligned(reference_address, smallest_chunk_word_size), - "Reference address not aligned to smallest chunk size."); - assert(is_aligned(word_size, smallest_chunk_word_size), - "Word_size shall be a multiple of the smallest chunk size."); - // Calculate bitmap size: one bit per smallest_chunk_word_size'd area. - size_t num_bits = word_size / smallest_chunk_word_size; - _map_size = (num_bits + 7) / 8; - assert(_map_size * 8 >= num_bits, "sanity"); - _map[0] = (uint8_t*) os::malloc(_map_size, mtInternal); - _map[1] = (uint8_t*) os::malloc(_map_size, mtInternal); - assert(_map[0] != NULL && _map[1] != NULL, "Occupancy Map: allocation failed."); - memset(_map[1], 0, _map_size); - memset(_map[0], 0, _map_size); - // Sanity test: the first respectively last possible chunk start address in - // the covered range shall map to the first and last bit in the bitmap. - assert(get_bitpos_for_address(reference_address) == 0, - "First chunk address in range must map to fist bit in bitmap."); - assert(get_bitpos_for_address(reference_address + word_size - smallest_chunk_word_size) == num_bits - 1, - "Last chunk address in range must map to last bit in bitmap."); -} - -OccupancyMap::~OccupancyMap() { - os::free(_map[0]); - os::free(_map[1]); -} - -#ifdef ASSERT -// Verify occupancy map for the address range [from, to). -// We need to tell it the address range, because the memory the -// occupancy map is covering may not be fully comitted yet. -void OccupancyMap::verify(MetaWord* from, MetaWord* to) { - Metachunk* chunk = NULL; - int nth_bit_for_chunk = 0; - MetaWord* chunk_end = NULL; - for (MetaWord* p = from; p < to; p += _smallest_chunk_word_size) { - const unsigned pos = get_bitpos_for_address(p); - // Check the chunk-starts-info: - if (get_bit_at_position(pos, layer_chunk_start_map)) { - // Chunk start marked in bitmap. - chunk = (Metachunk*) p; - if (chunk_end != NULL) { - assert(chunk_end == p, "Unexpected chunk start found at %p (expected " - "the next chunk to start at %p).", p, chunk_end); - } - assert(chunk->is_valid_sentinel(), "Invalid chunk at address %p.", p); - if (chunk->get_chunk_type() != HumongousIndex) { - guarantee(is_aligned(p, chunk->word_size()), "Chunk %p not aligned.", p); - } - chunk_end = p + chunk->word_size(); - nth_bit_for_chunk = 0; - assert(chunk_end <= to, "Chunk end overlaps test address range."); - } else { - // No chunk start marked in bitmap. - assert(chunk != NULL, "Chunk should start at start of address range."); - assert(p < chunk_end, "Did not find expected chunk start at %p.", p); - nth_bit_for_chunk ++; - } - // Check the in-use-info: - const bool in_use_bit = get_bit_at_position(pos, layer_in_use_map); - if (in_use_bit) { - assert(!chunk->is_tagged_free(), "Chunk %p: marked in-use in map but is free (bit %u).", - chunk, nth_bit_for_chunk); - } else { - assert(chunk->is_tagged_free(), "Chunk %p: marked free in map but is in use (bit %u).", - chunk, nth_bit_for_chunk); - } - } -} - -// Verify that a given chunk is correctly accounted for in the bitmap. -void OccupancyMap::verify_for_chunk(Metachunk* chunk) { - assert(chunk_starts_at_address((MetaWord*) chunk), - "No chunk start marked in map for chunk %p.", chunk); - // For chunks larger than the minimal chunk size, no other chunk - // must start in its area. - if (chunk->word_size() > _smallest_chunk_word_size) { - assert(!is_any_bit_set_in_region(((MetaWord*) chunk) + _smallest_chunk_word_size, - chunk->word_size() - _smallest_chunk_word_size, layer_chunk_start_map), - "No chunk must start within another chunk."); - } - if (!chunk->is_tagged_free()) { - assert(is_region_in_use((MetaWord*)chunk, chunk->word_size()), - "Chunk %p is in use but marked as free in map (%d %d).", - chunk, chunk->get_chunk_type(), chunk->get_origin()); - } else { - assert(!is_region_in_use((MetaWord*)chunk, chunk->word_size()), - "Chunk %p is free but marked as in-use in map (%d %d).", - chunk, chunk->get_chunk_type(), chunk->get_origin()); - } -} - -#endif // ASSERT - -} // namespace metaspace - - diff --git a/src/hotspot/share/memory/metaspace/occupancyMap.hpp b/src/hotspot/share/memory/metaspace/occupancyMap.hpp deleted file mode 100644 index 4f52d3734d4..00000000000 --- a/src/hotspot/share/memory/metaspace/occupancyMap.hpp +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018 SAP SE. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_MEMORY_METASPACE_OCCUPANCYMAP_HPP -#define SHARE_MEMORY_METASPACE_OCCUPANCYMAP_HPP - -#include "memory/allocation.hpp" -#include "utilities/debug.hpp" -#include "utilities/globalDefinitions.hpp" - - -namespace metaspace { - -class Metachunk; - -// Helper for Occupancy Bitmap. A type trait to give an all-bits-are-one-unsigned constant. -template struct all_ones { static const T value; }; -template <> struct all_ones { static const uint64_t value = 0xFFFFFFFFFFFFFFFFULL; }; -template <> struct all_ones { static const uint32_t value = 0xFFFFFFFF; }; - -// The OccupancyMap is a bitmap which, for a given VirtualSpaceNode, -// keeps information about -// - where a chunk starts -// - whether a chunk is in-use or free -// A bit in this bitmap represents one range of memory in the smallest -// chunk size (SpecializedChunk or ClassSpecializedChunk). -class OccupancyMap : public CHeapObj { - - // The address range this map covers. - const MetaWord* const _reference_address; - const size_t _word_size; - - // The word size of a specialized chunk, aka the number of words one - // bit in this map represents. - const size_t _smallest_chunk_word_size; - - // map data - // Data are organized in two bit layers: - // The first layer is the chunk-start-map. Here, a bit is set to mark - // the corresponding region as the head of a chunk. - // The second layer is the in-use-map. Here, a set bit indicates that - // the corresponding belongs to a chunk which is in use. - uint8_t* _map[2]; - - enum { layer_chunk_start_map = 0, layer_in_use_map = 1 }; - - // length, in bytes, of bitmap data - size_t _map_size; - - // Returns true if bit at position pos at bit-layer layer is set. - bool get_bit_at_position(unsigned pos, unsigned layer) const { - assert(layer == 0 || layer == 1, "Invalid layer %d", layer); - const unsigned byteoffset = pos / 8; - assert(byteoffset < _map_size, - "invalid byte offset (%u), map size is " SIZE_FORMAT ".", byteoffset, _map_size); - const unsigned mask = 1 << (pos % 8); - return (_map[layer][byteoffset] & mask) > 0; - } - - // Changes bit at position pos at bit-layer layer to value v. - void set_bit_at_position(unsigned pos, unsigned layer, bool v) { - assert(layer == 0 || layer == 1, "Invalid layer %d", layer); - const unsigned byteoffset = pos / 8; - assert(byteoffset < _map_size, - "invalid byte offset (%u), map size is " SIZE_FORMAT ".", byteoffset, _map_size); - const unsigned mask = 1 << (pos % 8); - if (v) { - _map[layer][byteoffset] |= mask; - } else { - _map[layer][byteoffset] &= ~mask; - } - } - - // Optimized case of is_any_bit_set_in_region for 32/64bit aligned access: - // pos is 32/64 aligned and num_bits is 32/64. - // This is the typical case when coalescing to medium chunks, whose size is - // 32 or 64 times the specialized chunk size (depending on class or non class - // case), so they occupy 64 bits which should be 64bit aligned, because - // chunks are chunk-size aligned. - template - bool is_any_bit_set_in_region_3264(unsigned pos, unsigned num_bits, unsigned layer) const { - assert(_map_size > 0, "not initialized"); - assert(layer == 0 || layer == 1, "Invalid layer %d.", layer); - assert(pos % (sizeof(T) * 8) == 0, "Bit position must be aligned (%u).", pos); - assert(num_bits == (sizeof(T) * 8), "Number of bits incorrect (%u).", num_bits); - const size_t byteoffset = pos / 8; - assert(byteoffset <= (_map_size - sizeof(T)), - "Invalid byte offset (" SIZE_FORMAT "), map size is " SIZE_FORMAT ".", byteoffset, _map_size); - const T w = *(T*)(_map[layer] + byteoffset); - return w > 0 ? true : false; - } - - // Returns true if any bit in region [pos1, pos1 + num_bits) is set in bit-layer layer. - bool is_any_bit_set_in_region(unsigned pos, unsigned num_bits, unsigned layer) const { - if (pos % 32 == 0 && num_bits == 32) { - return is_any_bit_set_in_region_3264(pos, num_bits, layer); - } else if (pos % 64 == 0 && num_bits == 64) { - return is_any_bit_set_in_region_3264(pos, num_bits, layer); - } else { - for (unsigned n = 0; n < num_bits; n ++) { - if (get_bit_at_position(pos + n, layer)) { - return true; - } - } - } - return false; - } - - // Returns true if any bit in region [p, p+word_size) is set in bit-layer layer. - bool is_any_bit_set_in_region(MetaWord* p, size_t word_size, unsigned layer) const { - assert(word_size % _smallest_chunk_word_size == 0, - "Region size " SIZE_FORMAT " not a multiple of smallest chunk size.", word_size); - const unsigned pos = get_bitpos_for_address(p); - const unsigned num_bits = (unsigned) (word_size / _smallest_chunk_word_size); - return is_any_bit_set_in_region(pos, num_bits, layer); - } - - // Optimized case of set_bits_of_region for 32/64bit aligned access: - // pos is 32/64 aligned and num_bits is 32/64. - // This is the typical case when coalescing to medium chunks, whose size - // is 32 or 64 times the specialized chunk size (depending on class or non - // class case), so they occupy 64 bits which should be 64bit aligned, - // because chunks are chunk-size aligned. - template - void set_bits_of_region_T(unsigned pos, unsigned num_bits, unsigned layer, bool v) { - assert(pos % (sizeof(T) * 8) == 0, "Bit position must be aligned to %u (%u).", - (unsigned)(sizeof(T) * 8), pos); - assert(num_bits == (sizeof(T) * 8), "Number of bits incorrect (%u), expected %u.", - num_bits, (unsigned)(sizeof(T) * 8)); - const size_t byteoffset = pos / 8; - assert(byteoffset <= (_map_size - sizeof(T)), - "invalid byte offset (" SIZE_FORMAT "), map size is " SIZE_FORMAT ".", byteoffset, _map_size); - T* const pw = (T*)(_map[layer] + byteoffset); - *pw = v ? all_ones::value : (T) 0; - } - - // Set all bits in a region starting at pos to a value. - void set_bits_of_region(unsigned pos, unsigned num_bits, unsigned layer, bool v) { - assert(_map_size > 0, "not initialized"); - assert(layer == 0 || layer == 1, "Invalid layer %d.", layer); - if (pos % 32 == 0 && num_bits == 32) { - set_bits_of_region_T(pos, num_bits, layer, v); - } else if (pos % 64 == 0 && num_bits == 64) { - set_bits_of_region_T(pos, num_bits, layer, v); - } else { - for (unsigned n = 0; n < num_bits; n ++) { - set_bit_at_position(pos + n, layer, v); - } - } - } - - // Helper: sets all bits in a region [p, p+word_size). - void set_bits_of_region(MetaWord* p, size_t word_size, unsigned layer, bool v) { - assert(word_size % _smallest_chunk_word_size == 0, - "Region size " SIZE_FORMAT " not a multiple of smallest chunk size.", word_size); - const unsigned pos = get_bitpos_for_address(p); - const unsigned num_bits = (unsigned) (word_size / _smallest_chunk_word_size); - set_bits_of_region(pos, num_bits, layer, v); - } - - // Helper: given an address, return the bit position representing that address. - unsigned get_bitpos_for_address(const MetaWord* p) const { - assert(_reference_address != NULL, "not initialized"); - assert(p >= _reference_address && p < _reference_address + _word_size, - "Address %p out of range for occupancy map [%p..%p).", - p, _reference_address, _reference_address + _word_size); - assert(is_aligned(p, _smallest_chunk_word_size * sizeof(MetaWord)), - "Address not aligned (%p).", p); - const ptrdiff_t d = (p - _reference_address) / _smallest_chunk_word_size; - assert(d >= 0 && (size_t)d < _map_size * 8, "Sanity."); - return (unsigned) d; - } - - public: - - OccupancyMap(const MetaWord* reference_address, size_t word_size, size_t smallest_chunk_word_size); - ~OccupancyMap(); - - // Returns true if at address x a chunk is starting. - bool chunk_starts_at_address(MetaWord* p) const { - const unsigned pos = get_bitpos_for_address(p); - return get_bit_at_position(pos, layer_chunk_start_map); - } - - void set_chunk_starts_at_address(MetaWord* p, bool v) { - const unsigned pos = get_bitpos_for_address(p); - set_bit_at_position(pos, layer_chunk_start_map, v); - } - - // Removes all chunk-start-bits inside a region, typically as a - // result of a chunk merge. - void wipe_chunk_start_bits_in_region(MetaWord* p, size_t word_size) { - set_bits_of_region(p, word_size, layer_chunk_start_map, false); - } - - // Returns true if there are life (in use) chunks in the region limited - // by [p, p+word_size). - bool is_region_in_use(MetaWord* p, size_t word_size) const { - return is_any_bit_set_in_region(p, word_size, layer_in_use_map); - } - - // Marks the region starting at p with the size word_size as in use - // or free, depending on v. - void set_region_in_use(MetaWord* p, size_t word_size, bool v) { - set_bits_of_region(p, word_size, layer_in_use_map, v); - } - - // Verify occupancy map for the address range [from, to). - // We need to tell it the address range, because the memory the - // occupancy map is covering may not be fully comitted yet. - DEBUG_ONLY(void verify(MetaWord* from, MetaWord* to);) - - // Verify that a given chunk is correctly accounted for in the bitmap. - DEBUG_ONLY(void verify_for_chunk(Metachunk* chunk);) - -}; - -} // namespace metaspace - -#endif // SHARE_MEMORY_METASPACE_OCCUPANCYMAP_HPP diff --git a/src/hotspot/share/memory/metaspace/printCLDMetaspaceInfoClosure.cpp b/src/hotspot/share/memory/metaspace/printCLDMetaspaceInfoClosure.cpp index 99ca6570889..d207415b9b5 100644 --- a/src/hotspot/share/memory/metaspace/printCLDMetaspaceInfoClosure.cpp +++ b/src/hotspot/share/memory/metaspace/printCLDMetaspaceInfoClosure.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,26 +22,32 @@ * questions. * */ + #include "precompiled.hpp" #include "classfile/classLoaderData.inline.hpp" #include "classfile/javaClasses.hpp" +#include "memory/classLoaderMetaspace.hpp" +#include "memory/metaspace/metaspaceCommon.hpp" #include "memory/metaspace/printCLDMetaspaceInfoClosure.hpp" #include "memory/metaspace/printMetaspaceInfoKlassClosure.hpp" -#include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" #include "runtime/safepoint.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" - namespace metaspace { PrintCLDMetaspaceInfoClosure::PrintCLDMetaspaceInfoClosure(outputStream* out, size_t scale, bool do_print, - bool do_print_classes, bool break_down_by_chunktype) -: _out(out), _scale(scale), _do_print(do_print), _do_print_classes(do_print_classes) -, _break_down_by_chunktype(break_down_by_chunktype) -, _num_loaders(0), _num_loaders_without_metaspace(0), _num_loaders_unloading(0) -, _num_classes(0), _num_classes_shared(0) + bool do_print_classes, bool break_down_by_chunktype) : + _out(out), + _scale(scale), + _do_print(do_print), + _do_print_classes(do_print_classes), + _break_down_by_chunktype(break_down_by_chunktype), + _num_loaders(0), + _num_loaders_without_metaspace(0), + _num_loaders_unloading(0), + _num_classes(0), _num_classes_shared(0) { memset(_num_loaders_by_spacetype, 0, sizeof(_num_loaders_by_spacetype)); memset(_num_classes_by_spacetype, 0, sizeof(_num_classes_by_spacetype)); @@ -56,36 +63,35 @@ class CountKlassClosure : public KlassClosure { CountKlassClosure() : _num_classes(0), _num_classes_shared(0) {} void do_klass(Klass* k) { - _num_classes ++; + _num_classes++; if (k->is_shared()) { - _num_classes_shared ++; + _num_classes_shared++; } } }; // end: PrintKlassInfoClosure void PrintCLDMetaspaceInfoClosure::do_cld(ClassLoaderData* cld) { - assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); if (cld->is_unloading()) { - _num_loaders_unloading ++; + _num_loaders_unloading++; return; } ClassLoaderMetaspace* msp = cld->metaspace_or_null(); if (msp == NULL) { - _num_loaders_without_metaspace ++; + _num_loaders_without_metaspace++; return; } // Collect statistics for this class loader metaspace - ClassLoaderMetaspaceStatistics this_cld_stat; + ClmsStats this_cld_stat; msp->add_to_statistics(&this_cld_stat); // And add it to the running totals _stats_total.add(this_cld_stat); - _num_loaders ++; + _num_loaders++; _stats_by_spacetype[msp->space_type()].add(this_cld_stat); _num_loaders_by_spacetype[msp->space_type()] ++; @@ -100,12 +106,10 @@ void PrintCLDMetaspaceInfoClosure::do_cld(ClassLoaderData* cld) { // Optionally, print if (_do_print) { - _out->print(UINTX_FORMAT_W(4) ": ", _num_loaders); // Print "CLD for [,] instance of " // or "CLD for , loaded by [,] instance of " - ResourceMark rm; const char* name = NULL; const char* class_name = NULL; @@ -161,9 +165,7 @@ void PrintCLDMetaspaceInfoClosure::do_cld(ClassLoaderData* cld) { // Print statistics this_cld_stat.print_on(_out, _scale, _break_down_by_chunktype); _out->cr(); - } - } } // namespace metaspace diff --git a/src/hotspot/share/memory/metaspace/printCLDMetaspaceInfoClosure.hpp b/src/hotspot/share/memory/metaspace/printCLDMetaspaceInfoClosure.hpp index 092662a07bd..c58c25caadd 100644 --- a/src/hotspot/share/memory/metaspace/printCLDMetaspaceInfoClosure.hpp +++ b/src/hotspot/share/memory/metaspace/printCLDMetaspaceInfoClosure.hpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,10 +48,10 @@ class PrintCLDMetaspaceInfoClosure : public CLDClosure { uintx _num_loaders; uintx _num_loaders_without_metaspace; uintx _num_loaders_unloading; - ClassLoaderMetaspaceStatistics _stats_total; + ClmsStats _stats_total; uintx _num_loaders_by_spacetype [Metaspace::MetaspaceTypeCount]; - ClassLoaderMetaspaceStatistics _stats_by_spacetype [Metaspace::MetaspaceTypeCount]; + ClmsStats _stats_by_spacetype [Metaspace::MetaspaceTypeCount]; uintx _num_classes_by_spacetype [Metaspace::MetaspaceTypeCount]; uintx _num_classes_shared_by_spacetype [Metaspace::MetaspaceTypeCount]; @@ -58,7 +59,7 @@ class PrintCLDMetaspaceInfoClosure : public CLDClosure { uintx _num_classes_shared; PrintCLDMetaspaceInfoClosure(outputStream* out, size_t scale, bool do_print, - bool do_print_classes, bool break_down_by_chunktype); + bool do_print_classes, bool break_down_by_chunktype); void do_cld(ClassLoaderData* cld); }; diff --git a/src/hotspot/share/memory/metaspace/printMetaspaceInfoKlassClosure.cpp b/src/hotspot/share/memory/metaspace/printMetaspaceInfoKlassClosure.cpp index 8473c403165..e89c1662838 100644 --- a/src/hotspot/share/memory/metaspace/printMetaspaceInfoKlassClosure.cpp +++ b/src/hotspot/share/memory/metaspace/printMetaspaceInfoKlassClosure.cpp @@ -23,15 +23,13 @@ * */ #include "precompiled.hpp" - -#include "memory/metaspaceShared.hpp" #include "memory/metaspace/printMetaspaceInfoKlassClosure.hpp" +#include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" #include "oops/reflectionAccessorImplKlassHelper.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" - namespace metaspace { PrintMetaspaceInfoKlassClosure::PrintMetaspaceInfoKlassClosure(outputStream* out, bool do_print) @@ -39,7 +37,7 @@ PrintMetaspaceInfoKlassClosure::PrintMetaspaceInfoKlassClosure(outputStream* out {} void PrintMetaspaceInfoKlassClosure::do_klass(Klass* k) { - _cnt ++; + _cnt++; _out->cr_indent(); _out->print(UINTX_FORMAT_W(4) ": ", _cnt); diff --git a/src/hotspot/share/memory/metaspace/rootChunkArea.cpp b/src/hotspot/share/memory/metaspace/rootChunkArea.cpp new file mode 100644 index 00000000000..7ef4b6d8e5d --- /dev/null +++ b/src/hotspot/share/memory/metaspace/rootChunkArea.cpp @@ -0,0 +1,512 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "logging/log.hpp" +#include "memory/allocation.hpp" +#include "memory/metaspace/chunkHeaderPool.hpp" +#include "memory/metaspace/chunkManager.hpp" +#include "memory/metaspace/freeChunkList.hpp" +#include "memory/metaspace/metachunk.hpp" +#include "memory/metaspace/metaspaceCommon.hpp" +#include "memory/metaspace/rootChunkArea.hpp" +#include "runtime/mutexLocker.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" + +namespace metaspace { + +RootChunkArea::RootChunkArea(const MetaWord* base) : + _base(base), + _first_chunk(NULL) +{} + +RootChunkArea::~RootChunkArea() { + // This is called when a VirtualSpaceNode is destructed (purged). + // All chunks should be free of course. In fact, there should only + // be one chunk, since all free chunks should have been merged. + if (_first_chunk != NULL) { + assert(_first_chunk->is_root_chunk() && _first_chunk->is_free(), + "Cannot delete root chunk area if not all chunks are free."); + ChunkHeaderPool::pool()->return_chunk_header(_first_chunk); + } +} + +// Initialize: allocate a root node and a root chunk header; return the +// root chunk header. It will be partly initialized. +// Note: this just allocates a memory-less header; memory itself is allocated inside VirtualSpaceNode. +Metachunk* RootChunkArea::alloc_root_chunk_header(VirtualSpaceNode* node) { + assert(_first_chunk == 0, "already have a root"); + Metachunk* c = ChunkHeaderPool::pool()->allocate_chunk_header(); + c->initialize(node, const_cast(_base), chunklevel::ROOT_CHUNK_LEVEL); + _first_chunk = c; + return c; +} + +// Given a chunk c, split it recursively until you get a chunk of the given target_level. +// +// The resulting target chunk resides at the same address as the original chunk. +// The resulting splinters are added to freelists. +// +// Returns pointer to the result chunk; the splitted-off chunks are added as +// free chunks to the freelists. +void RootChunkArea::split(chunklevel_t target_level, Metachunk* c, FreeChunkListVector* freelists) { + // Splitting a chunk once works like this: + // + // For a given chunk we want to split: + // - increase the chunk level (which halves its size) + // - (but leave base address as it is since it will be the leader of the newly + // created chunk pair) + // - then create a new chunk header of the same level, set its memory range + // to cover the second half of the old chunk. + // - wire them up (prev_in_vs/next_in_vs) + // - return the follower chunk as "splinter chunk" in the splinters array. + + // Doing this multiple times will create a new free splinter chunk for every + // level we split: + // + // A <- original chunk + // + // B B <- split into two halves + // + // C C B <- first half split again + // + // D D C B <- first half split again ... + // + + DEBUG_ONLY(check_pointer(c->base());) + DEBUG_ONLY(c->verify();) + assert(c->is_free(), "Can only split free chunks."); + + DEBUG_ONLY(chunklevel::check_valid_level(target_level)); + assert(target_level > c->level(), "Wrong target level"); + + const chunklevel_t starting_level = c->level(); + + while (c->level() < target_level) { + + log_trace(metaspace)("Splitting chunk: " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(c)); + + c->inc_level(); + Metachunk* splinter_chunk = ChunkHeaderPool::pool()->allocate_chunk_header(); + splinter_chunk->initialize(c->vsnode(), c->end(), c->level()); + + // Fix committed words info: If over the half of the original chunk was + // committed, committed area spills over into the follower chunk. + const size_t old_committed_words = c->committed_words(); + if (old_committed_words > c->word_size()) { + c->set_committed_words(c->word_size()); + splinter_chunk->set_committed_words(old_committed_words - c->word_size()); + } else { + splinter_chunk->set_committed_words(0); + } + + // Insert splinter chunk into vs list + if (c->next_in_vs() != NULL) { + c->next_in_vs()->set_prev_in_vs(splinter_chunk); + } + splinter_chunk->set_next_in_vs(c->next_in_vs()); + splinter_chunk->set_prev_in_vs(c); + c->set_next_in_vs(splinter_chunk); + + log_trace(metaspace)(".. Result chunk: " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(c)); + log_trace(metaspace)(".. Splinter chunk: " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(splinter_chunk)); + + // Add splinter to free lists + freelists->add(splinter_chunk); + } + + assert(c->level() == target_level, "Sanity"); + + DEBUG_ONLY(verify();) + DEBUG_ONLY(c->verify();) +} + +// Given a chunk, attempt to merge it recursively with its neighboring chunks. +// +// If successful (merged at least once), returns address of +// the merged chunk; NULL otherwise. +// +// The merged chunks are removed from the freelists. +// +// !!! Please note that if this method returns a non-NULL value, the +// original chunk will be invalid and should not be accessed anymore! !!! +Metachunk* RootChunkArea::merge(Metachunk* c, FreeChunkListVector* freelists) { + // Note rules: + // + // - a chunk always has a buddy, unless it is a root chunk. + // - In that buddy pair, a chunk is either leader or follower. + // - a chunk's base address is always aligned at its size. + // - if chunk is leader, its base address is also aligned to the size of the next + // lower level, at least. A follower chunk is not. + + // How we merge once: + // + // For a given chunk c, which has to be free and non-root, we do: + // - find out if we are the leader or the follower chunk + // - if we are leader, next_in_vs must be the follower; if we are follower, + // prev_in_vs must be the leader. Now we have the buddy chunk. + // - However, if the buddy chunk itself is split (of a level higher than us) + // we cannot merge. + // - we can only merge if the buddy is of the same level as we are and it is + // free. + // - Then we merge by simply removing the follower chunk from the address range + // linked list (returning the now useless header to the pool) and decreasing + // the leader chunk level by one. That makes it double the size. + + // Example: + // (lower case chunks are free, the * indicates the chunk we want to merge): + // + // ........................ + // d d*c b A <- we return the second (d*) chunk... + // + // c* c b A <- we merge it with its predecessor and decrease its level... + // + // b* b A <- we merge it again, since its new neighbor was free too... + // + // a* A <- we merge it again, since its new neighbor was free too... + // + // And we are done, since its new neighbor, (A), is not free. We would also be done + // if the new neighbor itself is splintered. + + DEBUG_ONLY(check_pointer(c->base());) + assert(!c->is_root_chunk(), "Cannot be merged further."); + assert(c->is_free(), "Can only merge free chunks."); + + DEBUG_ONLY(c->verify();) + + log_trace(metaspace)("Attempting to merge chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(c)); + + const chunklevel_t starting_level = c->level(); + + bool stop = false; + Metachunk* result = NULL; + + do { + + // First find out if this chunk is the leader of its pair + const bool is_leader = c->is_leader(); + + // Note: this is either our buddy or a splinter of the buddy. + Metachunk* const buddy = c->is_leader() ? c->next_in_vs() : c->prev_in_vs(); + DEBUG_ONLY(buddy->verify();) + + // A buddy chunk must be of the same or higher level (so, same size or smaller) + // never be larger. + assert(buddy->level() >= c->level(), "Sanity"); + + // Is this really my buddy (same level) or a splinter of it (higher level)? + // Also, is it free? + if (buddy->level() != c->level() || buddy->is_free() == false) { + log_trace(metaspace)("cannot merge with chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(buddy)); + stop = true; + } else { + log_trace(metaspace)("will merge with chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(buddy)); + + // We can merge with the buddy. + // First, remove buddy from the chunk manager. + assert(buddy->is_free(), "Sanity"); + freelists->remove(buddy); + + // Determine current leader and follower + Metachunk* leader; + Metachunk* follower; + if (is_leader) { + leader = c; follower = buddy; + } else { + leader = buddy; follower = c; + } + + // Last checkpoint + assert(leader->end() == follower->base() && + leader->level() == follower->level() && + leader->is_free() && follower->is_free(), "Sanity"); + + // The new merged chunk is as far committed as possible (if leader + // chunk is fully committed, as far as the follower chunk). + size_t merged_committed_words = leader->committed_words(); + if (merged_committed_words == leader->word_size()) { + merged_committed_words += follower->committed_words(); + } + + // Leader survives, follower chunk is freed. Remove follower from vslist .. + leader->set_next_in_vs(follower->next_in_vs()); + if (follower->next_in_vs() != NULL) { + follower->next_in_vs()->set_prev_in_vs(leader); + } + + // .. and return follower chunk header to pool for reuse. + ChunkHeaderPool::pool()->return_chunk_header(follower); + + // Leader level gets decreased (leader chunk doubles in size) but + // base address stays the same. + leader->dec_level(); + + // set commit boundary + leader->set_committed_words(merged_committed_words); + + // If the leader is now of root chunk size, stop merging + if (leader->is_root_chunk()) { + stop = true; + } + + result = c = leader; + DEBUG_ONLY(leader->verify();) + } + } while (!stop); + +#ifdef ASSERT + verify(); + if (result != NULL) { + result->verify(); + } +#endif // ASSERT + return result; +} + +// Given a chunk c, which must be "in use" and must not be a root chunk, attempt to +// enlarge it in place by claiming its trailing buddy. +// +// This will only work if c is the leader of the buddy pair and the trailing buddy is free. +// +// If successful, the follower chunk will be removed from the freelists, the leader chunk c will +// double in size (level decreased by one). +// +// On success, true is returned, false otherwise. +bool RootChunkArea::attempt_enlarge_chunk(Metachunk* c, FreeChunkListVector* freelists) { + DEBUG_ONLY(check_pointer(c->base());) + assert(!c->is_root_chunk(), "Cannot be merged further."); + + // There is no real reason for this limitation other than it is not + // needed on free chunks since they should be merged already: + assert(c->is_in_use(), "Can only enlarge in use chunks."); + DEBUG_ONLY(c->verify();) + + if (!c->is_leader()) { + return false; + } + + // We are the leader, so the buddy must follow us. + Metachunk* const buddy = c->next_in_vs(); + DEBUG_ONLY(buddy->verify();) + + // Of course buddy cannot be larger than us. + assert(buddy->level() >= c->level(), "Sanity"); + + // We cannot merge buddy in if it is not free... + if (!buddy->is_free()) { + return false; + } + // ... nor if it is splintered. + if (buddy->level() != c->level()) { + return false; + } + + // Okay, lets enlarge c. + log_trace(metaspace)("Enlarging chunk " METACHUNK_FULL_FORMAT " by merging in follower " METACHUNK_FULL_FORMAT ".", + METACHUNK_FULL_FORMAT_ARGS(c), METACHUNK_FULL_FORMAT_ARGS(buddy)); + + // the enlarged c is as far committed as possible: + size_t merged_committed_words = c->committed_words(); + if (merged_committed_words == c->word_size()) { + merged_committed_words += buddy->committed_words(); + } + + // Remove buddy from vs list... + Metachunk* successor = buddy->next_in_vs(); + if (successor != NULL) { + successor->set_prev_in_vs(c); + } + c->set_next_in_vs(successor); + + // .. and from freelist ... + freelists->remove(buddy); + + // .. and return its empty husk to the pool... + ChunkHeaderPool::pool()->return_chunk_header(buddy); + + // Then decrease level of c. + c->dec_level(); + + // and correct committed words if needed. + c->set_committed_words(merged_committed_words); + + log_debug(metaspace)("Enlarged chunk " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(c)); + + DEBUG_ONLY(verify()); + return true; +} + +// Returns true if this root chunk area is completely free: +// In that case, it should only contain one chunk (maximally merged, so a root chunk) +// and it should be free. +bool RootChunkArea::is_free() const { + return _first_chunk == NULL || + (_first_chunk->is_root_chunk() && _first_chunk->is_free()); +} + +#ifdef ASSERT + +#define assrt_(cond, ...) \ + if (!(cond)) { \ + fdStream errst(2); \ + this->print_on(&errst); \ + vmassert(cond, __VA_ARGS__); \ + } + +void RootChunkArea::verify() const { + assert_lock_strong(MetaspaceExpand_lock); + assert_is_aligned(_base, chunklevel::MAX_CHUNK_BYTE_SIZE); + + // Iterate thru all chunks in this area. They must be ordered correctly, + // being adjacent to each other, and cover the complete area + int num_chunk = 0; + + if (_first_chunk != NULL) { + assrt_(_first_chunk->prev_in_vs() == NULL, "Sanity"); + + const Metachunk* c = _first_chunk; + const MetaWord* expected_next_base = _base; + const MetaWord* const area_end = _base + word_size(); + + while (c != NULL) { + assrt_(c->is_free() || c->is_in_use(), + "Chunk No. %d " METACHUNK_FORMAT " - invalid state.", + num_chunk, METACHUNK_FORMAT_ARGS(c)); + assrt_(c->base() == expected_next_base, + "Chunk No. %d " METACHUNK_FORMAT " - unexpected base.", + num_chunk, METACHUNK_FORMAT_ARGS(c)); + assrt_(c->base() >= base() && c->end() <= end(), + "chunk %d " METACHUNK_FORMAT " oob for this root area [" PTR_FORMAT ".." PTR_FORMAT ").", + num_chunk, METACHUNK_FORMAT_ARGS(c), p2i(base()), p2i(end())); + assrt_(is_aligned(c->base(), c->word_size()), + "misaligned chunk %d " METACHUNK_FORMAT ".", num_chunk, METACHUNK_FORMAT_ARGS(c)); + + c->verify_neighborhood(); + c->verify(); + expected_next_base = c->end(); + num_chunk++; + c = c->next_in_vs(); + } + assrt_(expected_next_base == _base + word_size(), "Sanity"); + } +} + +void RootChunkArea::verify_area_is_ideally_merged() const { + SOMETIMES(assert_lock_strong(MetaspaceExpand_lock);) + int num_chunk = 0; + for (const Metachunk* c = _first_chunk; c != NULL; c = c->next_in_vs()) { + if (!c->is_root_chunk() && c->is_free()) { + // If a chunk is free, it must not have a buddy which is also free, because + // those chunks should have been merged. + // In other words, a buddy shall be either in-use or splintered + // (which in turn would mean part of it are in use). + Metachunk* const buddy = c->is_leader() ? c->next_in_vs() : c->prev_in_vs(); + assrt_(buddy->is_in_use() || buddy->level() > c->level(), + "Chunk No. %d " METACHUNK_FORMAT " : missed merge opportunity with neighbor " METACHUNK_FORMAT ".", + num_chunk, METACHUNK_FORMAT_ARGS(c), METACHUNK_FORMAT_ARGS(buddy)); + } + num_chunk++; + } +} + +#endif + +void RootChunkArea::print_on(outputStream* st) const { + st->print(PTR_FORMAT ": ", p2i(base())); + if (_first_chunk != NULL) { + const Metachunk* c = _first_chunk; + // 01234567890123 + const char* letters_for_levels_cap = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const char* letters_for_levels = "abcdefghijklmnopqrstuvwxyz"; + while (c != NULL) { + const chunklevel_t l = c->level(); + if (l >= 0 && (size_t)l < strlen(letters_for_levels)) { + st->print("%c", c->is_free() ? letters_for_levels[c->level()] : letters_for_levels_cap[c->level()]); + } else { + // Obviously garbage, but lets not crash. + st->print("?"); + } + c = c->next_in_vs(); + } + } else { + st->print(" (no chunks)"); + } + st->cr(); +} + +// Create an array of ChunkTree objects, all initialized to NULL, covering +// a given memory range. Memory range must be a multiple of root chunk size. +RootChunkAreaLUT::RootChunkAreaLUT(const MetaWord* base, size_t word_size) : + _base(base), + _num((int)(word_size / chunklevel::MAX_CHUNK_WORD_SIZE)), + _arr(NULL) +{ + assert_is_aligned(word_size, chunklevel::MAX_CHUNK_WORD_SIZE); + _arr = NEW_C_HEAP_ARRAY(RootChunkArea, _num, mtClass); + const MetaWord* this_base = _base; + for (int i = 0; i < _num; i++) { + RootChunkArea* rca = new(_arr + i) RootChunkArea(this_base); + assert(rca == _arr + i, "Sanity"); + this_base += chunklevel::MAX_CHUNK_WORD_SIZE; + } +} + +RootChunkAreaLUT::~RootChunkAreaLUT() { + for (int i = 0; i < _num; i++) { + _arr[i].~RootChunkArea(); + } + FREE_C_HEAP_ARRAY(RootChunkArea, _arr); +} + +// Returns true if all areas in this area table are free (only contain free chunks). +bool RootChunkAreaLUT::is_free() const { + for (int i = 0; i < _num; i++) { + if (!_arr[i].is_free()) { + return false; + } + } + return true; +} + +#ifdef ASSERT + +void RootChunkAreaLUT::verify() const { + for (int i = 0; i < _num; i++) { + check_pointer(_arr[i].base()); + _arr[i].verify(); + } +} + +#endif + +void RootChunkAreaLUT::print_on(outputStream* st) const { + for (int i = 0; i < _num; i++) { + st->print("%2d:", i); + _arr[i].print_on(st); + } +} + +} // end: namespace metaspace diff --git a/src/hotspot/share/memory/metaspace/rootChunkArea.hpp b/src/hotspot/share/memory/metaspace/rootChunkArea.hpp new file mode 100644 index 00000000000..9ba6d736018 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/rootChunkArea.hpp @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_ROOTCHUNKAREA_HPP +#define SHARE_MEMORY_METASPACE_ROOTCHUNKAREA_HPP + +#include "memory/allocation.hpp" +#include "memory/metaspace/chunklevel.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" + +class outputStream; + +namespace metaspace { + +class Metachunk; +class MetachunkClosure; +class FreeChunkListVector; +class VirtualSpaceNode; + +// RootChunkArea manages a memory area covering a single root chunk. +// +// Such an area may contain a single root chunk, or a number of chunks the +// root chunk was split into. +// +// RootChunkArea contains the functionality to merge and split chunks in +// buddy allocator fashion. +// + +class RootChunkArea { + + // The base address of this area. + // Todo: this may be somewhat superfluous since RootChunkArea only exist in the + // context of a series of chunks, so the address is somewhat implicit. Remove? + const MetaWord* const _base; + + // The first chunk in this area; if this area is maximally + // folded, this is the root chunk covering the whole area size. + Metachunk* _first_chunk; + +public: + + RootChunkArea(const MetaWord* base); + ~RootChunkArea(); + + // Initialize: allocate a root node and a root chunk header; return the + // root chunk header. It will be partly initialized. + // Note: this just allocates a memory-less header; memory itself is allocated inside VirtualSpaceNode. + Metachunk* alloc_root_chunk_header(VirtualSpaceNode* node); + + // Given a chunk c, split it recursively until you get a chunk of the given target_level. + // + // The resulting target chunk resides at the same address as the original chunk. + // The resulting splinters are added to freelists. + // + // Returns pointer to the result chunk; the splitted-off chunks are added as + // free chunks to the freelists. + void split(chunklevel_t target_level, Metachunk* c, FreeChunkListVector* freelists); + + // Given a chunk, attempt to merge it recursively with its neighboring chunks. + // + // If successful (merged at least once), returns address of + // the merged chunk; NULL otherwise. + // + // The merged chunks are removed from the freelists. + // + // !!! Please note that if this method returns a non-NULL value, the + // original chunk will be invalid and should not be accessed anymore! !!! + Metachunk* merge(Metachunk* c, FreeChunkListVector* freelists); + + // Given a chunk c, which must be "in use" and must not be a root chunk, attempt to + // enlarge it in place by claiming its trailing buddy. + // + // This will only work if c is the leader of the buddy pair and the trailing buddy is free. + // + // If successful, the follower chunk will be removed from the freelists, the leader chunk c will + // double in size (level decreased by one). + // + // On success, true is returned, false otherwise. + bool attempt_enlarge_chunk(Metachunk* c, FreeChunkListVector* freelists); + + /// range /// + + const MetaWord* base() const { return _base; } + size_t word_size() const { return chunklevel::MAX_CHUNK_WORD_SIZE; } + const MetaWord* end() const { return _base + word_size(); } + + // Direct access to the first chunk (use with care) + Metachunk* first_chunk() { return _first_chunk; } + const Metachunk* first_chunk() const { return _first_chunk; } + + // Returns true if this root chunk area is completely free: + // In that case, it should only contain one chunk (maximally merged, so a root chunk) + // and it should be free. + bool is_free() const; + + //// Debug stuff //// + +#ifdef ASSERT + void check_pointer(const MetaWord* p) const { + assert(p >= _base && p < _base + word_size(), + "pointer " PTR_FORMAT " oob for this root area [" PTR_FORMAT ".." PTR_FORMAT ")", + p2i(p), p2i(_base), p2i(_base + word_size())); + } + void verify() const; + + // This is a separate operation from verify(). We should be able to call verify() + // from almost anywhere, regardless of state, but verify_area_is_ideally_merged() + // can only be called outside split and merge ops. + void verify_area_is_ideally_merged() const; +#endif // ASSERT + + void print_on(outputStream* st) const; + +}; + +// RootChunkAreaLUT (lookup table) manages a series of contiguous root chunk areas +// in memory (in the context of a VirtualSpaceNode). It allows finding the containing +// root chunk for any given memory address. It allows for easy iteration over all +// root chunks. +// Beyond that it is unexciting. +class RootChunkAreaLUT { + + // Base address of the whole area. + const MetaWord* const _base; + + // Number of root chunk areas. + const int _num; + + // Array of RootChunkArea objects. + RootChunkArea* _arr; + +#ifdef ASSERT + void check_pointer(const MetaWord* p) const { + assert(p >= base() && p < base() + word_size(), "Invalid pointer"); + } +#endif + + // Given an address into this range, return the index into the area array for the + // area this address falls into. + int index_by_address(const MetaWord* p) const { + DEBUG_ONLY(check_pointer(p);) + int idx = (int)((p - base()) / chunklevel::MAX_CHUNK_WORD_SIZE); + assert(idx >= 0 && idx < _num, "Sanity"); + return idx; + } + +public: + + RootChunkAreaLUT(const MetaWord* base, size_t word_size); + ~RootChunkAreaLUT(); + + // Given a memory address into the range this array covers, return the + // corresponding area object. If none existed at this position, create it + // on demand. + RootChunkArea* get_area_by_address(const MetaWord* p) const { + const int idx = index_by_address(p); + RootChunkArea* ra = _arr + idx; + DEBUG_ONLY(ra->check_pointer(p);) + return _arr + idx; + } + + // Access area by its index + int number_of_areas() const { return _num; } + RootChunkArea* get_area_by_index(int index) { assert(index >= 0 && index < _num, "oob"); return _arr + index; } + const RootChunkArea* get_area_by_index(int index) const { assert(index >= 0 && index < _num, "oob"); return _arr + index; } + + /// range /// + + const MetaWord* base() const { return _base; } + size_t word_size() const { return _num * chunklevel::MAX_CHUNK_WORD_SIZE; } + const MetaWord* end() const { return _base + word_size(); } + + // Returns true if all areas in this area table are free (only contain free chunks). + bool is_free() const; + + DEBUG_ONLY(void verify() const;) + + void print_on(outputStream* st) const; + +}; + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_ROOTCHUNKAREA_HPP diff --git a/src/hotspot/share/memory/metaspace/runningCounters.cpp b/src/hotspot/share/memory/metaspace/runningCounters.cpp new file mode 100644 index 00000000000..8b54202182a --- /dev/null +++ b/src/hotspot/share/memory/metaspace/runningCounters.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "memory/metaspace/chunkManager.hpp" +#include "memory/metaspace/counters.hpp" +#include "memory/metaspace/runningCounters.hpp" +#include "memory/metaspace/virtualSpaceList.hpp" + +namespace metaspace { + +SizeAtomicCounter RunningCounters::_used_class_counter; +SizeAtomicCounter RunningCounters::_used_nonclass_counter; + +// Return reserved size, in words, for Metaspace +size_t RunningCounters::reserved_words() { + return reserved_words_class() + reserved_words_nonclass(); +} + +size_t RunningCounters::reserved_words_class() { + VirtualSpaceList* vs = VirtualSpaceList::vslist_class(); + return vs != NULL ? vs->reserved_words() : 0; +} + +size_t RunningCounters::reserved_words_nonclass() { + return VirtualSpaceList::vslist_nonclass()->reserved_words(); +} + +// Return total committed size, in words, for Metaspace +size_t RunningCounters::committed_words() { + return committed_words_class() + committed_words_nonclass(); +} + +size_t RunningCounters::committed_words_class() { + VirtualSpaceList* vs = VirtualSpaceList::vslist_class(); + return vs != NULL ? vs->committed_words() : 0; +} + +size_t RunningCounters::committed_words_nonclass() { + return VirtualSpaceList::vslist_nonclass()->committed_words(); +} + +// ---- used chunks ----- + +// Returns size, in words, used for metadata. +size_t RunningCounters::used_words() { + return used_words_class() + used_words_nonclass(); +} + +size_t RunningCounters::used_words_class() { + return _used_class_counter.get(); +} + +size_t RunningCounters::used_words_nonclass() { + return _used_nonclass_counter.get(); +} + +// ---- free chunks ----- + +// Returns size, in words, of all chunks in all freelists. +size_t RunningCounters::free_chunks_words() { + return free_chunks_words_class() + free_chunks_words_nonclass(); +} + +size_t RunningCounters::free_chunks_words_class() { + ChunkManager* cm = ChunkManager::chunkmanager_class(); + return cm != NULL ? cm->total_word_size() : 0; +} + +size_t RunningCounters::free_chunks_words_nonclass() { + return ChunkManager::chunkmanager_nonclass()->total_word_size(); +} + +} // namespace metaspace + diff --git a/src/hotspot/share/memory/metaspace/runningCounters.hpp b/src/hotspot/share/memory/metaspace/runningCounters.hpp new file mode 100644 index 00000000000..8ef751896d3 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/runningCounters.hpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_RUNNINGCOUNTERS_HPP +#define SHARE_MEMORY_METASPACE_RUNNINGCOUNTERS_HPP + +#include "memory/allocation.hpp" +#include "memory/metaspace/counters.hpp" + +namespace metaspace { + +// This class is a convenience interface for accessing global metaspace counters. +class RunningCounters : public AllStatic { + + static SizeAtomicCounter _used_class_counter; + static SizeAtomicCounter _used_nonclass_counter; + +public: + + // ---- virtual memory ----- + + // Return reserved size, in words, for Metaspace + static size_t reserved_words(); + static size_t reserved_words_class(); + static size_t reserved_words_nonclass(); + + // Return total committed size, in words, for Metaspace + static size_t committed_words(); + static size_t committed_words_class(); + static size_t committed_words_nonclass(); + + // ---- used chunks ----- + + // Returns size, in words, used for metadata. + static size_t used_words(); + static size_t used_words_class(); + static size_t used_words_nonclass(); + + // ---- free chunks ----- + + // Returns size, in words, of all chunks in all freelists. + static size_t free_chunks_words(); + static size_t free_chunks_words_class(); + static size_t free_chunks_words_nonclass(); + + // Direct access to the counters. + static SizeAtomicCounter* used_nonclass_counter() { return &_used_nonclass_counter; } + static SizeAtomicCounter* used_class_counter() { return &_used_class_counter; } + +}; + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_RUNNINGCOUNTERS_HPP diff --git a/src/hotspot/share/memory/metaspace/smallBlocks.hpp b/src/hotspot/share/memory/metaspace/smallBlocks.hpp deleted file mode 100644 index 35b6519f5c5..00000000000 --- a/src/hotspot/share/memory/metaspace/smallBlocks.hpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_MEMORY_METASPACE_SMALLBLOCKS_HPP -#define SHARE_MEMORY_METASPACE_SMALLBLOCKS_HPP - -#include "memory/allocation.hpp" -#include "memory/binaryTreeDictionary.hpp" -#include "memory/metaspace/metablock.hpp" -#include "utilities/globalDefinitions.hpp" - -class outputStream; - -namespace metaspace { - -class SmallBlocks : public CHeapObj { - - const static uint _small_block_max_size = sizeof(TreeChunk >)/HeapWordSize; - // Note: this corresponds to the imposed miminum allocation size, see SpaceManager::get_allocation_word_size() - const static uint _small_block_min_size = sizeof(Metablock)/HeapWordSize; - -private: - FreeList _small_lists[_small_block_max_size - _small_block_min_size]; - - FreeList& list_at(size_t word_size) { - assert(word_size >= _small_block_min_size, "There are no metaspace objects less than %u words", _small_block_min_size); - return _small_lists[word_size - _small_block_min_size]; - } - -public: - SmallBlocks() { - for (uint i = _small_block_min_size; i < _small_block_max_size; i++) { - uint k = i - _small_block_min_size; - _small_lists[k].set_size(i); - } - } - - // Returns the total size, in words, of all blocks, across all block sizes. - size_t total_size() const; - - // Returns the total number of all blocks across all block sizes. - uintx total_num_blocks() const; - - static uint small_block_max_size() { return _small_block_max_size; } - static uint small_block_min_size() { return _small_block_min_size; } - - MetaWord* get_block(size_t word_size) { - if (list_at(word_size).count() > 0) { - MetaWord* new_block = (MetaWord*) list_at(word_size).get_chunk_at_head(); - return new_block; - } else { - return NULL; - } - } - void return_block(Metablock* free_chunk, size_t word_size) { - list_at(word_size).return_chunk_at_head(free_chunk, false); - assert(list_at(word_size).count() > 0, "Should have a chunk"); - } - - void print_on(outputStream* st) const; - -}; - -} // namespace metaspace - - -#endif // SHARE_MEMORY_METASPACE_SMALLBLOCKS_HPP diff --git a/src/hotspot/share/memory/metaspace/spaceManager.cpp b/src/hotspot/share/memory/metaspace/spaceManager.cpp deleted file mode 100644 index 236e8cbb328..00000000000 --- a/src/hotspot/share/memory/metaspace/spaceManager.cpp +++ /dev/null @@ -1,528 +0,0 @@ -/* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ -#include "precompiled.hpp" - -#include "logging/log.hpp" -#include "logging/logStream.hpp" -#include "memory/metaspace/chunkManager.hpp" -#include "memory/metaspace/metachunk.hpp" -#include "memory/metaspace/metaDebug.hpp" -#include "memory/metaspace/metaspaceCommon.hpp" -#include "memory/metaspace/spaceManager.hpp" -#include "memory/metaspace/virtualSpaceList.hpp" -#include "runtime/atomic.hpp" -#include "runtime/init.hpp" -#include "services/memoryService.hpp" -#include "utilities/debug.hpp" -#include "utilities/globalDefinitions.hpp" - -namespace metaspace { - -#define assert_counter(expected_value, real_value, msg) \ - assert( (expected_value) == (real_value), \ - "Counter mismatch (%s): expected " SIZE_FORMAT \ - ", but got: " SIZE_FORMAT ".", msg, expected_value, \ - real_value); - -// SpaceManager methods - -size_t SpaceManager::adjust_initial_chunk_size(size_t requested, bool is_class_space) { - size_t chunk_sizes[] = { - specialized_chunk_size(is_class_space), - small_chunk_size(is_class_space), - medium_chunk_size(is_class_space) - }; - - // Adjust up to one of the fixed chunk sizes ... - for (size_t i = 0; i < ARRAY_SIZE(chunk_sizes); i++) { - if (requested <= chunk_sizes[i]) { - return chunk_sizes[i]; - } - } - - // ... or return the size as a humongous chunk. - return requested; -} - -size_t SpaceManager::adjust_initial_chunk_size(size_t requested) const { - return adjust_initial_chunk_size(requested, is_class()); -} - -size_t SpaceManager::get_initial_chunk_size(Metaspace::MetaspaceType type) const { - size_t requested; - - if (is_class()) { - switch (type) { - case Metaspace::BootMetaspaceType: requested = Metaspace::first_class_chunk_word_size(); break; - case Metaspace::ClassMirrorHolderMetaspaceType: requested = ClassSpecializedChunk; break; - case Metaspace::ReflectionMetaspaceType: requested = ClassSpecializedChunk; break; - default: requested = ClassSmallChunk; break; - } - } else { - switch (type) { - case Metaspace::BootMetaspaceType: requested = Metaspace::first_chunk_word_size(); break; - case Metaspace::ClassMirrorHolderMetaspaceType: requested = SpecializedChunk; break; - case Metaspace::ReflectionMetaspaceType: requested = SpecializedChunk; break; - default: requested = SmallChunk; break; - } - } - - // Adjust to one of the fixed chunk sizes (unless humongous) - const size_t adjusted = adjust_initial_chunk_size(requested); - - assert(adjusted != 0, "Incorrect initial chunk size. Requested: " - SIZE_FORMAT " adjusted: " SIZE_FORMAT, requested, adjusted); - - return adjusted; -} - -void SpaceManager::locked_print_chunks_in_use_on(outputStream* st) const { - - for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { - st->print("SpaceManager: " UINTX_FORMAT " %s chunks.", - num_chunks_by_type(i), chunk_size_name(i)); - } - - chunk_manager()->locked_print_free_chunks(st); -} - -size_t SpaceManager::calc_chunk_size(size_t word_size) { - - // Decide between a small chunk and a medium chunk. Up to - // _small_chunk_limit small chunks can be allocated. - // After that a medium chunk is preferred. - size_t chunk_word_size; - - // Special case for hidden metadata space. - // ClassMirrorHolder metadata space is usually small since it is used for - // class loader data's whose life cycle is governed by one class such as a - // non-strong hidden class or unsafe anonymous class. The majority within 1K - 2K range and - // rarely about 4K (64-bits JVM). - // Instead of jumping to SmallChunk after initial chunk exhausted, keeping allocation - // from SpecializeChunk up to _anon_or_delegating_metadata_specialize_chunk_limit (4) - // reduces space waste from 60+% to around 30%. - if ((_space_type == Metaspace::ClassMirrorHolderMetaspaceType || _space_type == Metaspace::ReflectionMetaspaceType) && - _mdtype == Metaspace::NonClassType && - num_chunks_by_type(SpecializedIndex) < anon_and_delegating_metadata_specialize_chunk_limit && - word_size + Metachunk::overhead() <= SpecializedChunk) { - return SpecializedChunk; - } - - if (num_chunks_by_type(MediumIndex) == 0 && - num_chunks_by_type(SmallIndex) < small_chunk_limit) { - chunk_word_size = (size_t) small_chunk_size(); - if (word_size + Metachunk::overhead() > small_chunk_size()) { - chunk_word_size = medium_chunk_size(); - } - } else { - chunk_word_size = medium_chunk_size(); - } - - // Might still need a humongous chunk. Enforce - // humongous allocations sizes to be aligned up to - // the smallest chunk size. - size_t if_humongous_sized_chunk = - align_up(word_size + Metachunk::overhead(), - smallest_chunk_size()); - chunk_word_size = - MAX2((size_t) chunk_word_size, if_humongous_sized_chunk); - - assert(!SpaceManager::is_humongous(word_size) || - chunk_word_size == if_humongous_sized_chunk, - "Size calculation is wrong, word_size " SIZE_FORMAT - " chunk_word_size " SIZE_FORMAT, - word_size, chunk_word_size); - Log(gc, metaspace, alloc) log; - if (log.is_trace() && SpaceManager::is_humongous(word_size)) { - log.trace("Metadata humongous allocation:"); - log.trace(" word_size " PTR_FORMAT, word_size); - log.trace(" chunk_word_size " PTR_FORMAT, chunk_word_size); - log.trace(" chunk overhead " PTR_FORMAT, Metachunk::overhead()); - } - return chunk_word_size; -} - -void SpaceManager::track_metaspace_memory_usage() { - if (is_init_completed()) { - if (is_class()) { - MemoryService::track_compressed_class_memory_usage(); - } - MemoryService::track_metaspace_memory_usage(); - } -} - -MetaWord* SpaceManager::grow_and_allocate(size_t word_size) { - assert_lock_strong(_lock); - assert(vs_list()->current_virtual_space() != NULL, - "Should have been set"); - assert(current_chunk() == NULL || - current_chunk()->allocate(word_size) == NULL, - "Don't need to expand"); - MutexLocker cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); - - if (log_is_enabled(Trace, gc, metaspace, freelist)) { - size_t words_left = 0; - size_t words_used = 0; - if (current_chunk() != NULL) { - words_left = current_chunk()->free_word_size(); - words_used = current_chunk()->used_word_size(); - } - log_trace(gc, metaspace, freelist)("SpaceManager::grow_and_allocate for " SIZE_FORMAT " words " SIZE_FORMAT " words used " SIZE_FORMAT " words left", - word_size, words_used, words_left); - } - - // Get another chunk - size_t chunk_word_size = calc_chunk_size(word_size); - Metachunk* next = get_new_chunk(chunk_word_size); - - MetaWord* mem = NULL; - - // If a chunk was available, add it to the in-use chunk list - // and do an allocation from it. - if (next != NULL) { - // Add to this manager's list of chunks in use. - // If the new chunk is humongous, it was created to serve a single large allocation. In that - // case it usually makes no sense to make it the current chunk, since the next allocation would - // need to allocate a new chunk anyway, while we would now prematurely retire a perfectly - // good chunk which could be used for more normal allocations. - bool make_current = true; - if (next->get_chunk_type() == HumongousIndex && - current_chunk() != NULL) { - make_current = false; - } - add_chunk(next, make_current); - mem = next->allocate(word_size); - } - - // Track metaspace memory usage statistic. - track_metaspace_memory_usage(); - - return mem; -} - -void SpaceManager::print_on(outputStream* st) const { - SpaceManagerStatistics stat; - add_to_statistics(&stat); // will lock _lock. - stat.print_on(st, 1*K, false); -} - -SpaceManager::SpaceManager(Metaspace::MetadataType mdtype, - Metaspace::MetaspaceType space_type,// - Mutex* lock) : - _lock(lock), - _mdtype(mdtype), - _space_type(space_type), - _chunk_list(NULL), - _current_chunk(NULL), - _overhead_words(0), - _capacity_words(0), - _used_words(0), - _block_freelists(NULL) { - Metadebug::init_allocation_fail_alot_count(); - memset(_num_chunks_by_type, 0, sizeof(_num_chunks_by_type)); - log_trace(gc, metaspace, freelist)("SpaceManager(): " PTR_FORMAT, p2i(this)); -} - -void SpaceManager::account_for_new_chunk(const Metachunk* new_chunk) { - - assert_lock_strong(MetaspaceExpand_lock); - - _capacity_words += new_chunk->word_size(); - _overhead_words += Metachunk::overhead(); - DEBUG_ONLY(new_chunk->verify()); - _num_chunks_by_type[new_chunk->get_chunk_type()] ++; - - // Adjust global counters: - MetaspaceUtils::inc_capacity(mdtype(), new_chunk->word_size()); - MetaspaceUtils::inc_overhead(mdtype(), Metachunk::overhead()); -} - -void SpaceManager::account_for_allocation(size_t words) { - // Note: we should be locked with the ClassloaderData-specific metaspace lock. - // We may or may not be locked with the global metaspace expansion lock. - assert_lock_strong(lock()); - - // Add to the per SpaceManager totals. This can be done non-atomically. - _used_words += words; - - // Adjust global counters. This will be done atomically. - MetaspaceUtils::inc_used(mdtype(), words); -} - -void SpaceManager::account_for_spacemanager_death() { - - assert_lock_strong(MetaspaceExpand_lock); - - MetaspaceUtils::dec_capacity(mdtype(), _capacity_words); - MetaspaceUtils::dec_overhead(mdtype(), _overhead_words); - MetaspaceUtils::dec_used(mdtype(), _used_words); -} - -SpaceManager::~SpaceManager() { - - // This call this->_lock which can't be done while holding MetaspaceExpand_lock - DEBUG_ONLY(verify_metrics()); - - MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); - - account_for_spacemanager_death(); - - Log(gc, metaspace, freelist) log; - if (log.is_trace()) { - log.trace("~SpaceManager(): " PTR_FORMAT, p2i(this)); - ResourceMark rm; - LogStream ls(log.trace()); - locked_print_chunks_in_use_on(&ls); - if (block_freelists() != NULL) { - block_freelists()->print_on(&ls); - } - } - - // Add all the chunks in use by this space manager - // to the global list of free chunks. - - // Follow each list of chunks-in-use and add them to the - // free lists. Each list is NULL terminated. - chunk_manager()->return_chunk_list(chunk_list()); -#ifdef ASSERT - _chunk_list = NULL; - _current_chunk = NULL; -#endif - -#ifdef ASSERT - EVERY_NTH(VerifyMetaspaceInterval) - chunk_manager()->locked_verify(true); - END_EVERY_NTH -#endif - - if (_block_freelists != NULL) { - delete _block_freelists; - } -} - -void SpaceManager::deallocate(MetaWord* p, size_t word_size) { - assert_lock_strong(lock()); - // Allocations and deallocations are in raw_word_size - size_t raw_word_size = get_allocation_word_size(word_size); - // Lazily create a block_freelist - if (block_freelists() == NULL) { - _block_freelists = new BlockFreelist(); - } - block_freelists()->return_block(p, raw_word_size); - DEBUG_ONLY(Atomic::inc(&(g_internal_statistics.num_deallocs))); -} - -// Adds a chunk to the list of chunks in use. -void SpaceManager::add_chunk(Metachunk* new_chunk, bool make_current) { - - assert_lock_strong(_lock); - assert(new_chunk != NULL, "Should not be NULL"); - assert(new_chunk->next() == NULL, "Should not be on a list"); - - new_chunk->reset_empty(); - - // Find the correct list and and set the current - // chunk for that list. - ChunkIndex index = chunk_manager()->list_index(new_chunk->word_size()); - - if (make_current) { - // If we are to make the chunk current, retire the old current chunk and replace - // it with the new chunk. - retire_current_chunk(); - set_current_chunk(new_chunk); - } - - // Add the new chunk at the head of its respective chunk list. - new_chunk->set_next(_chunk_list); - _chunk_list = new_chunk; - - // Adjust counters. - account_for_new_chunk(new_chunk); - - assert(new_chunk->is_empty(), "Not ready for reuse"); - Log(gc, metaspace, freelist) log; - if (log.is_trace()) { - log.trace("SpaceManager::added chunk: "); - ResourceMark rm; - LogStream ls(log.trace()); - new_chunk->print_on(&ls); - chunk_manager()->locked_print_free_chunks(&ls); - } -} - -void SpaceManager::retire_current_chunk() { - if (current_chunk() != NULL) { - size_t remaining_words = current_chunk()->free_word_size(); - if (remaining_words >= SmallBlocks::small_block_min_size()) { - MetaWord* ptr = current_chunk()->allocate(remaining_words); - deallocate(ptr, remaining_words); - account_for_allocation(remaining_words); - } - } -} - -Metachunk* SpaceManager::get_new_chunk(size_t chunk_word_size) { - // Get a chunk from the chunk freelist - Metachunk* next = chunk_manager()->chunk_freelist_allocate(chunk_word_size); - - if (next == NULL) { - next = vs_list()->get_new_chunk(chunk_word_size, - medium_chunk_bunch()); - } - - Log(gc, metaspace, alloc) log; - if (log.is_trace() && next != NULL && - SpaceManager::is_humongous(next->word_size())) { - log.trace(" new humongous chunk word size " PTR_FORMAT, next->word_size()); - } - - return next; -} - -MetaWord* SpaceManager::allocate(size_t word_size) { - MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag); - size_t raw_word_size = get_allocation_word_size(word_size); - BlockFreelist* fl = block_freelists(); - MetaWord* p = NULL; - - // Allocation from the dictionary is expensive in the sense that - // the dictionary has to be searched for a size. Don't allocate - // from the dictionary until it starts to get fat. Is this - // a reasonable policy? Maybe an skinny dictionary is fast enough - // for allocations. Do some profiling. JJJ - if (fl != NULL && fl->total_size() > allocation_from_dictionary_limit) { - p = fl->get_block(raw_word_size); - if (p != NULL) { - DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_allocs_from_deallocated_blocks)); - } - } - if (p == NULL) { - p = allocate_work(raw_word_size); - } - -#ifdef ASSERT - EVERY_NTH(VerifyMetaspaceInterval) - verify_metrics_locked(); - END_EVERY_NTH -#endif - - return p; -} - -// Returns the address of spaced allocated for "word_size". -// This methods does not know about blocks (Metablocks) -MetaWord* SpaceManager::allocate_work(size_t word_size) { - assert_lock_strong(lock()); -#ifdef ASSERT - if (Metadebug::test_metadata_failure()) { - return NULL; - } -#endif - // Is there space in the current chunk? - MetaWord* result = NULL; - - if (current_chunk() != NULL) { - result = current_chunk()->allocate(word_size); - } - - if (result == NULL) { - result = grow_and_allocate(word_size); - } - - if (result != NULL) { - account_for_allocation(word_size); - } - - return result; -} - -void SpaceManager::verify() { - Metachunk* curr = chunk_list(); - while (curr != NULL) { - DEBUG_ONLY(do_verify_chunk(curr);) - assert(curr->is_tagged_free() == false, "Chunk should be tagged as in use."); - curr = curr->next(); - } -} - -void SpaceManager::verify_chunk_size(Metachunk* chunk) { - assert(is_humongous(chunk->word_size()) || - chunk->word_size() == medium_chunk_size() || - chunk->word_size() == small_chunk_size() || - chunk->word_size() == specialized_chunk_size(), - "Chunk size is wrong"); - return; -} - -void SpaceManager::add_to_statistics_locked(SpaceManagerStatistics* out) const { - assert_lock_strong(lock()); - Metachunk* chunk = chunk_list(); - while (chunk != NULL) { - UsedChunksStatistics& chunk_stat = out->chunk_stats(chunk->get_chunk_type()); - chunk_stat.add_num(1); - chunk_stat.add_cap(chunk->word_size()); - chunk_stat.add_overhead(Metachunk::overhead()); - chunk_stat.add_used(chunk->used_word_size() - Metachunk::overhead()); - if (chunk != current_chunk()) { - chunk_stat.add_waste(chunk->free_word_size()); - } else { - chunk_stat.add_free(chunk->free_word_size()); - } - chunk = chunk->next(); - } - if (block_freelists() != NULL) { - out->add_free_blocks_info(block_freelists()->num_blocks(), block_freelists()->total_size()); - } -} - -void SpaceManager::add_to_statistics(SpaceManagerStatistics* out) const { - MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag); - add_to_statistics_locked(out); -} - -#ifdef ASSERT -void SpaceManager::verify_metrics_locked() const { - assert_lock_strong(lock()); - - SpaceManagerStatistics stat; - add_to_statistics_locked(&stat); - - UsedChunksStatistics chunk_stats = stat.totals(); - - DEBUG_ONLY(chunk_stats.check_sanity()); - - assert_counter(_capacity_words, chunk_stats.cap(), "SpaceManager::_capacity_words"); - assert_counter(_used_words, chunk_stats.used(), "SpaceManager::_used_words"); - assert_counter(_overhead_words, chunk_stats.overhead(), "SpaceManager::_overhead_words"); -} - -void SpaceManager::verify_metrics() const { - MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag); - verify_metrics_locked(); -} -#endif // ASSERT - - -} // namespace metaspace - diff --git a/src/hotspot/share/memory/metaspace/spaceManager.hpp b/src/hotspot/share/memory/metaspace/spaceManager.hpp deleted file mode 100644 index edf090d09c6..00000000000 --- a/src/hotspot/share/memory/metaspace/spaceManager.hpp +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_MEMORY_METASPACE_SPACEMANAGER_HPP -#define SHARE_MEMORY_METASPACE_SPACEMANAGER_HPP - -#include "memory/allocation.hpp" -#include "memory/metaspace.hpp" -#include "memory/metaspace/blockFreelist.hpp" -#include "memory/metaspace/metaspaceCommon.hpp" -#include "memory/metaspace/metachunk.hpp" -#include "memory/metaspace/metaspaceStatistics.hpp" -#include "utilities/debug.hpp" -#include "utilities/globalDefinitions.hpp" - -class outputStream; -class Mutex; - -namespace metaspace { - -// SpaceManager - used by Metaspace to handle allocations -class SpaceManager : public CHeapObj { - friend class ::ClassLoaderMetaspace; - friend class Metadebug; - - private: - - // protects allocations - Mutex* const _lock; - - // Type of metadata allocated. - const Metaspace::MetadataType _mdtype; - - // Type of metaspace - const Metaspace::MetaspaceType _space_type; - - // List of chunks in use by this SpaceManager. Allocations - // are done from the current chunk. The list is used for deallocating - // chunks when the SpaceManager is freed. - Metachunk* _chunk_list; - Metachunk* _current_chunk; - - enum { - - // Maximum number of small chunks to allocate to a SpaceManager - small_chunk_limit = 4, - - // Maximum number of specialize chunks to allocate for anonymous and delegating - // metadata space to a SpaceManager - anon_and_delegating_metadata_specialize_chunk_limit = 4, - - allocation_from_dictionary_limit = 4 * K - - }; - - // Some running counters, but lets keep their number small to not add to much to - // the per-classloader footprint. - // Note: capacity = used + free + waste + overhead. We do not keep running counters for - // free and waste. Their sum can be deduced from the three other values. - size_t _overhead_words; - size_t _capacity_words; - size_t _used_words; - uintx _num_chunks_by_type[NumberOfInUseLists]; - - // Free lists of blocks are per SpaceManager since they - // are assumed to be in chunks in use by the SpaceManager - // and all chunks in use by a SpaceManager are freed when - // the class loader using the SpaceManager is collected. - BlockFreelist* _block_freelists; - - private: - // Accessors - Metachunk* chunk_list() const { return _chunk_list; } - - BlockFreelist* block_freelists() const { return _block_freelists; } - - Metaspace::MetadataType mdtype() { return _mdtype; } - - VirtualSpaceList* vs_list() const { return Metaspace::get_space_list(_mdtype); } - ChunkManager* chunk_manager() const { return Metaspace::get_chunk_manager(_mdtype); } - - Metachunk* current_chunk() const { return _current_chunk; } - void set_current_chunk(Metachunk* v) { - _current_chunk = v; - } - - Metachunk* find_current_chunk(size_t word_size); - - // Add chunk to the list of chunks in use - void add_chunk(Metachunk* v, bool make_current); - void retire_current_chunk(); - - Mutex* lock() const { return _lock; } - - // Adds to the given statistic object. Expects to be locked with lock(). - void add_to_statistics_locked(SpaceManagerStatistics* out) const; - - // Verify internal counters against the current state. Expects to be locked with lock(). - DEBUG_ONLY(void verify_metrics_locked() const;) - - public: - SpaceManager(Metaspace::MetadataType mdtype, - Metaspace::MetaspaceType space_type, - Mutex* lock); - ~SpaceManager(); - - enum ChunkMultiples { - MediumChunkMultiple = 4 - }; - - static size_t specialized_chunk_size(bool is_class) { return is_class ? ClassSpecializedChunk : SpecializedChunk; } - static size_t small_chunk_size(bool is_class) { return is_class ? ClassSmallChunk : SmallChunk; } - static size_t medium_chunk_size(bool is_class) { return is_class ? ClassMediumChunk : MediumChunk; } - - static size_t smallest_chunk_size(bool is_class) { return specialized_chunk_size(is_class); } - - // Accessors - bool is_class() const { return _mdtype == Metaspace::ClassType; } - - size_t specialized_chunk_size() const { return specialized_chunk_size(is_class()); } - size_t small_chunk_size() const { return small_chunk_size(is_class()); } - size_t medium_chunk_size() const { return medium_chunk_size(is_class()); } - - size_t smallest_chunk_size() const { return smallest_chunk_size(is_class()); } - - size_t medium_chunk_bunch() const { return medium_chunk_size() * MediumChunkMultiple; } - - bool is_humongous(size_t word_size) { return word_size > medium_chunk_size(); } - - size_t capacity_words() const { return _capacity_words; } - size_t used_words() const { return _used_words; } - size_t overhead_words() const { return _overhead_words; } - - // Adjust local, global counters after a new chunk has been added. - void account_for_new_chunk(const Metachunk* new_chunk); - - // Adjust local, global counters after space has been allocated from the current chunk. - void account_for_allocation(size_t words); - - // Adjust global counters just before the SpaceManager dies, after all its chunks - // have been returned to the freelist. - void account_for_spacemanager_death(); - - // Adjust the initial chunk size to match one of the fixed chunk list sizes, - // or return the unadjusted size if the requested size is humongous. - static size_t adjust_initial_chunk_size(size_t requested, bool is_class_space); - size_t adjust_initial_chunk_size(size_t requested) const; - - // Get the initial chunks size for this metaspace type. - size_t get_initial_chunk_size(Metaspace::MetaspaceType type) const; - - // Todo: remove this once we have counters by chunk type. - uintx num_chunks_by_type(ChunkIndex chunk_type) const { return _num_chunks_by_type[chunk_type]; } - - Metachunk* get_new_chunk(size_t chunk_word_size); - - // Block allocation and deallocation. - // Allocates a block from the current chunk - MetaWord* allocate(size_t word_size); - - // Helper for allocations - MetaWord* allocate_work(size_t word_size); - - // Returns a block to the per manager freelist - void deallocate(MetaWord* p, size_t word_size); - - // Based on the allocation size and a minimum chunk size, - // returned chunk size (for expanding space for chunk allocation). - size_t calc_chunk_size(size_t allocation_word_size); - - // Called when an allocation from the current chunk fails. - // Gets a new chunk (may require getting a new virtual space), - // and allocates from that chunk. - MetaWord* grow_and_allocate(size_t word_size); - - // Notify memory usage to MemoryService. - void track_metaspace_memory_usage(); - - // debugging support. - - void print_on(outputStream* st) const; - void locked_print_chunks_in_use_on(outputStream* st) const; - - void verify(); - void verify_chunk_size(Metachunk* chunk); - - // This adjusts the size given to be greater than the minimum allocation size in - // words for data in metaspace. Esentially the minimum size is currently 3 words. - size_t get_allocation_word_size(size_t word_size) { - size_t byte_size = word_size * BytesPerWord; - - size_t raw_bytes_size = MAX2(byte_size, sizeof(Metablock)); - raw_bytes_size = align_up(raw_bytes_size, Metachunk::object_alignment()); - - size_t raw_word_size = raw_bytes_size / BytesPerWord; - assert(raw_word_size * BytesPerWord == raw_bytes_size, "Size problem"); - - return raw_word_size; - } - - // Adds to the given statistic object. - void add_to_statistics(SpaceManagerStatistics* out) const; - - // Verify internal counters against the current state. - DEBUG_ONLY(void verify_metrics() const;) - -}; - - -} // namespace metaspace - -#endif // SHARE_MEMORY_METASPACE_SPACEMANAGER_HPP diff --git a/src/hotspot/share/memory/metaspace/testHelpers.cpp b/src/hotspot/share/memory/metaspace/testHelpers.cpp new file mode 100644 index 00000000000..a648c570b33 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/testHelpers.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "memory/metaspace/metaspaceArena.hpp" +#include "memory/metaspace/metaspaceArenaGrowthPolicy.hpp" +#include "memory/metaspace/metaspaceContext.hpp" +#include "memory/metaspace/testHelpers.hpp" +#include "runtime/mutexLocker.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" + +namespace metaspace { + +///// MetaspaceTestArena ////// + +MetaspaceTestArena::MetaspaceTestArena(Mutex* lock, MetaspaceArena* arena) : + _lock(lock), + _arena(arena) +{} + +MetaspaceTestArena::~MetaspaceTestArena() { + delete _arena; + delete _lock; +} + +MetaWord* MetaspaceTestArena::allocate(size_t word_size) { + return _arena->allocate(word_size); +} + +void MetaspaceTestArena::deallocate(MetaWord* p, size_t word_size) { + return _arena->deallocate(p, word_size); +} + +///// MetaspaceTestArea ////// + +MetaspaceTestContext::MetaspaceTestContext(const char* name, size_t commit_limit, size_t reserve_limit) : + _name(name), + _reserve_limit(reserve_limit), + _commit_limit(commit_limit), + _context(NULL), + _commit_limiter(commit_limit == 0 ? max_uintx : commit_limit), // commit_limit == 0 -> no limit + _used_words_counter(), + _rs() +{ + assert(is_aligned(reserve_limit, Metaspace::reserve_alignment_words()), "reserve_limit (" SIZE_FORMAT ") " + "not aligned to metaspace reserve alignment (" SIZE_FORMAT ")", + reserve_limit, Metaspace::reserve_alignment_words()); + if (reserve_limit > 0) { + // have reserve limit -> non-expandable context + _rs = ReservedSpace(reserve_limit * BytesPerWord, Metaspace::reserve_alignment(), false); + _context = MetaspaceContext::create_nonexpandable_context(name, _rs, &_commit_limiter); + } else { + // no reserve limit -> expandable vslist + _context = MetaspaceContext::create_expandable_context(name, &_commit_limiter); + } + +} + +MetaspaceTestContext::~MetaspaceTestContext() { + DEBUG_ONLY(verify();) + MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); + delete _context; + if (_rs.is_reserved()) { + _rs.release(); + } +} + +// Create an arena, feeding off this area. +MetaspaceTestArena* MetaspaceTestContext::create_arena(Metaspace::MetaspaceType type) { + const ArenaGrowthPolicy* growth_policy = ArenaGrowthPolicy::policy_for_space_type(type, false); + Mutex* lock = new Mutex(Monitor::native, "MetaspaceTestArea-lock", false, Monitor::_safepoint_check_never); + MetaspaceArena* arena = NULL; + { + MutexLocker ml(lock, Mutex::_no_safepoint_check_flag); + arena = new MetaspaceArena(_context->cm(), growth_policy, lock, &_used_words_counter, _name); + } + return new MetaspaceTestArena(lock, arena); +} + +void MetaspaceTestContext::purge_area() { + _context->cm()->purge(); +} + +#ifdef ASSERT +void MetaspaceTestContext::verify() const { + if (_context != NULL) { + _context->verify(); + } +} +#endif + +void MetaspaceTestContext::print_on(outputStream* st) const { + _context->print_on(st); +} + +} // namespace metaspace + diff --git a/src/hotspot/share/memory/metaspace/testHelpers.hpp b/src/hotspot/share/memory/metaspace/testHelpers.hpp new file mode 100644 index 00000000000..ddcdce23930 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/testHelpers.hpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_MEMORY_METASPACE_TESTHELPERS_HPP +#define SHARE_MEMORY_METASPACE_TESTHELPERS_HPP + +#include "memory/allocation.hpp" +#include "memory/metaspace.hpp" +#include "memory/metaspace/commitLimiter.hpp" +#include "memory/metaspace/counters.hpp" +#include "memory/metaspace/metaspaceContext.hpp" +#include "memory/virtualspace.hpp" +#include "utilities/globalDefinitions.hpp" + +// This is just convenience classes for metaspace-related tests +// (jtreg, via whitebox API, and gtests) + +class ReservedSpace; +class Mutex; +class outputStream; + +namespace metaspace { + +class MetaspaceContext; +class MetaspaceArena; + +// Wraps a MetaspaceTestArena with its own lock for testing purposes. +class MetaspaceTestArena : public CHeapObj { + + Mutex* const _lock; + MetaspaceArena* const _arena; + +public: + + const MetaspaceArena* arena() const { + return _arena; + } + + MetaspaceTestArena(Mutex* lock, MetaspaceArena* arena); + ~MetaspaceTestArena(); + + MetaWord* allocate(size_t word_size); + void deallocate(MetaWord* p, size_t word_size); + +}; + +// Wraps an instance of a MetaspaceContext together with some side objects for easy use in test beds (whitebox, gtests) +class MetaspaceTestContext : public CHeapObj { + + const char* const _name; + const size_t _reserve_limit; + const size_t _commit_limit; + + MetaspaceContext* _context; + CommitLimiter _commit_limiter; + SizeAtomicCounter _used_words_counter; + + // For non-expandable contexts we keep track of the space + // and delete it at destruction time. + ReservedSpace _rs; + +public: + + // Note: limit == 0 means unlimited + // Reserve limit > 0 simulates a non-expandable VirtualSpaceList (like CompressedClassSpace) + // Commit limit > 0 simulates a limit to max commitable space (like MaxMetaspaceSize) + MetaspaceTestContext(const char* name, size_t commit_limit = 0, size_t reserve_limit = 0); + ~MetaspaceTestContext(); + + // Create an arena, feeding off this area. + MetaspaceTestArena* create_arena(Metaspace::MetaspaceType type); + + void purge_area(); + + // Accessors + const CommitLimiter& commit_limiter() const { return _commit_limiter; } + const VirtualSpaceList& vslist() const { return *(_context->vslist()); } + ChunkManager& cm() { return *(_context->cm()); } + + // Returns reserve- and commit limit we run the test with (in the real world, + // these would be equivalent to CompressedClassSpaceSize resp MaxMetaspaceSize) + size_t reserve_limit() const { return _reserve_limit == 0 ? max_uintx : 0; } + size_t commit_limit() const { return _commit_limit == 0 ? max_uintx : 0; } + + // Convenience function to retrieve total committed/used words + size_t used_words() const { return _used_words_counter.get(); } + size_t committed_words() const { return _commit_limiter.committed_words(); } + + DEBUG_ONLY(void verify() const;) + + void print_on(outputStream* st) const; + +}; + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_TESTHELPERS_HPP diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceList.cpp b/src/hotspot/share/memory/metaspace/virtualSpaceList.cpp index 66dec2734a6..1543da89789 100644 --- a/src/hotspot/share/memory/metaspace/virtualSpaceList.cpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceList.cpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,426 +23,233 @@ * */ - #include "precompiled.hpp" #include "logging/log.hpp" -#include "logging/logStream.hpp" #include "memory/metaspace.hpp" #include "memory/metaspace/chunkManager.hpp" -#include "memory/metaspace/metachunk.hpp" +#include "memory/metaspace/commitLimiter.hpp" +#include "memory/metaspace/counters.hpp" +#include "memory/metaspace/freeChunkList.hpp" +#include "memory/metaspace/metaspaceContext.hpp" #include "memory/metaspace/metaspaceCommon.hpp" #include "memory/metaspace/virtualSpaceList.hpp" #include "memory/metaspace/virtualSpaceNode.hpp" -#include "runtime/atomic.hpp" -#include "runtime/orderAccess.hpp" #include "runtime/mutexLocker.hpp" -#include "runtime/safepoint.hpp" namespace metaspace { - -VirtualSpaceList::~VirtualSpaceList() { - VirtualSpaceListIterator iter(virtual_space_list()); - while (iter.repeat()) { - VirtualSpaceNode* vsl = iter.get_next(); - delete vsl; - } +#define LOGFMT "VsList @" PTR_FORMAT " (%s)" +#define LOGFMT_ARGS p2i(this), this->_name + +// Create a new, empty, expandable list. +VirtualSpaceList::VirtualSpaceList(const char* name, CommitLimiter* commit_limiter) : + _name(name), + _first_node(NULL), + _can_expand(true), + _commit_limiter(commit_limiter), + _reserved_words_counter(), + _committed_words_counter() +{ +} + +// Create a new list. The list will contain one node only, which uses the given ReservedSpace. +// It will be not expandable beyond that first node. +VirtualSpaceList::VirtualSpaceList(const char* name, ReservedSpace rs, CommitLimiter* commit_limiter) : + _name(name), + _first_node(NULL), + _can_expand(false), + _commit_limiter(commit_limiter), + _reserved_words_counter(), + _committed_words_counter() +{ + // Create the first node spanning the existing ReservedSpace. This will be the only node created + // for this list since we cannot expand. + VirtualSpaceNode* vsn = VirtualSpaceNode::create_node(rs, _commit_limiter, + &_reserved_words_counter, &_committed_words_counter); + assert(vsn != NULL, "node creation failed"); + _first_node = vsn; + _first_node->set_next(NULL); + _nodes_counter.increment(); } -void VirtualSpaceList::inc_reserved_words(size_t v) { - assert_lock_strong(MetaspaceExpand_lock); - _reserved_words = _reserved_words + v; -} -void VirtualSpaceList::dec_reserved_words(size_t v) { - assert_lock_strong(MetaspaceExpand_lock); - _reserved_words = _reserved_words - v; -} - -#define assert_committed_below_limit() \ - assert(MetaspaceUtils::committed_bytes() <= MaxMetaspaceSize, \ - "Too much committed memory. Committed: " SIZE_FORMAT \ - " limit (MaxMetaspaceSize): " SIZE_FORMAT, \ - MetaspaceUtils::committed_bytes(), MaxMetaspaceSize); - -void VirtualSpaceList::inc_committed_words(size_t v) { +VirtualSpaceList::~VirtualSpaceList() { assert_lock_strong(MetaspaceExpand_lock); - _committed_words = _committed_words + v; - - assert_committed_below_limit(); -} -void VirtualSpaceList::dec_committed_words(size_t v) { + // Note: normally, there is no reason ever to delete a vslist since they are + // global objects, but for gtests it makes sense to allow this. + VirtualSpaceNode* vsn = _first_node; + VirtualSpaceNode* vsn2 = vsn; + while (vsn != NULL) { + vsn2 = vsn->next(); + delete vsn; + vsn = vsn2; + } +} + +// Create a new node and append it to the list. After +// this function, _current_node shall point to a new empty node. +// List must be expandable for this to work. +void VirtualSpaceList::create_new_node() { + assert(_can_expand, "List is not expandable"); assert_lock_strong(MetaspaceExpand_lock); - _committed_words = _committed_words - v; - assert_committed_below_limit(); + VirtualSpaceNode* vsn = VirtualSpaceNode::create_node(Settings::virtual_space_node_default_word_size(), + _commit_limiter, + &_reserved_words_counter, &_committed_words_counter); + vsn->set_next(_first_node); + _first_node = vsn; + _nodes_counter.increment(); } -void VirtualSpaceList::inc_virtual_space_count() { +// Allocate a root chunk from this list. +// Note: this just returns a chunk whose memory is reserved; no memory is committed yet. +// Hence, before using this chunk, it must be committed. +// Also, no limits are checked, since no committing takes place. +Metachunk* VirtualSpaceList::allocate_root_chunk() { assert_lock_strong(MetaspaceExpand_lock); - _virtual_space_count++; -} -void VirtualSpaceList::dec_virtual_space_count() { - assert_lock_strong(MetaspaceExpand_lock); - _virtual_space_count--; -} + if (_first_node == NULL || + _first_node->free_words() < chunklevel::MAX_CHUNK_WORD_SIZE) { -// Walk the list of VirtualSpaceNodes and delete -// nodes with a 0 container_count. Remove Metachunks in -// the node from their respective freelists. -void VirtualSpaceList::purge(ChunkManager* chunk_manager) { - assert_lock_strong(MetaspaceExpand_lock); - // Don't use a VirtualSpaceListIterator because this - // list is being changed and a straightforward use of an iterator is not safe. - VirtualSpaceNode* prev_vsl = virtual_space_list(); - VirtualSpaceNode* next_vsl = prev_vsl; - int num_purged_nodes = 0; - while (next_vsl != NULL) { - VirtualSpaceNode* vsl = next_vsl; - DEBUG_ONLY(vsl->verify(false);) - next_vsl = vsl->next(); - // Don't free the current virtual space since it will likely - // be needed soon. - if (vsl->container_count() == 0 && vsl != current_virtual_space()) { - log_trace(gc, metaspace, freelist)("Purging VirtualSpaceNode " PTR_FORMAT " (capacity: " SIZE_FORMAT - ", used: " SIZE_FORMAT ").", p2i(vsl), vsl->capacity_words_in_vs(), vsl->used_words_in_vs()); - DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_vsnodes_purged)); - // Unlink it from the list - if (prev_vsl == vsl) { - // This is the case of the current node being the first node. - assert(vsl == virtual_space_list(), "Expected to be the first node"); - set_virtual_space_list(vsl->next()); - } else { - prev_vsl->set_next(vsl->next()); - } +#ifdef ASSERT + // Since all allocations from a VirtualSpaceNode happen in + // root-chunk-size units, and the node size must be root-chunk-size aligned, + // we should never have left-over space. + if (_first_node != NULL) { + assert(_first_node->free_words() == 0, "Sanity"); + } +#endif - vsl->purge(chunk_manager); - dec_reserved_words(vsl->reserved_words()); - dec_committed_words(vsl->committed_words()); - dec_virtual_space_count(); - delete vsl; - num_purged_nodes ++; + if (_can_expand) { + create_new_node(); + UL2(debug, "added new node (now: %d).", num_nodes()); } else { - prev_vsl = vsl; + UL(debug, "list cannot expand."); + return NULL; // We cannot expand this list. } } - // Verify list -#ifdef ASSERT - if (num_purged_nodes > 0) { - verify(false); - } -#endif -} + Metachunk* c = _first_node->allocate_root_chunk(); + assert(c != NULL, "This should have worked"); + return c; +} -// This function looks at the mmap regions in the metaspace without locking. -// The chunks are added with store ordering and not deleted except for at -// unloading time during a safepoint. -VirtualSpaceNode* VirtualSpaceList::find_enclosing_space(const void* ptr) { - // List should be stable enough to use an iterator here because removing virtual - // space nodes is only allowed at a safepoint. - if (is_within_envelope((address)ptr)) { - VirtualSpaceListIterator iter(virtual_space_list()); - while (iter.repeat()) { - VirtualSpaceNode* vsn = iter.get_next(); - if (vsn->contains(ptr)) { - return vsn; +// Attempts to purge nodes. This will remove and delete nodes which only contain free chunks. +// The free chunks are removed from the freelists before the nodes are deleted. +// Return number of purged nodes. +int VirtualSpaceList::purge(FreeChunkListVector* freelists) { + assert_lock_strong(MetaspaceExpand_lock); + UL(debug, "purging."); + + VirtualSpaceNode* vsn = _first_node; + VirtualSpaceNode* prev_vsn = NULL; + int num = 0, num_purged = 0; + while (vsn != NULL) { + VirtualSpaceNode* next_vsn = vsn->next(); + bool purged = vsn->attempt_purge(freelists); + if (purged) { + // Note: from now on do not dereference vsn! + UL2(debug, "purged node @" PTR_FORMAT ".", p2i(vsn)); + if (_first_node == vsn) { + _first_node = next_vsn; + } + DEBUG_ONLY(vsn = (VirtualSpaceNode*)((uintptr_t)(0xdeadbeef));) + if (prev_vsn != NULL) { + prev_vsn->set_next(next_vsn); } + num_purged++; + _nodes_counter.decrement(); + } else { + prev_vsn = vsn; } + vsn = next_vsn; + num ++; } - return NULL; -} - -void VirtualSpaceList::retire_current_virtual_space() { - assert_lock_strong(MetaspaceExpand_lock); - - VirtualSpaceNode* vsn = current_virtual_space(); - - ChunkManager* cm = is_class() ? Metaspace::chunk_manager_class() : - Metaspace::chunk_manager_metadata(); - vsn->retire(cm); + UL2(debug, "purged %d nodes (before: %d, now: %d)", + num_purged, num, num_nodes()); + return num_purged; } -VirtualSpaceList::VirtualSpaceList(size_t word_size) : - _virtual_space_list(NULL), - _current_virtual_space(NULL), - _is_class(false), - _reserved_words(0), - _committed_words(0), - _virtual_space_count(0), - _envelope_lo((address)max_uintx), - _envelope_hi(NULL) { - MutexLocker cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); - create_new_virtual_space(word_size); -} +// Print all nodes in this space list. +void VirtualSpaceList::print_on(outputStream* st) const { + MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); -VirtualSpaceList::VirtualSpaceList(ReservedSpace rs) : - _virtual_space_list(NULL), - _current_virtual_space(NULL), - _is_class(true), - _reserved_words(0), - _committed_words(0), - _virtual_space_count(0), - _envelope_lo((address)max_uintx), - _envelope_hi(NULL) { - MutexLocker cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); - VirtualSpaceNode* class_entry = new VirtualSpaceNode(is_class(), rs); - bool succeeded = class_entry->initialize(); - if (succeeded) { - expand_envelope_to_include_node(class_entry); - // ensure lock-free iteration sees fully initialized node - OrderAccess::storestore(); - link_vs(class_entry); + st->print_cr("vsl %s:", _name); + const VirtualSpaceNode* vsn = _first_node; + int n = 0; + while (vsn != NULL) { + st->print("- node #%d: ", n); + vsn->print_on(st); + vsn = vsn->next(); + n++; } + st->print_cr("- total %d nodes, " SIZE_FORMAT " reserved words, " SIZE_FORMAT " committed words.", + n, reserved_words(), committed_words()); } -size_t VirtualSpaceList::free_bytes() { - return current_virtual_space()->free_words_in_vs() * BytesPerWord; -} - -// Allocate another meta virtual space and add it to the list. -bool VirtualSpaceList::create_new_virtual_space(size_t vs_word_size) { +#ifdef ASSERT +void VirtualSpaceList::verify_locked() const { assert_lock_strong(MetaspaceExpand_lock); - - if (is_class()) { - assert(false, "We currently don't support more than one VirtualSpace for" - " the compressed class space. The initialization of the" - " CCS uses another code path and should not hit this path."); - return false; - } - - if (vs_word_size == 0) { - assert(false, "vs_word_size should always be at least _reserve_alignment large."); - return false; - } - - // Reserve the space - size_t vs_byte_size = vs_word_size * BytesPerWord; - assert_is_aligned(vs_byte_size, Metaspace::reserve_alignment()); - - // Allocate the meta virtual space and initialize it. - VirtualSpaceNode* new_entry = new VirtualSpaceNode(is_class(), vs_byte_size); - if (!new_entry->initialize()) { - delete new_entry; - return false; - } else { - assert(new_entry->reserved_words() == vs_word_size, - "Reserved memory size differs from requested memory size"); - expand_envelope_to_include_node(new_entry); - // ensure lock-free iteration sees fully initialized node - OrderAccess::storestore(); - link_vs(new_entry); - DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_vsnodes_created)); - return true; - } - - DEBUG_ONLY(verify(false);) - -} - -void VirtualSpaceList::link_vs(VirtualSpaceNode* new_entry) { - if (virtual_space_list() == NULL) { - set_virtual_space_list(new_entry); + assert(_name != NULL, "Sanity"); + + int n = 0; + + if (_first_node != NULL) { + size_t total_reserved_words = 0; + size_t total_committed_words = 0; + const VirtualSpaceNode* vsn = _first_node; + while (vsn != NULL) { + n++; + vsn->verify_locked(); + total_reserved_words += vsn->word_size(); + total_committed_words += vsn->committed_words(); + vsn = vsn->next(); + } + _nodes_counter.check(n); + _reserved_words_counter.check(total_reserved_words); + _committed_words_counter.check(total_committed_words); } else { - current_virtual_space()->set_next(new_entry); - } - set_current_virtual_space(new_entry); - inc_reserved_words(new_entry->reserved_words()); - inc_committed_words(new_entry->committed_words()); - inc_virtual_space_count(); -#ifdef ASSERT - new_entry->mangle(); -#endif - LogTarget(Trace, gc, metaspace) lt; - if (lt.is_enabled()) { - LogStream ls(lt); - VirtualSpaceNode* vsl = current_virtual_space(); - ResourceMark rm; - vsl->print_on(&ls); + _reserved_words_counter.check(0); + _committed_words_counter.check(0); } } -bool VirtualSpaceList::expand_node_by(VirtualSpaceNode* node, - size_t min_words, - size_t preferred_words) { - size_t before = node->committed_words(); - - bool result = node->expand_by(min_words, preferred_words); - - size_t after = node->committed_words(); - - // after and before can be the same if the memory was pre-committed. - assert(after >= before, "Inconsistency"); - inc_committed_words(after - before); - - return result; +void VirtualSpaceList::verify() const { + MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); + verify_locked(); } +#endif -bool VirtualSpaceList::expand_by(size_t min_words, size_t preferred_words) { - assert_is_aligned(min_words, Metaspace::commit_alignment_words()); - assert_is_aligned(preferred_words, Metaspace::commit_alignment_words()); - assert(min_words <= preferred_words, "Invalid arguments"); - - const char* const class_or_not = (is_class() ? "class" : "non-class"); - - if (!MetaspaceGC::can_expand(min_words, this->is_class())) { - log_trace(gc, metaspace, freelist)("Cannot expand %s virtual space list.", - class_or_not); - return false; - } - - size_t allowed_expansion_words = MetaspaceGC::allowed_expansion(); - if (allowed_expansion_words < min_words) { - log_trace(gc, metaspace, freelist)("Cannot expand %s virtual space list (must try gc first).", - class_or_not); - return false; - } - - size_t max_expansion_words = MIN2(preferred_words, allowed_expansion_words); - - // Commit more memory from the the current virtual space. - bool vs_expanded = expand_node_by(current_virtual_space(), - min_words, - max_expansion_words); - if (vs_expanded) { - log_trace(gc, metaspace, freelist)("Expanded %s virtual space list.", - class_or_not); - return true; - } - log_trace(gc, metaspace, freelist)("%s virtual space list: retire current node.", - class_or_not); - retire_current_virtual_space(); - - // Get another virtual space. - size_t grow_vs_words = MAX2((size_t)VirtualSpaceSize, preferred_words); - grow_vs_words = align_up(grow_vs_words, Metaspace::reserve_alignment_words()); - - if (create_new_virtual_space(grow_vs_words)) { - if (current_virtual_space()->is_pre_committed()) { - // The memory was pre-committed, so we are done here. - assert(min_words <= current_virtual_space()->committed_words(), - "The new VirtualSpace was pre-committed, so it" - "should be large enough to fit the alloc request."); +// Returns true if this pointer is contained in one of our nodes. +bool VirtualSpaceList::contains(const MetaWord* p) const { + const VirtualSpaceNode* vsn = _first_node; + while (vsn != NULL) { + if (vsn->contains(p)) { return true; } - - return expand_node_by(current_virtual_space(), - min_words, - max_expansion_words); + vsn = vsn->next(); } - return false; } -// Given a chunk, calculate the largest possible padding space which -// could be required when allocating it. -static size_t largest_possible_padding_size_for_chunk(size_t chunk_word_size, bool is_class) { - const ChunkIndex chunk_type = get_chunk_type_by_size(chunk_word_size, is_class); - if (chunk_type != HumongousIndex) { - // Normal, non-humongous chunks are allocated at chunk size - // boundaries, so the largest padding space required would be that - // minus the smallest chunk size. - const size_t smallest_chunk_size = is_class ? ClassSpecializedChunk : SpecializedChunk; - return chunk_word_size - smallest_chunk_size; - } else { - // Humongous chunks are allocated at smallest-chunksize - // boundaries, so there is no padding required. - return 0; - } -} - - -Metachunk* VirtualSpaceList::get_new_chunk(size_t chunk_word_size, size_t suggested_commit_granularity) { - - // Allocate a chunk out of the current virtual space. - Metachunk* next = current_virtual_space()->get_chunk_vs(chunk_word_size); - - if (next != NULL) { - return next; - } - - // The expand amount is currently only determined by the requested sizes - // and not how much committed memory is left in the current virtual space. - - // We must have enough space for the requested size and any - // additional reqired padding chunks. - const size_t size_for_padding = largest_possible_padding_size_for_chunk(chunk_word_size, this->is_class()); - - size_t min_word_size = align_up(chunk_word_size + size_for_padding, Metaspace::commit_alignment_words()); - size_t preferred_word_size = align_up(suggested_commit_granularity, Metaspace::commit_alignment_words()); - if (min_word_size >= preferred_word_size) { - // Can happen when humongous chunks are allocated. - preferred_word_size = min_word_size; - } - - bool expanded = expand_by(min_word_size, preferred_word_size); - if (expanded) { - next = current_virtual_space()->get_chunk_vs(chunk_word_size); - assert(next != NULL, "The allocation was expected to succeed after the expansion"); - } - - return next; -} - -void VirtualSpaceList::print_on(outputStream* st, size_t scale) const { - st->print_cr(SIZE_FORMAT " nodes, current node: " PTR_FORMAT, - _virtual_space_count, p2i(_current_virtual_space)); - VirtualSpaceListIterator iter(virtual_space_list()); - while (iter.repeat()) { - st->cr(); - VirtualSpaceNode* node = iter.get_next(); - node->print_on(st, scale); - } -} - -void VirtualSpaceList::print_map(outputStream* st) const { - VirtualSpaceNode* list = virtual_space_list(); - VirtualSpaceListIterator iter(list); - unsigned i = 0; - while (iter.repeat()) { - st->print_cr("Node %u:", i); - VirtualSpaceNode* node = iter.get_next(); - node->print_map(st, this->is_class()); - i ++; +// Returns true if the vslist is not expandable and no more root chunks +// can be allocated. +bool VirtualSpaceList::is_full() const { + if (!_can_expand && _first_node != NULL && _first_node->free_words() == 0) { + return true; } + return false; } -// Given a node, expand range such that it includes the node. -void VirtualSpaceList::expand_envelope_to_include_node(const VirtualSpaceNode* node) { - _envelope_lo = MIN2(_envelope_lo, (address)node->low_boundary()); - _envelope_hi = MAX2(_envelope_hi, (address)node->high_boundary()); +// Convenience methods to return the global class-space chunkmanager +// and non-class chunkmanager, respectively. +VirtualSpaceList* VirtualSpaceList::vslist_class() { + return MetaspaceContext::context_class() == NULL ? NULL : MetaspaceContext::context_class()->vslist(); } - -#ifdef ASSERT -void VirtualSpaceList::verify(bool slow) { - VirtualSpaceNode* list = virtual_space_list(); - VirtualSpaceListIterator iter(list); - size_t reserved = 0; - size_t committed = 0; - size_t node_count = 0; - while (iter.repeat()) { - VirtualSpaceNode* node = iter.get_next(); - if (slow) { - node->verify(true); - } - // Check that the node resides fully within our envelope. - assert((address)node->low_boundary() >= _envelope_lo && (address)node->high_boundary() <= _envelope_hi, - "Node " SIZE_FORMAT " [" PTR_FORMAT ", " PTR_FORMAT ") outside envelope [" PTR_FORMAT ", " PTR_FORMAT ").", - node_count, p2i(node->low_boundary()), p2i(node->high_boundary()), p2i(_envelope_lo), p2i(_envelope_hi)); - reserved += node->reserved_words(); - committed += node->committed_words(); - node_count ++; - } - assert(reserved == reserved_words() && committed == committed_words() && node_count == _virtual_space_count, - "Mismatch: reserved real: " SIZE_FORMAT " expected: " SIZE_FORMAT - ", committed real: " SIZE_FORMAT " expected: " SIZE_FORMAT - ", node count real: " SIZE_FORMAT " expected: " SIZE_FORMAT ".", - reserved, reserved_words(), committed, committed_words(), - node_count, _virtual_space_count); +VirtualSpaceList* VirtualSpaceList::vslist_nonclass() { + return MetaspaceContext::context_nonclass() == NULL ? NULL : MetaspaceContext::context_nonclass()->vslist(); } -#endif // ASSERT } // namespace metaspace diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceList.hpp b/src/hotspot/share/memory/metaspace/virtualSpaceList.hpp index 937d2dcdb31..f105da7426e 100644 --- a/src/hotspot/share/memory/metaspace/virtualSpaceList.hpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceList.hpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,141 +27,121 @@ #define SHARE_MEMORY_METASPACE_VIRTUALSPACELIST_HPP #include "memory/allocation.hpp" +#include "memory/metaspace/commitLimiter.hpp" +#include "memory/metaspace/counters.hpp" #include "memory/metaspace/virtualSpaceNode.hpp" +#include "memory/virtualspace.hpp" #include "utilities/globalDefinitions.hpp" +class outputStream; namespace metaspace { class Metachunk; -class ChunkManager; +class FreeChunkListVector; + +// VirtualSpaceList manages a single (if its non-expandable) or +// a series of (if its expandable) virtual memory regions used +// for metaspace. +// +// Internally it holds a list of nodes (VirtualSpaceNode) each +// managing a single contiguous memory region. The first node of +// this list is the current node and used for allocation of new +// root chunks. +// +// Beyond access to those nodes and the ability to grow new nodes +// (if expandable) it allows for purging: purging this list means +// removing and unmapping all memory regions which are unused. -// List of VirtualSpaces for metadata allocation. class VirtualSpaceList : public CHeapObj { - friend class VirtualSpaceNode; - enum VirtualSpaceSizes { - VirtualSpaceSize = 256 * K - }; + // Name + const char* const _name; - // Head of the list - VirtualSpaceNode* _virtual_space_list; - // virtual space currently being used for allocations - VirtualSpaceNode* _current_virtual_space; + // Head of the list. + VirtualSpaceNode* _first_node; - // Is this VirtualSpaceList used for the compressed class space - bool _is_class; + // Number of nodes (kept for statistics only). + IntCounter _nodes_counter; - // Sum of reserved and committed memory in the virtual spaces - size_t _reserved_words; - size_t _committed_words; + // Whether this list can expand by allocating new nodes. + const bool _can_expand; - // Number of virtual spaces - size_t _virtual_space_count; + // Used to check limits before committing memory. + CommitLimiter* const _commit_limiter; - // Optimization: we keep an address range to quickly exclude pointers - // which are clearly not pointing into metaspace. This is an optimization for - // VirtualSpaceList::contains(). - address _envelope_lo; - address _envelope_hi; + // Statistics - bool is_within_envelope(address p) const { - return p >= _envelope_lo && p < _envelope_hi; - } + // Holds sum of reserved space, in words, over all list nodes. + SizeCounter _reserved_words_counter; - // Given a node, expand range such that it includes the node. - void expand_envelope_to_include_node(const VirtualSpaceNode* node); + // Holds sum of committed space, in words, over all list nodes. + SizeCounter _committed_words_counter; - ~VirtualSpaceList(); + // Create a new node and append it to the list. After + // this function, _current_node shall point to a new empty node. + // List must be expandable for this to work. + void create_new_node(); - VirtualSpaceNode* virtual_space_list() const { return _virtual_space_list; } +public: - void set_virtual_space_list(VirtualSpaceNode* v) { - _virtual_space_list = v; - } - void set_current_virtual_space(VirtualSpaceNode* v) { - _current_virtual_space = v; - } + // Create a new, empty, expandable list. + VirtualSpaceList(const char* name, CommitLimiter* commit_limiter); - void link_vs(VirtualSpaceNode* new_entry); + // Create a new list. The list will contain one node only, which uses the given ReservedSpace. + // It will be not expandable beyond that first node. + VirtualSpaceList(const char* name, ReservedSpace rs, CommitLimiter* commit_limiter); - // Get another virtual space and add it to the list. This - // is typically prompted by a failed attempt to allocate a chunk - // and is typically followed by the allocation of a chunk. - bool create_new_virtual_space(size_t vs_word_size); + virtual ~VirtualSpaceList(); - // Chunk up the unused committed space in the current - // virtual space and add the chunks to the free list. - void retire_current_virtual_space(); + // Allocate a root chunk from this list. + // Note: this just returns a chunk whose memory is reserved; no memory is committed yet. + // Hence, before using this chunk, it must be committed. + // May return NULL if vslist would need to be expanded to hold the new root node but + // the list cannot be expanded (in practice this means we reached CompressedClassSpaceSize). + Metachunk* allocate_root_chunk(); - DEBUG_ONLY(bool contains_node(const VirtualSpaceNode* node) const;) + // Attempts to purge nodes. This will remove and delete nodes which only contain free chunks. + // The free chunks are removed from the freelists before the nodes are deleted. + // Return number of purged nodes. + int purge(FreeChunkListVector* freelists); - public: - VirtualSpaceList(size_t word_size); - VirtualSpaceList(ReservedSpace rs); + //// Statistics //// - size_t free_bytes(); + // Return sum of reserved words in all nodes. + size_t reserved_words() const { return _reserved_words_counter.get(); } - Metachunk* get_new_chunk(size_t chunk_word_size, - size_t suggested_commit_granularity); + // Return sum of committed words in all nodes. + size_t committed_words() const { return _committed_words_counter.get(); } - bool expand_node_by(VirtualSpaceNode* node, - size_t min_words, - size_t preferred_words); + // Return number of nodes in this list. + int num_nodes() const { return _nodes_counter.get(); } - bool expand_by(size_t min_words, - size_t preferred_words); + //// Debug stuff //// + DEBUG_ONLY(void verify() const;) + DEBUG_ONLY(void verify_locked() const;) - VirtualSpaceNode* current_virtual_space() { - return _current_virtual_space; - } + // Print all nodes in this space list. + void print_on(outputStream* st) const; - bool is_class() const { return _is_class; } + // Returns true if this pointer is contained in one of our nodes. + bool contains(const MetaWord* p) const; - bool initialization_succeeded() { return _virtual_space_list != NULL; } + // Returns true if the list is not expandable and no more root chunks + // can be allocated. + bool is_full() const; - size_t reserved_words() { return _reserved_words; } - size_t reserved_bytes() { return reserved_words() * BytesPerWord; } - size_t committed_words() { return _committed_words; } - size_t committed_bytes() { return committed_words() * BytesPerWord; } + // Convenience methods to return the global class-space vslist + // and non-class vslist, respectively. + static VirtualSpaceList* vslist_class(); + static VirtualSpaceList* vslist_nonclass(); - void inc_reserved_words(size_t v); - void dec_reserved_words(size_t v); - void inc_committed_words(size_t v); - void dec_committed_words(size_t v); - void inc_virtual_space_count(); - void dec_virtual_space_count(); + // These exist purely to print limits of the compressed class space; + // if we ever change the ccs to not use a degenerated-list-of-one-node this + // will go away. + MetaWord* base_of_first_node() const { return _first_node != NULL ? _first_node->base() : NULL; } + size_t word_size_of_first_node() const { return _first_node != NULL ? _first_node->word_size() : 0; } - VirtualSpaceNode* find_enclosing_space(const void* ptr); - bool contains(const void* ptr) { return find_enclosing_space(ptr) != NULL; } - - // Unlink empty VirtualSpaceNodes and free it. - void purge(ChunkManager* chunk_manager); - - void print_on(outputStream* st) const { print_on(st, K); } - void print_on(outputStream* st, size_t scale) const; - void print_map(outputStream* st) const; - - DEBUG_ONLY(void verify(bool slow);) - - class VirtualSpaceListIterator : public StackObj { - VirtualSpaceNode* _virtual_spaces; - public: - VirtualSpaceListIterator(VirtualSpaceNode* virtual_spaces) : - _virtual_spaces(virtual_spaces) {} - - bool repeat() { - return _virtual_spaces != NULL; - } - - VirtualSpaceNode* get_next() { - VirtualSpaceNode* result = _virtual_spaces; - if (_virtual_spaces != NULL) { - _virtual_spaces = _virtual_spaces->next(); - } - return result; - } - }; }; } // namespace metaspace diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp index 19faef4ab76..88a6b9fe27c 100644 --- a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,566 +24,450 @@ */ #include "precompiled.hpp" - #include "logging/log.hpp" -#include "logging/logStream.hpp" -#include "memory/metaspace/metachunk.hpp" #include "memory/metaspace.hpp" -#include "memory/metaspace/chunkManager.hpp" -#include "memory/metaspace/metaDebug.hpp" +#include "memory/metaspace/chunkHeaderPool.hpp" +#include "memory/metaspace/chunklevel.hpp" +#include "memory/metaspace/commitLimiter.hpp" +#include "memory/metaspace/counters.hpp" +#include "memory/metaspace/freeChunkList.hpp" +#include "memory/metaspace/internalStats.hpp" +#include "memory/metaspace/metachunk.hpp" #include "memory/metaspace/metaspaceCommon.hpp" -#include "memory/metaspace/occupancyMap.hpp" +#include "memory/metaspace/metaspaceSettings.hpp" +#include "memory/metaspace/rootChunkArea.hpp" +#include "memory/metaspace/runningCounters.hpp" #include "memory/metaspace/virtualSpaceNode.hpp" -#include "memory/virtualspace.hpp" -#include "runtime/atomic.hpp" +#include "runtime/globals.hpp" +#include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" -#include "services/memTracker.hpp" -#include "utilities/copy.hpp" +#include "utilities/align.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" namespace metaspace { -// Decide if large pages should be committed when the memory is reserved. -static bool should_commit_large_pages_when_reserving(size_t bytes) { - if (UseLargePages && UseLargePagesInMetaspace && !os::can_commit_large_page_memory()) { - size_t words = bytes / BytesPerWord; - bool is_class = false; // We never reserve large pages for the class space. - if (MetaspaceGC::can_expand(words, is_class) && - MetaspaceGC::allowed_expansion() >= words) { - return true; - } - } +#define LOGFMT "VsListNode @" PTR_FORMAT " base " PTR_FORMAT " " +#define LOGFMT_ARGS p2i(this), p2i(_base) - return false; +#ifdef ASSERT +void check_pointer_is_aligned_to_commit_granule(const MetaWord* p) { + assert(is_aligned(p, Settings::commit_granule_bytes()), + "Pointer not aligned to commit granule size: " PTR_FORMAT ".", + p2i(p)); } +void check_word_size_is_aligned_to_commit_granule(size_t word_size) { + assert(is_aligned(word_size, Settings::commit_granule_words()), + "Not aligned to commit granule size: " SIZE_FORMAT ".", word_size); +} +#endif + +// Given an address range, ensure it is committed. +// +// The range has to be aligned to granule size. +// +// Function will: +// - check how many granules in that region are uncommitted; If all are committed, it +// returns true immediately. +// - check if committing those uncommitted granules would bring us over the commit limit +// (GC threshold, MaxMetaspaceSize). If true, it returns false. +// - commit the memory. +// - mark the range as committed in the commit mask +// +// Returns true if success, false if it did hit a commit limit. +bool VirtualSpaceNode::commit_range(MetaWord* p, size_t word_size) { + DEBUG_ONLY(check_pointer_is_aligned_to_commit_granule(p);) + DEBUG_ONLY(check_word_size_is_aligned_to_commit_granule(word_size);) + assert_lock_strong(MetaspaceExpand_lock); + + // First calculate how large the committed regions in this range are + const size_t committed_words_in_range = _commit_mask.get_committed_size_in_range(p, word_size); + DEBUG_ONLY(check_word_size_is_aligned_to_commit_granule(committed_words_in_range);) -// byte_size is the size of the associated virtualspace. -VirtualSpaceNode::VirtualSpaceNode(bool is_class, size_t bytes) : - _next(NULL), _is_class(is_class), _rs(), _top(NULL), _container_count(0), _occupancy_map(NULL) { - assert_is_aligned(bytes, Metaspace::reserve_alignment()); - bool large_pages = should_commit_large_pages_when_reserving(bytes); - _rs = ReservedSpace(bytes, Metaspace::reserve_alignment(), large_pages); + // By how much words we would increase commit charge + // were we to commit the given address range completely. + const size_t commit_increase_words = word_size - committed_words_in_range; - if (_rs.is_reserved()) { - assert(_rs.base() != NULL, "Catch if we get a NULL address"); - assert(_rs.size() != 0, "Catch if we get a 0 size"); - assert_is_aligned(_rs.base(), Metaspace::reserve_alignment()); - assert_is_aligned(_rs.size(), Metaspace::reserve_alignment()); + UL2(debug, "committing range " PTR_FORMAT ".." PTR_FORMAT "(" SIZE_FORMAT " words)", + p2i(p), p2i(p + word_size), word_size); - MemTracker::record_virtual_memory_type((address)_rs.base(), mtClass); + if (commit_increase_words == 0) { + UL(debug, "... already fully committed."); + return true; // Already fully committed, nothing to do. } -} -void VirtualSpaceNode::purge(ChunkManager* chunk_manager) { - // When a node is purged, lets give it a thorough examination. - DEBUG_ONLY(verify(true);) - Metachunk* chunk = first_chunk(); - Metachunk* invalid_chunk = (Metachunk*) top(); - while (chunk < invalid_chunk ) { - assert(chunk->is_tagged_free(), "Should be tagged free"); - MetaWord* next = ((MetaWord*)chunk) + chunk->word_size(); - chunk_manager->remove_chunk(chunk); - chunk->remove_sentinel(); - assert(chunk->next() == NULL && - chunk->prev() == NULL, - "Was not removed from its list"); - chunk = (Metachunk*) next; + // Before committing any more memory, check limits. + if (_commit_limiter->possible_expansion_words() < commit_increase_words) { + UL(debug, "... cannot commit (limit)."); + return false; } -} -void VirtualSpaceNode::print_map(outputStream* st, bool is_class) const { + // Commit... + if (os::commit_memory((char*)p, word_size * BytesPerWord, false) == false) { + vm_exit_out_of_memory(word_size * BytesPerWord, OOM_MMAP_ERROR, "Failed to commit metaspace."); + } - if (bottom() == top()) { - return; + if (AlwaysPreTouch) { + os::pretouch_memory(p, p + word_size); } - const size_t spec_chunk_size = is_class ? ClassSpecializedChunk : SpecializedChunk; - const size_t small_chunk_size = is_class ? ClassSmallChunk : SmallChunk; - const size_t med_chunk_size = is_class ? ClassMediumChunk : MediumChunk; + UL2(debug, "... committed " SIZE_FORMAT " additional words.", commit_increase_words); - int line_len = 100; - const size_t section_len = align_up(spec_chunk_size * line_len, med_chunk_size); - line_len = (int)(section_len / spec_chunk_size); + // ... tell commit limiter... + _commit_limiter->increase_committed(commit_increase_words); - static const int NUM_LINES = 4; + // ... update counters in containing vslist ... + _total_committed_words_counter->increment_by(commit_increase_words); - char* lines[NUM_LINES]; - for (int i = 0; i < NUM_LINES; i ++) { - lines[i] = (char*)os::malloc(line_len, mtInternal); - } - int pos = 0; - const MetaWord* p = bottom(); - const Metachunk* chunk = (const Metachunk*)p; - const MetaWord* chunk_end = p + chunk->word_size(); - while (p < top()) { - if (pos == line_len) { - pos = 0; - for (int i = 0; i < NUM_LINES; i ++) { - st->fill_to(22); - st->print_raw(lines[i], line_len); - st->cr(); - } - } - if (pos == 0) { - st->print(PTR_FORMAT ":", p2i(p)); - } - if (p == chunk_end) { - chunk = (Metachunk*)p; - chunk_end = p + chunk->word_size(); - } - // line 1: chunk starting points (a dot if that area is a chunk start). - lines[0][pos] = p == (const MetaWord*)chunk ? '.' : ' '; - - // Line 2: chunk type (x=spec, s=small, m=medium, h=humongous), uppercase if - // chunk is in use. - const bool chunk_is_free = ((Metachunk*)chunk)->is_tagged_free(); - if (chunk->word_size() == spec_chunk_size) { - lines[1][pos] = chunk_is_free ? 'x' : 'X'; - } else if (chunk->word_size() == small_chunk_size) { - lines[1][pos] = chunk_is_free ? 's' : 'S'; - } else if (chunk->word_size() == med_chunk_size) { - lines[1][pos] = chunk_is_free ? 'm' : 'M'; - } else if (chunk->word_size() > med_chunk_size) { - lines[1][pos] = chunk_is_free ? 'h' : 'H'; - } else { - ShouldNotReachHere(); - } + // ... and update the commit mask. + _commit_mask.mark_range_as_committed(p, word_size); - // Line 3: chunk origin - const ChunkOrigin origin = chunk->get_origin(); - lines[2][pos] = origin == origin_normal ? ' ' : '0' + (int) origin; +#ifdef ASSERT + // The commit boundary maintained in the CommitLimiter should be equal the sum of committed words + // in both class and non-class vslist (outside gtests). + if (_commit_limiter == CommitLimiter::globalLimiter()) { + assert(_commit_limiter->committed_words() == RunningCounters::committed_words(), "counter mismatch"); + } +#endif - // Line 4: Virgin chunk? Virgin chunks are chunks created as a byproduct of padding or splitting, - // but were never used. - lines[3][pos] = chunk->get_use_count() > 0 ? ' ' : 'v'; + InternalStats::inc_num_space_committed(); + return true; +} - p += spec_chunk_size; - pos ++; - } - if (pos > 0) { - for (int i = 0; i < NUM_LINES; i ++) { - st->fill_to(22); - st->print_raw(lines[i], line_len); - st->cr(); - } - } - for (int i = 0; i < NUM_LINES; i ++) { - os::free(lines[i]); - } +// Given an address range, ensure it is committed. +// +// The range does not have to be aligned to granule size. However, the function will always commit +// whole granules. +// +// Function will: +// - check how many granules in that region are uncommitted; If all are committed, it +// returns true immediately. +// - check if committing those uncommitted granules would bring us over the commit limit +// (GC threshold, MaxMetaspaceSize). If true, it returns false. +// - commit the memory. +// - mark the range as committed in the commit mask +// +// !! Careful: +// calling ensure_range_is_committed on a range which contains both committed and uncommitted +// areas will commit the whole area, thus erase the content in the existing committed parts. +// Make sure you never call this on an address range containing live data. !! +// +// Returns true if success, false if it did hit a commit limit. +bool VirtualSpaceNode::ensure_range_is_committed(MetaWord* p, size_t word_size) { + assert_lock_strong(MetaspaceExpand_lock); + assert(p != NULL && word_size > 0, "Sanity"); + MetaWord* p_start = align_down(p, Settings::commit_granule_bytes()); + MetaWord* p_end = align_up(p + word_size, Settings::commit_granule_bytes()); + return commit_range(p_start, p_end - p_start); } +// Given an address range (which has to be aligned to commit granule size): +// - uncommit it +// - mark it as uncommitted in the commit mask +void VirtualSpaceNode::uncommit_range(MetaWord* p, size_t word_size) { + DEBUG_ONLY(check_pointer_is_aligned_to_commit_granule(p);) + DEBUG_ONLY(check_word_size_is_aligned_to_commit_granule(word_size);) + assert_lock_strong(MetaspaceExpand_lock); -#ifdef ASSERT + // First calculate how large the committed regions in this range are + const size_t committed_words_in_range = _commit_mask.get_committed_size_in_range(p, word_size); + DEBUG_ONLY(check_word_size_is_aligned_to_commit_granule(committed_words_in_range);) -// Verify counters, all chunks in this list node and the occupancy map. -void VirtualSpaceNode::verify(bool slow) { - log_trace(gc, metaspace, freelist)("verifying %s virtual space node (%s).", - (is_class() ? "class space" : "metaspace"), (slow ? "slow" : "quick")); - // Fast mode: just verify chunk counters and basic geometry - // Slow mode: verify chunks and occupancy map - uintx num_in_use_chunks = 0; - Metachunk* chunk = first_chunk(); - Metachunk* invalid_chunk = (Metachunk*) top(); - - // Iterate the chunks in this node and verify each chunk. - while (chunk < invalid_chunk ) { - if (slow) { - do_verify_chunk(chunk); - } - if (!chunk->is_tagged_free()) { - num_in_use_chunks ++; - } - const size_t s = chunk->word_size(); - // Prevent endless loop on invalid chunk size. - assert(is_valid_chunksize(is_class(), s), "Invalid chunk size: " SIZE_FORMAT ".", s); - MetaWord* next = ((MetaWord*)chunk) + s; - chunk = (Metachunk*) next; - } - assert(_container_count == num_in_use_chunks, "Container count mismatch (real: " UINTX_FORMAT - ", counter: " UINTX_FORMAT ".", num_in_use_chunks, _container_count); - // Also verify the occupancy map. - if (slow) { - occupancy_map()->verify(bottom(), top()); + UL2(debug, "uncommitting range " PTR_FORMAT ".." PTR_FORMAT "(" SIZE_FORMAT " words)", + p2i(p), p2i(p + word_size), word_size); + + if (committed_words_in_range == 0) { + UL(debug, "... already fully uncommitted."); + return; // Already fully uncommitted, nothing to do. } -} -// Verify that all free chunks in this node are ideally merged -// (there not should be multiple small chunks where a large chunk could exist.) -void VirtualSpaceNode::verify_free_chunks_are_ideally_merged() { - Metachunk* chunk = first_chunk(); - Metachunk* invalid_chunk = (Metachunk*) top(); - // Shorthands. - const size_t size_med = (is_class() ? ClassMediumChunk : MediumChunk) * BytesPerWord; - const size_t size_small = (is_class() ? ClassSmallChunk : SmallChunk) * BytesPerWord; - int num_free_chunks_since_last_med_boundary = -1; - int num_free_chunks_since_last_small_boundary = -1; - bool error = false; - char err[256]; - while (!error && chunk < invalid_chunk ) { - // Test for missed chunk merge opportunities: count number of free chunks since last chunk boundary. - // Reset the counter when encountering a non-free chunk. - if (chunk->get_chunk_type() != HumongousIndex) { - if (chunk->is_tagged_free()) { - // Count successive free, non-humongous chunks. - if (is_aligned(chunk, size_small)) { - if (num_free_chunks_since_last_small_boundary > 0) { - error = true; - jio_snprintf(err, sizeof(err), "Missed chunk merge opportunity to merge a small chunk preceding " PTR_FORMAT ".", p2i(chunk)); - } else { - num_free_chunks_since_last_small_boundary = 0; - } - } else if (num_free_chunks_since_last_small_boundary != -1) { - num_free_chunks_since_last_small_boundary ++; - } - if (is_aligned(chunk, size_med)) { - if (num_free_chunks_since_last_med_boundary > 0) { - error = true; - jio_snprintf(err, sizeof(err), "Missed chunk merge opportunity to merge a medium chunk preceding " PTR_FORMAT ".", p2i(chunk)); - } else { - num_free_chunks_since_last_med_boundary = 0; - } - } else if (num_free_chunks_since_last_med_boundary != -1) { - num_free_chunks_since_last_med_boundary ++; - } - } else { - // Encountering a non-free chunk, reset counters. - num_free_chunks_since_last_med_boundary = -1; - num_free_chunks_since_last_small_boundary = -1; - } - } else { - // One cannot merge areas with a humongous chunk in the middle. Reset counters. - num_free_chunks_since_last_med_boundary = -1; - num_free_chunks_since_last_small_boundary = -1; - } + // Uncommit... + if (os::uncommit_memory((char*)p, word_size * BytesPerWord) == false) { + // Note: this can actually happen, since uncommit may increase the number of mappings. + fatal("Failed to uncommit metaspace."); + } - if (error) { - print_map(tty, is_class()); - fatal("%s", err); - } + UL2(debug, "... uncommitted " SIZE_FORMAT " words.", committed_words_in_range); - MetaWord* next = ((MetaWord*)chunk) + chunk->word_size(); - chunk = (Metachunk*) next; - } -} -#endif // ASSERT + // ... tell commit limiter... + _commit_limiter->decrease_committed(committed_words_in_range); -void VirtualSpaceNode::inc_container_count() { - assert_lock_strong(MetaspaceExpand_lock); - _container_count++; -} + // ... and global counters... + _total_committed_words_counter->decrement_by(committed_words_in_range); -void VirtualSpaceNode::dec_container_count() { - assert_lock_strong(MetaspaceExpand_lock); - _container_count--; -} + // ... and update the commit mask. + _commit_mask.mark_range_as_uncommitted(p, word_size); -VirtualSpaceNode::~VirtualSpaceNode() { - _rs.release(); - if (_occupancy_map != NULL) { - delete _occupancy_map; - } #ifdef ASSERT - size_t word_size = sizeof(*this) / BytesPerWord; - Copy::fill_to_words((HeapWord*) this, word_size, 0xf1f1f1f1); + // The commit boundary maintained in the CommitLimiter should be equal the sum of committed words + // in both class and non-class vslist (outside gtests). + if (_commit_limiter == CommitLimiter::globalLimiter()) { // We are outside a test scenario + assert(_commit_limiter->committed_words() == RunningCounters::committed_words(), "counter mismatch"); + } #endif + InternalStats::inc_num_space_uncommitted(); } -size_t VirtualSpaceNode::used_words_in_vs() const { - return pointer_delta(top(), bottom(), sizeof(MetaWord)); +//// creation, destruction //// + +VirtualSpaceNode::VirtualSpaceNode(ReservedSpace rs, bool owns_rs, CommitLimiter* limiter, + SizeCounter* reserve_counter, SizeCounter* commit_counter) : + _next(NULL), + _rs(rs), + _owns_rs(owns_rs), + _base((MetaWord*)rs.base()), + _word_size(rs.size() / BytesPerWord), + _used_words(0), + _commit_mask((MetaWord*)rs.base(), rs.size() / BytesPerWord), + _root_chunk_area_lut((MetaWord*)rs.base(), rs.size() / BytesPerWord), + _commit_limiter(limiter), + _total_reserved_words_counter(reserve_counter), + _total_committed_words_counter(commit_counter) +{ + UL2(debug, "born (word_size " SIZE_FORMAT ").", _word_size); + + // Update reserved counter in vslist + _total_reserved_words_counter->increment_by(_word_size); + + assert_is_aligned(_base, chunklevel::MAX_CHUNK_BYTE_SIZE); + assert_is_aligned(_word_size, chunklevel::MAX_CHUNK_WORD_SIZE); } -// Space committed in the VirtualSpace -size_t VirtualSpaceNode::capacity_words_in_vs() const { - return pointer_delta(end(), bottom(), sizeof(MetaWord)); +// Create a node of a given size (it will create its own space). +VirtualSpaceNode* VirtualSpaceNode::create_node(size_t word_size, + CommitLimiter* limiter, SizeCounter* reserve_words_counter, + SizeCounter* commit_words_counter) +{ + DEBUG_ONLY(assert_is_aligned(word_size, chunklevel::MAX_CHUNK_WORD_SIZE);) + ReservedSpace rs(word_size * BytesPerWord, + Settings::virtual_space_node_reserve_alignment_words() * BytesPerWord, + false // large + ); + if (!rs.is_reserved()) { + vm_exit_out_of_memory(word_size * BytesPerWord, OOM_MMAP_ERROR, "Failed to reserve memory for metaspace"); + } + assert_is_aligned(rs.base(), chunklevel::MAX_CHUNK_BYTE_SIZE); + InternalStats::inc_num_vsnodes_births(); + return new VirtualSpaceNode(rs, true, limiter, reserve_words_counter, commit_words_counter); } -size_t VirtualSpaceNode::free_words_in_vs() const { - return pointer_delta(end(), top(), sizeof(MetaWord)); +// Create a node over an existing space +VirtualSpaceNode* VirtualSpaceNode::create_node(ReservedSpace rs, CommitLimiter* limiter, + SizeCounter* reserve_words_counter, SizeCounter* commit_words_counter) +{ + InternalStats::inc_num_vsnodes_births(); + return new VirtualSpaceNode(rs, false, limiter, reserve_words_counter, commit_words_counter); } -// Given an address larger than top(), allocate padding chunks until top is at the given address. -void VirtualSpaceNode::allocate_padding_chunks_until_top_is_at(MetaWord* target_top) { - - assert(target_top > top(), "Sanity"); - - // Padding chunks are added to the freelist. - ChunkManager* const chunk_manager = Metaspace::get_chunk_manager(is_class()); - - // shorthands - const size_t spec_word_size = chunk_manager->specialized_chunk_word_size(); - const size_t small_word_size = chunk_manager->small_chunk_word_size(); - const size_t med_word_size = chunk_manager->medium_chunk_word_size(); - - while (top() < target_top) { +VirtualSpaceNode::~VirtualSpaceNode() { + DEBUG_ONLY(verify_locked();) - // We could make this coding more generic, but right now we only deal with two possible chunk sizes - // for padding chunks, so it is not worth it. - size_t padding_chunk_word_size = small_word_size; - if (is_aligned(top(), small_word_size * sizeof(MetaWord)) == false) { - assert_is_aligned(top(), spec_word_size * sizeof(MetaWord)); // Should always hold true. - padding_chunk_word_size = spec_word_size; - } - MetaWord* here = top(); - assert_is_aligned(here, padding_chunk_word_size * sizeof(MetaWord)); - inc_top(padding_chunk_word_size); - - // Create new padding chunk. - ChunkIndex padding_chunk_type = get_chunk_type_by_size(padding_chunk_word_size, is_class()); - assert(padding_chunk_type == SpecializedIndex || padding_chunk_type == SmallIndex, "sanity"); - - Metachunk* const padding_chunk = - ::new (here) Metachunk(padding_chunk_type, is_class(), padding_chunk_word_size, this); - assert(padding_chunk == (Metachunk*)here, "Sanity"); - DEBUG_ONLY(padding_chunk->set_origin(origin_pad);) - log_trace(gc, metaspace, freelist)("Created padding chunk in %s at " - PTR_FORMAT ", size " SIZE_FORMAT_HEX ".", - (is_class() ? "class space " : "metaspace"), - p2i(padding_chunk), padding_chunk->word_size() * sizeof(MetaWord)); - - // Mark chunk start in occupancy map. - occupancy_map()->set_chunk_starts_at_address((MetaWord*)padding_chunk, true); - - // Chunks are born as in-use (see MetaChunk ctor). So, before returning - // the padding chunk to its chunk manager, mark it as in use (ChunkManager - // will assert that). - do_update_in_use_info_for_chunk(padding_chunk, true); - - // Return Chunk to freelist. - inc_container_count(); - chunk_manager->return_single_chunk(padding_chunk); - // Please note: at this point, ChunkManager::return_single_chunk() - // may already have merged the padding chunk with neighboring chunks, so - // it may have vanished at this point. Do not reference the padding - // chunk beyond this point. + UL(debug, ": dies."); + if (_owns_rs) { + _rs.release(); } - assert(top() == target_top, "Sanity"); - -} // allocate_padding_chunks_until_top_is_at() - -// Allocates the chunk from the virtual space only. -// This interface is also used internally for debugging. Not all -// chunks removed here are necessarily used for allocation. -Metachunk* VirtualSpaceNode::take_from_committed(size_t chunk_word_size) { - // Non-humongous chunks are to be allocated aligned to their chunk - // size. So, start addresses of medium chunks are aligned to medium - // chunk size, those of small chunks to small chunk size and so - // forth. This facilitates merging of free chunks and reduces - // fragmentation. Chunk sizes are spec < small < medium, with each - // larger chunk size being a multiple of the next smaller chunk - // size. - // Because of this alignment, me may need to create a number of padding - // chunks. These chunks are created and added to the freelist. - - // The chunk manager to which we will give our padding chunks. - ChunkManager* const chunk_manager = Metaspace::get_chunk_manager(is_class()); - - // shorthands - const size_t spec_word_size = chunk_manager->specialized_chunk_word_size(); - const size_t small_word_size = chunk_manager->small_chunk_word_size(); - const size_t med_word_size = chunk_manager->medium_chunk_word_size(); - - assert(chunk_word_size == spec_word_size || chunk_word_size == small_word_size || - chunk_word_size >= med_word_size, "Invalid chunk size requested."); - - // Chunk alignment (in bytes) == chunk size unless humongous. - // Humongous chunks are aligned to the smallest chunk size (spec). - const size_t required_chunk_alignment = (chunk_word_size > med_word_size ? - spec_word_size : chunk_word_size) * sizeof(MetaWord); - - // Do we have enough space to create the requested chunk plus - // any padding chunks needed? - MetaWord* const next_aligned = - static_cast(align_up(top(), required_chunk_alignment)); - if (!is_available((next_aligned - top()) + chunk_word_size)) { - return NULL; - } + // Update counters in vslist + size_t committed = committed_words(); + _total_committed_words_counter->decrement_by(committed); + _total_reserved_words_counter->decrement_by(_word_size); - // Before allocating the requested chunk, allocate padding chunks if necessary. - // We only need to do this for small or medium chunks: specialized chunks are the - // smallest size, hence always aligned. Homungous chunks are allocated unaligned - // (implicitly, also aligned to smallest chunk size). - if ((chunk_word_size == med_word_size || chunk_word_size == small_word_size) && next_aligned > top()) { - log_trace(gc, metaspace, freelist)("Creating padding chunks in %s between %p and %p...", - (is_class() ? "class space " : "metaspace"), - top(), next_aligned); - allocate_padding_chunks_until_top_is_at(next_aligned); - // Now, top should be aligned correctly. - assert_is_aligned(top(), required_chunk_alignment); - } + // ... and tell commit limiter + _commit_limiter->decrease_committed(committed); - // Now, top should be aligned correctly. - assert_is_aligned(top(), required_chunk_alignment); - - // Bottom of the new chunk - MetaWord* chunk_limit = top(); - assert(chunk_limit != NULL, "Not safe to call this method"); - - // The virtual spaces are always expanded by the - // commit granularity to enforce the following condition. - // Without this the is_available check will not work correctly. - assert(_virtual_space.committed_size() == _virtual_space.actual_committed_size(), - "The committed memory doesn't match the expanded memory."); - - if (!is_available(chunk_word_size)) { - LogTarget(Trace, gc, metaspace, freelist) lt; - if (lt.is_enabled()) { - LogStream ls(lt); - ls.print("VirtualSpaceNode::take_from_committed() not available " SIZE_FORMAT " words ", chunk_word_size); - // Dump some information about the virtual space that is nearly full - print_on(&ls); - } - return NULL; - } + InternalStats::inc_num_vsnodes_deaths(); +} - // Take the space (bump top on the current virtual space). - inc_top(chunk_word_size); +//// Chunk allocation, splitting, merging ///// - // Initialize the chunk - ChunkIndex chunk_type = get_chunk_type_by_size(chunk_word_size, is_class()); - Metachunk* result = ::new (chunk_limit) Metachunk(chunk_type, is_class(), chunk_word_size, this); - assert(result == (Metachunk*)chunk_limit, "Sanity"); - occupancy_map()->set_chunk_starts_at_address((MetaWord*)result, true); - do_update_in_use_info_for_chunk(result, true); +// Allocate a root chunk from this node. Will fail and return NULL if the node is full +// - if we used up the whole address space of this node's memory region. +// (in case this node backs compressed class space, this is how we hit +// CompressedClassSpaceSize). +// Note that this just returns reserved memory; caller must take care of committing this +// chunk before using it. +Metachunk* VirtualSpaceNode::allocate_root_chunk() { + assert_lock_strong(MetaspaceExpand_lock); + assert_is_aligned(free_words(), chunklevel::MAX_CHUNK_WORD_SIZE); - inc_container_count(); + if (free_words() >= chunklevel::MAX_CHUNK_WORD_SIZE) { -#ifdef ASSERT - EVERY_NTH(VerifyMetaspaceInterval) - chunk_manager->locked_verify(true); - verify(true); - END_EVERY_NTH - do_verify_chunk(result); -#endif + MetaWord* loc = _base + _used_words; + _used_words += chunklevel::MAX_CHUNK_WORD_SIZE; - result->inc_use_count(); + RootChunkArea* rca = _root_chunk_area_lut.get_area_by_address(loc); - return result; + // Create a root chunk header and initialize it; + Metachunk* c = rca->alloc_root_chunk_header(this); + assert(c->base() == loc && c->vsnode() == this && + c->is_free(), "Sanity"); + DEBUG_ONLY(c->verify();) + + UL2(debug, "new root chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(c)); + return c; + } + return NULL; // Node is full. } +// Given a chunk c, split it recursively until you get a chunk of the given target_level. +// +// The resulting target chunk resides at the same address as the original chunk. +// The resulting splinters are added to freelists. +void VirtualSpaceNode::split(chunklevel_t target_level, Metachunk* c, FreeChunkListVector* freelists) { + assert_lock_strong(MetaspaceExpand_lock); + // Get the area associated with this chunk and let it handle the splitting + RootChunkArea* rca = _root_chunk_area_lut.get_area_by_address(c->base()); + DEBUG_ONLY(rca->verify_area_is_ideally_merged();) + rca->split(target_level, c, freelists); +} -// Expand the virtual space (commit more of the reserved space) -bool VirtualSpaceNode::expand_by(size_t min_words, size_t preferred_words) { - size_t min_bytes = min_words * BytesPerWord; - size_t preferred_bytes = preferred_words * BytesPerWord; +// Given a chunk, attempt to merge it recursively with its neighboring chunks. +// +// If successful (merged at least once), returns address of +// the merged chunk; NULL otherwise. +// +// The merged chunks are removed from the freelists. +// +// !!! Please note that if this method returns a non-NULL value, the +// original chunk will be invalid and should not be accessed anymore! !!! +Metachunk* VirtualSpaceNode::merge(Metachunk* c, FreeChunkListVector* freelists) { + assert(c != NULL && c->is_free(), "Sanity"); + assert_lock_strong(MetaspaceExpand_lock); - size_t uncommitted = virtual_space()->reserved_size() - virtual_space()->actual_committed_size(); + // Get the rca associated with this chunk and let it handle the merging + RootChunkArea* rca = _root_chunk_area_lut.get_area_by_address(c->base()); + Metachunk* c2 = rca->merge(c, freelists); + DEBUG_ONLY(rca->verify_area_is_ideally_merged();) + return c2; +} - if (uncommitted < min_bytes) { - return false; - } +// Given a chunk c, which must be "in use" and must not be a root chunk, attempt to +// enlarge it in place by claiming its trailing buddy. +// +// This will only work if c is the leader of the buddy pair and the trailing buddy is free. +// +// If successful, the follower chunk will be removed from the freelists, the leader chunk c will +// double in size (level decreased by one). +// +// On success, true is returned, false otherwise. +bool VirtualSpaceNode::attempt_enlarge_chunk(Metachunk* c, FreeChunkListVector* freelists) { + assert(c != NULL && c->is_in_use() && !c->is_root_chunk(), "Sanity"); + assert_lock_strong(MetaspaceExpand_lock); - size_t commit = MIN2(preferred_bytes, uncommitted); - bool result = virtual_space()->expand_by(commit, false); + // Get the rca associated with this chunk and let it handle the merging + RootChunkArea* rca = _root_chunk_area_lut.get_area_by_address(c->base()); - if (result) { - log_trace(gc, metaspace, freelist)("Expanded %s virtual space list node by " SIZE_FORMAT " words.", - (is_class() ? "class" : "non-class"), commit); - DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_committed_space_expanded)); - } else { - log_trace(gc, metaspace, freelist)("Failed to expand %s virtual space list node by " SIZE_FORMAT " words.", - (is_class() ? "class" : "non-class"), commit); + bool rc = rca->attempt_enlarge_chunk(c, freelists); + DEBUG_ONLY(rca->verify_area_is_ideally_merged();) + if (rc) { + InternalStats::inc_num_chunks_enlarged(); } - assert(result, "Failed to commit memory"); - - return result; + return rc; } -Metachunk* VirtualSpaceNode::get_chunk_vs(size_t chunk_word_size) { +// Attempts to purge the node: +// +// If all chunks living in this node are free, they will all be removed from +// the freelist they currently reside in. Then, the node will be deleted. +// +// Returns true if the node has been deleted, false if not. +// !! If this returns true, do not access the node from this point on. !! +bool VirtualSpaceNode::attempt_purge(FreeChunkListVector* freelists) { assert_lock_strong(MetaspaceExpand_lock); - Metachunk* result = take_from_committed(chunk_word_size); - return result; -} -bool VirtualSpaceNode::initialize() { - - if (!_rs.is_reserved()) { + if (!_owns_rs) { + // We do not allow purging of nodes if we do not own the + // underlying ReservedSpace (CompressClassSpace case). return false; } - // These are necessary restriction to make sure that the virtual space always - // grows in steps of Metaspace::commit_alignment(). If both base and size are - // aligned only the middle alignment of the VirtualSpace is used. - assert_is_aligned(_rs.base(), Metaspace::commit_alignment()); - assert_is_aligned(_rs.size(), Metaspace::commit_alignment()); - - // ReservedSpaces marked as special will have the entire memory - // pre-committed. Setting a committed size will make sure that - // committed_size and actual_committed_size agrees. - size_t pre_committed_size = _rs.special() ? _rs.size() : 0; - - bool result = virtual_space()->initialize_with_granularity(_rs, pre_committed_size, - Metaspace::commit_alignment()); - if (result) { - assert(virtual_space()->committed_size() == virtual_space()->actual_committed_size(), - "Checking that the pre-committed memory was registered by the VirtualSpace"); + // First find out if all areas are empty. Since empty chunks collapse to root chunk + // size, if all chunks in this node are free root chunks we are good to go. + if (!_root_chunk_area_lut.is_free()) { + return false; + } - set_top((MetaWord*)virtual_space()->low()); + UL(debug, ": purging."); + + // Okay, we can purge. Before we can do this, we need to remove all chunks from the freelist. + for (int narea = 0; narea < _root_chunk_area_lut.number_of_areas(); narea++) { + RootChunkArea* ra = _root_chunk_area_lut.get_area_by_index(narea); + Metachunk* c = ra->first_chunk(); + if (c != NULL) { + UL2(trace, "removing chunk from to-be-purged node: " + METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(c)); + assert(c->is_free() && c->is_root_chunk(), "Sanity"); + freelists->remove(c); + } } - // Initialize Occupancy Map. - const size_t smallest_chunk_size = is_class() ? ClassSpecializedChunk : SpecializedChunk; - _occupancy_map = new OccupancyMap(bottom(), reserved_words(), smallest_chunk_size); + // Now, delete the node, then right away return since this object is invalid. + delete this; - return result; + return true; } -void VirtualSpaceNode::print_on(outputStream* st, size_t scale) const { - size_t used_words = used_words_in_vs(); - size_t commit_words = committed_words(); - size_t res_words = reserved_words(); - VirtualSpace* vs = virtual_space(); +void VirtualSpaceNode::print_on(outputStream* st) const { + size_t scale = K; - st->print("node @" PTR_FORMAT ": ", p2i(this)); + st->print("base " PTR_FORMAT ": ", p2i(base())); st->print("reserved="); - print_scaled_words(st, res_words, scale); + print_scaled_words(st, word_size(), scale); st->print(", committed="); - print_scaled_words_and_percentage(st, commit_words, res_words, scale); + print_scaled_words_and_percentage(st, committed_words(), word_size(), scale); st->print(", used="); - print_scaled_words_and_percentage(st, used_words, res_words, scale); + print_scaled_words_and_percentage(st, used_words(), word_size(), scale); + st->cr(); - st->print(" [" PTR_FORMAT ", " PTR_FORMAT ", " - PTR_FORMAT ", " PTR_FORMAT ")", - p2i(bottom()), p2i(top()), p2i(end()), - p2i(vs->high_boundary())); + _root_chunk_area_lut.print_on(st); + _commit_mask.print_on(st); } -#ifdef ASSERT -void VirtualSpaceNode::mangle() { - size_t word_size = capacity_words_in_vs(); - Copy::fill_to_words((HeapWord*) low(), word_size, 0xf1f1f1f1); +// Returns size, in words, of committed space in this node alone. +// Note: iterates over commit mask and hence may be a tad expensive on large nodes. +size_t VirtualSpaceNode::committed_words() const { + return _commit_mask.get_committed_size(); } -#endif // ASSERT -void VirtualSpaceNode::retire(ChunkManager* chunk_manager) { - assert(is_class() == chunk_manager->is_class(), "Wrong ChunkManager?"); #ifdef ASSERT - verify(false); - EVERY_NTH(VerifyMetaspaceInterval) - verify(true); - END_EVERY_NTH -#endif - for (int i = (int)MediumIndex; i >= (int)ZeroIndex; --i) { - ChunkIndex index = (ChunkIndex)i; - size_t chunk_size = chunk_manager->size_by_index(index); - - while (free_words_in_vs() >= chunk_size) { - Metachunk* chunk = get_chunk_vs(chunk_size); - // Chunk will be allocated aligned, so allocation may require - // additional padding chunks. That may cause above allocation to - // fail. Just ignore the failed allocation and continue with the - // next smaller chunk size. As the VirtualSpaceNode comitted - // size should be a multiple of the smallest chunk size, we - // should always be able to fill the VirtualSpace completely. - if (chunk == NULL) { - break; +void VirtualSpaceNode::verify() const { + MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); + verify_locked(); +} + +volatile int test_access = 0; + +// Verify counters and basic structure. Slow mode: verify all chunks in depth +void VirtualSpaceNode::verify_locked() const { + assert_lock_strong(MetaspaceExpand_lock); + assert(base() != NULL, "Invalid base"); + assert(base() == (MetaWord*)_rs.base() && + word_size() == _rs.size() / BytesPerWord, + "Sanity"); + assert_is_aligned(base(), chunklevel::MAX_CHUNK_BYTE_SIZE); + assert(used_words() <= word_size(), "Sanity"); + // Since we only ever hand out root chunks from a vsnode, top should always be aligned + // to root chunk size. + assert_is_aligned(used_words(), chunklevel::MAX_CHUNK_WORD_SIZE); + + _commit_mask.verify(); + + // Verify memory against commit mask. + SOMETIMES( + for (MetaWord* p = base(); p < base() + used_words(); p += os::vm_page_size()) { + if (_commit_mask.is_committed_address(p)) { + test_access += *(int*)p; } - chunk_manager->return_single_chunk(chunk); } - } - assert(free_words_in_vs() == 0, "should be empty now"); + ) + + assert(committed_words() <= word_size(), "Sanity"); + assert_is_aligned(committed_words(), Settings::commit_granule_words()); + _root_chunk_area_lut.verify(); } +#endif + } // namespace metaspace diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceNode.hpp b/src/hotspot/share/memory/metaspace/virtualSpaceNode.hpp index b80a4f5339a..f3391cddc1a 100644 --- a/src/hotspot/share/memory/metaspace/virtualSpaceNode.hpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceNode.hpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +26,14 @@ #ifndef SHARE_MEMORY_METASPACE_VIRTUALSPACENODE_HPP #define SHARE_MEMORY_METASPACE_VIRTUALSPACENODE_HPP -#include "memory/virtualspace.hpp" +#include "memory/allocation.hpp" #include "memory/memRegion.hpp" +#include "memory/metaspace/commitMask.hpp" +#include "memory/metaspace/counters.hpp" +#include "memory/metaspace/metaspaceSettings.hpp" +#include "memory/metaspace/rootChunkArea.hpp" +#include "memory/virtualspace.hpp" +#include "utilities/bitMap.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -34,127 +41,246 @@ class outputStream; namespace metaspace { -class Metachunk; -class ChunkManager; -class OccupancyMap; +class CommitLimiter; +class FreeChunkListVector; + +// VirtualSpaceNode manages a single contiguous address range of metaspace. Logically that memory +// region is split up into a sequence of "root chunk areas", each one containing one root chunk +// or splinters of a root chunk. +// +// The underlying memory is also logically divided into a number of "commit granules", units of memory +// which may be committed or uncommitted independently from each other. +// +// (Both root chunk areas and commit granules have not much to do with each other - one is a way to +// reserve memory for the upper regions, see ChunkManager. One is a way to manage commited memory.) +// +// VirtualSpaceNode: +// - exposes a function to allocate a new root chunk (see VirtualSpaceNode::allocate_root_chunk()). +// +// - knows about the commit state of the memory region - which commit granule are committed, which +// are not. It exposes functions to commit and uncommit regions (without actively committing +// itself) +// +// - It has a reference to a "CommitLimiter", an interface to query whether committing is +// possible. That interface hides the various ways committing may be limited (GC threshold, +// MaxMetaspaceSize, ...) +// +// - It uses ReservedSpace to reserve its memory. It either owns the ReservedSpace or that +// space got handed in from outside (ccs). +// +// +// +// +// | root chunk area | root chunk area | root chunk area | <-- root chunk areas +// +// +-----------------------------------------------------------------------------------------------+ +// | | +// | `VirtualSpaceNode` memory | +// | | +// +-----------------------------------------------------------------------------------------------+ +// +// |x| |x|x|x| | | | |x|x|x| | | |x|x| | | |x|x|x|x| | | | | | | | |x| | | |x|x|x|x| | | |x| | | |x| <-- commit granules +// +// (x = committed) +// -// A VirtualSpaceList node. class VirtualSpaceNode : public CHeapObj { - friend class VirtualSpaceList; // Link to next VirtualSpaceNode VirtualSpaceNode* _next; - // Whether this node is contained in class or metaspace. - const bool _is_class; - - // total in the VirtualSpace + // The underlying space. This has been either created by this node + // and is owned by it, or has been handed in from outside (e.g. in + // case of CompressedClassSpace). ReservedSpace _rs; - VirtualSpace _virtual_space; - MetaWord* _top; - // count of chunks contained in this VirtualSpace - uintx _container_count; - - OccupancyMap* _occupancy_map; - // Convenience functions to access the _virtual_space - char* low() const { return virtual_space()->low(); } - char* high() const { return virtual_space()->high(); } - char* low_boundary() const { return virtual_space()->low_boundary(); } - char* high_boundary() const { return virtual_space()->high_boundary(); } + // True if the node owns the reserved space, false if not. + const bool _owns_rs; - // The first Metachunk will be allocated at the bottom of the - // VirtualSpace - Metachunk* first_chunk() { return (Metachunk*) bottom(); } + // Start pointer of the area. + MetaWord* const _base; - // Committed but unused space in the virtual space - size_t free_words_in_vs() const; + // Size, in words, of the whole node + const size_t _word_size; - // True if this node belongs to class metaspace. - bool is_class() const { return _is_class; } - - // Helper function for take_from_committed: allocate padding chunks - // until top is at the given address. - void allocate_padding_chunks_until_top_is_at(MetaWord* target_top); - - public: - - VirtualSpaceNode(bool is_class, size_t byte_size); - VirtualSpaceNode(bool is_class, ReservedSpace rs) : - _next(NULL), _is_class(is_class), _rs(rs), _top(NULL), _container_count(0), _occupancy_map(NULL) {} - ~VirtualSpaceNode(); + // Size, in words, of the range of this node which has been handed out in + // the form of root chunks. + size_t _used_words; - // Convenience functions for logical bottom and (committed) end - MetaWord* bottom() const { return (MetaWord*) _virtual_space.low(); } - MetaWord* end() const { return (MetaWord*) _virtual_space.high(); } + // The bitmap describing the commit state of the region: + // Each bit covers a region of 64K (see constants::commit_granule_size). + CommitMask _commit_mask; - const OccupancyMap* occupancy_map() const { return _occupancy_map; } - OccupancyMap* occupancy_map() { return _occupancy_map; } + // An array/lookup table of RootChunkArea objects. Each one describes a root chunk area. + RootChunkAreaLUT _root_chunk_area_lut; - bool contains(const void* ptr) { return ptr >= low() && ptr < high(); } + // Limiter object to ask before expanding the committed size of this node. + CommitLimiter* const _commit_limiter; - size_t reserved_words() const { return _virtual_space.reserved_size() / BytesPerWord; } - size_t committed_words() const { return _virtual_space.actual_committed_size() / BytesPerWord; } + // Points to outside size counters which we are to increase/decrease when we commit/uncommit + // space from this node. + SizeCounter* const _total_reserved_words_counter; + SizeCounter* const _total_committed_words_counter; - bool is_pre_committed() const { return _virtual_space.special(); } + /// committing, uncommitting /// - // address of next available space in _virtual_space; - // Accessors - VirtualSpaceNode* next() { return _next; } - void set_next(VirtualSpaceNode* v) { _next = v; } + // Given a pointer into this node, calculate the start of the commit granule + // the pointer points into. + MetaWord* calc_start_of_granule(MetaWord* p) const { + DEBUG_ONLY(check_pointer(p)); + return align_down(p, Settings::commit_granule_bytes()); + } - void set_top(MetaWord* v) { _top = v; } + // Given an address range, ensure it is committed. + // + // The range has to be aligned to granule size. + // + // Function will: + // - check how many granules in that region are uncommitted; If all are committed, it + // returns true immediately. + // - check if committing those uncommitted granules would bring us over the commit limit + // (GC threshold, MaxMetaspaceSize). If true, it returns false. + // - commit the memory. + // - mark the range as committed in the commit mask + // + // Returns true if success, false if it did hit a commit limit. + bool commit_range(MetaWord* p, size_t word_size); - // Accessors - VirtualSpace* virtual_space() const { return (VirtualSpace*) &_virtual_space; } + //// creation //// - // Returns true if "word_size" is available in the VirtualSpace - bool is_available(size_t word_size) { return word_size <= pointer_delta(end(), _top, sizeof(MetaWord)); } + // Create a new empty node spanning the given given reserved space. + VirtualSpaceNode(ReservedSpace rs, bool owns_rs, CommitLimiter* limiter, + SizeCounter* reserve_counter, SizeCounter* commit_counter); - MetaWord* top() const { return _top; } - void inc_top(size_t word_size) { _top += word_size; } +public: - uintx container_count() { return _container_count; } - void inc_container_count(); - void dec_container_count(); + // Create a node of a given size (it will create its own space). + static VirtualSpaceNode* create_node(size_t word_size, CommitLimiter* limiter, SizeCounter* reserve_words_counter, + SizeCounter* commit_words_counter); - // used and capacity in this single entry in the list - size_t used_words_in_vs() const; - size_t capacity_words_in_vs() const; + // Create a node over an existing space + static VirtualSpaceNode* create_node(ReservedSpace rs, CommitLimiter* limiter, SizeCounter* reserve_words_counter, + SizeCounter* commit_words_counter); - bool initialize(); - - // get space from the virtual space - Metachunk* take_from_committed(size_t chunk_word_size); - - // Allocate a chunk from the virtual space and return it. - Metachunk* get_chunk_vs(size_t chunk_word_size); - - // Expands the committed space by at least min_words words. - bool expand_by(size_t min_words, size_t preferred_words); - - // In preparation for deleting this node, remove all the chunks - // in the node from any freelist. - void purge(ChunkManager* chunk_manager); - - // If an allocation doesn't fit in the current node a new node is created. - // Allocate chunks out of the remaining committed space in this node - // to avoid wasting that memory. - // This always adds up because all the chunk sizes are multiples of - // the smallest chunk size. - void retire(ChunkManager* chunk_manager); - - void print_on(outputStream* st) const { print_on(st, K); } - void print_on(outputStream* st, size_t scale) const; - void print_map(outputStream* st, bool is_class) const; + ~VirtualSpaceNode(); - // Debug support - DEBUG_ONLY(void mangle();) - // Verify counters and basic structure. Slow mode: verify all chunks in depth and occupancy map. - DEBUG_ONLY(void verify(bool slow);) - // Verify that all free chunks in this node are ideally merged - // (there should not be multiple small chunks where a large chunk could exist.) - DEBUG_ONLY(void verify_free_chunks_are_ideally_merged();) + // Note: public for gtests only, could be private. + MetaWord* base() const { return _base; } + + // Reserved size of the whole node. + size_t word_size() const { return _word_size; } + + //// Chunk allocation, splitting, merging ///// + + // Allocate a root chunk from this node. Will fail and return NULL if the node is full + // - if we used up the whole address space of this node's memory region. + // (in case this node backs compressed class space, this is how we hit + // CompressedClassSpaceSize). + // Note that this just returns reserved memory; caller must take care of committing this + // chunk before using it. + Metachunk* allocate_root_chunk(); + + // Given a chunk c, split it recursively until you get a chunk of the given target_level. + // + // The resulting target chunk resides at the same address as the original chunk. + // The resulting splinters are added to freelists. + void split(chunklevel_t target_level, Metachunk* c, FreeChunkListVector* freelists); + + // Given a chunk, attempt to merge it recursively with its neighboring chunks. + // + // If successful (merged at least once), returns address of + // the merged chunk; NULL otherwise. + // + // The merged chunks are removed from the freelists. + // + // !!! Please note that if this method returns a non-NULL value, the + // original chunk will be invalid and should not be accessed anymore! !!! + Metachunk* merge(Metachunk* c, FreeChunkListVector* freelists); + + // Given a chunk c, which must be "in use" and must not be a root chunk, attempt to + // enlarge it in place by claiming its trailing buddy. + // + // This will only work if c is the leader of the buddy pair and the trailing buddy is free. + // + // If successful, the follower chunk will be removed from the freelists, the leader chunk c will + // double in size (level decreased by one). + // + // On success, true is returned, false otherwise. + bool attempt_enlarge_chunk(Metachunk* c, FreeChunkListVector* freelists); + + // Attempts to purge the node: + // + // If all chunks living in this node are free, they will all be removed from + // the freelist they currently reside in. Then, the node will be deleted. + // + // Returns true if the node has been deleted, false if not. + // !! If this returns true, do not access the node from this point on. !! + bool attempt_purge(FreeChunkListVector* freelists); + + // Attempts to uncommit free areas according to the rules set in settings. + // Returns number of words uncommitted. + size_t uncommit_free_areas(); + + /// misc ///// + + // Returns size, in words, of the used space in this node alone. + // (Notes: + // - This is the space handed out to the ChunkManager, so it is "used" from the viewpoint of this node, + // but not necessarily used for Metadata. + // - This may or may not be committed memory. + size_t used_words() const { return _used_words; } + + // Returns size, in words, of how much space is left in this node alone. + size_t free_words() const { return _word_size - _used_words; } + + // Returns size, in words, of committed space in this node alone. + // Note: iterates over commit mask and hence may be a tad expensive on large nodes. + size_t committed_words() const; + + //// Committing/uncommitting memory ///// + + // Given an address range, ensure it is committed. + // + // The range does not have to be aligned to granule size. However, the function will always commit + // whole granules. + // + // Function will: + // - check how many granules in that region are uncommitted; If all are committed, it + // returns true immediately. + // - check if committing those uncommitted granules would bring us over the commit limit + // (GC threshold, MaxMetaspaceSize). If true, it returns false. + // - commit the memory. + // - mark the range as committed in the commit mask + // + // Returns true if success, false if it did hit a commit limit. + bool ensure_range_is_committed(MetaWord* p, size_t word_size); + + // Given an address range (which has to be aligned to commit granule size): + // - uncommit it + // - mark it as uncommitted in the commit mask + void uncommit_range(MetaWord* p, size_t word_size); + + //// List stuff //// + VirtualSpaceNode* next() const { return _next; } + void set_next(VirtualSpaceNode* vsn) { _next = vsn; } + + /// Debug stuff //// + + // Print a description about this node. + void print_on(outputStream* st) const; + + // Verify counters and basic structure. Slow mode: verify all chunks in depth + bool contains(const MetaWord* p) const { + return p >= _base && p < _base + _used_words; + } + +#ifdef ASSERT + void check_pointer(const MetaWord* p) const { + assert(contains(p), "invalid pointer"); + } + void verify() const; + void verify_locked() const; +#endif }; diff --git a/src/hotspot/share/memory/metaspaceChunkFreeListSummary.hpp b/src/hotspot/share/memory/metaspaceChunkFreeListSummary.hpp index e593886aed4..d08f962ff1c 100644 --- a/src/hotspot/share/memory/metaspaceChunkFreeListSummary.hpp +++ b/src/hotspot/share/memory/metaspaceChunkFreeListSummary.hpp @@ -25,7 +25,9 @@ #ifndef SHARE_MEMORY_METASPACECHUNKFREELISTSUMMARY_HPP #define SHARE_MEMORY_METASPACECHUNKFREELISTSUMMARY_HPP +#include "utilities/globalDefinitions.hpp" +// Todo: will need to rework this, see JDK-8251342 class MetaspaceChunkFreeListSummary { size_t _num_specialized_chunks; size_t _num_small_chunks; diff --git a/src/hotspot/share/memory/metaspaceClosure.hpp b/src/hotspot/share/memory/metaspaceClosure.hpp index 964d429f435..6868165f923 100644 --- a/src/hotspot/share/memory/metaspaceClosure.hpp +++ b/src/hotspot/share/memory/metaspaceClosure.hpp @@ -105,7 +105,7 @@ class MetaspaceClosure { // Symbol* bar() { return (Symbol*) _obj; } // // [2] All Array dimensions are statically declared. - class Ref : public CHeapObj { + class Ref : public CHeapObj { Writability _writability; bool _keep_after_pushing; Ref* _next; diff --git a/src/hotspot/share/memory/metaspaceCounters.cpp b/src/hotspot/share/memory/metaspaceCounters.cpp index c40e68685df..5016a935f6f 100644 --- a/src/hotspot/share/memory/metaspaceCounters.cpp +++ b/src/hotspot/share/memory/metaspaceCounters.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,7 @@ #include "runtime/perfData.hpp" #include "utilities/exceptions.hpp" -class MetaspacePerfCounters: public CHeapObj { +class MetaspacePerfCounters: public CHeapObj { friend class VMStructs; PerfVariable* _capacity; PerfVariable* _used; diff --git a/src/hotspot/share/memory/metaspaceCounters.hpp b/src/hotspot/share/memory/metaspaceCounters.hpp index 8984fbc8717..33faf44f175 100644 --- a/src/hotspot/share/memory/metaspaceCounters.hpp +++ b/src/hotspot/share/memory/metaspaceCounters.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ class MetaspacePerfCounters; +// Todo: clean up after jep387, see JDK-8251392 class MetaspaceCounters: public AllStatic { static MetaspacePerfCounters* _perf_counters; static size_t used(); diff --git a/src/hotspot/share/memory/metaspaceShared.cpp b/src/hotspot/share/memory/metaspaceShared.cpp index 289385020aa..f5000089c2b 100644 --- a/src/hotspot/share/memory/metaspaceShared.cpp +++ b/src/hotspot/share/memory/metaspaceShared.cpp @@ -27,8 +27,9 @@ #include "classfile/classLoaderDataShared.hpp" #include "classfile/classListParser.hpp" #include "classfile/classLoaderExt.hpp" -#include "classfile/loaderConstraints.hpp" #include "classfile/javaClasses.inline.hpp" +#include "classfile/lambdaFormInvokers.hpp" +#include "classfile/loaderConstraints.hpp" #include "classfile/placeholders.hpp" #include "classfile/symbolTable.hpp" #include "classfile/stringTable.hpp" @@ -68,7 +69,7 @@ #include "utilities/defaultStream.hpp" #include "utilities/hashtable.inline.hpp" #if INCLUDE_G1GC -#include "gc/g1/g1CollectedHeap.hpp" +#include "gc/g1/g1CollectedHeap.inline.hpp" #endif ReservedSpace MetaspaceShared::_shared_rs; @@ -500,7 +501,7 @@ void MetaspaceShared::serialize(SerializeClosure* soc) { SystemDictionaryShared::serialize_well_known_klasses(soc); soc->do_tag(--tag); - CppVtables::serialize_cloned_cpp_vtptrs(soc); + CppVtables::serialize(soc); soc->do_tag(--tag); CDS_JAVA_HEAP_ONLY(ClassLoaderDataShared::serialize(soc)); @@ -731,9 +732,7 @@ void VM_PopulateDumpSharedSpace::doit() { builder.gather_source_objs(); - CppVtables::allocate_cloned_cpp_vtptrs(); - char* cloned_vtables = _mc_region.top(); - CppVtables::allocate_cpp_vtable_clones(); + char* cloned_vtables = CppVtables::dumptime_init(); { _mc_region.pack(&_rw_region); @@ -771,15 +770,20 @@ void VM_PopulateDumpSharedSpace::doit() { builder.relocate_well_known_klasses(); + log_info(cds)("Update method trampolines"); + builder.update_method_trampolines(); + log_info(cds)("Make classes shareable"); builder.make_klasses_shareable(); char* serialized_data = dump_read_only_tables(); _ro_region.pack(); + SystemDictionaryShared::adjust_lambda_proxy_class_dictionary(); + // The vtable clones contain addresses of the current process. // We don't want to write these addresses into the archive. Same for i2i buffer. - CppVtables::zero_cpp_vtable_clones_for_writing(); + CppVtables::zero_archived_vtables(); memset(MetaspaceShared::i2i_entry_code_buffers(), 0, MetaspaceShared::i2i_entry_code_buffers_size()); @@ -1050,8 +1054,14 @@ void MetaspaceShared::preload_and_dump(TRAPS) { if (SharedArchiveConfigFile) { log_info(cds)("Reading extra data from %s ...", SharedArchiveConfigFile); read_extra_data(SharedArchiveConfigFile, THREAD); + log_info(cds)("Reading extra data: done."); + } + + if (LambdaFormInvokers::lambdaform_lines() != NULL) { + log_info(cds)("Regenerate MethodHandle Holder classes..."); + LambdaFormInvokers::regenerate_holder_classes(THREAD); + log_info(cds)("Regenerate MethodHandle Holder classes done."); } - log_info(cds)("Reading extra data: done."); HeapShared::init_for_dumping(THREAD); @@ -1086,6 +1096,9 @@ int MetaspaceShared::preload_classes(const char* class_list_path, TRAPS) { int class_count = 0; while (parser.parse_one_line()) { + if (parser.lambda_form_line()) { + continue; + } Klass* klass = parser.load_current_class(THREAD); if (HAS_PENDING_EXCEPTION) { if (klass == NULL && @@ -1153,6 +1166,15 @@ bool MetaspaceShared::try_link_class(InstanceKlass* ik, TRAPS) { #if INCLUDE_CDS_JAVA_HEAP void VM_PopulateDumpSharedSpace::dump_java_heap_objects() { + if(!HeapShared::is_heap_object_archiving_allowed()) { + log_info(cds)( + "Archived java heap is not supported as UseG1GC, " + "UseCompressedOops and UseCompressedClassPointers are required." + "Current settings: UseG1GC=%s, UseCompressedOops=%s, UseCompressedClassPointers=%s.", + BOOL_TO_STR(UseG1GC), BOOL_TO_STR(UseCompressedOops), + BOOL_TO_STR(UseCompressedClassPointers)); + return; + } // Find all the interned strings that should be dumped. int i; for (i = 0; i < _global_klass_objects->length(); i++) { @@ -1700,12 +1722,10 @@ void MetaspaceShared::initialize_shared_spaces() { FileMapInfo *static_mapinfo = FileMapInfo::current_info(); _i2i_entry_code_buffers = static_mapinfo->i2i_entry_code_buffers(); _i2i_entry_code_buffers_size = static_mapinfo->i2i_entry_code_buffers_size(); - char* buffer = static_mapinfo->cloned_vtables(); - CppVtables::clone_cpp_vtables((intptr_t*)buffer); // Verify various attributes of the archive, plus initialize the // shared string/symbol tables - buffer = static_mapinfo->serialized_data(); + char* buffer = static_mapinfo->serialized_data(); intptr_t* array = (intptr_t*)buffer; ReadClosure rc(&array); serialize(&rc); diff --git a/src/hotspot/share/memory/metaspaceShared.hpp b/src/hotspot/share/memory/metaspaceShared.hpp index b8986e84fd3..a5c77931639 100644 --- a/src/hotspot/share/memory/metaspaceShared.hpp +++ b/src/hotspot/share/memory/metaspaceShared.hpp @@ -35,6 +35,10 @@ #define MAX_SHARED_DELTA (0x7FFFFFFF) +// Metaspace::allocate() requires that all blocks must be aligned with KlassAlignmentInBytes. +// We enforce the same alignment rule in blocks allocated from the shared space. +const int SharedSpaceObjectAlignment = KlassAlignmentInBytes; + class outputStream; class CHeapBitMap; class FileMapInfo; @@ -230,7 +234,7 @@ class MetaspaceShared : AllStatic { template static size_t ro_array_bytesize(int length) { size_t byte_size = Array::byte_sizeof(length, sizeof(T)); - return align_up(byte_size, BytesPerWord); + return align_up(byte_size, SharedSpaceObjectAlignment); } static address i2i_entry_code_buffers(size_t total_size); diff --git a/src/hotspot/share/memory/metaspaceTracer.hpp b/src/hotspot/share/memory/metaspaceTracer.hpp index adb7ae19789..9f878451580 100644 --- a/src/hotspot/share/memory/metaspaceTracer.hpp +++ b/src/hotspot/share/memory/metaspaceTracer.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ #include "memory/allocation.hpp" #include "memory/metaspace.hpp" -#include "memory/metaspaceGCThresholdUpdater.hpp" class ClassLoaderData; diff --git a/src/hotspot/share/memory/padded.hpp b/src/hotspot/share/memory/padded.hpp index 08003613609..feca46ac7c2 100644 --- a/src/hotspot/share/memory/padded.hpp +++ b/src/hotspot/share/memory/padded.hpp @@ -116,6 +116,7 @@ template class PaddedPrimitiveArray { public: static T* create_unfreeable(size_t length); + static T* create(size_t length, void** alloc_base); }; #endif // SHARE_MEMORY_PADDED_HPP diff --git a/src/hotspot/share/memory/padded.inline.hpp b/src/hotspot/share/memory/padded.inline.hpp index d698f5ee1b3..742db2fe3f8 100644 --- a/src/hotspot/share/memory/padded.inline.hpp +++ b/src/hotspot/share/memory/padded.inline.hpp @@ -82,11 +82,18 @@ T** Padded2DArray::create_unfreeable(uint rows, uint column template T* PaddedPrimitiveArray::create_unfreeable(size_t length) { + void* temp; + return create(length, &temp); +} + +template +T* PaddedPrimitiveArray::create(size_t length, void** alloc_base) { // Allocate a chunk of memory large enough to allow for some alignment. void* chunk = AllocateHeap(length * sizeof(T) + alignment, flags); memset(chunk, 0, length * sizeof(T) + alignment); + *alloc_base = chunk; return (T*)align_up(chunk, alignment); } diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index 6d7ac04554f..637fd233871 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -1125,7 +1125,7 @@ void Universe::verify(VerifyOption option, const char* prefix) { } if (should_verify_subset(Verify_MetaspaceUtils)) { log_debug(gc, verify)("MetaspaceUtils"); - MetaspaceUtils::verify_free_chunks(); + DEBUG_ONLY(MetaspaceUtils::verify();) } if (should_verify_subset(Verify_JNIHandles)) { log_debug(gc, verify)("JNIHandles"); diff --git a/src/hotspot/share/memory/virtualspace.cpp b/src/hotspot/share/memory/virtualspace.cpp index c67e9c1c78f..ab5849d6ac7 100644 --- a/src/hotspot/share/memory/virtualspace.cpp +++ b/src/hotspot/share/memory/virtualspace.cpp @@ -80,6 +80,30 @@ ReservedSpace::ReservedSpace(char* base, size_t size, size_t alignment, _executable = executable; } +// Helper method +static char* attempt_map_or_reserve_memory_at(char* base, size_t size, int fd) { + if (fd != -1) { + return os::attempt_map_memory_to_file_at(base, size, fd); + } + return os::attempt_reserve_memory_at(base, size); +} + +// Helper method +static char* map_or_reserve_memory(size_t size, int fd) { + if (fd != -1) { + return os::map_memory_to_file(size, fd); + } + return os::reserve_memory(size); +} + +// Helper method +static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int fd) { + if (fd != -1) { + return os::map_memory_to_file_aligned(size, alignment, fd); + } + return os::reserve_memory_aligned(size, alignment); +} + // Helper method static void unmap_or_release_memory(char* base, size_t size, bool is_file_mapped) { if (is_file_mapped) { @@ -188,13 +212,13 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large, // important. If available space is not detected, return NULL. if (requested_address != 0) { - base = os::attempt_reserve_memory_at(requested_address, size, _fd_for_heap); + base = attempt_map_or_reserve_memory_at(requested_address, size, _fd_for_heap); if (failed_to_reserve_as_requested(base, requested_address, size, false, _fd_for_heap != -1)) { // OS ignored requested address. Try different address. base = NULL; } } else { - base = os::reserve_memory_with_fd(size, _fd_for_heap); + base = map_or_reserve_memory(size, _fd_for_heap); } if (base == NULL) return; @@ -206,7 +230,7 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large, // Make sure that size is aligned size = align_up(size, alignment); - base = os::reserve_memory_aligned(size, alignment, _fd_for_heap); + base = map_or_reserve_memory_aligned(size, alignment, _fd_for_heap); if (requested_address != 0 && failed_to_reserve_as_requested(base, requested_address, size, false, _fd_for_heap != -1)) { @@ -372,13 +396,13 @@ void ReservedHeapSpace::try_reserve_heap(size_t size, } if (requested_address != 0) { - base = os::attempt_reserve_memory_at(requested_address, size, _fd_for_heap); + base = attempt_map_or_reserve_memory_at(requested_address, size, _fd_for_heap); } else { // Optimistically assume that the OSes returns an aligned base pointer. // When reserving a large address range, most OSes seem to align to at // least 64K. // If the returned memory is not aligned we will release and retry. - base = os::reserve_memory_with_fd(size, _fd_for_heap); + base = map_or_reserve_memory(size, _fd_for_heap); } } if (base == NULL) { return; } diff --git a/src/hotspot/share/oops/compressedOops.cpp b/src/hotspot/share/oops/compressedOops.cpp index 43fea6201e2..7f7b2a06fc8 100644 --- a/src/hotspot/share/oops/compressedOops.cpp +++ b/src/hotspot/share/oops/compressedOops.cpp @@ -27,6 +27,7 @@ #include "logging/log.hpp" #include "logging/logStream.hpp" #include "memory/memRegion.hpp" +#include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/compressedOops.hpp" #include "gc/shared/collectedHeap.hpp" diff --git a/src/hotspot/share/oops/compressedOops.hpp b/src/hotspot/share/oops/compressedOops.hpp index 60f099c9be9..c12f6bd197b 100644 --- a/src/hotspot/share/oops/compressedOops.hpp +++ b/src/hotspot/share/oops/compressedOops.hpp @@ -119,6 +119,7 @@ class CompressedOops : public AllStatic { static bool is_null(oop v) { return v == NULL; } static bool is_null(narrowOop v) { return v == narrowOop::null; } + static inline oop decode_raw_not_null(narrowOop v); static inline oop decode_raw(narrowOop v); static inline oop decode_not_null(narrowOop v); static inline oop decode(narrowOop v); @@ -126,10 +127,10 @@ class CompressedOops : public AllStatic { static inline narrowOop encode(oop v); // No conversions needed for these overloads - static oop decode_not_null(oop v) { return v; } - static oop decode(oop v) { return v; } - static narrowOop encode_not_null(narrowOop v) { return v; } - static narrowOop encode(narrowOop v) { return v; } + static inline oop decode_not_null(oop v); + static inline oop decode(oop v); + static inline narrowOop encode_not_null(narrowOop v); + static inline narrowOop encode(narrowOop v); static inline uint32_t narrow_oop_value(oop o); static inline uint32_t narrow_oop_value(narrowOop o); diff --git a/src/hotspot/share/oops/compressedOops.inline.hpp b/src/hotspot/share/oops/compressedOops.inline.hpp index 8c8a922094d..920e87703b4 100644 --- a/src/hotspot/share/oops/compressedOops.inline.hpp +++ b/src/hotspot/share/oops/compressedOops.inline.hpp @@ -41,6 +41,11 @@ // offset from the heap base. Saving the check for null can save instructions // in inner GC loops so these are separated. +inline oop CompressedOops::decode_raw_not_null(narrowOop v) { + assert(!is_null(v), "narrow oop value can never be zero"); + return decode_raw(v); +} + inline oop CompressedOops::decode_raw(narrowOop v) { return (oop)(void*)((uintptr_t)base() + ((uintptr_t)v << shift())); } @@ -49,6 +54,7 @@ inline oop CompressedOops::decode_not_null(narrowOop v) { assert(!is_null(v), "narrow oop value can never be zero"); oop result = decode_raw(v); assert(is_object_aligned(result), "address not aligned: " INTPTR_FORMAT, p2i((void*) result)); + assert(Universe::heap()->is_in(result), "object not in heap " PTR_FORMAT, p2i((void*) result)); return result; } @@ -63,7 +69,7 @@ inline narrowOop CompressedOops::encode_not_null(oop v) { uint64_t pd = (uint64_t)(pointer_delta((void*)v, (void*)base(), 1)); assert(OopEncodingHeapMax > pd, "change encoding max if new encoding"); narrowOop result = narrow_oop_cast(pd >> shift()); - assert(decode(result) == v, "reversibility"); + assert(decode_raw(result) == v, "reversibility"); return result; } @@ -71,6 +77,24 @@ inline narrowOop CompressedOops::encode(oop v) { return is_null(v) ? narrowOop::null : encode_not_null(v); } +inline oop CompressedOops::decode_not_null(oop v) { + assert(Universe::heap()->is_in(v), "object not in heap " PTR_FORMAT, p2i((void*) v)); + return v; +} + +inline oop CompressedOops::decode(oop v) { + assert(Universe::heap()->is_in_or_null(v), "object not in heap " PTR_FORMAT, p2i((void*) v)); + return v; +} + +inline narrowOop CompressedOops::encode_not_null(narrowOop v) { + return v; +} + +inline narrowOop CompressedOops::encode(narrowOop v) { + return v; +} + inline uint32_t CompressedOops::narrow_oop_value(oop o) { return narrow_oop_value(encode(o)); } diff --git a/src/hotspot/share/oops/fieldInfo.hpp b/src/hotspot/share/oops/fieldInfo.hpp index 18e834ea694..3e1836c6574 100644 --- a/src/hotspot/share/oops/fieldInfo.hpp +++ b/src/hotspot/share/oops/fieldInfo.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,8 +26,9 @@ #define SHARE_OOPS_FIELDINFO_HPP #include "oops/constantPool.hpp" +#include "oops/symbol.hpp" #include "oops/typeArrayOop.hpp" -#include "classfile/vmSymbols.hpp" +#include "utilities/vmEnums.hpp" // This class represents the field information contained in the fields // array of an InstanceKlass. Currently it's laid on top an array of @@ -46,19 +47,21 @@ class FieldInfo { // as an array of 6 shorts. #define FIELDINFO_TAG_SIZE 2 -#define FIELDINFO_TAG_BLANK 0 -#define FIELDINFO_TAG_OFFSET 1 -#define FIELDINFO_TAG_TYPE_PLAIN 2 -#define FIELDINFO_TAG_TYPE_CONTENDED 3 -#define FIELDINFO_TAG_MASK 3 +#define FIELDINFO_TAG_OFFSET 1 << 0 +#define FIELDINFO_TAG_CONTENDED 1 << 1 // Packed field has the tag, and can be either of: // hi bits <--------------------------- lo bits // |---------high---------|---------low---------| - // ..........................................00 - blank + // ..........................................CO + // ..........................................00 - non-contended field + // [--contention_group--]....................10 - contended field with contention group // [------------------offset----------------]01 - real field offset - // ......................[-------type-------]10 - plain field with type - // [--contention_group--][-------type-------]11 - contended field with type and contention group + + // Bit O indicates if the packed field contains an offset (O=1) or not (O=0) + // Bit C indicates if the field is contended (C=1) or not (C=0) + // (if it is contended, the high packed field contains the contention group) + enum FieldOffset { access_flags_offset = 0, name_index_offset = 1, @@ -102,78 +105,22 @@ class FieldInfo { u2 access_flags() const { return _shorts[access_flags_offset]; } u4 offset() const { - u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { - case FIELDINFO_TAG_OFFSET: - return build_int_from_shorts(_shorts[low_packed_offset], _shorts[high_packed_offset]) >> FIELDINFO_TAG_SIZE; -#ifndef PRODUCT - case FIELDINFO_TAG_TYPE_PLAIN: - fatal("Asking offset for the plain type field"); - case FIELDINFO_TAG_TYPE_CONTENDED: - fatal("Asking offset for the contended type field"); - case FIELDINFO_TAG_BLANK: - fatal("Asking offset for the blank field"); -#endif - } - ShouldNotReachHere(); - return 0; + assert((_shorts[low_packed_offset] & FIELDINFO_TAG_OFFSET) != 0, "Offset must have been set"); + return build_int_from_shorts(_shorts[low_packed_offset], _shorts[high_packed_offset]) >> FIELDINFO_TAG_SIZE; } bool is_contended() const { - u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { - case FIELDINFO_TAG_TYPE_PLAIN: - return false; - case FIELDINFO_TAG_TYPE_CONTENDED: - return true; -#ifndef PRODUCT - case FIELDINFO_TAG_OFFSET: - fatal("Asking contended flag for the field with offset"); - case FIELDINFO_TAG_BLANK: - fatal("Asking contended flag for the blank field"); -#endif - } - ShouldNotReachHere(); - return false; + return (_shorts[low_packed_offset] & FIELDINFO_TAG_CONTENDED) != 0; } u2 contended_group() const { - u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { - case FIELDINFO_TAG_TYPE_PLAIN: - return 0; - case FIELDINFO_TAG_TYPE_CONTENDED: - return _shorts[high_packed_offset]; -#ifndef PRODUCT - case FIELDINFO_TAG_OFFSET: - fatal("Asking the contended group for the field with offset"); - case FIELDINFO_TAG_BLANK: - fatal("Asking the contended group for the blank field"); -#endif - } - ShouldNotReachHere(); - return 0; + assert((_shorts[low_packed_offset] & FIELDINFO_TAG_OFFSET) == 0, "Offset must not have been set"); + assert((_shorts[low_packed_offset] & FIELDINFO_TAG_CONTENDED) != 0, "Field must be contended"); + return _shorts[high_packed_offset]; } - u2 allocation_type() const { - u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { - case FIELDINFO_TAG_TYPE_PLAIN: - case FIELDINFO_TAG_TYPE_CONTENDED: - return (lo >> FIELDINFO_TAG_SIZE); -#ifndef PRODUCT - case FIELDINFO_TAG_OFFSET: - fatal("Asking the field type for field with offset"); - case FIELDINFO_TAG_BLANK: - fatal("Asking the field type for the blank field"); -#endif - } - ShouldNotReachHere(); - return 0; - } - bool is_offset_set() const { - return (_shorts[low_packed_offset] & FIELDINFO_TAG_MASK) == FIELDINFO_TAG_OFFSET; + return (_shorts[low_packed_offset] & FIELDINFO_TAG_OFFSET)!= 0; } Symbol* name(ConstantPool* cp) const { @@ -199,41 +146,11 @@ class FieldInfo { _shorts[high_packed_offset] = extract_high_short_from_int(val); } - void set_allocation_type(int type) { - u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { - case FIELDINFO_TAG_BLANK: - _shorts[low_packed_offset] = ((type << FIELDINFO_TAG_SIZE)) & 0xFFFF; - _shorts[low_packed_offset] &= ~FIELDINFO_TAG_MASK; - _shorts[low_packed_offset] |= FIELDINFO_TAG_TYPE_PLAIN; - return; -#ifndef PRODUCT - case FIELDINFO_TAG_TYPE_PLAIN: - case FIELDINFO_TAG_TYPE_CONTENDED: - case FIELDINFO_TAG_OFFSET: - fatal("Setting the field type with overwriting"); -#endif - } - ShouldNotReachHere(); - } - void set_contended_group(u2 val) { - u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { - case FIELDINFO_TAG_TYPE_PLAIN: - _shorts[low_packed_offset] |= FIELDINFO_TAG_TYPE_CONTENDED; - _shorts[high_packed_offset] = val; - return; -#ifndef PRODUCT - case FIELDINFO_TAG_TYPE_CONTENDED: - fatal("Overwriting contended group"); - case FIELDINFO_TAG_BLANK: - fatal("Setting contended group for the blank field"); - case FIELDINFO_TAG_OFFSET: - fatal("Setting contended group for field with offset"); -#endif - } - ShouldNotReachHere(); + assert((_shorts[low_packed_offset] & FIELDINFO_TAG_OFFSET) == 0, "Offset must not have been set"); + assert((_shorts[low_packed_offset] & FIELDINFO_TAG_CONTENDED) == 0, "Overwritting contended group"); + _shorts[low_packed_offset] |= FIELDINFO_TAG_CONTENDED; + _shorts[high_packed_offset] = val; } bool is_internal() const { @@ -250,7 +167,7 @@ class FieldInfo { Symbol* lookup_symbol(int symbol_index) const { assert(is_internal(), "only internal fields"); - return vmSymbols::symbol_at((vmSymbols::SID)symbol_index); + return Symbol::vm_symbol_at(static_cast(symbol_index)); } }; diff --git a/src/hotspot/share/oops/fieldStreams.hpp b/src/hotspot/share/oops/fieldStreams.hpp index e36427acf52..d2387531f29 100644 --- a/src/hotspot/share/oops/fieldStreams.hpp +++ b/src/hotspot/share/oops/fieldStreams.hpp @@ -134,10 +134,6 @@ class FieldStreamBase : public StackObj { return field()->offset(); } - int allocation_type() const { - return field()->allocation_type(); - } - void set_offset(int offset) { field()->set_offset(offset); } diff --git a/src/hotspot/share/oops/generateOopMap.cpp b/src/hotspot/share/oops/generateOopMap.cpp index 94fea0df497..c72bd5cf7fe 100644 --- a/src/hotspot/share/oops/generateOopMap.cpp +++ b/src/hotspot/share/oops/generateOopMap.cpp @@ -27,6 +27,7 @@ #include "logging/log.hpp" #include "logging/logStream.hpp" #include "memory/allocation.inline.hpp" +#include "memory/resourceArea.hpp" #include "oops/constantPool.hpp" #include "oops/generateOopMap.hpp" #include "oops/oop.inline.hpp" diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index da508131199..a026fca809e 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -27,6 +27,7 @@ #include "aot/aotLoader.hpp" #include "classfile/classFileParser.hpp" #include "classfile/classFileStream.hpp" +#include "classfile/classListWriter.hpp" #include "classfile/classLoader.hpp" #include "classfile/classLoaderData.inline.hpp" #include "classfile/javaClasses.hpp" @@ -2991,19 +2992,39 @@ bool InstanceKlass::is_override(const methodHandle& super_method, Handle targetc return(is_same_class_package(targetclassloader(), targetclassname)); } + +static bool is_prohibited_package_slow(Symbol* class_name) { + // Caller has ResourceMark + int length; + jchar* unicode = class_name->as_unicode(length); + return (length >= 5 && + unicode[0] == 'j' && + unicode[1] == 'a' && + unicode[2] == 'v' && + unicode[3] == 'a' && + unicode[4] == '/'); +} + // Only boot and platform class loaders can define classes in "java/" packages. void InstanceKlass::check_prohibited_package(Symbol* class_name, ClassLoaderData* loader_data, TRAPS) { if (!loader_data->is_boot_class_loader_data() && !loader_data->is_platform_class_loader_data() && - class_name != NULL) { + class_name != NULL && class_name->utf8_length() >= 5) { ResourceMark rm(THREAD); - char* name = class_name->as_C_string(); - if (strncmp(name, JAVAPKG, JAVAPKG_LEN) == 0 && name[JAVAPKG_LEN] == '/') { + bool prohibited; + const u1* base = class_name->base(); + if ((base[0] | base[1] | base[2] | base[3] | base[4]) & 0x80) { + prohibited = is_prohibited_package_slow(class_name); + } else { + char* name = class_name->as_C_string(); + prohibited = (strncmp(name, JAVAPKG, JAVAPKG_LEN) == 0 && name[JAVAPKG_LEN] == '/'); + } + if (prohibited) { TempNewSymbol pkg_name = ClassLoader::package_from_class_name(class_name); assert(pkg_name != NULL, "Error in parsing package name starting with 'java/'"); - name = pkg_name->as_C_string(); + char* name = pkg_name->as_C_string(); const char* class_loader_name = loader_data->loader_name_and_id(); StringUtils::replace_no_expand(name, "/", "."); const char* msg_text1 = "Class loader (instance of): "; @@ -3586,9 +3607,19 @@ void InstanceKlass::oop_print_value_on(oop obj, outputStream* st) { st->print(" = "); vmtarget->print_value_on(st); } else { - java_lang_invoke_MemberName::clazz(obj)->print_value_on(st); + oop clazz = java_lang_invoke_MemberName::clazz(obj); + oop name = java_lang_invoke_MemberName::name(obj); + if (clazz != NULL) { + clazz->print_value_on(st); + } else { + st->print("NULL"); + } st->print("."); - java_lang_invoke_MemberName::name(obj)->print_value_on(st); + if (name != NULL) { + name->print_value_on(st); + } else { + st->print("NULL"); + } } } } @@ -4195,7 +4226,7 @@ unsigned char * InstanceKlass::get_cached_class_file_bytes() { void InstanceKlass::log_to_classlist(const ClassFileStream* stream) const { #if INCLUDE_CDS - if (DumpLoadedClassList && classlist_file->is_open()) { + if (ClassListWriter::is_enabled()) { if (!ClassLoader::has_jrt_entry()) { warning("DumpLoadedClassList and CDS are not supported in exploded build"); DumpLoadedClassList = NULL; @@ -4208,6 +4239,11 @@ void InstanceKlass::log_to_classlist(const ClassFileStream* stream) const { bool skip = false; if (is_shared()) { assert(stream == NULL, "shared class with stream"); + if (is_hidden()) { + // Don't include archived lambda proxy class in the classlist. + assert(!is_non_strong_hidden(), "unexpected non-strong hidden class"); + return; + } } else { assert(stream != NULL, "non-shared class without stream"); // skip hidden class and unsafe anonymous class. @@ -4235,8 +4271,9 @@ void InstanceKlass::log_to_classlist(const ClassFileStream* stream) const { tty->print_cr("skip writing class %s from source %s to classlist file", name()->as_C_string(), stream->source()); } else { - classlist_file->print_cr("%s", name()->as_C_string()); - classlist_file->flush(); + ClassListWriter w; + w.stream()->print_cr("%s", name()->as_C_string()); + w.stream()->flush(); } } #endif // INCLUDE_CDS diff --git a/src/hotspot/share/oops/instanceRefKlass.cpp b/src/hotspot/share/oops/instanceRefKlass.cpp index b8de138d59e..4b30a8edfe6 100644 --- a/src/hotspot/share/oops/instanceRefKlass.cpp +++ b/src/hotspot/share/oops/instanceRefKlass.cpp @@ -79,7 +79,7 @@ void InstanceRefKlass::update_nonstatic_oop_maps(Klass* k) { void InstanceRefKlass::oop_verify_on(oop obj, outputStream* st) { InstanceKlass::oop_verify_on(obj, st); // Verify referent field - oop referent = java_lang_ref_Reference::referent(obj); + oop referent = java_lang_ref_Reference::unknown_referent_no_keepalive(obj); if (referent != NULL) { guarantee(oopDesc::is_oop(referent), "referent field heap failed"); } diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp index 16f02330f66..44468459338 100644 --- a/src/hotspot/share/oops/klass.cpp +++ b/src/hotspot/share/oops/klass.cpp @@ -618,6 +618,7 @@ void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protec // Only recreate it if not present. A previous attempt to restore may have // gotten an OOM later but keep the mirror if it was created. if (java_mirror() == NULL) { + ResourceMark rm(THREAD); log_trace(cds, mirror)("Recreate mirror for %s", external_name()); java_lang_Class::create_mirror(this, loader, module_handle, protection_domain, Handle(), CHECK); } diff --git a/src/hotspot/share/oops/markWord.cpp b/src/hotspot/share/oops/markWord.cpp index 2c8ad2414db..acc007d2d48 100644 --- a/src/hotspot/share/oops/markWord.cpp +++ b/src/hotspot/share/oops/markWord.cpp @@ -25,20 +25,55 @@ #include "precompiled.hpp" #include "oops/markWord.hpp" #include "runtime/thread.inline.hpp" -#include "runtime/objectMonitor.hpp" +#include "runtime/objectMonitor.inline.hpp" #include "utilities/ostream.hpp" -void markWord::print_on(outputStream* st) const { +markWord markWord::displaced_mark_helper() const { + assert(has_displaced_mark_helper(), "check"); + if (has_monitor()) { + // Has an inflated monitor. Must be checked before has_locker(). + ObjectMonitor* monitor = this->monitor(); + return monitor->header(); + } + if (has_locker()) { // has a stack lock + BasicLock* locker = this->locker(); + return locker->displaced_header(); + } + // This should never happen: + fatal("bad header=" INTPTR_FORMAT, value()); + return markWord(value()); +} + +void markWord::set_displaced_mark_helper(markWord m) const { + assert(has_displaced_mark_helper(), "check"); + if (has_monitor()) { + // Has an inflated monitor. Must be checked before has_locker(). + ObjectMonitor* monitor = this->monitor(); + monitor->set_header(m); + return; + } + if (has_locker()) { // has a stack lock + BasicLock* locker = this->locker(); + locker->set_displaced_header(m); + return; + } + // This should never happen: + fatal("bad header=" INTPTR_FORMAT, value()); +} + +void markWord::print_on(outputStream* st, bool print_monitor_info) const { if (is_marked()) { // last bits = 11 st->print(" marked(" INTPTR_FORMAT ")", value()); } else if (has_monitor()) { // last bits = 10 // have to check has_monitor() before is_locked() st->print(" monitor(" INTPTR_FORMAT ")=", value()); - ObjectMonitor* mon = monitor(); - if (mon == NULL) { - st->print("NULL (this should never be seen!)"); - } else { - mon->print_on(st); + if (print_monitor_info) { + ObjectMonitor* mon = monitor(); + if (mon == NULL) { + st->print("NULL (this should never be seen!)"); + } else { + mon->print_on(st); + } } } else if (is_locked()) { // last bits != 01 => 00 // thin locked diff --git a/src/hotspot/share/oops/markWord.hpp b/src/hotspot/share/oops/markWord.hpp index 2846e1ae112..3af579aed03 100644 --- a/src/hotspot/share/oops/markWord.hpp +++ b/src/hotspot/share/oops/markWord.hpp @@ -286,16 +286,8 @@ class markWord { bool has_displaced_mark_helper() const { return ((value() & unlocked_value) == 0); } - markWord displaced_mark_helper() const { - assert(has_displaced_mark_helper(), "check"); - uintptr_t ptr = (value() & ~monitor_value); - return *(markWord*)ptr; - } - void set_displaced_mark_helper(markWord m) const { - assert(has_displaced_mark_helper(), "check"); - uintptr_t ptr = (value() & ~monitor_value); - ((markWord*)ptr)->_value = m._value; - } + markWord displaced_mark_helper() const; + void set_displaced_mark_helper(markWord m) const; markWord copy_set_hash(intptr_t hash) const { uintptr_t tmp = value() & (~hash_mask_in_place); tmp |= ((hash & hash_mask) << hash_shift); @@ -355,7 +347,7 @@ class markWord { static inline markWord prototype_for_klass(const Klass* klass); // Debugging - void print_on(outputStream* st) const; + void print_on(outputStream* st, bool print_monitor_info = true) const; // Prepare address of oop for placement into mark inline static markWord encode_pointer_as_mark(void* p) { return from_pointer(p).set_marked(); } diff --git a/src/hotspot/share/oops/method.cpp b/src/hotspot/share/oops/method.cpp index 88a106fbeed..c0c5c52feac 100644 --- a/src/hotspot/share/oops/method.cpp +++ b/src/hotspot/share/oops/method.cpp @@ -646,7 +646,7 @@ bool Method::compute_has_loops_flag() { Bytecodes::Code bc; while ((bc = bcs.next()) >= 0) { - switch( bc ) { + switch (bc) { case Bytecodes::_ifeq: case Bytecodes::_ifnull: case Bytecodes::_iflt: @@ -665,14 +665,42 @@ bool Method::compute_has_loops_flag() { case Bytecodes::_if_acmpne: case Bytecodes::_goto: case Bytecodes::_jsr: - if( bcs.dest() < bcs.next_bci() ) _access_flags.set_has_loops(); + if (bcs.dest() < bcs.next_bci()) _access_flags.set_has_loops(); break; case Bytecodes::_goto_w: case Bytecodes::_jsr_w: - if( bcs.dest_w() < bcs.next_bci() ) _access_flags.set_has_loops(); + if (bcs.dest_w() < bcs.next_bci()) _access_flags.set_has_loops(); break; + case Bytecodes::_lookupswitch: { + Bytecode_lookupswitch lookupswitch(this, bcs.bcp()); + if (lookupswitch.default_offset() < 0) { + _access_flags.set_has_loops(); + } else { + for (int i = 0; i < lookupswitch.number_of_pairs(); ++i) { + LookupswitchPair pair = lookupswitch.pair_at(i); + if (pair.offset() < 0) { + _access_flags.set_has_loops(); + break; + } + } + } + break; + } + case Bytecodes::_tableswitch: { + Bytecode_tableswitch tableswitch(this, bcs.bcp()); + if (tableswitch.default_offset() < 0) { + _access_flags.set_has_loops(); + } else { + for (int i = 0; i < tableswitch.length(); ++i) { + if (tableswitch.dest_offset_at(i) < 0) { + _access_flags.set_has_loops(); + } + } + } + break; + } default: break; } @@ -1079,17 +1107,9 @@ void Method::unlink_method() { _i2i_entry = Interpreter::entry_for_cds_method(methodHandle(Thread::current(), this)); _from_interpreted_entry = _i2i_entry; - if (DynamicDumpSharedSpaces) { - assert(_from_compiled_entry != NULL, "sanity"); - } else { - // TODO: Simplify the adapter trampoline allocation for static archiving. - // Remove the use of CDSAdapterHandlerEntry. - CDSAdapterHandlerEntry* cds_adapter = (CDSAdapterHandlerEntry*)adapter(); - constMethod()->set_adapter_trampoline(cds_adapter->get_adapter_trampoline()); - _from_compiled_entry = cds_adapter->get_c2i_entry_trampoline(); - assert(*((int*)_from_compiled_entry) == 0, - "must be NULL during dump time, to be initialized at run time"); - } + assert(_from_compiled_entry != NULL, "sanity"); + assert(*((int*)_from_compiled_entry) == 0, + "must be NULL during dump time, to be initialized at run time"); if (is_native()) { *native_function_addr() = NULL; @@ -1596,14 +1616,14 @@ methodHandle Method::clone_with_new_data(const methodHandle& m, u_char* new_code return newm; } -vmSymbols::SID Method::klass_id_for_intrinsics(const Klass* holder) { +vmSymbolID Method::klass_id_for_intrinsics(const Klass* holder) { // if loader is not the default loader (i.e., != NULL), we can't know the intrinsics // because we are not loading from core libraries // exception: the AES intrinsics come from lib/ext/sunjce_provider.jar // which does not use the class default class loader so we check for its loader here const InstanceKlass* ik = InstanceKlass::cast(holder); if ((ik->class_loader() != NULL) && !SystemDictionary::is_platform_class_loader(ik->class_loader())) { - return vmSymbols::NO_SID; // regardless of name, no intrinsics here + return vmSymbolID::NO_SID; // regardless of name, no intrinsics here } // see if the klass name is well-known: @@ -1618,20 +1638,20 @@ void Method::init_intrinsic_id() { assert(intrinsic_id_size_in_bytes() == sizeof(_intrinsic_id), ""); // the klass name is well-known: - vmSymbols::SID klass_id = klass_id_for_intrinsics(method_holder()); - assert(klass_id != vmSymbols::NO_SID, "caller responsibility"); + vmSymbolID klass_id = klass_id_for_intrinsics(method_holder()); + assert(klass_id != vmSymbolID::NO_SID, "caller responsibility"); // ditto for method and signature: - vmSymbols::SID name_id = vmSymbols::find_sid(name()); - if (klass_id != vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_MethodHandle) - && klass_id != vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_VarHandle) - && name_id == vmSymbols::NO_SID) { + vmSymbolID name_id = vmSymbols::find_sid(name()); + if (klass_id != VM_SYMBOL_ENUM_NAME(java_lang_invoke_MethodHandle) + && klass_id != VM_SYMBOL_ENUM_NAME(java_lang_invoke_VarHandle) + && name_id == vmSymbolID::NO_SID) { return; } - vmSymbols::SID sig_id = vmSymbols::find_sid(signature()); - if (klass_id != vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_MethodHandle) - && klass_id != vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_VarHandle) - && sig_id == vmSymbols::NO_SID) { + vmSymbolID sig_id = vmSymbols::find_sid(signature()); + if (klass_id != VM_SYMBOL_ENUM_NAME(java_lang_invoke_MethodHandle) + && klass_id != VM_SYMBOL_ENUM_NAME(java_lang_invoke_VarHandle) + && sig_id == vmSymbolID::NO_SID) { return; } jshort flags = access_flags().as_short(); @@ -1648,14 +1668,14 @@ void Method::init_intrinsic_id() { // A few slightly irregular cases: switch (klass_id) { - case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_StrictMath): + case VM_SYMBOL_ENUM_NAME(java_lang_StrictMath): // Second chance: check in regular Math. switch (name_id) { - case vmSymbols::VM_SYMBOL_ENUM_NAME(min_name): - case vmSymbols::VM_SYMBOL_ENUM_NAME(max_name): - case vmSymbols::VM_SYMBOL_ENUM_NAME(sqrt_name): + case VM_SYMBOL_ENUM_NAME(min_name): + case VM_SYMBOL_ENUM_NAME(max_name): + case VM_SYMBOL_ENUM_NAME(sqrt_name): // pretend it is the corresponding method in the non-strict class: - klass_id = vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_Math); + klass_id = VM_SYMBOL_ENUM_NAME(java_lang_Math); id = vmIntrinsics::find_id(klass_id, name_id, sig_id, flags); break; default: @@ -1664,8 +1684,8 @@ void Method::init_intrinsic_id() { break; // Signature-polymorphic methods: MethodHandle.invoke*, InvokeDynamic.*., VarHandle - case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_MethodHandle): - case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_VarHandle): + case VM_SYMBOL_ENUM_NAME(java_lang_invoke_MethodHandle): + case VM_SYMBOL_ENUM_NAME(java_lang_invoke_VarHandle): if (!is_native()) break; id = MethodHandles::signature_polymorphic_name_id(method_holder(), name()); if (is_static() != MethodHandles::is_signature_polymorphic_static(id)) diff --git a/src/hotspot/share/oops/method.hpp b/src/hotspot/share/oops/method.hpp index 4ae64cf7e79..a549e7253b3 100644 --- a/src/hotspot/share/oops/method.hpp +++ b/src/hotspot/share/oops/method.hpp @@ -40,6 +40,7 @@ #include "utilities/align.hpp" #include "utilities/growableArray.hpp" #include "utilities/macros.hpp" +#include "utilities/vmEnums.hpp" #if INCLUDE_JFR #include "jfr/support/jfrTraceIdExtension.hpp" #endif @@ -869,7 +870,7 @@ class Method : public Metadata { // Helper routines for intrinsic_id() and vmIntrinsics::method(). void init_intrinsic_id(); // updates from _none if a match - static vmSymbols::SID klass_id_for_intrinsics(const Klass* holder); + static vmSymbolID klass_id_for_intrinsics(const Klass* holder); bool caller_sensitive() { return (_flags & _caller_sensitive) != 0; diff --git a/src/hotspot/share/oops/methodData.cpp b/src/hotspot/share/oops/methodData.cpp index c7b34950cea..a1d71f273bc 100644 --- a/src/hotspot/share/oops/methodData.cpp +++ b/src/hotspot/share/oops/methodData.cpp @@ -1099,6 +1099,40 @@ ProfileData* MethodData::data_at(int data_index) const { return data_layout->data_in(); } +int DataLayout::cell_count() { + switch (tag()) { + case DataLayout::no_tag: + default: + ShouldNotReachHere(); + return 0; + case DataLayout::bit_data_tag: + return BitData::static_cell_count(); + case DataLayout::counter_data_tag: + return CounterData::static_cell_count(); + case DataLayout::jump_data_tag: + return JumpData::static_cell_count(); + case DataLayout::receiver_type_data_tag: + return ReceiverTypeData::static_cell_count(); + case DataLayout::virtual_call_data_tag: + return VirtualCallData::static_cell_count(); + case DataLayout::ret_data_tag: + return RetData::static_cell_count(); + case DataLayout::branch_data_tag: + return BranchData::static_cell_count(); + case DataLayout::multi_branch_data_tag: + return ((new MultiBranchData(this))->cell_count()); + case DataLayout::arg_info_data_tag: + return ((new ArgInfoData(this))->cell_count()); + case DataLayout::call_type_data_tag: + return ((new CallTypeData(this))->cell_count()); + case DataLayout::virtual_call_type_data_tag: + return ((new VirtualCallTypeData(this))->cell_count()); + case DataLayout::parameters_type_data_tag: + return ((new ParametersTypeData(this))->cell_count()); + case DataLayout::speculative_trap_data_tag: + return SpeculativeTrapData::static_cell_count(); + } +} ProfileData* DataLayout::data_in() { switch (tag()) { case DataLayout::no_tag: @@ -1142,6 +1176,16 @@ ProfileData* MethodData::next_data(ProfileData* current) const { return next; } +DataLayout* MethodData::next_data_layout(DataLayout* current) const { + int current_index = dp_to_di((address)current); + int next_index = current_index + current->size_in_bytes(); + if (out_of_bounds(next_index)) { + return NULL; + } + DataLayout* next = data_layout_at(next_index); + return next; +} + // Give each of the data entries a chance to perform specific // data initialization. void MethodData::post_initialize(BytecodeStream* stream) { @@ -1314,13 +1358,13 @@ bool MethodData::is_mature() const { // Translate a bci to its corresponding data index (di). address MethodData::bci_to_dp(int bci) { ResourceMark rm; - ProfileData* data = data_before(bci); - ProfileData* prev = NULL; - for ( ; is_valid(data); data = next_data(data)) { + DataLayout* data = data_layout_before(bci); + DataLayout* prev = NULL; + for ( ; is_valid(data); data = next_data_layout(data)) { if (data->bci() >= bci) { - if (data->bci() == bci) set_hint_di(dp_to_di(data->dp())); - else if (prev != NULL) set_hint_di(dp_to_di(prev->dp())); - return data->dp(); + if (data->bci() == bci) set_hint_di(dp_to_di((address)data)); + else if (prev != NULL) set_hint_di(dp_to_di((address)prev)); + return (address)data; } prev = data; } @@ -1329,11 +1373,11 @@ address MethodData::bci_to_dp(int bci) { // Translate a bci to its corresponding data, or NULL. ProfileData* MethodData::bci_to_data(int bci) { - ProfileData* data = data_before(bci); - for ( ; is_valid(data); data = next_data(data)) { + DataLayout* data = data_layout_before(bci); + for ( ; is_valid(data); data = next_data_layout(data)) { if (data->bci() == bci) { - set_hint_di(dp_to_di(data->dp())); - return data; + set_hint_di(dp_to_di((address)data)); + return data->data_in(); } else if (data->bci() > bci) { break; } @@ -1786,27 +1830,7 @@ void MethodData::clean_method_data(bool always_clean) { // methods out of MethodData for all methods. void MethodData::clean_weak_method_links() { ResourceMark rm; - for (ProfileData* data = first_data(); - is_valid(data); - data = next_data(data)) { - data->clean_weak_method_links(); - } - CleanExtraDataMethodClosure cl; clean_extra_data(&cl); verify_extra_data_clean(&cl); } - -#ifdef ASSERT -void MethodData::verify_clean_weak_method_links() { - ResourceMark rm; - for (ProfileData* data = first_data(); - is_valid(data); - data = next_data(data)) { - data->verify_clean_weak_method_links(); - } - - CleanExtraDataMethodClosure cl; - verify_extra_data_clean(&cl); -} -#endif // ASSERT diff --git a/src/hotspot/share/oops/methodData.hpp b/src/hotspot/share/oops/methodData.hpp index 4567b7d4c6c..bbac95b6122 100644 --- a/src/hotspot/share/oops/methodData.hpp +++ b/src/hotspot/share/oops/methodData.hpp @@ -236,12 +236,15 @@ class DataLayout { ProfileData* data_in(); + int size_in_bytes() { + int cells = cell_count(); + assert(cells >= 0, "invalid number of cells"); + return DataLayout::compute_size_in_bytes(cells); + } + int cell_count(); + // GC support void clean_weak_klass_links(bool always_clean); - - // Redefinition support - void clean_weak_method_links(); - DEBUG_ONLY(void verify_clean_weak_method_links();) }; @@ -455,10 +458,6 @@ class ProfileData : public ResourceObj { // GC support virtual void clean_weak_klass_links(bool always_clean) {} - // Redefinition support - virtual void clean_weak_method_links() {} - DEBUG_ONLY(virtual void verify_clean_weak_method_links() {}) - // CI translation: ProfileData can represent both MethodDataOop data // as well as CIMethodData data. This function is provided for translating // an oop in a ProfileData to the ci equivalent. Generally speaking, @@ -2061,14 +2060,15 @@ class MethodData : public Metadata { assert(!out_of_bounds(di), "hint_di out of bounds"); _hint_di = di; } - ProfileData* data_before(int bci) { + + DataLayout* data_layout_before(int bci) { // avoid SEGV on this edge case if (data_size() == 0) return NULL; - int hint = hint_di(); - if (data_layout_at(hint)->bci() <= bci) - return data_at(hint); - return first_data(); + DataLayout* layout = data_layout_at(hint_di()); + if (layout->bci() <= bci) + return layout; + return data_layout_at(first_di()); } // What is the index of the first data entry? @@ -2251,7 +2251,9 @@ class MethodData : public Metadata { // Walk through the data in order. ProfileData* first_data() const { return data_at(first_di()); } ProfileData* next_data(ProfileData* current) const; + DataLayout* next_data_layout(DataLayout* current) const; bool is_valid(ProfileData* current) const { return current != NULL; } + bool is_valid(DataLayout* current) const { return current != NULL; } // Convert a dp (data pointer) to a di (data index). int dp_to_di(address dp) const { @@ -2415,7 +2417,6 @@ class MethodData : public Metadata { void clean_method_data(bool always_clean); void clean_weak_method_links(); - DEBUG_ONLY(void verify_clean_weak_method_links();) Mutex* extra_data_lock() { return &_extra_data_lock; } }; diff --git a/src/hotspot/share/oops/symbol.cpp b/src/hotspot/share/oops/symbol.cpp index bca078a7644..06c2e25d666 100644 --- a/src/hotspot/share/oops/symbol.cpp +++ b/src/hotspot/share/oops/symbol.cpp @@ -26,6 +26,7 @@ #include "precompiled.hpp" #include "classfile/altHashing.hpp" #include "classfile/classLoaderData.hpp" +#include "classfile/vmSymbols.hpp" #include "gc/shared/collectedHeap.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" @@ -40,6 +41,8 @@ #include "runtime/signature.hpp" #include "utilities/utf8.hpp" +Symbol* Symbol::_vm_symbols[vmSymbols::number_of_symbols()]; + uint32_t Symbol::pack_hash_and_refcount(short hash, int refcount) { STATIC_ASSERT(PERM_REFCOUNT == ((1 << 16) - 1)); assert(refcount >= 0, "negative refcount"); @@ -417,3 +420,9 @@ bool Symbol::is_valid(Symbol* s) { // SymbolTable prints this in its statistics NOT_PRODUCT(size_t Symbol::_total_count = 0;) + +#ifndef PRODUCT +bool Symbol::is_valid_id(vmSymbolID vm_symbol_id) { + return vmSymbols::is_valid_id(vm_symbol_id); +} +#endif diff --git a/src/hotspot/share/oops/symbol.hpp b/src/hotspot/share/oops/symbol.hpp index 51add5ad42a..f1f51fc2d18 100644 --- a/src/hotspot/share/oops/symbol.hpp +++ b/src/hotspot/share/oops/symbol.hpp @@ -28,6 +28,7 @@ #include "memory/allocation.hpp" #include "utilities/exceptions.hpp" #include "utilities/macros.hpp" +#include "utilities/vmEnums.hpp" // A Symbol is a canonicalized string. // All Symbols reside in global SymbolTable and are reference counted. @@ -103,6 +104,8 @@ class ClassLoaderData; class Symbol : public MetaspaceObj { friend class VMStructs; friend class SymbolTable; + friend class vmSymbols; + friend class JVMCIVMStructs; private: @@ -112,6 +115,8 @@ class Symbol : public MetaspaceObj { u2 _length; u1 _body[2]; + static Symbol* _vm_symbols[]; + enum { max_symbol_length = 0xffff }; @@ -275,6 +280,13 @@ class Symbol : public MetaspaceObj { static bool is_valid(Symbol* s); + static bool is_valid_id(vmSymbolID vm_symbol_id) PRODUCT_RETURN_(return true;); + + static Symbol* vm_symbol_at(vmSymbolID vm_symbol_id) { + assert(is_valid_id(vm_symbol_id), "must be"); + return _vm_symbols[static_cast(vm_symbol_id)]; + } + #ifndef PRODUCT // Empty constructor to create a dummy symbol object on stack // only for getting its vtable pointer. diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index eb1293afa12..ed3690e59b5 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -328,10 +328,9 @@ Node *AddINode::Ideal(PhaseGVN *phase, bool can_reshape) { //------------------------------Identity--------------------------------------- // Fold (x-y)+y OR y+(x-y) into x Node* AddINode::Identity(PhaseGVN* phase) { - if( in(1)->Opcode() == Op_SubI && phase->eqv(in(1)->in(2),in(2)) ) { + if (in(1)->Opcode() == Op_SubI && in(1)->in(2) == in(2)) { return in(1)->in(1); - } - else if( in(2)->Opcode() == Op_SubI && phase->eqv(in(2)->in(2),in(1)) ) { + } else if (in(2)->Opcode() == Op_SubI && in(2)->in(2) == in(1)) { return in(2)->in(1); } return AddNode::Identity(phase); @@ -445,10 +444,9 @@ Node *AddLNode::Ideal(PhaseGVN *phase, bool can_reshape) { //------------------------------Identity--------------------------------------- // Fold (x-y)+y OR y+(x-y) into x Node* AddLNode::Identity(PhaseGVN* phase) { - if( in(1)->Opcode() == Op_SubL && phase->eqv(in(1)->in(2),in(2)) ) { + if (in(1)->Opcode() == Op_SubL && in(1)->in(2) == in(2)) { return in(1)->in(1); - } - else if( in(2)->Opcode() == Op_SubL && phase->eqv(in(2)->in(2),in(1)) ) { + } else if (in(2)->Opcode() == Op_SubL && in(2)->in(2) == in(1)) { return in(2)->in(1); } return AddNode::Identity(phase); @@ -736,7 +734,7 @@ uint AddPNode::match_edge(uint idx) const { //------------------------------Identity--------------------------------------- Node* OrINode::Identity(PhaseGVN* phase) { // x | x => x - if (phase->eqv(in(1), in(2))) { + if (in(1) == in(2)) { return in(1); } @@ -823,7 +821,7 @@ const Type *OrINode::add_ring( const Type *t0, const Type *t1 ) const { //------------------------------Identity--------------------------------------- Node* OrLNode::Identity(PhaseGVN* phase) { // x | x => x - if (phase->eqv(in(1), in(2))) { + if (in(1) == in(2)) { return in(1); } @@ -1004,6 +1002,14 @@ const Type *MaxINode::add_ring( const Type *t0, const Type *t1 ) const { return TypeInt::make( MAX2(r0->_lo,r1->_lo), MAX2(r0->_hi,r1->_hi), MAX2(r0->_widen,r1->_widen) ); } +// Check if addition of an integer with type 't' and a constant 'c' can overflow +static bool can_overflow(const TypeInt* t, jint c) { + jint t_lo = t->_lo; + jint t_hi = t->_hi; + return ((c < 0 && (java_add(t_lo, c) > t_lo)) || + (c > 0 && (java_add(t_hi, c) < t_hi))); +} + //============================================================================= //------------------------------Idealize--------------------------------------- // MINs show up in range-check loop limit calculations. Look for @@ -1026,7 +1032,7 @@ Node *MinINode::Ideal(PhaseGVN *phase, bool can_reshape) { // Get left input & constant Node *x = l; - int x_off = 0; + jint x_off = 0; if( x->Opcode() == Op_AddI && // Check for "x+c0" and collect constant x->in(2)->is_Con() ) { const Type *t = x->in(2)->bottom_type(); @@ -1037,7 +1043,7 @@ Node *MinINode::Ideal(PhaseGVN *phase, bool can_reshape) { // Scan a right-spline-tree for MINs Node *y = r; - int y_off = 0; + jint y_off = 0; // Check final part of MIN tree if( y->Opcode() == Op_AddI && // Check for "y+c1" and collect constant y->in(2)->is_Con() ) { @@ -1051,6 +1057,7 @@ Node *MinINode::Ideal(PhaseGVN *phase, bool can_reshape) { return this; } + const TypeInt* tx = phase->type(x)->isa_int(); if( r->Opcode() == Op_MinI ) { assert( r != r->in(2), "dead loop in MinINode::Ideal" ); @@ -1067,18 +1074,23 @@ Node *MinINode::Ideal(PhaseGVN *phase, bool can_reshape) { if( x->_idx > y->_idx ) return new MinINode(r->in(1),phase->transform(new MinINode(l,r->in(2)))); - // See if covers: MIN2(x+c0,MIN2(y+c1,z)) - if( !phase->eqv(x,y) ) return NULL; - // If (y == x) transform MIN2(x+c0, MIN2(x+c1,z)) into - // MIN2(x+c0 or x+c1 which less, z). - return new MinINode(phase->transform(new AddINode(x,phase->intcon(MIN2(x_off,y_off)))),r->in(2)); + // Transform MIN2(x + c0, MIN2(x + c1, z)) into MIN2(x + MIN2(c0, c1), z) + // if x == y and the additions can't overflow. + if (x == y && tx != NULL && + !can_overflow(tx, x_off) && + !can_overflow(tx, y_off)) { + return new MinINode(phase->transform(new AddINode(x, phase->intcon(MIN2(x_off, y_off)))), r->in(2)); + } } else { - // See if covers: MIN2(x+c0,y+c1) - if( !phase->eqv(x,y) ) return NULL; - // If (y == x) transform MIN2(x+c0,x+c1) into x+c0 or x+c1 which less. - return new AddINode(x,phase->intcon(MIN2(x_off,y_off))); + // Transform MIN2(x + c0, y + c1) into x + MIN2(c0, c1) + // if x == y and the additions can't overflow. + if (x == y && tx != NULL && + !can_overflow(tx, x_off) && + !can_overflow(tx, y_off)) { + return new AddINode(x,phase->intcon(MIN2(x_off,y_off))); + } } - + return NULL; } //------------------------------add_ring--------------------------------------- diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index 6e6bca1d493..06f647ba7f0 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -281,6 +281,30 @@ class MinINode : public MaxNode { virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); }; +//------------------------------MaxLNode--------------------------------------- +// MAXimum of 2 longs. +class MaxLNode : public MaxNode { +public: + MaxLNode(Node *in1, Node *in2) : MaxNode(in1, in2) {} + virtual int Opcode() const; + virtual const Type *add_ring(const Type*, const Type*) const { return TypeLong::LONG; } + virtual const Type *add_id() const { return TypeLong::make(min_jlong); } + virtual const Type *bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } +}; + +//------------------------------MinLNode--------------------------------------- +// MINimum of 2 longs. +class MinLNode : public MaxNode { +public: + MinLNode(Node *in1, Node *in2) : MaxNode(in1, in2) {} + virtual int Opcode() const; + virtual const Type *add_ring(const Type*, const Type*) const { return TypeLong::LONG; } + virtual const Type *add_id() const { return TypeLong::make(max_jlong); } + virtual const Type *bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } +}; + //------------------------------MaxFNode--------------------------------------- // Maximum of 2 floats. class MaxFNode : public MaxNode { diff --git a/src/hotspot/share/opto/arraycopynode.cpp b/src/hotspot/share/opto/arraycopynode.cpp index d73b2634cf0..9d9b21ae95d 100644 --- a/src/hotspot/share/opto/arraycopynode.cpp +++ b/src/hotspot/share/opto/arraycopynode.cpp @@ -183,7 +183,7 @@ Node* ArrayCopyNode::try_clone_instance(PhaseGVN *phase, bool can_reshape, int c const Type* src_type = phase->type(base_src); - MergeMemNode* mem = MergeMemNode::make(in_mem); + MergeMemNode* mem = phase->transform(MergeMemNode::make(in_mem))->as_MergeMem(); const TypeInstPtr* inst_src = src_type->isa_instptr(); @@ -367,7 +367,7 @@ void ArrayCopyNode::array_copy_test_overlap(PhaseGVN *phase, bool can_reshape, b Node* ArrayCopyNode::array_copy_forward(PhaseGVN *phase, bool can_reshape, Node*& forward_ctl, - MergeMemNode* mm, + Node* mem, const TypePtr* atp_src, const TypePtr* atp_dest, Node* adr_src, @@ -379,7 +379,7 @@ Node* ArrayCopyNode::array_copy_forward(PhaseGVN *phase, int count) { if (!forward_ctl->is_top()) { // copy forward - mm = mm->clone()->as_MergeMem(); + MergeMemNode* mm = MergeMemNode::make(mem); if (count > 0) { BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); @@ -392,7 +392,7 @@ Node* ArrayCopyNode::array_copy_forward(PhaseGVN *phase, v = load(bs, phase, forward_ctl, mm, next_src, atp_src, value_type, copy_type); store(bs, phase, forward_ctl, mm, next_dest, atp_dest, v, value_type, copy_type); } - } else if(can_reshape) { + } else if (can_reshape) { PhaseIterGVN* igvn = phase->is_IterGVN(); igvn->_worklist.push(adr_src); igvn->_worklist.push(adr_dest); @@ -405,7 +405,7 @@ Node* ArrayCopyNode::array_copy_forward(PhaseGVN *phase, Node* ArrayCopyNode::array_copy_backward(PhaseGVN *phase, bool can_reshape, Node*& backward_ctl, - MergeMemNode* mm, + Node* mem, const TypePtr* atp_src, const TypePtr* atp_dest, Node* adr_src, @@ -417,7 +417,7 @@ Node* ArrayCopyNode::array_copy_backward(PhaseGVN *phase, int count) { if (!backward_ctl->is_top()) { // copy backward - mm = mm->clone()->as_MergeMem(); + MergeMemNode* mm = MergeMemNode::make(mem); BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); assert(copy_type != T_OBJECT || !bs->array_copy_requires_gc_barriers(false, T_OBJECT, false, BarrierSetC2::Optimization), "only tightly coupled allocations for object arrays"); @@ -432,7 +432,7 @@ Node* ArrayCopyNode::array_copy_backward(PhaseGVN *phase, } Node* v = load(bs, phase, backward_ctl, mm, adr_src, atp_src, value_type, copy_type); store(bs, phase, backward_ctl, mm, adr_dest, atp_dest, v, value_type, copy_type); - } else if(can_reshape) { + } else if (can_reshape) { PhaseIterGVN* igvn = phase->is_IterGVN(); igvn->_worklist.push(adr_src); igvn->_worklist.push(adr_dest); @@ -564,11 +564,7 @@ Node *ArrayCopyNode::Ideal(PhaseGVN *phase, bool can_reshape) { Node* dest = in(ArrayCopyNode::Dest); const TypePtr* atp_src = get_address_type(phase, _src_type, src); const TypePtr* atp_dest = get_address_type(phase, _dest_type, dest); - - Node *in_mem = in(TypeFunc::Memory); - if (!in_mem->is_MergeMem()) { - in_mem = MergeMemNode::make(in_mem); - } + Node* in_mem = in(TypeFunc::Memory); if (can_reshape) { assert(!phase->is_IterGVN()->delay_transform(), "cannot delay transforms"); @@ -580,13 +576,13 @@ Node *ArrayCopyNode::Ideal(PhaseGVN *phase, bool can_reshape) { array_copy_test_overlap(phase, can_reshape, disjoint_bases, count, forward_ctl, backward_ctl); Node* forward_mem = array_copy_forward(phase, can_reshape, forward_ctl, - in_mem->as_MergeMem(), + in_mem, atp_src, atp_dest, adr_src, base_src, adr_dest, base_dest, copy_type, value_type, count); Node* backward_mem = array_copy_backward(phase, can_reshape, backward_ctl, - in_mem->as_MergeMem(), + in_mem, atp_src, atp_dest, adr_src, base_src, adr_dest, base_dest, copy_type, value_type, count); diff --git a/src/hotspot/share/opto/arraycopynode.hpp b/src/hotspot/share/opto/arraycopynode.hpp index 4764715a60a..da17efc1607 100644 --- a/src/hotspot/share/opto/arraycopynode.hpp +++ b/src/hotspot/share/opto/arraycopynode.hpp @@ -100,12 +100,12 @@ class ArrayCopyNode : public CallNode { bool disjoint_bases, int count, Node*& forward_ctl, Node*& backward_ctl); Node* array_copy_forward(PhaseGVN *phase, bool can_reshape, Node*& ctl, - MergeMemNode* mm, + Node* mem, const TypePtr* atp_src, const TypePtr* atp_dest, Node* adr_src, Node* base_src, Node* adr_dest, Node* base_dest, BasicType copy_type, const Type* value_type, int count); Node* array_copy_backward(PhaseGVN *phase, bool can_reshape, Node*& ctl, - MergeMemNode* mm, + Node* mem, const TypePtr* atp_src, const TypePtr* atp_dest, Node* adr_src, Node* base_src, Node* adr_dest, Node* base_dest, BasicType copy_type, const Type* value_type, int count); diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 302749b5b1b..0bf650c2eb8 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -53,8 +53,8 @@ "Randomize worklist traversal in IGVN") \ \ product(uint, StressSeed, 0, DIAGNOSTIC, \ - "Seed for IGVN stress testing (if unset, a random one is " \ - "generated") \ + "Seed for randomized stress testing (if unset, a random one is " \ + "generated)") \ range(0, max_juint) \ \ develop(bool, StressMethodHandleLinkerInlining, false, \ @@ -360,9 +360,6 @@ "Limit of ops to make speculative when using CMOVE") \ range(0, max_jint) \ \ - product(bool, UseRDPCForConstantTableBase, false, \ - "Use Sparc RDPC instruction for the constant table base.") \ - \ notproduct(bool, PrintIdealGraph, false, \ "Print ideal graph to XML file / network interface. " \ "By default attempts to connect to the visualizer on a socket.") \ @@ -743,6 +740,15 @@ product(bool, UseMontgomerySquareIntrinsic, false, DIAGNOSTIC, \ "Enables intrinsification of BigInteger.montgomerySquare()") \ \ + product(bool, EnableVectorSupport, false, EXPERIMENTAL, \ + "Enables VectorSupport intrinsics") \ + \ + product(bool, EnableVectorReboxing, false, EXPERIMENTAL, \ + "Enables reboxing of vectors") \ + \ + product(bool, EnableVectorAggressiveReboxing, false, EXPERIMENTAL, \ + "Enables aggressive reboxing of vectors") \ + \ product(bool, UseTypeSpeculation, true, \ "Speculatively propagate types from profiles") \ \ @@ -778,7 +784,13 @@ "Move predicates out of loops based on profiling data") \ \ product(bool, ExpandSubTypeCheckAtParseTime, false, DIAGNOSTIC, \ - "Do not use subtype check macro node") + "Do not use subtype check macro node") \ + \ + develop(uintx, StressLongCountedLoop, 0, \ + "if > 0, convert int counted loops to long counted loops" \ + "to stress handling of long counted loops: run inner loop" \ + "for at most jint_max / StressLongCountedLoop") \ + range(0, max_juint) \ // end of C2_FLAGS diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index ea2ccc3d3da..5576f266bae 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -93,8 +93,7 @@ void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, boo assert(is_initialized(), "Compiler thread must be initialized"); bool subsume_loads = SubsumeLoads; - bool do_escape_analysis = DoEscapeAnalysis && !env->should_retain_local_variables() - && !env->jvmti_can_get_owned_monitor_info(); + bool do_escape_analysis = DoEscapeAnalysis; bool eliminate_boxing = EliminateAutoBox; while (!env->failing()) { @@ -496,6 +495,7 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt case vmIntrinsics::_indexOfIU: case vmIntrinsics::_indexOfIUL: case vmIntrinsics::_indexOfU_char: + case vmIntrinsics::_indexOfL_char: case vmIntrinsics::_toBytesStringU: case vmIntrinsics::_getCharsStringU: case vmIntrinsics::_getCharStringU: @@ -626,6 +626,7 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt case vmIntrinsics::_sha_implCompress: case vmIntrinsics::_sha2_implCompress: case vmIntrinsics::_sha5_implCompress: + case vmIntrinsics::_sha3_implCompress: case vmIntrinsics::_digestBase_implCompressMB: case vmIntrinsics::_multiplyToLen: case vmIntrinsics::_squareToLen: @@ -648,6 +649,28 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt case vmIntrinsics::_isCompileConstant: case vmIntrinsics::_Preconditions_checkIndex: break; + + case vmIntrinsics::_VectorUnaryOp: + case vmIntrinsics::_VectorBinaryOp: + case vmIntrinsics::_VectorTernaryOp: + case vmIntrinsics::_VectorBroadcastCoerced: + case vmIntrinsics::_VectorShuffleIota: + case vmIntrinsics::_VectorShuffleToVector: + case vmIntrinsics::_VectorLoadOp: + case vmIntrinsics::_VectorStoreOp: + case vmIntrinsics::_VectorGatherOp: + case vmIntrinsics::_VectorScatterOp: + case vmIntrinsics::_VectorReductionCoerced: + case vmIntrinsics::_VectorTest: + case vmIntrinsics::_VectorBlend: + case vmIntrinsics::_VectorRearrange: + case vmIntrinsics::_VectorCompare: + case vmIntrinsics::_VectorBroadcastInt: + case vmIntrinsics::_VectorConvert: + case vmIntrinsics::_VectorInsert: + case vmIntrinsics::_VectorExtract: + return EnableVectorSupport; + default: return false; } diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index 606325d4dd6..b3e9a38e284 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -429,7 +429,7 @@ void LateInlineCallGenerator::do_late_inline() { // This check is done here because for_method_handle_inline() method // needs jvms for inlined state. if (!do_late_inline_check(jvms)) { - map->disconnect_inputs(NULL, C); + map->disconnect_inputs(C); return; } @@ -456,7 +456,6 @@ void LateInlineCallGenerator::do_late_inline() { result = (result_size == 1) ? kit.pop() : kit.pop_pair(); } - C->set_has_loops(C->has_loops() || _inline_cg->method()->has_loops()); C->env()->notice_inlined_method(_inline_cg->method()); C->set_inlining_progress(true); C->set_do_cleanup(kit.stopped()); // path is dead; needs cleanup @@ -537,7 +536,7 @@ class LateInlineStringCallGenerator : public LateInlineCallGenerator { C->add_string_late_inline(this); - JVMState* new_jvms = DirectCallGenerator::generate(jvms); + JVMState* new_jvms = DirectCallGenerator::generate(jvms); return new_jvms; } @@ -561,7 +560,7 @@ class LateInlineBoxingCallGenerator : public LateInlineCallGenerator { C->add_boxing_late_inline(this); - JVMState* new_jvms = DirectCallGenerator::generate(jvms); + JVMState* new_jvms = DirectCallGenerator::generate(jvms); return new_jvms; } }; @@ -570,6 +569,28 @@ CallGenerator* CallGenerator::for_boxing_late_inline(ciMethod* method, CallGener return new LateInlineBoxingCallGenerator(method, inline_cg); } +class LateInlineVectorReboxingCallGenerator : public LateInlineCallGenerator { + + public: + LateInlineVectorReboxingCallGenerator(ciMethod* method, CallGenerator* inline_cg) : + LateInlineCallGenerator(method, inline_cg, /*is_pure=*/true) {} + + virtual JVMState* generate(JVMState* jvms) { + Compile *C = Compile::current(); + + C->log_inline_id(this); + + C->add_vector_reboxing_late_inline(this); + + JVMState* new_jvms = DirectCallGenerator::generate(jvms); + return new_jvms; + } +}; + +// static CallGenerator* for_vector_reboxing_late_inline(ciMethod* m, CallGenerator* inline_cg); +CallGenerator* CallGenerator::for_vector_reboxing_late_inline(ciMethod* method, CallGenerator* inline_cg) { + return new LateInlineVectorReboxingCallGenerator(method, inline_cg); +} //---------------------------WarmCallGenerator-------------------------------- // Internal class which handles initial deferral of inlining decisions. class WarmCallGenerator : public CallGenerator { diff --git a/src/hotspot/share/opto/callGenerator.hpp b/src/hotspot/share/opto/callGenerator.hpp index 8bfbe76194f..952dcf202c9 100644 --- a/src/hotspot/share/opto/callGenerator.hpp +++ b/src/hotspot/share/opto/callGenerator.hpp @@ -127,6 +127,7 @@ class CallGenerator : public ResourceObj { static CallGenerator* for_mh_late_inline(ciMethod* caller, ciMethod* callee, bool input_not_const); static CallGenerator* for_string_late_inline(ciMethod* m, CallGenerator* inline_cg); static CallGenerator* for_boxing_late_inline(ciMethod* m, CallGenerator* inline_cg); + static CallGenerator* for_vector_reboxing_late_inline(ciMethod* m, CallGenerator* inline_cg); // How to make a call but defer the decision whether to inline or not. static CallGenerator* for_warm_call(WarmCallInfo* ci, diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index 268cec7732c..78a632bf8ac 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -967,6 +967,46 @@ bool CallJavaNode::cmp( const Node &n ) const { return CallNode::cmp(call) && _method == call._method && _override_symbolic_info == call._override_symbolic_info; } + +void CallJavaNode::copy_call_debug_info(PhaseIterGVN* phase, SafePointNode *sfpt) { + // Copy debug information and adjust JVMState information + uint old_dbg_start = sfpt->is_Call() ? sfpt->as_Call()->tf()->domain()->cnt() : (uint)TypeFunc::Parms+1; + uint new_dbg_start = tf()->domain()->cnt(); + int jvms_adj = new_dbg_start - old_dbg_start; + assert (new_dbg_start == req(), "argument count mismatch"); + Compile* C = phase->C; + + // SafePointScalarObject node could be referenced several times in debug info. + // Use Dict to record cloned nodes. + Dict* sosn_map = new Dict(cmpkey,hashkey); + for (uint i = old_dbg_start; i < sfpt->req(); i++) { + Node* old_in = sfpt->in(i); + // Clone old SafePointScalarObjectNodes, adjusting their field contents. + if (old_in != NULL && old_in->is_SafePointScalarObject()) { + SafePointScalarObjectNode* old_sosn = old_in->as_SafePointScalarObject(); + bool new_node; + Node* new_in = old_sosn->clone(sosn_map, new_node); + if (new_node) { // New node? + new_in->set_req(0, C->root()); // reset control edge + new_in = phase->transform(new_in); // Register new node. + } + old_in = new_in; + } + add_req(old_in); + } + + // JVMS may be shared so clone it before we modify it + set_jvms(sfpt->jvms() != NULL ? sfpt->jvms()->clone_deep(C) : NULL); + for (JVMState *jvms = this->jvms(); jvms != NULL; jvms = jvms->caller()) { + jvms->set_map(this); + jvms->set_locoff(jvms->locoff()+jvms_adj); + jvms->set_stkoff(jvms->stkoff()+jvms_adj); + jvms->set_monoff(jvms->monoff()+jvms_adj); + jvms->set_scloff(jvms->scloff()+jvms_adj); + jvms->set_endoff(jvms->endoff()+jvms_adj); + } +} + #ifdef ASSERT bool CallJavaNode::validate_symbolic_info() const { if (method() == NULL) { @@ -1159,7 +1199,9 @@ Node* SafePointNode::Identity(PhaseGVN* phase) { if( in(TypeFunc::Control)->is_SafePoint() ) return in(TypeFunc::Control); - if( in(0)->is_Proj() ) { + // Transforming long counted loops requires a safepoint node. Do not + // eliminate a safepoint until loop opts are over. + if (in(0)->is_Proj() && !phase->C->major_progress()) { Node *n0 = in(0)->in(0); // Check if he is a call projection (except Leaf Call) if( n0->is_Catch() ) { @@ -1182,8 +1224,12 @@ Node* SafePointNode::Identity(PhaseGVN* phase) { //------------------------------Value------------------------------------------ const Type* SafePointNode::Value(PhaseGVN* phase) const { - if( phase->type(in(0)) == Type::TOP ) return Type::TOP; - if( phase->eqv( in(0), this ) ) return Type::TOP; // Dead infinite loop + if (phase->type(in(0)) == Type::TOP) { + return Type::TOP; + } + if (in(0) == this) { + return Type::TOP; // Dead infinite loop + } return Type::CONTROL; } @@ -1332,11 +1378,13 @@ uint SafePointScalarObjectNode::match_edge(uint idx) const { } SafePointScalarObjectNode* -SafePointScalarObjectNode::clone(Dict* sosn_map) const { +SafePointScalarObjectNode::clone(Dict* sosn_map, bool& new_node) const { void* cached = (*sosn_map)[(void*)this]; if (cached != NULL) { + new_node = false; return (SafePointScalarObjectNode*)cached; } + new_node = true; SafePointScalarObjectNode* res = (SafePointScalarObjectNode*)Node::clone(); sosn_map->Insert((void*)this, (void*)res); return res; diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index 16f5a0baae5..4688b01d534 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -329,7 +329,8 @@ class SafePointNode : public MultiNode { const TypePtr* adr_type = NULL) : MultiNode( edges ), _jvms(jvms), - _adr_type(adr_type) + _adr_type(adr_type), + _has_ea_local_in_scope(false) { init_class_id(Class_SafePoint); } @@ -337,6 +338,7 @@ class SafePointNode : public MultiNode { JVMState* const _jvms; // Pointer to list of JVM State objects const TypePtr* _adr_type; // What type of memory does this node produce? ReplacedNodes _replaced_nodes; // During parsing: list of pair of nodes from calls to GraphKit::replace_in_map() + bool _has_ea_local_in_scope; // NoEscape or ArgEscape objects in JVM States // Many calls take *all* of memory as input, // but some produce a limited subset of that memory as output. @@ -456,6 +458,12 @@ class SafePointNode : public MultiNode { bool has_replaced_nodes() const { return !_replaced_nodes.is_empty(); } + void set_has_ea_local_in_scope(bool b) { + _has_ea_local_in_scope = b; + } + bool has_ea_local_in_scope() const { + return _has_ea_local_in_scope; + } void disconnect_from_root(PhaseIterGVN *igvn); @@ -527,7 +535,7 @@ class SafePointScalarObjectNode: public TypeNode { // corresponds appropriately to "this" in "new_call". Assumes that // "sosn_map" is a map, specific to the translation of "s" to "new_call", // mapping old SafePointScalarObjectNodes to new, to avoid multiple copies. - SafePointScalarObjectNode* clone(Dict* sosn_map) const; + SafePointScalarObjectNode* clone(Dict* sosn_map, bool& new_node) const; #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; @@ -635,6 +643,8 @@ class CallNode : public SafePointNode { bool is_call_to_arraycopystub() const; + virtual void copy_call_debug_info(PhaseIterGVN* phase, SafePointNode *sfpt) {} + #ifndef PRODUCT virtual void dump_req(outputStream *st = tty) const; virtual void dump_spec(outputStream *st) const; @@ -656,6 +666,7 @@ class CallJavaNode : public CallNode { bool _method_handle_invoke; bool _override_symbolic_info; // Override symbolic call site info from bytecode ciMethod* _method; // Method being direct called + bool _arg_escape; // ArgEscape in parameter list public: const int _bci; // Byte Code Index of call byte code CallJavaNode(const TypeFunc* tf , address addr, ciMethod* method, int bci) @@ -663,7 +674,8 @@ class CallJavaNode : public CallNode { _optimized_virtual(false), _method_handle_invoke(false), _override_symbolic_info(false), - _method(method), _bci(bci) + _method(method), + _arg_escape(false), _bci(bci) { init_class_id(Class_CallJava); } @@ -677,6 +689,9 @@ class CallJavaNode : public CallNode { bool is_method_handle_invoke() const { return _method_handle_invoke; } void set_override_symbolic_info(bool f) { _override_symbolic_info = f; } bool override_symbolic_info() const { return _override_symbolic_info; } + void set_arg_escape(bool f) { _arg_escape = f; } + bool arg_escape() const { return _arg_escape; } + void copy_call_debug_info(PhaseIterGVN* phase, SafePointNode *sfpt); DEBUG_ONLY( bool validate_symbolic_info() const; ) @@ -701,8 +716,6 @@ class CallStaticJavaNode : public CallJavaNode { init_flags(Flag_is_macro); C->add_macro_node(this); } - _is_scalar_replaceable = false; - _is_non_escaping = false; } CallStaticJavaNode(const TypeFunc* tf, address addr, const char* name, int bci, const TypePtr* adr_type) @@ -710,15 +723,9 @@ class CallStaticJavaNode : public CallJavaNode { init_class_id(Class_CallStaticJava); // This node calls a runtime stub, which often has narrow memory effects. _adr_type = adr_type; - _is_scalar_replaceable = false; - _is_non_escaping = false; _name = name; } - // Result of Escape Analysis - bool _is_scalar_replaceable; - bool _is_non_escaping; - // If this is an uncommon trap, return the request code, else zero. int uncommon_trap_request() const; static int extract_uncommon_trap_request(const Node* call); diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index 26f3cb1345c..11927391907 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -231,37 +231,56 @@ Node *CastIINode::Ideal(PhaseGVN *phase, bool can_reshape) { // Do not narrow the type of range check dependent CastIINodes to // avoid corruption of the graph if a CastII is replaced by TOP but // the corresponding range check is not removed. - if (can_reshape && !_range_check_dependency && !phase->C->major_progress()) { - const TypeInt* this_type = this->type()->is_int(); - const TypeInt* in_type = phase->type(in(1))->isa_int(); - if (in_type != NULL && this_type != NULL && - (in_type->_lo != this_type->_lo || - in_type->_hi != this_type->_hi)) { - jint lo1 = this_type->_lo; - jint hi1 = this_type->_hi; - int w1 = this_type->_widen; - - if (lo1 >= 0) { - // Keep a range assertion of >=0. - lo1 = 0; hi1 = max_jint; - } else if (hi1 < 0) { - // Keep a range assertion of <0. - lo1 = min_jint; hi1 = -1; - } else { - lo1 = min_jint; hi1 = max_jint; - } - const TypeInt* wtype = TypeInt::make(MAX2(in_type->_lo, lo1), - MIN2(in_type->_hi, hi1), - MAX2((int)in_type->_widen, w1)); - if (wtype != type()) { - set_type(wtype); - return this; + if (can_reshape && !_range_check_dependency) { + if (phase->C->post_loop_opts_phase()) { + const TypeInt* this_type = this->type()->is_int(); + const TypeInt* in_type = phase->type(in(1))->isa_int(); + if (in_type != NULL && this_type != NULL && + (in_type->_lo != this_type->_lo || + in_type->_hi != this_type->_hi)) { + jint lo1 = this_type->_lo; + jint hi1 = this_type->_hi; + int w1 = this_type->_widen; + + if (lo1 >= 0) { + // Keep a range assertion of >=0. + lo1 = 0; hi1 = max_jint; + } else if (hi1 < 0) { + // Keep a range assertion of <0. + lo1 = min_jint; hi1 = -1; + } else { + lo1 = min_jint; hi1 = max_jint; + } + const TypeInt* wtype = TypeInt::make(MAX2(in_type->_lo, lo1), + MIN2(in_type->_hi, hi1), + MAX2((int)in_type->_widen, w1)); + if (wtype != type()) { + set_type(wtype); + return this; + } } + } else { + phase->C->record_for_post_loop_opts_igvn(this); } } return NULL; } +Node* CastIINode::Identity(PhaseGVN* phase) { + Node* progress = ConstraintCastNode::Identity(phase); + if (progress != this) { + return progress; + } + if (_range_check_dependency) { + if (phase->C->post_loop_opts_phase()) { + return this->in(1); + } else { + phase->C->record_for_post_loop_opts_igvn(this); + } + } + return this; +} + bool CastIINode::cmp(const Node &n) const { return ConstraintCastNode::cmp(n) && ((CastIINode&)n)._range_check_dependency == _range_check_dependency; } @@ -290,9 +309,17 @@ Node* CheckCastPPNode::Identity(PhaseGVN* phase) { if (_carry_dependency) { return this; } - // Toned down to rescue meeting at a Phi 3 different oops all implementing - // the same interface. - return (phase->type(in(1)) == phase->type(this)) ? in(1) : this; + const Type* t = phase->type(in(1)); + if (EnableVectorReboxing && in(1)->Opcode() == Op_VectorBox) { + if (t->higher_equal_speculative(phase->type(this))) { + return in(1); + } + } else if (t == phase->type(this)) { + // Toned down to rescue meeting at a Phi 3 different oops all implementing + // the same interface. + return in(1); + } + return this; } //------------------------------Value------------------------------------------ diff --git a/src/hotspot/share/opto/castnode.hpp b/src/hotspot/share/opto/castnode.hpp index e4fe29a438e..bcc8e0f26a8 100644 --- a/src/hotspot/share/opto/castnode.hpp +++ b/src/hotspot/share/opto/castnode.hpp @@ -75,6 +75,7 @@ class CastIINode: public ConstraintCastNode { } virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } + virtual Node* Identity(PhaseGVN* phase); virtual const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); const bool has_range_check() { diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 667641b20ef..486d0b2bec8 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -43,6 +43,7 @@ #include "opto/regmask.hpp" #include "opto/runtime.hpp" #include "opto/subnode.hpp" +#include "opto/vectornode.hpp" #include "utilities/vmError.hpp" // Portions of code courtesy of Clifford Click @@ -315,37 +316,60 @@ static bool check_compare_clipping( bool less_than, IfNode *iff, ConNode *limit, //------------------------------is_unreachable_region-------------------------- // Find if the Region node is reachable from the root. -bool RegionNode::is_unreachable_region(PhaseGVN *phase) const { - assert(req() == 2, ""); +bool RegionNode::is_unreachable_region(const PhaseGVN* phase) { + Node* top = phase->C->top(); + assert(req() == 2 || (req() == 3 && in(1) != NULL && in(2) == top), "sanity check arguments"); + if (_is_unreachable_region) { + // Return cached result from previous evaluation which should still be valid + assert(is_unreachable_from_root(phase), "walk the graph again and check if its indeed unreachable"); + return true; + } // First, cut the simple case of fallthrough region when NONE of // region's phis references itself directly or through a data node. + if (is_possible_unsafe_loop(phase)) { + // If we have a possible unsafe loop, check if the region node is actually unreachable from root. + if (is_unreachable_from_root(phase)) { + _is_unreachable_region = true; + return true; + } + } + return false; +} + +bool RegionNode::is_possible_unsafe_loop(const PhaseGVN* phase) const { uint max = outcnt(); uint i; for (i = 0; i < max; i++) { - Node* phi = raw_out(i); - if (phi != NULL && phi->is_Phi()) { - assert(phase->eqv(phi->in(0), this) && phi->req() == 2, ""); - if (phi->outcnt() == 0) + Node* n = raw_out(i); + if (n != NULL && n->is_Phi()) { + PhiNode* phi = n->as_Phi(); + assert(phi->in(0) == this, "sanity check phi"); + if (phi->outcnt() == 0) { continue; // Safe case - no loops + } if (phi->outcnt() == 1) { Node* u = phi->raw_out(0); // Skip if only one use is an other Phi or Call or Uncommon trap. // It is safe to consider this case as fallthrough. - if (u != NULL && (u->is_Phi() || u->is_CFG())) + if (u != NULL && (u->is_Phi() || u->is_CFG())) { continue; + } } // Check when phi references itself directly or through an other node. - if (phi->as_Phi()->simple_data_loop_check(phi->in(1)) >= PhiNode::Unsafe) + if (phi->as_Phi()->simple_data_loop_check(phi->in(1)) >= PhiNode::Unsafe) { break; // Found possible unsafe data loop. + } } } - if (i >= max) + if (i >= max) { return false; // An unsafe case was NOT found - don't need graph walk. + } + return true; +} - // Unsafe case - check if the Region node is reachable from root. +bool RegionNode::is_unreachable_from_root(const PhaseGVN* phase) const { ResourceMark rm; - Node_List nstack; VectorSet visited; @@ -359,7 +383,7 @@ bool RegionNode::is_unreachable_region(PhaseGVN *phase) const { for (uint i = 0; i < max; i++) { Node* m = n->raw_out(i); if (m != NULL && m->is_CFG()) { - if (phase->eqv(m, this)) { + if (m == this) { return false; // We reached the Region node - it is not dead. } if (!visited.test_set(m->_idx)) @@ -367,7 +391,6 @@ bool RegionNode::is_unreachable_region(PhaseGVN *phase) const { } } } - return true; // The Region node is unreachable - it is dead. } @@ -545,7 +568,7 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { for (j = outs(); has_out(j); j++) { Node *n = out(j); if( n->is_Phi() ) { - assert( igvn->eqv(n->in(0), this), "" ); + assert(n->in(0) == this, ""); assert( n->req() == 2 && n->in(1) != NULL, "Only one data input expected" ); // Break dead loop data path. // Eagerly replace phis with top to avoid regionless phis. @@ -596,7 +619,7 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { // The fallthrough case since we already checked dead loops above. parent_ctrl = in(1); assert(parent_ctrl != NULL, "Region is a copy of some non-null control"); - assert(!igvn->eqv(parent_ctrl, this), "Close dead loop"); + assert(parent_ctrl != this, "Close dead loop"); } if (!add_to_worklist) igvn->add_users_to_worklist(this); // Check for further allowed opts @@ -618,7 +641,7 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { igvn->replace_node(n, in); } else if( n->is_Region() ) { // Update all incoming edges - assert( !igvn->eqv(n, this), "Must be removed from DefUse edges"); + assert(n != this, "Must be removed from DefUse edges"); uint uses_found = 0; for( uint k=1; k < n->req(); k++ ) { if( n->in(k) == this ) { @@ -631,12 +654,12 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { } } else { - assert( igvn->eqv(n->in(0), this), "Expect RegionNode to be control parent"); + assert(n->in(0) == this, "Expect RegionNode to be control parent"); n->set_req(0, parent_ctrl); } #ifdef ASSERT for( uint k=0; k < n->req(); k++ ) { - assert( !igvn->eqv(n->in(k), this), "All uses of RegionNode should be gone"); + assert(n->in(k) != this, "All uses of RegionNode should be gone"); } #endif } @@ -1929,16 +1952,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Determine if this input is backedge of a loop. // (Skip new phis which have no uses and dead regions). if (outcnt() > 0 && r->in(0) != NULL) { - // First, take the short cut when we know it is a loop and - // the EntryControl data path is dead. - // Loop node may have only one input because entry path - // is removed in PhaseIdealLoop::Dominators(). - assert(!r->is_Loop() || r->req() <= 3, "Loop node should have 3 or less inputs"); - bool is_loop = (r->is_Loop() && r->req() == 3); - // Then, check if there is a data loop when phi references itself directly - // or through other data nodes. - if ((is_loop && !uin->eqv_uncast(in(LoopNode::EntryControl))) || - (!is_loop && is_unsafe_data_reference(uin))) { + if (is_data_loop(r->as_Region(), uin, phase)) { // Break this data loop to avoid creation of a dead loop. if (can_reshape) { return top; @@ -2064,7 +2078,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (can_reshape) { opt = split_flow_path(phase, this); // This optimization only modifies phi - don't need to check for dead loop. - assert(opt == NULL || phase->eqv(opt, this), "do not elide phi"); + assert(opt == NULL || opt == this, "do not elide phi"); if (opt != NULL) return opt; } @@ -2180,7 +2194,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (ii->is_MergeMem()) { MergeMemNode* n = ii->as_MergeMem(); merge_width = MAX2(merge_width, n->req()); - saw_self = saw_self || phase->eqv(n->base_memory(), this); + saw_self = saw_self || (n->base_memory() == this); } } @@ -2288,12 +2302,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { Node* phi = mms.memory(); mms.set_memory(phase->transform(phi)); } - if (igvn) { // Unhook. - igvn->hash_delete(hook); - for (uint i = 1; i < hook->req(); i++) { - hook->set_req(i, NULL); - } - } + hook->destruct(igvn); // Replace self with the result. return result; } @@ -2374,9 +2383,66 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } #endif + // Phi (VB ... VB) => VB (Phi ...) (Phi ...) + if (EnableVectorReboxing && can_reshape && progress == NULL) { + PhaseIterGVN* igvn = phase->is_IterGVN(); + + bool all_inputs_are_equiv_vboxes = true; + for (uint i = 1; i < req(); ++i) { + Node* n = in(i); + if (in(i)->Opcode() != Op_VectorBox) { + all_inputs_are_equiv_vboxes = false; + break; + } + // Check that vector type of vboxes is equivalent + if (i != 1) { + if (Type::cmp(in(i-0)->in(VectorBoxNode::Value)->bottom_type(), + in(i-1)->in(VectorBoxNode::Value)->bottom_type()) != 0) { + all_inputs_are_equiv_vboxes = false; + break; + } + if (Type::cmp(in(i-0)->in(VectorBoxNode::Box)->bottom_type(), + in(i-1)->in(VectorBoxNode::Box)->bottom_type()) != 0) { + all_inputs_are_equiv_vboxes = false; + break; + } + } + } + + if (all_inputs_are_equiv_vboxes) { + VectorBoxNode* vbox = static_cast(in(1)); + PhiNode* new_vbox_phi = new PhiNode(r, vbox->box_type()); + PhiNode* new_vect_phi = new PhiNode(r, vbox->vec_type()); + for (uint i = 1; i < req(); ++i) { + VectorBoxNode* old_vbox = static_cast(in(i)); + new_vbox_phi->set_req(i, old_vbox->in(VectorBoxNode::Box)); + new_vect_phi->set_req(i, old_vbox->in(VectorBoxNode::Value)); + } + igvn->register_new_node_with_optimizer(new_vbox_phi, this); + igvn->register_new_node_with_optimizer(new_vect_phi, this); + progress = new VectorBoxNode(igvn->C, new_vbox_phi, new_vect_phi, vbox->box_type(), vbox->vec_type()); + } + } + return progress; // Return any progress } +bool PhiNode::is_data_loop(RegionNode* r, Node* uin, const PhaseGVN* phase) { + // First, take the short cut when we know it is a loop and the EntryControl data path is dead. + // The loop node may only have one input because the entry path was removed in PhaseIdealLoop::Dominators(). + // Then, check if there is a data loop when the phi references itself directly or through other data nodes. + assert(!r->is_Loop() || r->req() <= 3, "Loop node should have 3 or less inputs"); + const bool is_loop = (r->is_Loop() && r->req() == 3); + const Node* top = phase->C->top(); + if (is_loop) { + return !uin->eqv_uncast(in(LoopNode::EntryControl)); + } else { + // We have a data loop either with an unsafe data reference or if a region is unreachable. + return is_unsafe_data_reference(uin) + || (r->req() == 3 && (r->in(1) != top && r->in(2) == top && r->is_unreachable_region(phase))); + } +} + //------------------------------is_tripcount----------------------------------- bool PhiNode::is_tripcount() const { return (in(0) != NULL && in(0)->is_CountedLoop() && diff --git a/src/hotspot/share/opto/cfgnode.hpp b/src/hotspot/share/opto/cfgnode.hpp index 3b7bd883f3d..72a81e74421 100644 --- a/src/hotspot/share/opto/cfgnode.hpp +++ b/src/hotspot/share/opto/cfgnode.hpp @@ -64,15 +64,20 @@ class PhaseIdealLoop; // correspond 1-to-1 with RegionNode inputs. The zero input of a PhiNode is // the RegionNode, and the zero input of the RegionNode is itself. class RegionNode : public Node { +private: + bool _is_unreachable_region; + + bool is_possible_unsafe_loop(const PhaseGVN* phase) const; + bool is_unreachable_from_root(const PhaseGVN* phase) const; public: // Node layout (parallels PhiNode): enum { Region, // Generally points to self. Control // Control arcs are [1..len) }; - RegionNode( uint required ) : Node(required) { + RegionNode(uint required) : Node(required), _is_unreachable_region(false) { init_class_id(Class_Region); - init_req(0,this); + init_req(0, this); } Node* is_copy() const { @@ -84,18 +89,19 @@ class RegionNode : public Node { PhiNode* has_phi() const; // returns an arbitrary phi user, or NULL PhiNode* has_unique_phi() const; // returns the unique phi user, or NULL // Is this region node unreachable from root? - bool is_unreachable_region(PhaseGVN *phase) const; + bool is_unreachable_region(const PhaseGVN* phase); virtual int Opcode() const; - virtual bool pinned() const { return (const Node *)in(0) == this; } - virtual bool is_CFG () const { return true; } - virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash + virtual uint size_of() const { return sizeof(*this); } + virtual bool pinned() const { return (const Node*)in(0) == this; } + virtual bool is_CFG() const { return true; } + virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash virtual bool depends_only_on_test() const { return false; } - virtual const Type *bottom_type() const { return Type::CONTROL; } + virtual const Type* bottom_type() const { return Type::CONTROL; } virtual const Type* Value(PhaseGVN* phase) const; virtual Node* Identity(PhaseGVN* phase); - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); virtual const RegMask &out_RegMask() const; - bool try_clean_mem_phi(PhaseGVN *phase); + bool try_clean_mem_phi(PhaseGVN* phase); bool optimize_trichotomy(PhaseIterGVN* igvn); }; @@ -135,6 +141,7 @@ class PhiNode : public TypeNode { // Determine if CMoveNode::is_cmove_id can be used at this join point. Node* is_cmove_id(PhaseTransform* phase, int true_path); bool wait_for_region_igvn(PhaseGVN* phase); + bool is_data_loop(RegionNode* r, Node* uin, const PhaseGVN* phase); public: // Node layout (parallels RegionNode): diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index e03666fb359..fa4b85fb234 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -198,9 +198,10 @@ macro(LoopLimit) macro(Mach) macro(MachProj) macro(MulAddS2I) +macro(MaxI) +macro(MaxL) macro(MaxD) macro(MaxF) -macro(MaxI) macro(MemBarAcquire) macro(LoadFence) macro(SetVectMaskI) @@ -212,9 +213,10 @@ macro(MemBarReleaseLock) macro(MemBarVolatile) macro(MemBarStoreStore) macro(MergeMem) -macro(MinD) -macro(MinF) macro(MinI) +macro(MinL) +macro(MinF) +macro(MinD) macro(ModD) macro(ModF) macro(ModI) @@ -229,6 +231,8 @@ macro(MulHiL) macro(MulI) macro(MulL) macro(Multi) +macro(NegI) +macro(NegL) macro(NegD) macro(NegF) macro(NeverBranch) @@ -324,6 +328,8 @@ macro(TailJump) macro(MacroLogicV) macro(ThreadLocal) macro(Unlock) +macro(URShiftB) +macro(URShiftS) macro(URShiftI) macro(URShiftL) macro(XorI) @@ -366,6 +372,7 @@ macro(AbsVI) macro(AbsVL) macro(AbsVF) macro(AbsVD) +macro(NegVI) macro(NegVF) macro(NegVD) macro(SqrtVD) @@ -395,7 +402,9 @@ macro(MaxV) macro(MinReductionV) macro(MaxReductionV) macro(LoadVector) +macro(LoadVectorGather) macro(StoreVector) +macro(StoreVectorScatter) macro(Pack) macro(PackB) macro(PackS) @@ -424,3 +433,24 @@ macro(Digit) macro(LowerCase) macro(UpperCase) macro(Whitespace) +macro(VectorBox) +macro(VectorBoxAllocate) +macro(VectorUnbox) +macro(VectorMaskWrapper) +macro(VectorMaskCmp) +macro(VectorTest) +macro(VectorBlend) +macro(VectorRearrange) +macro(VectorLoadMask) +macro(VectorLoadShuffle) +macro(VectorLoadConst) +macro(VectorStoreMask) +macro(VectorReinterpret) +macro(VectorCast) +macro(VectorCastB2X) +macro(VectorCastS2X) +macro(VectorCastI2X) +macro(VectorCastL2X) +macro(VectorCastF2X) +macro(VectorCastD2X) +macro(VectorInsert) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index b91441dc38b..a170fb197ab 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -68,6 +68,7 @@ #include "opto/runtime.hpp" #include "opto/stringopts.hpp" #include "opto/type.hpp" +#include "opto/vector.hpp" #include "opto/vectornode.hpp" #include "runtime/arguments.hpp" #include "runtime/globals_extension.hpp" @@ -118,9 +119,9 @@ class IntrinsicDescPair { }; int Compile::intrinsic_insertion_index(ciMethod* m, bool is_virtual, bool& found) { #ifdef ASSERT - for (int i = 1; i < _intrinsics->length(); i++) { - CallGenerator* cg1 = _intrinsics->at(i-1); - CallGenerator* cg2 = _intrinsics->at(i); + for (int i = 1; i < _intrinsics.length(); i++) { + CallGenerator* cg1 = _intrinsics.at(i-1); + CallGenerator* cg2 = _intrinsics.at(i); assert(cg1->method() != cg2->method() ? cg1->method() < cg2->method() : cg1->is_virtual() < cg2->is_virtual(), @@ -128,28 +129,24 @@ int Compile::intrinsic_insertion_index(ciMethod* m, bool is_virtual, bool& found } #endif IntrinsicDescPair pair(m, is_virtual); - return _intrinsics->find_sorted(&pair, found); + return _intrinsics.find_sorted(&pair, found); } void Compile::register_intrinsic(CallGenerator* cg) { - if (_intrinsics == NULL) { - _intrinsics = new (comp_arena())GrowableArray(comp_arena(), 60, 0, NULL); - } - int len = _intrinsics->length(); bool found = false; int index = intrinsic_insertion_index(cg->method(), cg->is_virtual(), found); assert(!found, "registering twice"); - _intrinsics->insert_before(index, cg); + _intrinsics.insert_before(index, cg); assert(find_intrinsic(cg->method(), cg->is_virtual()) == cg, "registration worked"); } CallGenerator* Compile::find_intrinsic(ciMethod* m, bool is_virtual) { assert(m->is_loaded(), "don't try this on unloaded methods"); - if (_intrinsics != NULL) { + if (_intrinsics.length() > 0) { bool found = false; int index = intrinsic_insertion_index(m, is_virtual, found); if (found) { - return _intrinsics->at(index); + return _intrinsics.at(index); } } // Lazily create intrinsics for intrinsic IDs well-known in the runtime. @@ -167,9 +164,7 @@ CallGenerator* Compile::find_intrinsic(ciMethod* m, bool is_virtual) { return NULL; } -// Compile:: register_library_intrinsics and make_vm_intrinsic are defined -// in library_call.cpp. - +// Compile::make_vm_intrinsic is defined in library_call.cpp. #ifndef PRODUCT // statistics gathering... @@ -351,6 +346,15 @@ void Compile::remove_useless_late_inlines(GrowableArray* inlines inlines->trunc_to(inlines->length()-shift); } +void Compile::remove_useless_nodes(GrowableArray& node_list, Unique_Node_List& useful) { + for (int i = node_list.length() - 1; i >= 0; i--) { + Node* n = node_list.at(i); + if (!useful.member(n)) { + node_list.remove_if_existing(n); + } + } +} + // Disconnect all useless nodes by disconnecting those at the boundary. void Compile::remove_useless_nodes(Unique_Node_List &useful) { uint next = 0; @@ -365,7 +369,7 @@ void Compile::remove_useless_nodes(Unique_Node_List &useful) { int max = n->outcnt(); for (int j = 0; j < max; ++j) { Node* child = n->raw_out(j); - if (! useful.member(child)) { + if (!useful.member(child)) { assert(!child->is_top() || child != top(), "If top is cached in Compile object it is in useful list"); // Only need to remove this out-edge to the useless node @@ -378,40 +382,18 @@ void Compile::remove_useless_nodes(Unique_Node_List &useful) { record_for_igvn(n->unique_out()); } } - // Remove useless macro and predicate opaq nodes - for (int i = C->macro_count()-1; i >= 0; i--) { - Node* n = C->macro_node(i); - if (!useful.member(n)) { - remove_macro_node(n); - } - } - // Remove useless CastII nodes with range check dependency - for (int i = range_check_cast_count() - 1; i >= 0; i--) { - Node* cast = range_check_cast_node(i); - if (!useful.member(cast)) { - remove_range_check_cast(cast); - } - } - // Remove useless expensive nodes - for (int i = C->expensive_count()-1; i >= 0; i--) { - Node* n = C->expensive_node(i); - if (!useful.member(n)) { - remove_expensive_node(n); - } - } - // Remove useless Opaque4 nodes - for (int i = opaque4_count() - 1; i >= 0; i--) { - Node* opaq = opaque4_node(i); - if (!useful.member(opaq)) { - remove_opaque4_node(opaq); - } - } + + remove_useless_nodes(_macro_nodes, useful); // remove useless macro and predicate opaq nodes + remove_useless_nodes(_expensive_nodes, useful); // remove useless expensive nodes + remove_useless_nodes(_for_post_loop_igvn, useful); // remove useless node recorded for post loop opts IGVN pass + BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); bs->eliminate_useless_gc_barriers(useful, this); // clean up the late inline lists remove_useless_late_inlines(&_string_late_inlines, useful); remove_useless_late_inlines(&_boxing_late_inlines, useful); remove_useless_late_inlines(&_late_inlines, useful); + remove_useless_late_inlines(&_vector_reboxing_late_inlines, useful); debug_only(verify_graph_edges(true/*check for no_dead_code*/);) } @@ -514,6 +496,7 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci, _stub_name(NULL), _stub_entry_point(NULL), _max_node_limit(MaxNodeLimit), + _post_loop_opts_phase(false), _inlining_progress(false), _inlining_incrementally(false), _do_cleanup(false), @@ -531,6 +514,11 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci, _directive(directive), _log(ci_env->log()), _failure_reason(NULL), + _intrinsics (comp_arena(), 0, 0, NULL), + _macro_nodes (comp_arena(), 8, 0, NULL), + _predicate_opaqs (comp_arena(), 8, 0, NULL), + _expensive_nodes (comp_arena(), 8, 0, NULL), + _for_post_loop_igvn(comp_arena(), 8, 0, NULL), _congraph(NULL), NOT_PRODUCT(_printer(NULL) COMMA) _dead_node_list(comp_arena()), @@ -545,6 +533,7 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci, _late_inlines(comp_arena(), 2, 0, NULL), _string_late_inlines(comp_arena(), 2, 0, NULL), _boxing_late_inlines(comp_arena(), 2, 0, NULL), + _vector_reboxing_late_inlines(comp_arena(), 2, 0, NULL), _late_inlines_pos(0), _number_of_mh_late_inlines(0), _print_inlining_stream(NULL), @@ -729,9 +718,9 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci, if (failing()) return; NOT_PRODUCT( verify_graph_edges(); ) - // If IGVN is randomized for stress testing, seed random number - // generation and log the seed for repeatability. - if (StressIGVN) { + // If LCM, GCM, or IGVN are randomized for stress testing, seed + // random number generation and log the seed for repeatability. + if (StressLCM || StressGCM || StressIGVN) { _stress_seed = FLAG_IS_DEFAULT(StressSeed) ? static_cast(Ticks::now().nanoseconds()) : StressSeed; if (_log != NULL) { @@ -814,6 +803,7 @@ Compile::Compile( ciEnv* ci_env, _stub_name(stub_name), _stub_entry_point(NULL), _max_node_limit(MaxNodeLimit), + _post_loop_opts_phase(false), _inlining_progress(false), _inlining_incrementally(false), _has_reserved_stack_access(false), @@ -922,7 +912,7 @@ void Compile::Init(int aliaslevel) { _fixed_slots = 0; set_has_split_ifs(false); - set_has_loops(has_method() && method()->has_loops()); // first approximation + set_has_loops(false); // first approximation set_has_stringbuilder(false); set_has_boxed_value(false); _trap_can_recompile = false; // no traps emitted yet @@ -939,8 +929,6 @@ void Compile::Init(int aliaslevel) { set_max_inline_size(MaxInlineSize); set_freq_inline_size(FreqInlineSize); set_do_scheduling(OptoScheduling); - set_do_count_invocations(false); - set_do_method_data_update(false); set_do_vector_loop(false); @@ -1012,16 +1000,10 @@ void Compile::Init(int aliaslevel) { // A NULL adr_type hits in the cache right away. Preload the right answer. probe_alias_cache(NULL)->_index = AliasIdxTop; - _intrinsics = NULL; - _macro_nodes = new(comp_arena()) GrowableArray(comp_arena(), 8, 0, NULL); - _predicate_opaqs = new(comp_arena()) GrowableArray(comp_arena(), 8, 0, NULL); - _expensive_nodes = new(comp_arena()) GrowableArray(comp_arena(), 8, 0, NULL); - _range_check_casts = new(comp_arena()) GrowableArray(comp_arena(), 8, 0, NULL); - _opaque4_nodes = new(comp_arena()) GrowableArray(comp_arena(), 8, 0, NULL); - register_library_intrinsics(); #ifdef ASSERT _type_verify_symmetry = true; _phase_optimize_finished = false; + _exception_backedge = false; #endif } @@ -1146,8 +1128,7 @@ void Compile::print_missing_nodes() { } } void Compile::record_modified_node(Node* n) { - if (_modified_nodes != NULL && !_inlining_incrementally && - n->outcnt() != 0 && !n->is_Con()) { + if (_modified_nodes != NULL && !_inlining_incrementally && !n->is_Con()) { _modified_nodes->push(n); } } @@ -1788,46 +1769,30 @@ void Compile::cleanup_loop_predicates(PhaseIterGVN &igvn) { assert(predicate_count()==0, "should be clean!"); } -void Compile::add_range_check_cast(Node* n) { - assert(n->isa_CastII()->has_range_check(), "CastII should have range check dependency"); - assert(!_range_check_casts->contains(n), "duplicate entry in range check casts"); - _range_check_casts->append(n); -} - -// Remove all range check dependent CastIINodes. -void Compile::remove_range_check_casts(PhaseIterGVN &igvn) { - for (int i = range_check_cast_count(); i > 0; i--) { - Node* cast = range_check_cast_node(i-1); - assert(cast->isa_CastII()->has_range_check(), "CastII should have range check dependency"); - igvn.replace_node(cast, cast->in(1)); +void Compile::record_for_post_loop_opts_igvn(Node* n) { + if (!n->for_post_loop_opts_igvn()) { + assert(!_for_post_loop_igvn.contains(n), "duplicate"); + n->add_flag(Node::NodeFlags::Flag_for_post_loop_opts_igvn); + _for_post_loop_igvn.append(n); } - assert(range_check_cast_count() == 0, "should be empty"); } -void Compile::add_opaque4_node(Node* n) { - assert(n->Opcode() == Op_Opaque4, "Opaque4 only"); - assert(!_opaque4_nodes->contains(n), "duplicate entry in Opaque4 list"); - _opaque4_nodes->append(n); +void Compile::remove_from_post_loop_opts_igvn(Node* n) { + n->remove_flag(Node::NodeFlags::Flag_for_post_loop_opts_igvn); + _for_post_loop_igvn.remove(n); } -// Remove all Opaque4 nodes. -void Compile::remove_opaque4_nodes(PhaseIterGVN &igvn) { - for (int i = opaque4_count(); i > 0; i--) { - Node* opaq = opaque4_node(i-1); - assert(opaq->Opcode() == Op_Opaque4, "Opaque4 only"); - // With Opaque4 nodes, the expectation is that the test of input 1 - // is always equal to the constant value of input 2. So we can - // remove the Opaque4 and replace it by input 2. In debug builds, - // leave the non constant test in instead to sanity check that it - // never fails (if it does, that subgraph was constructed so, at - // runtime, a Halt node is executed). -#ifdef ASSERT - igvn.replace_node(opaq, opaq->in(1)); -#else - igvn.replace_node(opaq, opaq->in(2)); -#endif +void Compile::process_for_post_loop_opts_igvn(PhaseIterGVN& igvn) { + if (_for_post_loop_igvn.length() == 0) { + return; // no work to do } - assert(opaque4_count() == 0, "should be empty"); + while (_for_post_loop_igvn.length() > 0) { + Node* n = _for_post_loop_igvn.pop(); + n->remove_flag(Node::NodeFlags::Flag_for_post_loop_opts_igvn); + igvn._worklist.push(n); + } + igvn.optimize(); + assert(_for_post_loop_igvn.length() == 0, "no more delayed nodes allowed"); } // StringOpts and late inlining of string methods @@ -1961,6 +1926,8 @@ void Compile::inline_incrementally(PhaseIterGVN& igvn) { inline_incrementally_cleanup(igvn); + print_method(PHASE_INCREMENTAL_INLINE_STEP, 3); + if (failing()) return; } assert( igvn._worklist.size() == 0, "should be done with igvn" ); @@ -1982,9 +1949,9 @@ void Compile::inline_incrementally(PhaseIterGVN& igvn) { bool Compile::optimize_loops(PhaseIterGVN& igvn, LoopOptsMode mode) { - if(_loop_opts_cnt > 0) { + if (_loop_opts_cnt > 0) { debug_only( int cnt = 0; ); - while(major_progress() && (_loop_opts_cnt > 0)) { + while (major_progress() && (_loop_opts_cnt > 0)) { TracePhase tp("idealLoop", &timers[_t_idealLoop]); assert( cnt++ < 40, "infinite cycle in loop optimization" ); PhaseIdealLoop::optimize(igvn, mode); @@ -2095,6 +2062,16 @@ void Compile::Optimize() { // so keep only the actual candidates for optimizations. cleanup_expensive_nodes(igvn); + assert(EnableVectorSupport || !has_vbox_nodes(), "sanity"); + if (EnableVectorSupport && has_vbox_nodes()) { + TracePhase tp("", &timers[_t_vector]); + PhaseVector pv(igvn); + pv.optimize_vector_boxes(); + + print_method(PHASE_ITER_GVN_AFTER_VECTOR, 2); + } + assert(!has_vbox_nodes(), "sanity"); + if (!failing() && RenumberLiveNodes && live_nodes() + NodeLimitFudgeFactor < unique()) { Compile::TracePhase tp("", &timers[_t_renumberLive]); initial_gvn()->replace_with(&igvn); @@ -2104,9 +2081,11 @@ void Compile::Optimize() { ResourceMark rm; PhaseRenumberLive prl = PhaseRenumberLive(initial_gvn(), for_igvn(), &new_worklist); } + Unique_Node_List* save_for_igvn = for_igvn(); set_for_igvn(&new_worklist); igvn = PhaseIterGVN(initial_gvn()); igvn.optimize(); + set_for_igvn(save_for_igvn); } // Perform escape analysis @@ -2215,12 +2194,6 @@ void Compile::Optimize() { PhaseIdealLoop::verify(igvn); } - if (range_check_cast_count() > 0) { - // No more loop optimizations. Remove all range check dependent CastIINodes. - C->remove_range_check_casts(igvn); - igvn.optimize(); - } - #ifdef ASSERT bs->verify_gc_barriers(this, BarrierSetC2::BeforeMacroExpand); #endif @@ -2244,10 +2217,9 @@ void Compile::Optimize() { print_method(PHASE_BARRIER_EXPANSION, 2); } - if (opaque4_count() > 0) { - C->remove_opaque4_nodes(igvn); - igvn.optimize(); - } + C->set_post_loop_opts_phase(); // no more loop opts allowed + + process_for_post_loop_opts_igvn(igvn); if (C->max_vector_size() > 0) { C->optimize_logic_cones(igvn); @@ -2255,6 +2227,7 @@ void Compile::Optimize() { } DEBUG_ONLY( _modified_nodes = NULL; ) + assert(igvn._worklist.size() == 0, "not empty"); } // (End scope of igvn; run destructor if necessary for asserts.) process_print_inlining(); @@ -2271,6 +2244,35 @@ void Compile::Optimize() { DEBUG_ONLY(set_phase_optimize_finished();) } +void Compile::inline_vector_reboxing_calls() { + if (C->_vector_reboxing_late_inlines.length() > 0) { + PhaseGVN* gvn = C->initial_gvn(); + + _late_inlines_pos = C->_late_inlines.length(); + while (_vector_reboxing_late_inlines.length() > 0) { + CallGenerator* cg = _vector_reboxing_late_inlines.pop(); + cg->do_late_inline(); + if (failing()) return; + print_method(PHASE_INLINE_VECTOR_REBOX, cg->call_node()); + } + _vector_reboxing_late_inlines.trunc_to(0); + } +} + +bool Compile::has_vbox_nodes() { + if (C->_vector_reboxing_late_inlines.length() > 0) { + return true; + } + for (int macro_idx = C->macro_count() - 1; macro_idx >= 0; macro_idx--) { + Node * n = C->macro_node(macro_idx); + assert(n->is_macro(), "only macro nodes expected here"); + if (n->Opcode() == Op_VectorUnbox || n->Opcode() == Op_VectorBox || n->Opcode() == Op_VectorBoxAllocate) { + return true; + } + } + return false; +} + //---------------------------- Bitwise operation packing optimization --------------------------- static bool is_vector_unary_bitwise_op(Node* n) { @@ -2617,8 +2619,8 @@ void Compile::Code_Gen() { if (failing()) { return; } + print_method(PHASE_AFTER_MATCHING, 3); } - // In debug mode can dump m._nodes.dump() for mapping of ideal to machine // nodes. Mapping is only valid at the root of each matched subtree. NOT_PRODUCT( verify_graph_edges(); ) @@ -2771,7 +2773,7 @@ void Compile::eliminate_redundant_card_marks(Node* n) { // Eliminate the previous StoreCM prev->set_req(MemNode::Memory, mem->in(MemNode::Memory)); assert(mem->outcnt() == 0, "should be dead"); - mem->disconnect_inputs(NULL, this); + mem->disconnect_inputs(this); } else { prev = mem; } @@ -2797,7 +2799,8 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) { // Check for commutative opcode switch( nop ) { case Op_AddI: case Op_AddF: case Op_AddD: case Op_AddL: - case Op_MaxI: case Op_MinI: + case Op_MaxI: case Op_MaxL: case Op_MaxF: case Op_MaxD: + case Op_MinI: case Op_MinL: case Op_MinF: case Op_MinD: case Op_MulI: case Op_MulF: case Op_MulD: case Op_MulL: case Op_AndL: case Op_XorL: case Op_OrL: case Op_AndI: case Op_XorI: case Op_OrI: { @@ -3053,7 +3056,7 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f n->set_req(AddPNode::Base, nn); n->set_req(AddPNode::Address, nn); if (addp->outcnt() == 0) { - addp->disconnect_inputs(NULL, this); + addp->disconnect_inputs(this); } } } @@ -3126,12 +3129,12 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f n->subsume_by(new_in1, this); if (in1->outcnt() == 0) { - in1->disconnect_inputs(NULL, this); + in1->disconnect_inputs(this); } } else { n->subsume_by(n->in(1), this); if (n->outcnt() == 0) { - n->disconnect_inputs(NULL, this); + n->disconnect_inputs(this); } } break; @@ -3209,10 +3212,10 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f Node* cmpN = new CmpNNode(in1->in(1), new_in2); n->subsume_by(cmpN, this); if (in1->outcnt() == 0) { - in1->disconnect_inputs(NULL, this); + in1->disconnect_inputs(this); } if (in2->outcnt() == 0) { - in2->disconnect_inputs(NULL, this); + in2->disconnect_inputs(this); } } } @@ -3243,7 +3246,7 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f } } if (in1->outcnt() == 0) { - in1->disconnect_inputs(NULL, this); + in1->disconnect_inputs(this); } break; } @@ -3347,6 +3350,8 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f case Op_LoadVector: case Op_StoreVector: + case Op_LoadVectorGather: + case Op_StoreVectorScatter: break; case Op_AddReductionVI: @@ -3378,6 +3383,7 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f } break; case Op_Loop: + assert(!n->as_Loop()->is_transformed_long_loop() || _loop_opts_cnt == 0, "should have been turned into a counted loop"); case Op_CountedLoop: case Op_OuterStripMinedLoop: if (n->as_Loop()->is_inner_loop()) { @@ -3409,7 +3415,7 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f } } if (in2->outcnt() == 0) { // Remove dead node - in2->disconnect_inputs(NULL, this); + in2->disconnect_inputs(this); } } break; @@ -3441,7 +3447,7 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f wq.push(in); } } - m->disconnect_inputs(NULL, this); + m->disconnect_inputs(this); } } } @@ -3584,7 +3590,7 @@ void Compile::final_graph_reshaping_walk( Node_Stack &nstack, Node *root, Final_ n->set_req(j, in->in(1)); } if (in->outcnt() == 0) { - in->disconnect_inputs(NULL, this); + in->disconnect_inputs(this); } } } @@ -3632,7 +3638,7 @@ bool Compile::final_graph_reshaping() { // be freely moved to the least frequent code path by gcm. assert(OptimizeExpensiveOps || expensive_count() == 0, "optimization off but list non empty?"); for (int i = 0; i < expensive_count(); i++) { - _expensive_nodes->at(i)->set_req(0, NULL); + _expensive_nodes.at(i)->set_req(0, NULL); } Final_Reshape_Counts frc; @@ -4053,8 +4059,6 @@ Node* Compile::constrained_convI2L(PhaseGVN* phase, Node* value, const TypeInt* // ConvI2L node may be eliminated independently of the range check, causing the data path // to become TOP while the control path is still there (although it's unreachable). value->set_req(0, ctrl); - // Save CastII node to remove it after loop optimizations. - phase->C->add_range_check_cast(value); value = phase->transform(value); } const TypeLong* ltype = TypeLong::make(itype->_lo, itype->_hi, itype->_widen); @@ -4284,13 +4288,13 @@ int Compile::cmp_expensive_nodes(Node** n1p, Node** n2p) { void Compile::sort_expensive_nodes() { if (!expensive_nodes_sorted()) { - _expensive_nodes->sort(cmp_expensive_nodes); + _expensive_nodes.sort(cmp_expensive_nodes); } } bool Compile::expensive_nodes_sorted() const { - for (int i = 1; i < _expensive_nodes->length(); i++) { - if (cmp_expensive_nodes(_expensive_nodes->adr_at(i), _expensive_nodes->adr_at(i-1)) < 0) { + for (int i = 1; i < _expensive_nodes.length(); i++) { + if (cmp_expensive_nodes(_expensive_nodes.adr_at(i), _expensive_nodes.adr_at(i-1)) < 0) { return false; } } @@ -4298,7 +4302,7 @@ bool Compile::expensive_nodes_sorted() const { } bool Compile::should_optimize_expensive_nodes(PhaseIterGVN &igvn) { - if (_expensive_nodes->length() == 0) { + if (_expensive_nodes.length() == 0) { return false; } @@ -4306,23 +4310,23 @@ bool Compile::should_optimize_expensive_nodes(PhaseIterGVN &igvn) { // Take this opportunity to remove dead nodes from the list int j = 0; - for (int i = 0; i < _expensive_nodes->length(); i++) { - Node* n = _expensive_nodes->at(i); + for (int i = 0; i < _expensive_nodes.length(); i++) { + Node* n = _expensive_nodes.at(i); if (!n->is_unreachable(igvn)) { assert(n->is_expensive(), "should be expensive"); - _expensive_nodes->at_put(j, n); + _expensive_nodes.at_put(j, n); j++; } } - _expensive_nodes->trunc_to(j); + _expensive_nodes.trunc_to(j); // Then sort the list so that similar nodes are next to each other // and check for at least two nodes of identical kind with same data // inputs. sort_expensive_nodes(); - for (int i = 0; i < _expensive_nodes->length()-1; i++) { - if (cmp_expensive_nodes(_expensive_nodes->adr_at(i), _expensive_nodes->adr_at(i+1)) == 0) { + for (int i = 0; i < _expensive_nodes.length()-1; i++) { + if (cmp_expensive_nodes(_expensive_nodes.adr_at(i), _expensive_nodes.adr_at(i+1)) == 0) { return true; } } @@ -4331,7 +4335,7 @@ bool Compile::should_optimize_expensive_nodes(PhaseIterGVN &igvn) { } void Compile::cleanup_expensive_nodes(PhaseIterGVN &igvn) { - if (_expensive_nodes->length() == 0) { + if (_expensive_nodes.length() == 0) { return; } @@ -4345,43 +4349,43 @@ void Compile::cleanup_expensive_nodes(PhaseIterGVN &igvn) { int identical = 0; int i = 0; bool modified = false; - for (; i < _expensive_nodes->length()-1; i++) { + for (; i < _expensive_nodes.length()-1; i++) { assert(j <= i, "can't write beyond current index"); - if (_expensive_nodes->at(i)->Opcode() == _expensive_nodes->at(i+1)->Opcode()) { + if (_expensive_nodes.at(i)->Opcode() == _expensive_nodes.at(i+1)->Opcode()) { identical++; - _expensive_nodes->at_put(j++, _expensive_nodes->at(i)); + _expensive_nodes.at_put(j++, _expensive_nodes.at(i)); continue; } if (identical > 0) { - _expensive_nodes->at_put(j++, _expensive_nodes->at(i)); + _expensive_nodes.at_put(j++, _expensive_nodes.at(i)); identical = 0; } else { - Node* n = _expensive_nodes->at(i); + Node* n = _expensive_nodes.at(i); igvn.replace_input_of(n, 0, NULL); igvn.hash_insert(n); modified = true; } } if (identical > 0) { - _expensive_nodes->at_put(j++, _expensive_nodes->at(i)); - } else if (_expensive_nodes->length() >= 1) { - Node* n = _expensive_nodes->at(i); + _expensive_nodes.at_put(j++, _expensive_nodes.at(i)); + } else if (_expensive_nodes.length() >= 1) { + Node* n = _expensive_nodes.at(i); igvn.replace_input_of(n, 0, NULL); igvn.hash_insert(n); modified = true; } - _expensive_nodes->trunc_to(j); + _expensive_nodes.trunc_to(j); if (modified) { igvn.optimize(); } } void Compile::add_expensive_node(Node * n) { - assert(!_expensive_nodes->contains(n), "duplicate entry in expensive list"); + assert(!_expensive_nodes.contains(n), "duplicate entry in expensive list"); assert(n->is_expensive(), "expensive nodes with non-null control here only"); assert(!n->is_CFG() && !n->is_Mem(), "no cfg or memory nodes here"); if (OptimizeExpensiveOps) { - _expensive_nodes->append(n); + _expensive_nodes.append(n); } else { // Clear control input and let IGVN optimize expensive nodes if // OptimizeExpensiveOps is off. @@ -4488,7 +4492,7 @@ int Compile::random() { #define RANDOMIZED_DOMAIN_MASK ((1 << (RANDOMIZED_DOMAIN_POW + 1)) - 1) bool Compile::randomized_select(int count) { assert(count > 0, "only positive"); - return (os::random() & RANDOMIZED_DOMAIN_MASK) < (RANDOMIZED_DOMAIN / count); + return (random() & RANDOMIZED_DOMAIN_MASK) < (RANDOMIZED_DOMAIN / count); } CloneMap& Compile::clone_map() { return _clone_map; } @@ -4559,34 +4563,51 @@ void Compile::sort_macro_nodes() { if (n->is_Allocate()) { if (i != allocates) { Node* tmp = macro_node(allocates); - _macro_nodes->at_put(allocates, n); - _macro_nodes->at_put(i, tmp); + _macro_nodes.at_put(allocates, n); + _macro_nodes.at_put(i, tmp); } allocates++; } } } -void Compile::print_method(CompilerPhaseType cpt, int level, int idx) { +void Compile::print_method(CompilerPhaseType cpt, const char *name, int level, int idx) { EventCompilerPhase event; if (event.should_commit()) { CompilerEvent::PhaseEvent::post(event, C->_latest_stage_start_counter, cpt, C->_compile_id, level); } - #ifndef PRODUCT if (should_print(level)) { - char output[1024]; - if (idx != 0) { - jio_snprintf(output, sizeof(output), "%s:%d", CompilerPhaseTypeHelper::to_string(cpt), idx); - } else { - jio_snprintf(output, sizeof(output), "%s", CompilerPhaseTypeHelper::to_string(cpt)); - } - _printer->print_method(output, level); + _printer->print_method(name, level); } #endif C->_latest_stage_start_counter.stamp(); } +void Compile::print_method(CompilerPhaseType cpt, int level, int idx) { + char output[1024]; +#ifndef PRODUCT + if (idx != 0) { + jio_snprintf(output, sizeof(output), "%s:%d", CompilerPhaseTypeHelper::to_string(cpt), idx); + } else { + jio_snprintf(output, sizeof(output), "%s", CompilerPhaseTypeHelper::to_string(cpt)); + } +#endif + print_method(cpt, output, level, idx); +} + +void Compile::print_method(CompilerPhaseType cpt, Node* n, int level) { + ResourceMark rm; + stringStream ss; + ss.print_raw(CompilerPhaseTypeHelper::to_string(cpt)); + if (n != NULL) { + ss.print(": %d %s ", n->_idx, NodeClassNames[n->Opcode()]); + } else { + ss.print_raw(": NULL"); + } + C->print_method(cpt, ss.as_string(), level); +} + void Compile::end_method(int level) { EventCompilerPhase event; if (event.should_commit()) { diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index afc6279c30c..46c4dcfb9c6 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -262,6 +262,8 @@ class Compile : public Phase { // allocator i.e. locks, original deopt pc, etc. uintx _max_node_limit; // Max unique node count during a single compilation. + bool _post_loop_opts_phase; // Loop opts are finished. + int _major_progress; // Count of something big happening bool _inlining_progress; // progress doing incremental inlining? bool _inlining_incrementally;// Are we doing incremental inlining (post parse) @@ -280,8 +282,6 @@ class Compile : public Phase { bool _do_inlining; // True if we intend to do inlining bool _do_scheduling; // True if we intend to do scheduling bool _do_freq_based_layout; // True if we intend to do frequency based block layout - bool _do_count_invocations; // True if we generate code to count invocations - bool _do_method_data_update; // True if we generate code to update MethodData*s bool _do_vector_loop; // True if allowed to execute loop in parallel iterations bool _use_cmove; // True if CMove should be used without profitability analysis bool _age_code; // True if we need to profile code age (decrement the aging counter) @@ -309,12 +309,11 @@ class Compile : public Phase { DirectiveSet* _directive; // Compiler directive CompileLog* _log; // from CompilerThread const char* _failure_reason; // for record_failure/failing pattern - GrowableArray* _intrinsics; // List of intrinsics. - GrowableArray* _macro_nodes; // List of nodes which need to be expanded before matching. - GrowableArray* _predicate_opaqs; // List of Opaque1 nodes for the loop predicates. - GrowableArray* _expensive_nodes; // List of nodes that are expensive to compute and that we'd better not let the GVN freely common - GrowableArray* _range_check_casts; // List of CastII nodes with a range check dependency - GrowableArray* _opaque4_nodes; // List of Opaque4 nodes that have a default value + GrowableArray _intrinsics; // List of intrinsics. + GrowableArray _macro_nodes; // List of nodes which need to be expanded before matching. + GrowableArray _predicate_opaqs; // List of Opaque1 nodes for the loop predicates. + GrowableArray _expensive_nodes; // List of nodes that are expensive to compute and that we'd better not let the GVN freely common + GrowableArray _for_post_loop_igvn; // List of nodes for IGVN after loop opts are over ConnectionGraph* _congraph; #ifndef PRODUCT IdealGraphPrinter* _printer; @@ -376,12 +375,12 @@ class Compile : public Phase { Unique_Node_List* _for_igvn; // Initial work-list for next round of Iterative GVN WarmCallInfo* _warm_calls; // Sorted work-list for heat-based inlining. - GrowableArray _late_inlines; // List of CallGenerators to be revisited after - // main parsing has finished. + GrowableArray _late_inlines; // List of CallGenerators to be revisited after main parsing has finished. GrowableArray _string_late_inlines; // same but for string operations - GrowableArray _boxing_late_inlines; // same but for boxing operations + GrowableArray _vector_reboxing_late_inlines; // same but for vector reboxing operations + int _late_inlines_pos; // Where in the queue should the next late inlining candidate go (emulate depth first inlining) uint _number_of_mh_late_inlines; // number of method handle late inlining still pending @@ -429,6 +428,7 @@ class Compile : public Phase { PrintInliningBuffer& print_inlining_current(); void log_late_inline_failure(CallGenerator* cg, const char* msg); + DEBUG_ONLY(bool _exception_backedge;) public: @@ -569,10 +569,6 @@ class Compile : public Phase { void set_do_scheduling(bool z) { _do_scheduling = z; } bool do_freq_based_layout() const{ return _do_freq_based_layout; } void set_do_freq_based_layout(bool z){ _do_freq_based_layout = z; } - bool do_count_invocations() const{ return _do_count_invocations; } - void set_do_count_invocations(bool z){ _do_count_invocations = z; } - bool do_method_data_update() const { return _do_method_data_update; } - void set_do_method_data_update(bool z) { _do_method_data_update = z; } bool do_vector_loop() const { return _do_vector_loop; } void set_do_vector_loop(bool z) { _do_vector_loop = z; } bool use_cmove() const { return _use_cmove; } @@ -643,7 +639,9 @@ class Compile : public Phase { #endif } + void print_method(CompilerPhaseType cpt, const char *name, int level = 1, int idx = 0); void print_method(CompilerPhaseType cpt, int level = 1, int idx = 0); + void print_method(CompilerPhaseType cpt, Node* n, int level = 3); #ifndef PRODUCT void igv_print_method_to_file(const char* phase_name = "Debug", bool append = false); @@ -654,63 +652,54 @@ class Compile : public Phase { void end_method(int level = 1); - int macro_count() const { return _macro_nodes->length(); } - int predicate_count() const { return _predicate_opaqs->length();} - int expensive_count() const { return _expensive_nodes->length(); } - Node* macro_node(int idx) const { return _macro_nodes->at(idx); } - Node* predicate_opaque1_node(int idx) const { return _predicate_opaqs->at(idx);} - Node* expensive_node(int idx) const { return _expensive_nodes->at(idx); } + int macro_count() const { return _macro_nodes.length(); } + int predicate_count() const { return _predicate_opaqs.length();} + int expensive_count() const { return _expensive_nodes.length(); } + + Node* macro_node(int idx) const { return _macro_nodes.at(idx); } + Node* predicate_opaque1_node(int idx) const { return _predicate_opaqs.at(idx);} + Node* expensive_node(int idx) const { return _expensive_nodes.at(idx); } + ConnectionGraph* congraph() { return _congraph;} void set_congraph(ConnectionGraph* congraph) { _congraph = congraph;} void add_macro_node(Node * n) { //assert(n->is_macro(), "must be a macro node"); - assert(!_macro_nodes->contains(n), "duplicate entry in expand list"); - _macro_nodes->append(n); + assert(!_macro_nodes.contains(n), "duplicate entry in expand list"); + _macro_nodes.append(n); } void remove_macro_node(Node* n) { // this function may be called twice for a node so we can only remove it // if it's still existing. - _macro_nodes->remove_if_existing(n); + _macro_nodes.remove_if_existing(n); // remove from _predicate_opaqs list also if it is there if (predicate_count() > 0) { - _predicate_opaqs->remove_if_existing(n); + _predicate_opaqs.remove_if_existing(n); } } void add_expensive_node(Node* n); void remove_expensive_node(Node* n) { - _expensive_nodes->remove_if_existing(n); + _expensive_nodes.remove_if_existing(n); } void add_predicate_opaq(Node* n) { - assert(!_predicate_opaqs->contains(n), "duplicate entry in predicate opaque1"); - assert(_macro_nodes->contains(n), "should have already been in macro list"); - _predicate_opaqs->append(n); + assert(!_predicate_opaqs.contains(n), "duplicate entry in predicate opaque1"); + assert(_macro_nodes.contains(n), "should have already been in macro list"); + _predicate_opaqs.append(n); } - // Range check dependent CastII nodes that can be removed after loop optimizations - void add_range_check_cast(Node* n); - void remove_range_check_cast(Node* n) { - _range_check_casts->remove_if_existing(n); - } - Node* range_check_cast_node(int idx) const { return _range_check_casts->at(idx); } - int range_check_cast_count() const { return _range_check_casts->length(); } - // Remove all range check dependent CastIINodes. - void remove_range_check_casts(PhaseIterGVN &igvn); - - void add_opaque4_node(Node* n); - void remove_opaque4_node(Node* n) { - _opaque4_nodes->remove_if_existing(n); - } - Node* opaque4_node(int idx) const { return _opaque4_nodes->at(idx); } - int opaque4_count() const { return _opaque4_nodes->length(); } - void remove_opaque4_nodes(PhaseIterGVN &igvn); + bool post_loop_opts_phase() { return _post_loop_opts_phase; } + void set_post_loop_opts_phase() { _post_loop_opts_phase = true; } + + void record_for_post_loop_opts_igvn(Node* n); + void remove_from_post_loop_opts_igvn(Node* n); + void process_for_post_loop_opts_igvn(PhaseIterGVN& igvn); void sort_macro_nodes(); // remove the opaque nodes that protect the predicates so that the unused checks and // uncommon traps will be eliminated from the graph. void cleanup_loop_predicates(PhaseIterGVN &igvn); - bool is_predicate_opaq(Node * n) { - return _predicate_opaqs->contains(n); + bool is_predicate_opaq(Node* n) { + return _predicate_opaqs.contains(n); } // Are there candidate expensive nodes for optimization? @@ -864,10 +853,13 @@ class Compile : public Phase { bool allow_intrinsics = true); bool should_delay_inlining(ciMethod* call_method, JVMState* jvms) { return should_delay_string_inlining(call_method, jvms) || - should_delay_boxing_inlining(call_method, jvms); + should_delay_boxing_inlining(call_method, jvms) || + should_delay_vector_inlining(call_method, jvms); } bool should_delay_string_inlining(ciMethod* call_method, JVMState* jvms); bool should_delay_boxing_inlining(ciMethod* call_method, JVMState* jvms); + bool should_delay_vector_inlining(ciMethod* call_method, JVMState* jvms); + bool should_delay_vector_reboxing_inlining(ciMethod* call_method, JVMState* jvms); // Helper functions to identify inlining potential at call-site ciMethod* optimize_virtual_call(ciMethod* caller, int bci, ciInstanceKlass* klass, @@ -939,7 +931,12 @@ class Compile : public Phase { _boxing_late_inlines.push(cg); } + void add_vector_reboxing_late_inline(CallGenerator* cg) { + _vector_reboxing_late_inlines.push(cg); + } + void remove_useless_late_inlines(GrowableArray* inlines, Unique_Node_List &useful); + void remove_useless_nodes (GrowableArray& node_list, Unique_Node_List &useful); void process_print_inlining(); void dump_print_inlining(); @@ -968,6 +965,9 @@ class Compile : public Phase { bool optimize_loops(PhaseIterGVN& igvn, LoopOptsMode mode); void remove_root_to_sfpts_edges(PhaseIterGVN& igvn); + void inline_vector_reboxing_calls(); + bool has_vbox_nodes(); + // Matching, CFG layout, allocation, code generation PhaseCFG* cfg() { return _cfg; } bool has_java_calls() const { return _java_calls > 0; } @@ -1069,7 +1069,6 @@ class Compile : public Phase { void verify_top(Node*) const PRODUCT_RETURN; // Intrinsic setup. - void register_library_intrinsics(); // initializer CallGenerator* make_vm_intrinsic(ciMethod* m, bool is_virtual); // constructor int intrinsic_insertion_index(ciMethod* m, bool is_virtual, bool& found); // helper CallGenerator* find_intrinsic(ciMethod* m, bool is_virtual); // query fn @@ -1139,7 +1138,7 @@ class Compile : public Phase { // Auxiliary methods for randomized fuzzing/stressing int random(); - static bool randomized_select(int count); + bool randomized_select(int count); // supporting clone_map CloneMap& clone_map(); @@ -1166,6 +1165,8 @@ class Compile : public Phase { #endif // IA32 #ifdef ASSERT bool _type_verify_symmetry; + void set_exception_backedge() { _exception_backedge = true; } + bool has_exception_backedge() const { return _exception_backedge; } #endif }; diff --git a/src/hotspot/share/opto/convertnode.cpp b/src/hotspot/share/opto/convertnode.cpp index 20de89a0d98..c08e8591fb1 100644 --- a/src/hotspot/share/opto/convertnode.cpp +++ b/src/hotspot/share/opto/convertnode.cpp @@ -258,56 +258,72 @@ static inline bool long_ranges_overlap(jlong lo1, jlong hi1, // Two ranges overlap iff one range's low point falls in the other range. return (lo2 <= lo1 && lo1 <= hi2) || (lo1 <= lo2 && lo2 <= hi1); } + +// If there is an existing ConvI2L node with the given parent and type, return +// it. Otherwise, create and return a new one. Both reusing existing ConvI2L +// nodes and postponing the idealization of new ones are needed to avoid an +// explosion of recursive Ideal() calls when compiling long AddI chains. +static Node* find_or_make_convI2L(PhaseIterGVN* igvn, Node* parent, + const TypeLong* type) { + Node* n = new ConvI2LNode(parent, type); + Node* existing = igvn->hash_find_insert(n); + if (existing != NULL) { + n->destruct(igvn); + return existing; + } + return igvn->register_new_node_with_optimizer(n); +} #endif //------------------------------Ideal------------------------------------------ Node *ConvI2LNode::Ideal(PhaseGVN *phase, bool can_reshape) { + PhaseIterGVN *igvn = phase->is_IterGVN(); const TypeLong* this_type = this->type()->is_long(); Node* this_changed = NULL; - // If _major_progress, then more loop optimizations follow. Do NOT - // remove this node's type assertion until no more loop ops can happen. - // The progress bit is set in the major loop optimizations THEN comes the - // call to IterGVN and any chance of hitting this code. Cf. Opaque1Node. - if (can_reshape && !phase->C->major_progress()) { - const TypeInt* in_type = phase->type(in(1))->isa_int(); - if (in_type != NULL && this_type != NULL && - (in_type->_lo != this_type->_lo || - in_type->_hi != this_type->_hi)) { - // Although this WORSENS the type, it increases GVN opportunities, - // because I2L nodes with the same input will common up, regardless - // of slightly differing type assertions. Such slight differences - // arise routinely as a result of loop unrolling, so this is a - // post-unrolling graph cleanup. Choose a type which depends only - // on my input. (Exception: Keep a range assertion of >=0 or <0.) - jlong lo1 = this_type->_lo; - jlong hi1 = this_type->_hi; - int w1 = this_type->_widen; - if (lo1 != (jint)lo1 || - hi1 != (jint)hi1 || - lo1 > hi1) { - // Overflow leads to wraparound, wraparound leads to range saturation. - lo1 = min_jint; hi1 = max_jint; - } else if (lo1 >= 0) { - // Keep a range assertion of >=0. - lo1 = 0; hi1 = max_jint; - } else if (hi1 < 0) { - // Keep a range assertion of <0. - lo1 = min_jint; hi1 = -1; - } else { - lo1 = min_jint; hi1 = max_jint; - } - const TypeLong* wtype = TypeLong::make(MAX2((jlong)in_type->_lo, lo1), - MIN2((jlong)in_type->_hi, hi1), - MAX2((int)in_type->_widen, w1)); - if (wtype != type()) { - set_type(wtype); - // Note: this_type still has old type value, for the logic below. - this_changed = this; - } + if (igvn != NULL) { + // Do NOT remove this node's type assertion until no more loop ops can happen. + if (phase->C->post_loop_opts_phase()) { + const TypeInt* in_type = phase->type(in(1))->isa_int(); + if (in_type != NULL && this_type != NULL && + (in_type->_lo != this_type->_lo || + in_type->_hi != this_type->_hi)) { + // Although this WORSENS the type, it increases GVN opportunities, + // because I2L nodes with the same input will common up, regardless + // of slightly differing type assertions. Such slight differences + // arise routinely as a result of loop unrolling, so this is a + // post-unrolling graph cleanup. Choose a type which depends only + // on my input. (Exception: Keep a range assertion of >=0 or <0.) + jlong lo1 = this_type->_lo; + jlong hi1 = this_type->_hi; + int w1 = this_type->_widen; + if (lo1 != (jint)lo1 || + hi1 != (jint)hi1 || + lo1 > hi1) { + // Overflow leads to wraparound, wraparound leads to range saturation. + lo1 = min_jint; hi1 = max_jint; + } else if (lo1 >= 0) { + // Keep a range assertion of >=0. + lo1 = 0; hi1 = max_jint; + } else if (hi1 < 0) { + // Keep a range assertion of <0. + lo1 = min_jint; hi1 = -1; + } else { + lo1 = min_jint; hi1 = max_jint; } + const TypeLong* wtype = TypeLong::make(MAX2((jlong)in_type->_lo, lo1), + MIN2((jlong)in_type->_hi, hi1), + MAX2((int)in_type->_widen, w1)); + if (wtype != type()) { + set_type(wtype); + // Note: this_type still has old type value, for the logic below. + this_changed = this; + } + } + } else { + phase->C->record_for_post_loop_opts_igvn(this); + } } - #ifdef _LP64 // Convert ConvI2L(AddI(x, y)) to AddL(ConvI2L(x), ConvI2L(y)) // but only if x and y have subranges that cannot cause 32-bit overflow, @@ -334,10 +350,9 @@ Node *ConvI2LNode::Ideal(PhaseGVN *phase, bool can_reshape) { Node* z = in(1); int op = z->Opcode(); if (op == Op_AddI || op == Op_SubI) { - if (!can_reshape) { - // Postpone this optimization to after parsing because with deep AddNode - // chains a large amount of dead ConvI2L nodes might be created that are - // not removed during parsing. As a result, we might hit the node limit. + if (igvn == NULL) { + // Postpone this optimization to iterative GVN, where we can handle deep + // AddI chains without an exponential number of recursive Ideal() calls. phase->record_for_igvn(this); return this_changed; } @@ -399,12 +414,8 @@ Node *ConvI2LNode::Ideal(PhaseGVN *phase, bool can_reshape) { } assert(rxlo == (int)rxlo && rxhi == (int)rxhi, "x should not overflow"); assert(rylo == (int)rylo && ryhi == (int)ryhi, "y should not overflow"); - Node* cx = phase->C->constrained_convI2L(phase, x, TypeInt::make(rxlo, rxhi, widen), NULL); - Node *hook = new Node(1); - hook->init_req(0, cx); // Add a use to cx to prevent him from dying - Node* cy = phase->C->constrained_convI2L(phase, y, TypeInt::make(rylo, ryhi, widen), NULL); - hook->del_req(0); // Just yank bogus edge - hook->destruct(); + Node* cx = find_or_make_convI2L(igvn, x, TypeLong::make(rxlo, rxhi, widen)); + Node* cy = find_or_make_convI2L(igvn, y, TypeLong::make(rylo, ryhi, widen)); switch (op) { case Op_AddI: return new AddLNode(cx, cy); case Op_SubI: return new SubLNode(cx, cy); @@ -449,9 +460,13 @@ const Type* ConvL2INode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; const TypeLong *tl = t->is_long(); - if (tl->is_con()) + if (tl->is_con()) { // Easy case. return TypeInt::make((jint)tl->get_con()); + } + if (tl->_lo >= min_jint && tl->_hi <= max_jint) { + return TypeInt::make((jint)tl->_lo, (jint)tl->_hi, tl->_widen); + } return bottom_type(); } diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp index 968b1ddd5f3..2e5d6568270 100644 --- a/src/hotspot/share/opto/divnode.cpp +++ b/src/hotspot/share/opto/divnode.cpp @@ -326,14 +326,7 @@ static Node* long_by_long_mulhi(PhaseGVN* phase, Node* dividend, jlong magic_con Node* temp2 = phase->transform(new RShiftLNode(w1, phase->intcon(N / 2))); // Remove the bogus extra edges used to keep things alive - PhaseIterGVN* igvn = phase->is_IterGVN(); - if (igvn != NULL) { - igvn->remove_dead_node(hook); - } else { - for (int i = 0; i < 4; i++) { - hook->set_req(i, NULL); - } - } + hook->destruct(phase); return new AddLNode(temp1, temp2); } @@ -505,8 +498,9 @@ const Type* DivINode::Value(PhaseGVN* phase) const { if( t2 == Type::TOP ) return Type::TOP; // x/x == 1 since we always generate the dynamic divisor check for 0. - if( phase->eqv( in(1), in(2) ) ) + if (in(1) == in(2)) { return TypeInt::ONE; + } // Either input is BOTTOM ==> the result is the local BOTTOM const Type *bot = bottom_type(); @@ -610,8 +604,9 @@ const Type* DivLNode::Value(PhaseGVN* phase) const { if( t2 == Type::TOP ) return Type::TOP; // x/x == 1 since we always generate the dynamic divisor check for 0. - if( phase->eqv( in(1), in(2) ) ) + if (in(1) == in(2)) { return TypeLong::ONE; + } // Either input is BOTTOM ==> the result is the local BOTTOM const Type *bot = bottom_type(); @@ -685,9 +680,10 @@ const Type* DivFNode::Value(PhaseGVN* phase) const { // x/x == 1, we ignore 0/0. // Note: if t1 and t2 are zero then result is NaN (JVMS page 213) // Does not work for variables because of NaN's - if( phase->eqv( in(1), in(2) ) && t1->base() == Type::FloatCon) - if (!g_isnan(t1->getf()) && g_isfinite(t1->getf()) && t1->getf() != 0.0) // could be negative ZERO or NaN - return TypeF::ONE; + if (in(1) == in(2) && t1->base() == Type::FloatCon && + !g_isnan(t1->getf()) && g_isfinite(t1->getf()) && t1->getf() != 0.0) { // could be negative ZERO or NaN + return TypeF::ONE; + } if( t2 == TypeF::ONE ) return t1; @@ -773,9 +769,10 @@ const Type* DivDNode::Value(PhaseGVN* phase) const { // x/x == 1, we ignore 0/0. // Note: if t1 and t2 are zero then result is NaN (JVMS page 213) // Does not work for variables because of NaN's - if( phase->eqv( in(1), in(2) ) && t1->base() == Type::DoubleCon) - if (!g_isnan(t1->getd()) && g_isfinite(t1->getd()) && t1->getd() != 0.0) // could be negative ZERO or NaN - return TypeD::ONE; + if (in(1) == in(2) && t1->base() == Type::DoubleCon && + !g_isnan(t1->getd()) && g_isfinite(t1->getd()) && t1->getd() != 0.0) { // could be negative ZERO or NaN + return TypeD::ONE; + } if( t2 == TypeD::ONE ) return t1; @@ -912,11 +909,7 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) { // cmov2 is now the mod // Now remove the bogus extra edges used to keep things alive - if (can_reshape) { - phase->is_IterGVN()->remove_dead_node(hook); - } else { - hook->set_req(0, NULL); // Just yank bogus edge during Parse phase - } + hook->destruct(phase); return cmov2; } } @@ -968,11 +961,7 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) { } // Now remove the bogus extra edges used to keep things alive - if (can_reshape) { - phase->is_IterGVN()->remove_dead_node(hook); - } else { - hook->set_req(0, NULL); // Just yank bogus edge during Parse phase - } + hook->destruct(phase); // return the value return result; @@ -990,7 +979,9 @@ const Type* ModINode::Value(PhaseGVN* phase) const { // 0 MOD X is 0 if( t1 == TypeInt::ZERO ) return TypeInt::ZERO; // X MOD X is 0 - if( phase->eqv( in(1), in(2) ) ) return TypeInt::ZERO; + if (in(1) == in(2)) { + return TypeInt::ZERO; + } // Either input is BOTTOM ==> the result is the local BOTTOM const Type *bot = bottom_type(); @@ -1085,11 +1076,7 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) { // cmov2 is now the mod // Now remove the bogus extra edges used to keep things alive - if (can_reshape) { - phase->is_IterGVN()->remove_dead_node(hook); - } else { - hook->set_req(0, NULL); // Just yank bogus edge during Parse phase - } + hook->destruct(phase); return cmov2; } } @@ -1141,11 +1128,7 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) { } // Now remove the bogus extra edges used to keep things alive - if (can_reshape) { - phase->is_IterGVN()->remove_dead_node(hook); - } else { - hook->set_req(0, NULL); // Just yank bogus edge during Parse phase - } + hook->destruct(phase); // return the value return result; @@ -1163,7 +1146,9 @@ const Type* ModLNode::Value(PhaseGVN* phase) const { // 0 MOD X is 0 if( t1 == TypeLong::ZERO ) return TypeLong::ZERO; // X MOD X is 0 - if( phase->eqv( in(1), in(2) ) ) return TypeLong::ZERO; + if (in(1) == in(2)) { + return TypeLong::ZERO; + } // Either input is BOTTOM ==> the result is the local BOTTOM const Type *bot = bottom_type(); diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp index ba5f737592e..13053bb6317 100644 --- a/src/hotspot/share/opto/doCall.cpp +++ b/src/hotspot/share/opto/doCall.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,6 +38,7 @@ #include "opto/rootnode.hpp" #include "opto/runtime.hpp" #include "opto/subnode.hpp" +#include "prims/methodHandles.hpp" #include "prims/nativeLookup.hpp" #include "runtime/sharedRuntime.hpp" @@ -134,6 +135,8 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool if (cg->does_virtual_dispatch()) { cg_intrinsic = cg; cg = NULL; + } else if (should_delay_vector_inlining(callee, jvms)) { + return CallGenerator::for_late_inline(callee, cg); } else { return cg; } @@ -184,6 +187,8 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool return CallGenerator::for_string_late_inline(callee, cg); } else if (should_delay_boxing_inlining(callee, jvms)) { return CallGenerator::for_boxing_late_inline(callee, cg); + } else if (should_delay_vector_reboxing_inlining(callee, jvms)) { + return CallGenerator::for_vector_reboxing_late_inline(callee, cg); } else if ((should_delay || AlwaysIncrementalInline)) { return CallGenerator::for_late_inline(callee, cg); } @@ -421,6 +426,14 @@ bool Compile::should_delay_boxing_inlining(ciMethod* call_method, JVMState* jvms return false; } +bool Compile::should_delay_vector_inlining(ciMethod* call_method, JVMState* jvms) { + return EnableVectorSupport && call_method->is_vector_method(); +} + +bool Compile::should_delay_vector_reboxing_inlining(ciMethod* call_method, JVMState* jvms) { + return EnableVectorSupport && (call_method->intrinsic_id() == vmIntrinsics::_VectorRebox); +} + // uncommon-trap call-sites where callee is unloaded, uninitialized or will not link bool Parse::can_not_compile_call_site(ciMethod *dest_method, ciInstanceKlass* klass) { // Additional inputs to consider... @@ -630,10 +643,6 @@ void Parse::do_call() { receiver = record_profiled_receiver_for_speculation(receiver); } - // Bump method data counters (We profile *before* the call is made - // because exceptions don't return to the call site.) - profile_call(receiver); - JVMState* new_jvms = cg->generate(jvms); if (new_jvms == NULL) { // When inlining attempt fails (e.g., too many arguments), @@ -657,7 +666,6 @@ void Parse::do_call() { if (cg->is_inline()) { // Accumulate has_loops estimate - C->set_has_loops(C->has_loops() || cg->method()->has_loops()); C->env()->notice_inlined_method(cg->method()); } diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 681fdb82510..5742f4e862b 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -123,6 +123,7 @@ bool ConnectionGraph::compute_escape() { GrowableArray java_objects_worklist; GrowableArray non_escaped_worklist; GrowableArray oop_fields_worklist; + GrowableArray sfn_worklist; DEBUG_ONLY( GrowableArray addp_worklist; ) { Compile::TracePhase tp("connectionGraph", &Phase::timers[Phase::_t_connectionGraph]); @@ -188,6 +189,9 @@ bool ConnectionGraph::compute_escape() { Node* m = n->fast_out(i); // Get user ideal_nodes.push(m); } + if (n-> is_SafePoint()) { + sfn_worklist.append(n->as_SafePoint()); + } } if (non_escaped_worklist.length() == 0) { _collecting = false; @@ -242,9 +246,6 @@ bool ConnectionGraph::compute_escape() { if (n->is_Allocate()) { n->as_Allocate()->_is_non_escaping = noescape; } - if (n->is_CallStaticJava()) { - n->as_CallStaticJava()->_is_non_escaping = noescape; - } if (noescape && ptn->scalar_replaceable()) { adjust_scalar_replaceable_state(ptn); if (ptn->scalar_replaceable()) { @@ -317,9 +318,89 @@ bool ConnectionGraph::compute_escape() { tty->cr(); #endif } + + // Annotate at safepoints if they have <= ArgEscape objects in their scope and at + // java calls if they pass ArgEscape objects as parameters. + if (has_non_escaping_obj && + (C->env()->should_retain_local_variables() || + C->env()->jvmti_can_get_owned_monitor_info() || + C->env()->jvmti_can_walk_any_space() || + DeoptimizeObjectsALot)) { + int sfn_length = sfn_worklist.length(); + for (int next = 0; next < sfn_length; next++) { + SafePointNode* sfn = sfn_worklist.at(next); + sfn->set_has_ea_local_in_scope(has_ea_local_in_scope(sfn)); + if (sfn->is_CallJava()) { + CallJavaNode* call = sfn->as_CallJava(); + call->set_arg_escape(has_arg_escape(call)); + } + } + } + return has_non_escaping_obj; } +// Returns true if there is an object in the scope of sfn that does not escape globally. +bool ConnectionGraph::has_ea_local_in_scope(SafePointNode* sfn) { + Compile* C = _compile; + for (JVMState* jvms = sfn->jvms(); jvms != NULL; jvms = jvms->caller()) { + if (C->env()->should_retain_local_variables() || C->env()->jvmti_can_walk_any_space() || + DeoptimizeObjectsALot) { + // Jvmti agents can access locals. Must provide info about local objects at runtime. + int num_locs = jvms->loc_size(); + for (int idx = 0; idx < num_locs; idx++) { + Node* l = sfn->local(jvms, idx); + if (not_global_escape(l)) { + return true; + } + } + } + if (C->env()->jvmti_can_get_owned_monitor_info() || + C->env()->jvmti_can_walk_any_space() || DeoptimizeObjectsALot) { + // Jvmti agents can read monitors. Must provide info about locked objects at runtime. + int num_mon = jvms->nof_monitors(); + for (int idx = 0; idx < num_mon; idx++) { + Node* m = sfn->monitor_obj(jvms, idx); + if (m != NULL && not_global_escape(m)) { + return true; + } + } + } + } + return false; +} + +// Returns true if at least one of the arguments to the call is an object +// that does not escape globally. +bool ConnectionGraph::has_arg_escape(CallJavaNode* call) { + if (call->method() != NULL) { + uint max_idx = TypeFunc::Parms + call->method()->arg_size(); + for (uint idx = TypeFunc::Parms; idx < max_idx; idx++) { + Node* p = call->in(idx); + if (not_global_escape(p)) { + return true; + } + } + } else { + const char* name = call->as_CallStaticJava()->_name; + assert(name != NULL, "no name"); + // no arg escapes through uncommon traps + if (strcmp(name, "uncommon_trap") != 0) { + // process_call_arguments() assumes that all arguments escape globally + const TypeTuple* d = call->tf()->domain(); + for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { + const Type* at = d->field_at(i); + if (at->isa_oopptr() != NULL) { + return true; + } + } + } + } + return false; +} + + + // Utility function for nodes that load an object void ConnectionGraph::add_objload_to_connection_graph(Node *n, Unique_Node_List *delayed_worklist) { // Using isa_ptr() instead of isa_oopptr() for LoadP and Phi because @@ -1001,6 +1082,8 @@ void ConnectionGraph::process_call_arguments(CallNode *call) { strcmp(call->as_CallLeaf()->_name, "sha256_implCompressMB") == 0 || strcmp(call->as_CallLeaf()->_name, "sha512_implCompress") == 0 || strcmp(call->as_CallLeaf()->_name, "sha512_implCompressMB") == 0 || + strcmp(call->as_CallLeaf()->_name, "sha3_implCompress") == 0 || + strcmp(call->as_CallLeaf()->_name, "sha3_implCompressMB") == 0 || strcmp(call->as_CallLeaf()->_name, "multiplyToLen") == 0 || strcmp(call->as_CallLeaf()->_name, "squareToLen") == 0 || strcmp(call->as_CallLeaf()->_name, "mulAdd") == 0 || @@ -2982,11 +3065,6 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, // so it could be eliminated if it has no uses. alloc->as_Allocate()->_is_scalar_replaceable = true; } - if (alloc->is_CallStaticJava()) { - // Set the scalar_replaceable flag for boxing method - // so it could be eliminated if it has no uses. - alloc->as_CallStaticJava()->_is_scalar_replaceable = true; - } continue; } if (!n->is_CheckCastPP()) { // not unique CheckCastPP. @@ -3035,11 +3113,6 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, // so it could be eliminated. alloc->as_Allocate()->_is_scalar_replaceable = true; } - if (alloc->is_CallStaticJava()) { - // Set the scalar_replaceable flag for boxing method - // so it could be eliminated. - alloc->as_CallStaticJava()->_is_scalar_replaceable = true; - } set_escape_state(ptnode_adr(n->_idx), es); // CheckCastPP escape state // in order for an object to be scalar-replaceable, it must be: // - a direct allocation (not a call returning an object) diff --git a/src/hotspot/share/opto/escape.hpp b/src/hotspot/share/opto/escape.hpp index af7efa7391f..3ae6ce5f2d4 100644 --- a/src/hotspot/share/opto/escape.hpp +++ b/src/hotspot/share/opto/escape.hpp @@ -553,6 +553,11 @@ class ConnectionGraph: public ResourceObj { return (phi == NULL) ? NULL : phi->as_Phi(); } + // Returns true if there is an object in the scope of sfn that does not escape globally. + bool has_ea_local_in_scope(SafePointNode* sfn); + + bool has_arg_escape(CallJavaNode* call); + // Notify optimizer that a node has been modified void record_for_optimizer(Node *n); diff --git a/src/hotspot/share/opto/gcm.cpp b/src/hotspot/share/opto/gcm.cpp index f6109adfdf3..330809a4596 100644 --- a/src/hotspot/share/opto/gcm.cpp +++ b/src/hotspot/share/opto/gcm.cpp @@ -610,16 +610,6 @@ Block* PhaseCFG::insert_anti_dependences(Block* LCA, Node* load, bool verify) { Node_List non_early_stores(area); // all relevant stores outside of early bool must_raise_LCA = false; -#ifdef TRACK_PHI_INPUTS - // %%% This extra checking fails because MergeMem nodes are not GVNed. - // Provide "phi_inputs" to check if every input to a PhiNode is from the - // original memory state. This indicates a PhiNode for which should not - // prevent the load from sinking. For such a block, set_raise_LCA_mark - // may be overly conservative. - // Mechanism: count inputs seen for each Phi encountered in worklist_store. - DEBUG_ONLY(GrowableArray phi_inputs(area, C->unique(),0,0)); -#endif - // 'load' uses some memory state; look for users of the same state. // Recurse through MergeMem nodes to the stores that use them. @@ -761,19 +751,6 @@ Block* PhaseCFG::insert_anti_dependences(Block* LCA, Node* load, bool verify) { } } assert(found_match, "no worklist bug"); -#ifdef TRACK_PHI_INPUTS -#ifdef ASSERT - // This assert asks about correct handling of PhiNodes, which may not - // have all input edges directly from 'mem'. See BugId 4621264 - int num_mem_inputs = phi_inputs.at_grow(store->_idx,0) + 1; - // Increment by exactly one even if there are multiple copies of 'mem' - // coming into the phi, because we will run this block several times - // if there are several copies of 'mem'. (That's how DU iterators work.) - phi_inputs.at_put(store->_idx, num_mem_inputs); - assert(PhiNode::Input + num_mem_inputs < store->req(), - "Expect at least one phi input will not be from original memory state"); -#endif //ASSERT -#endif //TRACK_PHI_INPUTS } else if (store_block != early) { // 'store' is between the current LCA and earliest possible block. // Label its block, and decide later on how to raise the LCA @@ -1198,7 +1175,7 @@ Block* PhaseCFG::hoist_to_cheaper_block(Block* LCA, Block* early, Node* self) { #endif cand_cnt++; if (LCA_freq < least_freq || // Better Frequency - (StressGCM && Compile::randomized_select(cand_cnt)) || // Should be randomly accepted in stress mode + (StressGCM && C->randomized_select(cand_cnt)) || // Should be randomly accepted in stress mode (!StressGCM && // Otherwise, choose with latency !in_latency && // No block containing latency LCA_freq < least_freq * delta && // No worse frequency diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 9861fd8800f..805c7b6d0b1 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -163,7 +163,7 @@ void GraphKit::verify_exception_state(SafePointNode* ex_map) { void GraphKit::stop_and_kill_map() { SafePointNode* dead_map = stop(); if (dead_map != NULL) { - dead_map->disconnect_inputs(NULL, C); // Mark the map as killed. + dead_map->disconnect_inputs(C); // Mark the map as killed. assert(dead_map->is_killed(), "must be so marked"); } } @@ -1960,7 +1960,7 @@ void GraphKit::replace_call(CallNode* call, Node* result, bool do_replaced_nodes } // Disconnect the call from the graph - call->disconnect_inputs(NULL, C); + call->disconnect_inputs(C); C->gvn_replace_by(call, C->top()); // Clean up any MergeMems that feed other MergeMems since the diff --git a/src/hotspot/share/opto/ifg.cpp b/src/hotspot/share/opto/ifg.cpp index c8cf3bad9d8..ddd237b0863 100644 --- a/src/hotspot/share/opto/ifg.cpp +++ b/src/hotspot/share/opto/ifg.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -616,7 +616,7 @@ bool PhaseChaitin::remove_node_if_not_used(Block* b, uint location, Node* n, uin if (lrg._def == n) { lrg._def = 0; } - n->disconnect_inputs(NULL, C); + n->disconnect_inputs(C); _cfg.unmap_node_from_block(n); n->replace_by(C->top()); return true; diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index ddf543ab822..f2a24ed1857 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -999,8 +999,7 @@ bool IfNode::fold_compares_helper(ProjNode* proj, ProjNode* success, ProjNode* f if (adjusted_lim == NULL) { adjusted_lim = igvn->transform(new SubINode(hi, lo)); } - hook->del_req(0); // Just yank bogus edge - hook->destruct(); + hook->destruct(igvn); Node* newcmp = igvn->transform(new CmpUNode(adjusted_val, adjusted_lim)); Node* newbool = igvn->transform(new BoolNode(newcmp, cond)); diff --git a/src/hotspot/share/opto/indexSet.cpp b/src/hotspot/share/opto/indexSet.cpp index f4a596a4ff3..eb90151eef0 100644 --- a/src/hotspot/share/opto/indexSet.cpp +++ b/src/hotspot/share/opto/indexSet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -241,7 +241,7 @@ IndexSet::IndexSet (IndexSet *set) { set_block(i, &_empty_block); } else { BitBlock *new_block = alloc_block(); - memcpy(new_block->words(), block->words(), sizeof(uint32_t) * words_per_block); + memcpy(new_block->words(), block->words(), sizeof(uintptr_t) * words_per_block); set_block(i, new_block); } } diff --git a/src/hotspot/share/opto/indexSet.hpp b/src/hotspot/share/opto/indexSet.hpp index 5f57faaeb32..7ed34116d1c 100644 --- a/src/hotspot/share/opto/indexSet.hpp +++ b/src/hotspot/share/opto/indexSet.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,9 +60,12 @@ class IndexSet : public ResourceObj { // membership of the element in the set. // The lengths of the index bitfields - enum { bit_index_length = 5, - word_index_length = 3, - block_index_length = 8 // not used + enum { + // Each block consists of 256 bits + block_index_length = 8, + // Split over 4 or 8 words depending on bitness + word_index_length = block_index_length - LogBitsPerWord, + bit_index_length = block_index_length - word_index_length, }; // Derived constants used for manipulating the index bitfields @@ -88,7 +91,7 @@ class IndexSet : public ResourceObj { return mask_bits(element >> word_index_offset,word_index_mask); } static uint get_bit_index(uint element) { - return mask_bits(element,bit_index_mask); + return mask_bits(element, bit_index_mask); } //------------------------------ class BitBlock ---------------------------- @@ -102,17 +105,17 @@ class IndexSet : public ResourceObj { // All of BitBlocks fields and methods are declared private. We limit // access to IndexSet and IndexSetIterator. - // A BitBlock is composed of some number of 32 bit words. When a BitBlock + // A BitBlock is composed of some number of 32- or 64-bit words. When a BitBlock // is not in use by any IndexSet, it is stored on a free list. The next field - // is used by IndexSet to mainting this free list. + // is used by IndexSet to maintain this free list. union { - uint32_t _words[words_per_block]; + uintptr_t _words[words_per_block]; BitBlock *_next; } _data; // accessors - uint32_t* words() { return _data._words; } + uintptr_t* words() { return _data._words; } void set_next(BitBlock *next) { _data._next = next; } BitBlock *next() { return _data._next; } @@ -121,32 +124,32 @@ class IndexSet : public ResourceObj { // not assume that the block index has been masked out. void clear() { - memset(words(), 0, sizeof(uint32_t) * words_per_block); + memset(words(), 0, sizeof(uintptr_t) * words_per_block); } bool member(uint element) { uint word_index = IndexSet::get_word_index(element); - uint bit_index = IndexSet::get_bit_index(element); + uintptr_t bit_index = IndexSet::get_bit_index(element); - return ((words()[word_index] & (uint32_t)(0x1 << bit_index)) != 0); + return ((words()[word_index] & (uintptr_t(1) << bit_index)) != 0); } bool insert(uint element) { uint word_index = IndexSet::get_word_index(element); - uint bit_index = IndexSet::get_bit_index(element); + uintptr_t bit_index = IndexSet::get_bit_index(element); - uint32_t bit = (0x1 << bit_index); - uint32_t before = words()[word_index]; + uintptr_t bit = uintptr_t(1) << bit_index; + uintptr_t before = words()[word_index]; words()[word_index] = before | bit; return ((before & bit) != 0); } bool remove(uint element) { uint word_index = IndexSet::get_word_index(element); - uint bit_index = IndexSet::get_bit_index(element); + uintptr_t bit_index = IndexSet::get_bit_index(element); - uint32_t bit = (0x1 << bit_index); - uint32_t before = words()[word_index]; + uintptr_t bit = uintptr_t(1) << bit_index; + uintptr_t before = words()[word_index]; words()[word_index] = before & ~bit; return ((before & bit) != 0); } @@ -376,7 +379,7 @@ class IndexSetIterator { private: // The current word we are inspecting - uint32_t _current; + uintptr_t _current; // What element number are we currently on? uint _value; @@ -391,7 +394,7 @@ class IndexSetIterator { uint _max_blocks; // A pointer to the contents of the current block - uint32_t *_words; + uintptr_t* _words; // A pointer to the blocks in our set IndexSet::BitBlock **_blocks; @@ -447,7 +450,7 @@ class IndexSetIterator { // Return the next element of the set. uint next_value() { - uint current = _current; + uintptr_t current = _current; assert(current != 0, "sanity"); uint advance = count_trailing_zeros(current); assert(((current >> advance) & 0x1) == 1, "sanity"); diff --git a/src/hotspot/share/opto/intrinsicnode.hpp b/src/hotspot/share/opto/intrinsicnode.hpp index f5d5be149bb..3d6e9a38d12 100644 --- a/src/hotspot/share/opto/intrinsicnode.hpp +++ b/src/hotspot/share/opto/intrinsicnode.hpp @@ -47,10 +47,11 @@ class PartialSubtypeCheckNode : public Node { // Base class for Ideal nodes used in String intrinsic code. class StrIntrinsicNode: public Node { public: - // Possible encodings of the two parameters passed to the string intrinsic. + // Possible encodings of the parameters passed to the string intrinsic. // 'L' stands for Latin1 and 'U' stands for UTF16. For example, 'LU' means that // the first string is Latin1 encoded and the second string is UTF16 encoded. - typedef enum ArgEncoding { LL, LU, UL, UU, none } ArgEnc; + // 'L' means that the single string is Latin1 encoded + typedef enum ArgEncoding { LL, LU, UL, UU, L, U, none } ArgEnc; protected: // Encoding of strings. Used to select the right version of the intrinsic. diff --git a/src/hotspot/share/opto/lcm.cpp b/src/hotspot/share/opto/lcm.cpp index 02a3b7e36ba..90452e97e23 100644 --- a/src/hotspot/share/opto/lcm.cpp +++ b/src/hotspot/share/opto/lcm.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -430,7 +430,7 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo tmp1->replace_by(tmp); tmp2->replace_by(tmp1); tmp->replace_by(tmp2); - tmp->destruct(); + tmp->destruct(NULL); } // Remove the existing null check; use a new implicit null check instead. @@ -449,7 +449,7 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo old_tst->set_req(i3, NULL); if (in->outcnt() == 0) { // Remove dead input node - in->disconnect_inputs(NULL, C); + in->disconnect_inputs(C); block->find_remove(in); } } @@ -632,7 +632,7 @@ Node* PhaseCFG::select( cand_cnt++; if (choice < n_choice || (choice == n_choice && - ((StressLCM && Compile::randomized_select(cand_cnt)) || + ((StressLCM && C->randomized_select(cand_cnt)) || (!StressLCM && (latency < n_latency || (latency == n_latency && @@ -686,6 +686,7 @@ void PhaseCFG::adjust_register_pressure(Node* n, Block* block, intptr_t* recalc_ case Op_StoreP: case Op_StoreN: case Op_StoreVector: + case Op_StoreVectorScatter: case Op_StoreNKlass: for (uint k = 1; k < m->req(); k++) { Node *in = m->in(k); @@ -1369,7 +1370,7 @@ void PhaseCFG::call_catch_cleanup(Block* block) { // Remove the now-dead cloned ops for(uint i3 = beg; i3 < end; i3++ ) { - block->get_node(beg)->disconnect_inputs(NULL, C); + block->get_node(beg)->disconnect_inputs(C); block->remove_node(beg); } @@ -1382,7 +1383,7 @@ void PhaseCFG::call_catch_cleanup(Block* block) { Node *n = sb->get_node(j); if (n->outcnt() == 0 && (!n->is_Proj() || n->as_Proj()->in(0)->outcnt() == 1) ){ - n->disconnect_inputs(NULL, C); + n->disconnect_inputs(C); sb->remove_node(j); new_cnt--; } diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 1b26cac022f..9bb7ea8c034 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -37,15 +37,13 @@ #include "opto/addnode.hpp" #include "opto/arraycopynode.hpp" #include "opto/c2compiler.hpp" -#include "opto/callGenerator.hpp" #include "opto/castnode.hpp" #include "opto/cfgnode.hpp" #include "opto/convertnode.hpp" #include "opto/countbitsnode.hpp" -#include "opto/intrinsicnode.hpp" #include "opto/idealKit.hpp" +#include "opto/library_call.hpp" #include "opto/mathexactnode.hpp" -#include "opto/movenode.hpp" #include "opto/mulnode.hpp" #include "opto/narrowptrnode.hpp" #include "opto/opaquenode.hpp" @@ -60,291 +58,6 @@ #include "utilities/macros.hpp" #include "utilities/powerOfTwo.hpp" -class LibraryIntrinsic : public InlineCallGenerator { - // Extend the set of intrinsics known to the runtime: - public: - private: - bool _is_virtual; - bool _does_virtual_dispatch; - int8_t _predicates_count; // Intrinsic is predicated by several conditions - int8_t _last_predicate; // Last generated predicate - vmIntrinsics::ID _intrinsic_id; - - public: - LibraryIntrinsic(ciMethod* m, bool is_virtual, int predicates_count, bool does_virtual_dispatch, vmIntrinsics::ID id) - : InlineCallGenerator(m), - _is_virtual(is_virtual), - _does_virtual_dispatch(does_virtual_dispatch), - _predicates_count((int8_t)predicates_count), - _last_predicate((int8_t)-1), - _intrinsic_id(id) - { - } - virtual bool is_intrinsic() const { return true; } - virtual bool is_virtual() const { return _is_virtual; } - virtual bool is_predicated() const { return _predicates_count > 0; } - virtual int predicates_count() const { return _predicates_count; } - virtual bool does_virtual_dispatch() const { return _does_virtual_dispatch; } - virtual JVMState* generate(JVMState* jvms); - virtual Node* generate_predicate(JVMState* jvms, int predicate); - vmIntrinsics::ID intrinsic_id() const { return _intrinsic_id; } -}; - - -// Local helper class for LibraryIntrinsic: -class LibraryCallKit : public GraphKit { - private: - LibraryIntrinsic* _intrinsic; // the library intrinsic being called - Node* _result; // the result node, if any - int _reexecute_sp; // the stack pointer when bytecode needs to be reexecuted - - const TypeOopPtr* sharpen_unsafe_type(Compile::AliasType* alias_type, const TypePtr *adr_type); - - public: - LibraryCallKit(JVMState* jvms, LibraryIntrinsic* intrinsic) - : GraphKit(jvms), - _intrinsic(intrinsic), - _result(NULL) - { - // Check if this is a root compile. In that case we don't have a caller. - if (!jvms->has_method()) { - _reexecute_sp = sp(); - } else { - // Find out how many arguments the interpreter needs when deoptimizing - // and save the stack pointer value so it can used by uncommon_trap. - // We find the argument count by looking at the declared signature. - bool ignored_will_link; - ciSignature* declared_signature = NULL; - ciMethod* ignored_callee = caller()->get_method_at_bci(bci(), ignored_will_link, &declared_signature); - const int nargs = declared_signature->arg_size_for_bc(caller()->java_code_at_bci(bci())); - _reexecute_sp = sp() + nargs; // "push" arguments back on stack - } - } - - virtual LibraryCallKit* is_LibraryCallKit() const { return (LibraryCallKit*)this; } - - ciMethod* caller() const { return jvms()->method(); } - int bci() const { return jvms()->bci(); } - LibraryIntrinsic* intrinsic() const { return _intrinsic; } - vmIntrinsics::ID intrinsic_id() const { return _intrinsic->intrinsic_id(); } - ciMethod* callee() const { return _intrinsic->method(); } - - bool try_to_inline(int predicate); - Node* try_to_predicate(int predicate); - - void push_result() { - // Push the result onto the stack. - if (!stopped() && result() != NULL) { - BasicType bt = result()->bottom_type()->basic_type(); - push_node(bt, result()); - } - } - - private: - void fatal_unexpected_iid(vmIntrinsics::ID iid) { - fatal("unexpected intrinsic %d: %s", iid, vmIntrinsics::name_at(iid)); - } - - void set_result(Node* n) { assert(_result == NULL, "only set once"); _result = n; } - void set_result(RegionNode* region, PhiNode* value); - Node* result() { return _result; } - - virtual int reexecute_sp() { return _reexecute_sp; } - - // Helper functions to inline natives - Node* generate_guard(Node* test, RegionNode* region, float true_prob); - Node* generate_slow_guard(Node* test, RegionNode* region); - Node* generate_fair_guard(Node* test, RegionNode* region); - Node* generate_negative_guard(Node* index, RegionNode* region, - // resulting CastII of index: - Node* *pos_index = NULL); - Node* generate_limit_guard(Node* offset, Node* subseq_length, - Node* array_length, - RegionNode* region); - void generate_string_range_check(Node* array, Node* offset, - Node* length, bool char_count); - Node* generate_current_thread(Node* &tls_output); - Node* load_mirror_from_klass(Node* klass); - Node* load_klass_from_mirror_common(Node* mirror, bool never_see_null, - RegionNode* region, int null_path, - int offset); - Node* load_klass_from_mirror(Node* mirror, bool never_see_null, - RegionNode* region, int null_path) { - int offset = java_lang_Class::klass_offset(); - return load_klass_from_mirror_common(mirror, never_see_null, - region, null_path, - offset); - } - Node* load_array_klass_from_mirror(Node* mirror, bool never_see_null, - RegionNode* region, int null_path) { - int offset = java_lang_Class::array_klass_offset(); - return load_klass_from_mirror_common(mirror, never_see_null, - region, null_path, - offset); - } - Node* generate_access_flags_guard(Node* kls, - int modifier_mask, int modifier_bits, - RegionNode* region); - Node* generate_interface_guard(Node* kls, RegionNode* region); - Node* generate_hidden_class_guard(Node* kls, RegionNode* region); - Node* generate_array_guard(Node* kls, RegionNode* region) { - return generate_array_guard_common(kls, region, false, false); - } - Node* generate_non_array_guard(Node* kls, RegionNode* region) { - return generate_array_guard_common(kls, region, false, true); - } - Node* generate_objArray_guard(Node* kls, RegionNode* region) { - return generate_array_guard_common(kls, region, true, false); - } - Node* generate_non_objArray_guard(Node* kls, RegionNode* region) { - return generate_array_guard_common(kls, region, true, true); - } - Node* generate_array_guard_common(Node* kls, RegionNode* region, - bool obj_array, bool not_array); - Node* generate_virtual_guard(Node* obj_klass, RegionNode* slow_region); - CallJavaNode* generate_method_call(vmIntrinsics::ID method_id, - bool is_virtual = false, bool is_static = false); - CallJavaNode* generate_method_call_static(vmIntrinsics::ID method_id) { - return generate_method_call(method_id, false, true); - } - CallJavaNode* generate_method_call_virtual(vmIntrinsics::ID method_id) { - return generate_method_call(method_id, true, false); - } - Node * load_field_from_object(Node * fromObj, const char * fieldName, const char * fieldTypeString, bool is_exact, bool is_static, ciInstanceKlass * fromKls); - Node * field_address_from_object(Node * fromObj, const char * fieldName, const char * fieldTypeString, bool is_exact, bool is_static, ciInstanceKlass * fromKls); - - Node* make_string_method_node(int opcode, Node* str1_start, Node* cnt1, Node* str2_start, Node* cnt2, StrIntrinsicNode::ArgEnc ae); - bool inline_string_compareTo(StrIntrinsicNode::ArgEnc ae); - bool inline_string_indexOf(StrIntrinsicNode::ArgEnc ae); - bool inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae); - Node* make_indexOf_node(Node* src_start, Node* src_count, Node* tgt_start, Node* tgt_count, - RegionNode* region, Node* phi, StrIntrinsicNode::ArgEnc ae); - bool inline_string_indexOfChar(); - bool inline_string_equals(StrIntrinsicNode::ArgEnc ae); - bool inline_string_toBytesU(); - bool inline_string_getCharsU(); - bool inline_string_copy(bool compress); - bool inline_string_char_access(bool is_store); - Node* round_double_node(Node* n); - bool runtime_math(const TypeFunc* call_type, address funcAddr, const char* funcName); - bool inline_math_native(vmIntrinsics::ID id); - bool inline_math(vmIntrinsics::ID id); - bool inline_double_math(vmIntrinsics::ID id); - template - bool inline_math_overflow(Node* arg1, Node* arg2); - void inline_math_mathExact(Node* math, Node* test); - bool inline_math_addExactI(bool is_increment); - bool inline_math_addExactL(bool is_increment); - bool inline_math_multiplyExactI(); - bool inline_math_multiplyExactL(); - bool inline_math_multiplyHigh(); - bool inline_math_negateExactI(); - bool inline_math_negateExactL(); - bool inline_math_subtractExactI(bool is_decrement); - bool inline_math_subtractExactL(bool is_decrement); - bool inline_min_max(vmIntrinsics::ID id); - bool inline_notify(vmIntrinsics::ID id); - Node* generate_min_max(vmIntrinsics::ID id, Node* x, Node* y); - // This returns Type::AnyPtr, RawPtr, or OopPtr. - int classify_unsafe_addr(Node* &base, Node* &offset, BasicType type); - Node* make_unsafe_address(Node*& base, Node* offset, DecoratorSet decorators, BasicType type = T_ILLEGAL, bool can_cast = false); - - typedef enum { Relaxed, Opaque, Volatile, Acquire, Release } AccessKind; - DecoratorSet mo_decorator_for_access_kind(AccessKind kind); - bool inline_unsafe_access(bool is_store, BasicType type, AccessKind kind, bool is_unaligned); - static bool klass_needs_init_guard(Node* kls); - bool inline_unsafe_allocate(); - bool inline_unsafe_newArray(bool uninitialized); - bool inline_unsafe_writeback0(); - bool inline_unsafe_writebackSync0(bool is_pre); - bool inline_unsafe_copyMemory(); - bool inline_native_currentThread(); - - bool inline_native_time_funcs(address method, const char* funcName); -#ifdef JFR_HAVE_INTRINSICS - bool inline_native_classID(); - bool inline_native_getEventWriter(); -#endif - bool inline_native_Class_query(vmIntrinsics::ID id); - bool inline_native_subtype_check(); - bool inline_native_getLength(); - bool inline_array_copyOf(bool is_copyOfRange); - bool inline_array_equals(StrIntrinsicNode::ArgEnc ae); - bool inline_preconditions_checkIndex(); - void copy_to_clone(Node* obj, Node* alloc_obj, Node* obj_size, bool is_array); - bool inline_native_clone(bool is_virtual); - bool inline_native_Reflection_getCallerClass(); - // Helper function for inlining native object hash method - bool inline_native_hashcode(bool is_virtual, bool is_static); - bool inline_native_getClass(); - - // Helper functions for inlining arraycopy - bool inline_arraycopy(); - AllocateArrayNode* tightly_coupled_allocation(Node* ptr, - RegionNode* slow_region); - JVMState* arraycopy_restore_alloc_state(AllocateArrayNode* alloc, int& saved_reexecute_sp); - void arraycopy_move_allocation_here(AllocateArrayNode* alloc, Node* dest, JVMState* saved_jvms, int saved_reexecute_sp, - uint new_idx); - - typedef enum { LS_get_add, LS_get_set, LS_cmp_swap, LS_cmp_swap_weak, LS_cmp_exchange } LoadStoreKind; - bool inline_unsafe_load_store(BasicType type, LoadStoreKind kind, AccessKind access_kind); - bool inline_unsafe_fence(vmIntrinsics::ID id); - bool inline_onspinwait(); - bool inline_fp_conversions(vmIntrinsics::ID id); - bool inline_number_methods(vmIntrinsics::ID id); - bool inline_reference_get(); - bool inline_Class_cast(); - bool inline_aescrypt_Block(vmIntrinsics::ID id); - bool inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id); - bool inline_electronicCodeBook_AESCrypt(vmIntrinsics::ID id); - bool inline_counterMode_AESCrypt(vmIntrinsics::ID id); - Node* inline_cipherBlockChaining_AESCrypt_predicate(bool decrypting); - Node* inline_electronicCodeBook_AESCrypt_predicate(bool decrypting); - Node* inline_counterMode_AESCrypt_predicate(); - Node* get_key_start_from_aescrypt_object(Node* aescrypt_object); - Node* get_original_key_start_from_aescrypt_object(Node* aescrypt_object); - bool inline_ghash_processBlocks(); - bool inline_base64_encodeBlock(); - bool inline_digestBase_implCompress(vmIntrinsics::ID id); - bool inline_digestBase_implCompressMB(int predicate); - bool inline_digestBase_implCompressMB(Node* digestBaseObj, ciInstanceKlass* instklass, - bool long_state, address stubAddr, const char *stubName, - Node* src_start, Node* ofs, Node* limit); - Node* get_state_from_digest_object(Node *digestBase_object); - Node* get_long_state_from_digest_object(Node *digestBase_object); - Node* inline_digestBase_implCompressMB_predicate(int predicate); - bool inline_encodeISOArray(); - bool inline_updateCRC32(); - bool inline_updateBytesCRC32(); - bool inline_updateByteBufferCRC32(); - Node* get_table_from_crc32c_class(ciInstanceKlass *crc32c_class); - bool inline_updateBytesCRC32C(); - bool inline_updateDirectByteBufferCRC32C(); - bool inline_updateBytesAdler32(); - bool inline_updateByteBufferAdler32(); - bool inline_multiplyToLen(); - bool inline_hasNegatives(); - bool inline_squareToLen(); - bool inline_mulAdd(); - bool inline_montgomeryMultiply(); - bool inline_montgomerySquare(); - bool inline_bigIntegerShift(bool isRightShift); - bool inline_vectorizedMismatch(); - bool inline_fma(vmIntrinsics::ID id); - bool inline_character_compare(vmIntrinsics::ID id); - bool inline_fp_min_max(vmIntrinsics::ID id); - - bool inline_profileBoolean(); - bool inline_isCompileConstant(); - void clear_upper_avx() { -#ifdef X86 - if (UseAVX >= 2) { - C->set_clear_upper_avx(true); - } -#endif - } -}; - //---------------------------make_vm_intrinsic---------------------------- CallGenerator* Compile::make_vm_intrinsic(ciMethod* m, bool is_virtual) { vmIntrinsics::ID id = m->intrinsic_id(); @@ -382,12 +95,6 @@ CallGenerator* Compile::make_vm_intrinsic(ciMethod* m, bool is_virtual) { } } -//----------------------register_library_intrinsics----------------------- -// Initialize this file's data structures, for each Compile instance. -void Compile::register_library_intrinsics() { - // Nothing to do here. -} - JVMState* LibraryIntrinsic::generate(JVMState* jvms) { LibraryCallKit kit(jvms, this); Compile* C = kit.C; @@ -453,6 +160,7 @@ JVMState* LibraryIntrinsic::generate(JVMState* jvms) { } C->gather_intrinsic_statistics(intrinsic_id(), is_virtual(), Compile::_intrinsic_failed); C->print_inlining_update(this); + return NULL; } @@ -530,7 +238,6 @@ bool LibraryCallKit::try_to_inline(int predicate) { } assert(merged_memory(), ""); - switch (intrinsic_id()) { case vmIntrinsics::_hashCode: return inline_native_hashcode(intrinsic()->is_virtual(), !is_static); case vmIntrinsics::_identityHashCode: return inline_native_hashcode(/*!virtual*/ false, is_static); @@ -591,7 +298,8 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_indexOfIL: return inline_string_indexOfI(StrIntrinsicNode::LL); case vmIntrinsics::_indexOfIU: return inline_string_indexOfI(StrIntrinsicNode::UU); case vmIntrinsics::_indexOfIUL: return inline_string_indexOfI(StrIntrinsicNode::UL); - case vmIntrinsics::_indexOfU_char: return inline_string_indexOfChar(); + case vmIntrinsics::_indexOfU_char: return inline_string_indexOfChar(StrIntrinsicNode::U); + case vmIntrinsics::_indexOfL_char: return inline_string_indexOfChar(StrIntrinsicNode::L); case vmIntrinsics::_equalsL: return inline_string_equals(StrIntrinsicNode::LL); case vmIntrinsics::_equalsU: return inline_string_equals(StrIntrinsicNode::UU); @@ -834,6 +542,7 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_sha_implCompress: case vmIntrinsics::_sha2_implCompress: case vmIntrinsics::_sha5_implCompress: + case vmIntrinsics::_sha3_implCompress: return inline_digestBase_implCompress(intrinsic_id()); case vmIntrinsics::_digestBase_implCompressMB: @@ -911,6 +620,45 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_minD: return inline_fp_min_max(intrinsic_id()); + case vmIntrinsics::_VectorUnaryOp: + return inline_vector_nary_operation(1); + case vmIntrinsics::_VectorBinaryOp: + return inline_vector_nary_operation(2); + case vmIntrinsics::_VectorTernaryOp: + return inline_vector_nary_operation(3); + case vmIntrinsics::_VectorBroadcastCoerced: + return inline_vector_broadcast_coerced(); + case vmIntrinsics::_VectorShuffleIota: + return inline_vector_shuffle_iota(); + case vmIntrinsics::_VectorShuffleToVector: + return inline_vector_shuffle_to_vector(); + case vmIntrinsics::_VectorLoadOp: + return inline_vector_mem_operation(/*is_store=*/false); + case vmIntrinsics::_VectorStoreOp: + return inline_vector_mem_operation(/*is_store=*/true); + case vmIntrinsics::_VectorGatherOp: + return inline_vector_gather_scatter(/*is_scatter*/ false); + case vmIntrinsics::_VectorScatterOp: + return inline_vector_gather_scatter(/*is_scatter*/ true); + case vmIntrinsics::_VectorReductionCoerced: + return inline_vector_reduction(); + case vmIntrinsics::_VectorTest: + return inline_vector_test(); + case vmIntrinsics::_VectorBlend: + return inline_vector_blend(); + case vmIntrinsics::_VectorRearrange: + return inline_vector_rearrange(); + case vmIntrinsics::_VectorCompare: + return inline_vector_compare(); + case vmIntrinsics::_VectorBroadcastInt: + return inline_vector_broadcast_int(); + case vmIntrinsics::_VectorConvert: + return inline_vector_convert(); + case vmIntrinsics::_VectorInsert: + return inline_vector_insert(); + case vmIntrinsics::_VectorExtract: + return inline_vector_extract(); + default: // If you get here, it may be that someone has added a new intrinsic // to the list in vmSymbols.hpp without implementing it here. @@ -1431,7 +1179,7 @@ Node* LibraryCallKit::make_indexOf_node(Node* src_start, Node* src_count, Node* } //-----------------------------inline_string_indexOfChar----------------------- -bool LibraryCallKit::inline_string_indexOfChar() { +bool LibraryCallKit::inline_string_indexOfChar(StrIntrinsicNode::ArgEnc ae) { if (too_many_traps(Deoptimization::Reason_intrinsic)) { return false; } @@ -1446,12 +1194,12 @@ bool LibraryCallKit::inline_string_indexOfChar() { src = must_be_not_null(src, true); - Node* src_offset = _gvn.transform(new LShiftINode(from_index, intcon(1))); + Node* src_offset = ae == StrIntrinsicNode::L ? from_index : _gvn.transform(new LShiftINode(from_index, intcon(1))); Node* src_start = array_element_address(src, src_offset, T_BYTE); Node* src_count = _gvn.transform(new SubINode(max, from_index)); // Range checks - generate_string_range_check(src, src_offset, src_count, true); + generate_string_range_check(src, src_offset, src_count, ae == StrIntrinsicNode::U); if (stopped()) { return true; } @@ -1459,7 +1207,7 @@ bool LibraryCallKit::inline_string_indexOfChar() { RegionNode* region = new RegionNode(3); Node* phi = new PhiNode(region, TypeInt::INT); - Node* result = new StrIndexOfCharNode(control(), memory(TypeAryPtr::BYTES), src_start, src_count, tgt, StrIntrinsicNode::none); + Node* result = new StrIndexOfCharNode(control(), memory(TypeAryPtr::BYTES), src_start, src_count, tgt, ae); C->set_has_split_ifs(true); // Has chance for split-if optimization _gvn.transform(result); @@ -2254,7 +2002,7 @@ LibraryCallKit::classify_unsafe_addr(Node* &base, Node* &offset, BasicType type) } } -inline Node* LibraryCallKit::make_unsafe_address(Node*& base, Node* offset, DecoratorSet decorators, BasicType type, bool can_cast) { +Node* LibraryCallKit::make_unsafe_address(Node*& base, Node* offset, DecoratorSet decorators, BasicType type, bool can_cast) { Node* uncasted_base = base; int kind = classify_unsafe_addr(uncasted_base, offset, type); if (kind == Type::RawPtr) { @@ -6441,6 +6189,9 @@ bool LibraryCallKit::inline_base64_encodeBlock() { // Calculate SHA5 (i.e., SHA-384 or SHA-512) for single-block byte[] array. // void com.sun.security.provider.SHA5.implCompress(byte[] buf, int ofs) // +// Calculate SHA3 (i.e., SHA3-224 or SHA3-256 or SHA3-384 or SHA3-512) for single-block byte[] array. +// void com.sun.security.provider.SHA3.implCompress(byte[] buf, int ofs) +// bool LibraryCallKit::inline_digestBase_implCompress(vmIntrinsics::ID id) { assert(callee()->signature()->size() == 2, "sha_implCompress has 2 parameters"); @@ -6463,34 +6214,43 @@ bool LibraryCallKit::inline_digestBase_implCompress(vmIntrinsics::ID id) { src = must_be_not_null(src, true); Node* src_start = array_element_address(src, ofs, src_elem); Node* state = NULL; + Node* digest_length = NULL; address stubAddr; const char *stubName; switch(id) { case vmIntrinsics::_md5_implCompress: assert(UseMD5Intrinsics, "need MD5 instruction support"); - state = get_state_from_digest_object(digestBase_obj); + state = get_state_from_digest_object(digestBase_obj, "[I"); stubAddr = StubRoutines::md5_implCompress(); stubName = "md5_implCompress"; break; case vmIntrinsics::_sha_implCompress: assert(UseSHA1Intrinsics, "need SHA1 instruction support"); - state = get_state_from_digest_object(digestBase_obj); + state = get_state_from_digest_object(digestBase_obj, "[I"); stubAddr = StubRoutines::sha1_implCompress(); stubName = "sha1_implCompress"; break; case vmIntrinsics::_sha2_implCompress: assert(UseSHA256Intrinsics, "need SHA256 instruction support"); - state = get_state_from_digest_object(digestBase_obj); + state = get_state_from_digest_object(digestBase_obj, "[I"); stubAddr = StubRoutines::sha256_implCompress(); stubName = "sha256_implCompress"; break; case vmIntrinsics::_sha5_implCompress: assert(UseSHA512Intrinsics, "need SHA512 instruction support"); - state = get_long_state_from_digest_object(digestBase_obj); + state = get_state_from_digest_object(digestBase_obj, "[J"); stubAddr = StubRoutines::sha512_implCompress(); stubName = "sha512_implCompress"; break; + case vmIntrinsics::_sha3_implCompress: + assert(UseSHA3Intrinsics, "need SHA3 instruction support"); + state = get_state_from_digest_object(digestBase_obj, "[B"); + stubAddr = StubRoutines::sha3_implCompress(); + stubName = "sha3_implCompress"; + digest_length = get_digest_length_from_digest_object(digestBase_obj); + if (digest_length == NULL) return false; + break; default: fatal_unexpected_iid(id); return false; @@ -6501,22 +6261,29 @@ bool LibraryCallKit::inline_digestBase_implCompress(vmIntrinsics::ID id) { if (stubAddr == NULL) return false; // Call the stub. - Node* call = make_runtime_call(RC_LEAF|RC_NO_FP, OptoRuntime::digestBase_implCompress_Type(), - stubAddr, stubName, TypePtr::BOTTOM, - src_start, state); + Node* call; + if (digest_length == NULL) { + call = make_runtime_call(RC_LEAF|RC_NO_FP, OptoRuntime::digestBase_implCompress_Type(false), + stubAddr, stubName, TypePtr::BOTTOM, + src_start, state); + } else { + call = make_runtime_call(RC_LEAF|RC_NO_FP, OptoRuntime::digestBase_implCompress_Type(true), + stubAddr, stubName, TypePtr::BOTTOM, + src_start, state, digest_length); + } return true; } //------------------------------inline_digestBase_implCompressMB----------------------- // -// Calculate MD5/SHA/SHA2/SHA5 for multi-block byte[] array. +// Calculate MD5/SHA/SHA2/SHA5/SHA3 for multi-block byte[] array. // int com.sun.security.provider.DigestBase.implCompressMultiBlock(byte[] b, int ofs, int limit) // bool LibraryCallKit::inline_digestBase_implCompressMB(int predicate) { - assert(UseMD5Intrinsics || UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics, - "need MD5/SHA1/SHA256/SHA512 instruction support"); - assert((uint)predicate < 4, "sanity"); + assert(UseMD5Intrinsics || UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics || UseSHA3Intrinsics, + "need MD5/SHA1/SHA256/SHA512/SHA3 instruction support"); + assert((uint)predicate < 5, "sanity"); assert(callee()->signature()->size() == 3, "digestBase_implCompressMB has 3 parameters"); Node* digestBase_obj = argument(0); // The receiver was checked for NULL already. @@ -6542,7 +6309,7 @@ bool LibraryCallKit::inline_digestBase_implCompressMB(int predicate) { const char* klass_digestBase_name = NULL; const char* stub_name = NULL; address stub_addr = NULL; - bool long_state = false; + const char* state_type = "[I"; switch (predicate) { case 0: @@ -6571,7 +6338,15 @@ bool LibraryCallKit::inline_digestBase_implCompressMB(int predicate) { klass_digestBase_name = "sun/security/provider/SHA5"; stub_name = "sha512_implCompressMB"; stub_addr = StubRoutines::sha512_implCompressMB(); - long_state = true; + state_type = "[J"; + } + break; + case 4: + if (vmIntrinsics::is_intrinsic_available(vmIntrinsics::_sha3_implCompress)) { + klass_digestBase_name = "sun/security/provider/SHA3"; + stub_name = "sha3_implCompressMB"; + stub_addr = StubRoutines::sha3_implCompressMB(); + state_type = "[B"; } break; default: @@ -6589,33 +6364,43 @@ bool LibraryCallKit::inline_digestBase_implCompressMB(int predicate) { ciKlass* klass_digestBase = tinst->klass()->as_instance_klass()->find_klass(ciSymbol::make(klass_digestBase_name)); assert(klass_digestBase->is_loaded(), "predicate checks that this class is loaded"); ciInstanceKlass* instklass_digestBase = klass_digestBase->as_instance_klass(); - return inline_digestBase_implCompressMB(digestBase_obj, instklass_digestBase, long_state, stub_addr, stub_name, src_start, ofs, limit); + return inline_digestBase_implCompressMB(digestBase_obj, instklass_digestBase, state_type, stub_addr, stub_name, src_start, ofs, limit); } return false; } //------------------------------inline_digestBase_implCompressMB----------------------- bool LibraryCallKit::inline_digestBase_implCompressMB(Node* digestBase_obj, ciInstanceKlass* instklass_digestBase, - bool long_state, address stubAddr, const char *stubName, + const char* state_type, address stubAddr, const char *stubName, Node* src_start, Node* ofs, Node* limit) { const TypeKlassPtr* aklass = TypeKlassPtr::make(instklass_digestBase); const TypeOopPtr* xtype = aklass->as_instance_type(); Node* digest_obj = new CheckCastPPNode(control(), digestBase_obj, xtype); digest_obj = _gvn.transform(digest_obj); - Node* state; - if (long_state) { - state = get_long_state_from_digest_object(digest_obj); - } else { - state = get_state_from_digest_object(digest_obj); - } + Node* state = get_state_from_digest_object(digest_obj, state_type); if (state == NULL) return false; + Node* digest_length = NULL; + if (strcmp("sha3_implCompressMB", stubName) == 0) { + digest_length = get_digest_length_from_digest_object(digest_obj); + if (digest_length == NULL) return false; + } + // Call the stub. - Node* call = make_runtime_call(RC_LEAF|RC_NO_FP, - OptoRuntime::digestBase_implCompressMB_Type(), - stubAddr, stubName, TypePtr::BOTTOM, - src_start, state, ofs, limit); + Node* call; + if (digest_length == NULL) { + call = make_runtime_call(RC_LEAF|RC_NO_FP, + OptoRuntime::digestBase_implCompressMB_Type(false), + stubAddr, stubName, TypePtr::BOTTOM, + src_start, state, ofs, limit); + } else { + call = make_runtime_call(RC_LEAF|RC_NO_FP, + OptoRuntime::digestBase_implCompressMB_Type(true), + stubAddr, stubName, TypePtr::BOTTOM, + src_start, state, digest_length, ofs, limit); + } + // return ofs (int) Node* result = _gvn.transform(new ProjNode(call, TypeFunc::Parms)); set_result(result); @@ -6624,9 +6409,9 @@ bool LibraryCallKit::inline_digestBase_implCompressMB(Node* digestBase_obj, ciIn } //------------------------------get_state_from_digest_object----------------------- -Node * LibraryCallKit::get_state_from_digest_object(Node *digest_object) { - Node* digest_state = load_field_from_object(digest_object, "state", "[I", /*is_exact*/ false); - assert (digest_state != NULL, "wrong version of sun.security.provider.MD5/SHA/SHA2"); +Node * LibraryCallKit::get_state_from_digest_object(Node *digest_object, const char *state_type) { + Node* digest_state = load_field_from_object(digest_object, "state", state_type, /*is_exact*/ false); + assert (digest_state != NULL, "wrong version of sun.security.provider.MD5/SHA/SHA2/SHA5/SHA3"); if (digest_state == NULL) return (Node *) NULL; // now have the array, need to get the start address of the state array @@ -6634,26 +6419,22 @@ Node * LibraryCallKit::get_state_from_digest_object(Node *digest_object) { return state; } -//------------------------------get_long_state_from_digest_object----------------------- -Node * LibraryCallKit::get_long_state_from_digest_object(Node *digest_object) { - Node* digest_state = load_field_from_object(digest_object, "state", "[J", /*is_exact*/ false); - assert (digest_state != NULL, "wrong version of sun.security.provider.SHA5"); - if (digest_state == NULL) return (Node *) NULL; - - // now have the array, need to get the start address of the state array - Node* state = array_element_address(digest_state, intcon(0), T_LONG); - return state; +//------------------------------get_digest_length_from_sha3_object---------------------------------- +Node * LibraryCallKit::get_digest_length_from_digest_object(Node *digest_object) { + Node* digest_length = load_field_from_object(digest_object, "digestLength", "I", /*is_exact*/ false); + assert (digest_length != NULL, "sanity"); + return digest_length; } //----------------------------inline_digestBase_implCompressMB_predicate---------------------------- // Return node representing slow path of predicate check. // the pseudo code we want to emulate with this predicate is: -// if (digestBaseObj instanceof MD5/SHA/SHA2/SHA5) do_intrinsic, else do_javapath +// if (digestBaseObj instanceof MD5/SHA/SHA2/SHA5/SHA3) do_intrinsic, else do_javapath // Node* LibraryCallKit::inline_digestBase_implCompressMB_predicate(int predicate) { - assert(UseMD5Intrinsics || UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics, - "need MD5/SHA1/SHA256/SHA512 instruction support"); - assert((uint)predicate < 4, "sanity"); + assert(UseMD5Intrinsics || UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics || UseSHA3Intrinsics, + "need MD5/SHA1/SHA256/SHA512/SHA3 instruction support"); + assert((uint)predicate < 5, "sanity"); // The receiver was checked for NULL already. Node* digestBaseObj = argument(0); @@ -6689,6 +6470,12 @@ Node* LibraryCallKit::inline_digestBase_implCompressMB_predicate(int predicate) klass_name = "sun/security/provider/SHA5"; } break; + case 4: + if (UseSHA3Intrinsics) { + // we want to do an instanceof comparison against the SHA3 class + klass_name = "sun/security/provider/SHA3"; + } + break; default: fatal("unknown SHA intrinsic predicate: %d", predicate); } diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp new file mode 100644 index 00000000000..ff144d71b5a --- /dev/null +++ b/src/hotspot/share/opto/library_call.hpp @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "ci/ciMethod.hpp" +#include "classfile/javaClasses.hpp" +#include "opto/callGenerator.hpp" +#include "opto/graphKit.hpp" +#include "opto/castnode.hpp" +#include "opto/convertnode.hpp" +#include "opto/intrinsicnode.hpp" +#include "opto/movenode.hpp" + +class LibraryIntrinsic : public InlineCallGenerator { + // Extend the set of intrinsics known to the runtime: + public: + private: + bool _is_virtual; + bool _does_virtual_dispatch; + int8_t _predicates_count; // Intrinsic is predicated by several conditions + int8_t _last_predicate; // Last generated predicate + vmIntrinsics::ID _intrinsic_id; + + public: + LibraryIntrinsic(ciMethod* m, bool is_virtual, int predicates_count, bool does_virtual_dispatch, vmIntrinsics::ID id) + : InlineCallGenerator(m), + _is_virtual(is_virtual), + _does_virtual_dispatch(does_virtual_dispatch), + _predicates_count((int8_t)predicates_count), + _last_predicate((int8_t)-1), + _intrinsic_id(id) + { + } + virtual bool is_intrinsic() const { return true; } + virtual bool is_virtual() const { return _is_virtual; } + virtual bool is_predicated() const { return _predicates_count > 0; } + virtual int predicates_count() const { return _predicates_count; } + virtual bool does_virtual_dispatch() const { return _does_virtual_dispatch; } + virtual JVMState* generate(JVMState* jvms); + virtual Node* generate_predicate(JVMState* jvms, int predicate); + vmIntrinsics::ID intrinsic_id() const { return _intrinsic_id; } +}; + + +// Local helper class for LibraryIntrinsic: +class LibraryCallKit : public GraphKit { + private: + LibraryIntrinsic* _intrinsic; // the library intrinsic being called + Node* _result; // the result node, if any + int _reexecute_sp; // the stack pointer when bytecode needs to be reexecuted + + const TypeOopPtr* sharpen_unsafe_type(Compile::AliasType* alias_type, const TypePtr *adr_type); + + public: + LibraryCallKit(JVMState* jvms, LibraryIntrinsic* intrinsic) + : GraphKit(jvms), + _intrinsic(intrinsic), + _result(NULL) + { + // Check if this is a root compile. In that case we don't have a caller. + if (!jvms->has_method()) { + _reexecute_sp = sp(); + } else { + // Find out how many arguments the interpreter needs when deoptimizing + // and save the stack pointer value so it can used by uncommon_trap. + // We find the argument count by looking at the declared signature. + bool ignored_will_link; + ciSignature* declared_signature = NULL; + ciMethod* ignored_callee = caller()->get_method_at_bci(bci(), ignored_will_link, &declared_signature); + const int nargs = declared_signature->arg_size_for_bc(caller()->java_code_at_bci(bci())); + _reexecute_sp = sp() + nargs; // "push" arguments back on stack + } + } + + virtual LibraryCallKit* is_LibraryCallKit() const { return (LibraryCallKit*)this; } + + ciMethod* caller() const { return jvms()->method(); } + int bci() const { return jvms()->bci(); } + LibraryIntrinsic* intrinsic() const { return _intrinsic; } + vmIntrinsics::ID intrinsic_id() const { return _intrinsic->intrinsic_id(); } + ciMethod* callee() const { return _intrinsic->method(); } + + bool try_to_inline(int predicate); + Node* try_to_predicate(int predicate); + + void push_result() { + // Push the result onto the stack. + if (!stopped() && result() != NULL) { + BasicType bt = result()->bottom_type()->basic_type(); + push_node(bt, result()); + } + } + + private: + void fatal_unexpected_iid(vmIntrinsics::ID iid) { + fatal("unexpected intrinsic %d: %s", iid, vmIntrinsics::name_at(iid)); + } + + void set_result(Node* n) { assert(_result == NULL, "only set once"); _result = n; } + void set_result(RegionNode* region, PhiNode* value); + Node* result() { return _result; } + + virtual int reexecute_sp() { return _reexecute_sp; } + + // Helper functions to inline natives + Node* generate_guard(Node* test, RegionNode* region, float true_prob); + Node* generate_slow_guard(Node* test, RegionNode* region); + Node* generate_fair_guard(Node* test, RegionNode* region); + Node* generate_negative_guard(Node* index, RegionNode* region, + // resulting CastII of index: + Node* *pos_index = NULL); + Node* generate_limit_guard(Node* offset, Node* subseq_length, + Node* array_length, + RegionNode* region); + void generate_string_range_check(Node* array, Node* offset, + Node* length, bool char_count); + Node* generate_current_thread(Node* &tls_output); + Node* load_mirror_from_klass(Node* klass); + Node* load_klass_from_mirror_common(Node* mirror, bool never_see_null, + RegionNode* region, int null_path, + int offset); + Node* load_klass_from_mirror(Node* mirror, bool never_see_null, + RegionNode* region, int null_path) { + int offset = java_lang_Class::klass_offset(); + return load_klass_from_mirror_common(mirror, never_see_null, + region, null_path, + offset); + } + Node* load_array_klass_from_mirror(Node* mirror, bool never_see_null, + RegionNode* region, int null_path) { + int offset = java_lang_Class::array_klass_offset(); + return load_klass_from_mirror_common(mirror, never_see_null, + region, null_path, + offset); + } + Node* generate_access_flags_guard(Node* kls, + int modifier_mask, int modifier_bits, + RegionNode* region); + Node* generate_interface_guard(Node* kls, RegionNode* region); + Node* generate_hidden_class_guard(Node* kls, RegionNode* region); + Node* generate_array_guard(Node* kls, RegionNode* region) { + return generate_array_guard_common(kls, region, false, false); + } + Node* generate_non_array_guard(Node* kls, RegionNode* region) { + return generate_array_guard_common(kls, region, false, true); + } + Node* generate_objArray_guard(Node* kls, RegionNode* region) { + return generate_array_guard_common(kls, region, true, false); + } + Node* generate_non_objArray_guard(Node* kls, RegionNode* region) { + return generate_array_guard_common(kls, region, true, true); + } + Node* generate_array_guard_common(Node* kls, RegionNode* region, + bool obj_array, bool not_array); + Node* generate_virtual_guard(Node* obj_klass, RegionNode* slow_region); + CallJavaNode* generate_method_call(vmIntrinsics::ID method_id, + bool is_virtual = false, bool is_static = false); + CallJavaNode* generate_method_call_static(vmIntrinsics::ID method_id) { + return generate_method_call(method_id, false, true); + } + CallJavaNode* generate_method_call_virtual(vmIntrinsics::ID method_id) { + return generate_method_call(method_id, true, false); + } + Node * load_field_from_object(Node * fromObj, const char * fieldName, const char * fieldTypeString, bool is_exact, bool is_static, ciInstanceKlass * fromKls); + Node * field_address_from_object(Node * fromObj, const char * fieldName, const char * fieldTypeString, bool is_exact, bool is_static, ciInstanceKlass * fromKls); + + Node* make_string_method_node(int opcode, Node* str1_start, Node* cnt1, Node* str2_start, Node* cnt2, StrIntrinsicNode::ArgEnc ae); + bool inline_string_compareTo(StrIntrinsicNode::ArgEnc ae); + bool inline_string_indexOf(StrIntrinsicNode::ArgEnc ae); + bool inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae); + Node* make_indexOf_node(Node* src_start, Node* src_count, Node* tgt_start, Node* tgt_count, + RegionNode* region, Node* phi, StrIntrinsicNode::ArgEnc ae); + bool inline_string_indexOfChar(StrIntrinsicNode::ArgEnc ae); + bool inline_string_equals(StrIntrinsicNode::ArgEnc ae); + bool inline_string_toBytesU(); + bool inline_string_getCharsU(); + bool inline_string_copy(bool compress); + bool inline_string_char_access(bool is_store); + Node* round_double_node(Node* n); + bool runtime_math(const TypeFunc* call_type, address funcAddr, const char* funcName); + bool inline_math_native(vmIntrinsics::ID id); + bool inline_math(vmIntrinsics::ID id); + bool inline_double_math(vmIntrinsics::ID id); + template + bool inline_math_overflow(Node* arg1, Node* arg2); + void inline_math_mathExact(Node* math, Node* test); + bool inline_math_addExactI(bool is_increment); + bool inline_math_addExactL(bool is_increment); + bool inline_math_multiplyExactI(); + bool inline_math_multiplyExactL(); + bool inline_math_multiplyHigh(); + bool inline_math_negateExactI(); + bool inline_math_negateExactL(); + bool inline_math_subtractExactI(bool is_decrement); + bool inline_math_subtractExactL(bool is_decrement); + bool inline_min_max(vmIntrinsics::ID id); + bool inline_notify(vmIntrinsics::ID id); + Node* generate_min_max(vmIntrinsics::ID id, Node* x, Node* y); + // This returns Type::AnyPtr, RawPtr, or OopPtr. + int classify_unsafe_addr(Node* &base, Node* &offset, BasicType type); + Node* make_unsafe_address(Node*& base, Node* offset, DecoratorSet decorators, BasicType type = T_ILLEGAL, bool can_cast = false); + + typedef enum { Relaxed, Opaque, Volatile, Acquire, Release } AccessKind; + DecoratorSet mo_decorator_for_access_kind(AccessKind kind); + bool inline_unsafe_access(bool is_store, BasicType type, AccessKind kind, bool is_unaligned); + static bool klass_needs_init_guard(Node* kls); + bool inline_unsafe_allocate(); + bool inline_unsafe_newArray(bool uninitialized); + bool inline_unsafe_writeback0(); + bool inline_unsafe_writebackSync0(bool is_pre); + bool inline_unsafe_copyMemory(); + bool inline_native_currentThread(); + + bool inline_native_time_funcs(address method, const char* funcName); +#ifdef JFR_HAVE_INTRINSICS + bool inline_native_classID(); + bool inline_native_getEventWriter(); +#endif + bool inline_native_Class_query(vmIntrinsics::ID id); + bool inline_native_subtype_check(); + bool inline_native_getLength(); + bool inline_array_copyOf(bool is_copyOfRange); + bool inline_array_equals(StrIntrinsicNode::ArgEnc ae); + bool inline_preconditions_checkIndex(); + void copy_to_clone(Node* obj, Node* alloc_obj, Node* obj_size, bool is_array); + bool inline_native_clone(bool is_virtual); + bool inline_native_Reflection_getCallerClass(); + // Helper function for inlining native object hash method + bool inline_native_hashcode(bool is_virtual, bool is_static); + bool inline_native_getClass(); + + // Helper functions for inlining arraycopy + bool inline_arraycopy(); + AllocateArrayNode* tightly_coupled_allocation(Node* ptr, + RegionNode* slow_region); + JVMState* arraycopy_restore_alloc_state(AllocateArrayNode* alloc, int& saved_reexecute_sp); + void arraycopy_move_allocation_here(AllocateArrayNode* alloc, Node* dest, JVMState* saved_jvms, int saved_reexecute_sp, + uint new_idx); + + typedef enum { LS_get_add, LS_get_set, LS_cmp_swap, LS_cmp_swap_weak, LS_cmp_exchange } LoadStoreKind; + bool inline_unsafe_load_store(BasicType type, LoadStoreKind kind, AccessKind access_kind); + bool inline_unsafe_fence(vmIntrinsics::ID id); + bool inline_onspinwait(); + bool inline_fp_conversions(vmIntrinsics::ID id); + bool inline_number_methods(vmIntrinsics::ID id); + bool inline_reference_get(); + bool inline_Class_cast(); + bool inline_aescrypt_Block(vmIntrinsics::ID id); + bool inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id); + bool inline_electronicCodeBook_AESCrypt(vmIntrinsics::ID id); + bool inline_counterMode_AESCrypt(vmIntrinsics::ID id); + Node* inline_cipherBlockChaining_AESCrypt_predicate(bool decrypting); + Node* inline_electronicCodeBook_AESCrypt_predicate(bool decrypting); + Node* inline_counterMode_AESCrypt_predicate(); + Node* get_key_start_from_aescrypt_object(Node* aescrypt_object); + Node* get_original_key_start_from_aescrypt_object(Node* aescrypt_object); + bool inline_ghash_processBlocks(); + bool inline_base64_encodeBlock(); + bool inline_digestBase_implCompress(vmIntrinsics::ID id); + bool inline_digestBase_implCompressMB(int predicate); + bool inline_digestBase_implCompressMB(Node* digestBaseObj, ciInstanceKlass* instklass, + const char* state_type, address stubAddr, const char *stubName, + Node* src_start, Node* ofs, Node* limit); + Node* get_state_from_digest_object(Node *digestBase_object, const char* state_type); + Node* get_digest_length_from_digest_object(Node *digestBase_object); + Node* inline_digestBase_implCompressMB_predicate(int predicate); + bool inline_encodeISOArray(); + bool inline_updateCRC32(); + bool inline_updateBytesCRC32(); + bool inline_updateByteBufferCRC32(); + Node* get_table_from_crc32c_class(ciInstanceKlass *crc32c_class); + bool inline_updateBytesCRC32C(); + bool inline_updateDirectByteBufferCRC32C(); + bool inline_updateBytesAdler32(); + bool inline_updateByteBufferAdler32(); + bool inline_multiplyToLen(); + bool inline_hasNegatives(); + bool inline_squareToLen(); + bool inline_mulAdd(); + bool inline_montgomeryMultiply(); + bool inline_montgomerySquare(); + bool inline_bigIntegerShift(bool isRightShift); + bool inline_vectorizedMismatch(); + bool inline_fma(vmIntrinsics::ID id); + bool inline_character_compare(vmIntrinsics::ID id); + bool inline_fp_min_max(vmIntrinsics::ID id); + + bool inline_profileBoolean(); + bool inline_isCompileConstant(); + + // Vector API support + bool inline_vector_nary_operation(int n); + bool inline_vector_broadcast_coerced(); + bool inline_vector_shuffle_to_vector(); + bool inline_vector_shuffle_iota(); + bool inline_vector_mem_operation(bool is_store); + bool inline_vector_gather_scatter(bool is_scatter); + bool inline_vector_reduction(); + bool inline_vector_test(); + bool inline_vector_blend(); + bool inline_vector_rearrange(); + bool inline_vector_compare(); + bool inline_vector_broadcast_int(); + bool inline_vector_convert(); + bool inline_vector_extract(); + bool inline_vector_insert(); + Node* box_vector(Node* in, const TypeInstPtr* vbox_type, BasicType bt, int num_elem); + Node* unbox_vector(Node* in, const TypeInstPtr* vbox_type, BasicType bt, int num_elem, bool shuffle_to_vector = false); + Node* shift_count(Node* cnt, int shift_op, BasicType bt, int num_elem); + + enum VectorMaskUseType { + VecMaskUseLoad, + VecMaskUseStore, + VecMaskUseAll, + VecMaskNotUsed + }; + + bool arch_supports_vector(int op, int num_elem, BasicType type, VectorMaskUseType mask_use_type, bool has_scalar_args = false); + + void clear_upper_avx() { +#ifdef X86 + if (UseAVX >= 2) { + C->set_clear_upper_avx(true); + } +#endif + } +}; + diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp index 7f04d2ac6c2..fbe0c94a474 100644 --- a/src/hotspot/share/opto/loopPredicate.cpp +++ b/src/hotspot/share/opto/loopPredicate.cpp @@ -727,7 +727,7 @@ BoolNode* PhaseIdealLoop::rc_predicate(IdealLoopTree *loop, Node* ctrl, idx_type = (TypeInt*)mul->mul_ring(idx_type, scale_type); if (overflow || TypeInt::INT->higher_equal(idx_type)) { // May overflow - mul->destruct(); + mul->destruct(&_igvn); if (!overflow) { max_idx_expr = new ConvI2LNode(max_idx_expr); register_new_node(max_idx_expr, ctrl); diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index ff266916b4b..8ab78ce620d 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -1045,16 +1045,6 @@ void IdealLoopTree::policy_unroll_slp_analysis(CountedLoopNode *cl, PhaseIdealLo } } -//------------------------------policy_align----------------------------------- -// Return TRUE or FALSE if the loop should be cache-line aligned. Gather the -// expression that does the alignment. Note that only one array base can be -// aligned in a loop (unless the VM guarantees mutual alignment). Note that -// if we vectorize short memory ops into longer memory ops, we may want to -// increase alignment. -bool IdealLoopTree::policy_align(PhaseIdealLoop *phase) const { - return false; -} - //------------------------------policy_range_check----------------------------- // Return TRUE or FALSE if the loop should be range-check-eliminated or not. // When TRUE, the estimated node budget is also requested. @@ -2300,65 +2290,78 @@ void PhaseIdealLoop::mark_reductions(IdealLoopTree *loop) { } //------------------------------adjust_limit----------------------------------- -// Helper function for add_constraint(). -Node* PhaseIdealLoop::adjust_limit(int stride_con, Node * scale, Node *offset, Node *rc_limit, Node *loop_limit, Node *pre_ctrl, bool round_up) { - // Compute "I :: (limit-offset)/scale" - Node *con = new SubINode(rc_limit, offset); - register_new_node(con, pre_ctrl); - Node *X = new DivINode(0, con, scale); - register_new_node(X, pre_ctrl); - - // When the absolute value of scale is greater than one, the integer - // division may round limit down so add one to the limit. - if (round_up) { - X = new AddINode(X, _igvn.intcon(1)); - register_new_node(X, pre_ctrl); - } - - // Adjust loop limit - loop_limit = (stride_con > 0) - ? (Node*)(new MinINode(loop_limit, X)) - : (Node*)(new MaxINode(loop_limit, X)); - register_new_node(loop_limit, pre_ctrl); - return loop_limit; +// Helper function that computes new loop limit as (rc_limit-offset)/scale +Node* PhaseIdealLoop::adjust_limit(bool is_positive_stride, Node* scale, Node* offset, Node* rc_limit, Node* old_limit, Node* pre_ctrl, bool round) { + Node* sub = new SubLNode(rc_limit, offset); + register_new_node(sub, pre_ctrl); + Node* limit = new DivLNode(NULL, sub, scale); + register_new_node(limit, pre_ctrl); + + // When the absolute value of scale is greater than one, the division + // may round limit down/up, so add/sub one to/from the limit. + if (round) { + limit = new AddLNode(limit, _igvn.longcon(is_positive_stride ? -1 : 1)); + register_new_node(limit, pre_ctrl); + } + + // Clamp the limit to handle integer under-/overflows. + // When reducing the limit, clamp to [min_jint, old_limit]: + // MIN(old_limit, MAX(limit, min_jint)) + // When increasing the limit, clamp to [old_limit, max_jint]: + // MAX(old_limit, MIN(limit, max_jint)) + Node* cmp = new CmpLNode(limit, _igvn.longcon(is_positive_stride ? min_jint : max_jint)); + register_new_node(cmp, pre_ctrl); + Node* bol = new BoolNode(cmp, is_positive_stride ? BoolTest::lt : BoolTest::gt); + register_new_node(bol, pre_ctrl); + limit = new ConvL2INode(limit); + register_new_node(limit, pre_ctrl); + limit = new CMoveINode(bol, limit, _igvn.intcon(is_positive_stride ? min_jint : max_jint), TypeInt::INT); + register_new_node(limit, pre_ctrl); + + limit = is_positive_stride ? (Node*)(new MinINode(old_limit, limit)) + : (Node*)(new MaxINode(old_limit, limit)); + register_new_node(limit, pre_ctrl); + return limit; } //------------------------------add_constraint--------------------------------- // Constrain the main loop iterations so the conditions: -// low_limit <= scale_con * I + offset < upper_limit -// always holds true. That is, either increase the number of iterations in -// the pre-loop or the post-loop until the condition holds true in the main -// loop. Stride, scale, offset and limit are all loop invariant. Further, -// stride and scale are constants (offset and limit often are). -void PhaseIdealLoop::add_constraint(int stride_con, int scale_con, Node *offset, Node *low_limit, Node *upper_limit, Node *pre_ctrl, Node **pre_limit, Node **main_limit) { - // For positive stride, the pre-loop limit always uses a MAX function - // and the main loop a MIN function. For negative stride these are - // reversed. - - // Also for positive stride*scale the affine function is increasing, so the - // pre-loop must check for underflow and the post-loop for overflow. - // Negative stride*scale reverses this; pre-loop checks for overflow and - // post-loop for underflow. - - Node *scale = _igvn.intcon(scale_con); +// low_limit <= scale_con*I + offset < upper_limit +// always hold true. That is, either increase the number of iterations in the +// pre-loop or reduce the number of iterations in the main-loop until the condition +// holds true in the main-loop. Stride, scale, offset and limit are all loop +// invariant. Further, stride and scale are constants (offset and limit often are). +void PhaseIdealLoop::add_constraint(jlong stride_con, jlong scale_con, Node* offset, Node* low_limit, Node* upper_limit, Node* pre_ctrl, Node** pre_limit, Node** main_limit) { + assert(_igvn.type(offset)->isa_long() != NULL && _igvn.type(low_limit)->isa_long() != NULL && + _igvn.type(upper_limit)->isa_long() != NULL, "arguments should be long values"); + + // For a positive stride, we need to reduce the main-loop limit and + // increase the pre-loop limit. This is reversed for a negative stride. + bool is_positive_stride = (stride_con > 0); + + // If the absolute scale value is greater one, division in 'adjust_limit' may require + // rounding. Make sure the ABS method correctly handles min_jint. + // Only do this for the pre-loop, one less iteration of the main loop doesn't hurt. + bool round = ABS(scale_con) > 1; + + Node* scale = _igvn.longcon(scale_con); set_ctrl(scale, C->root()); if ((stride_con^scale_con) >= 0) { // Use XOR to avoid overflow + // Positive stride*scale: the affine function is increasing, + // the pre-loop checks for underflow and the post-loop for overflow. + // The overflow limit: scale*I+offset < upper_limit - // For main-loop compute + // For the main-loop limit compute: // ( if (scale > 0) /* and stride > 0 */ // I < (upper_limit-offset)/scale // else /* scale < 0 and stride < 0 */ // I > (upper_limit-offset)/scale // ) - // - // (upper_limit-offset) may overflow or underflow. - // But it is fine since main loop will either have - // less iterations or will be skipped in such case. - *main_limit = adjust_limit(stride_con, scale, offset, upper_limit, *main_limit, pre_ctrl, false); - - // The underflow limit: low_limit <= scale*I+offset. - // For pre-loop compute + *main_limit = adjust_limit(is_positive_stride, scale, offset, upper_limit, *main_limit, pre_ctrl, false); + + // The underflow limit: low_limit <= scale*I+offset + // For the pre-loop limit compute: // NOT(scale*I+offset >= low_limit) // scale*I+offset < low_limit // ( if (scale > 0) /* and stride > 0 */ @@ -2366,39 +2369,13 @@ void PhaseIdealLoop::add_constraint(int stride_con, int scale_con, Node *offset, // else /* scale < 0 and stride < 0 */ // I > (low_limit-offset)/scale // ) + *pre_limit = adjust_limit(!is_positive_stride, scale, offset, low_limit, *pre_limit, pre_ctrl, round); + } else { + // Negative stride*scale: the affine function is decreasing, + // the pre-loop checks for overflow and the post-loop for underflow. - if (low_limit->get_int() == -max_jint) { - // We need this guard when scale*pre_limit+offset >= limit - // due to underflow. So we need execute pre-loop until - // scale*I+offset >= min_int. But (min_int-offset) will - // underflow when offset > 0 and X will be > original_limit - // when stride > 0. To avoid it we replace positive offset with 0. - // - // Also (min_int+1 == -max_int) is used instead of min_int here - // to avoid problem with scale == -1 (min_int/(-1) == min_int). - Node* shift = _igvn.intcon(31); - set_ctrl(shift, C->root()); - Node* sign = new RShiftINode(offset, shift); - register_new_node(sign, pre_ctrl); - offset = new AndINode(offset, sign); - register_new_node(offset, pre_ctrl); - } else { - assert(low_limit->get_int() == 0, "wrong low limit for range check"); - // The only problem we have here when offset == min_int - // since (0-min_int) == min_int. It may be fine for stride > 0 - // but for stride < 0 X will be < original_limit. To avoid it - // max(pre_limit, original_limit) is used in do_range_check(). - } - // Pass (-stride) to indicate pre_loop_cond = NOT(main_loop_cond); - *pre_limit = adjust_limit((-stride_con), scale, offset, low_limit, *pre_limit, pre_ctrl, - scale_con > 1 && stride_con > 0); - - } else { // stride_con*scale_con < 0 - // For negative stride*scale pre-loop checks for overflow and - // post-loop for underflow. - // // The overflow limit: scale*I+offset < upper_limit - // For pre-loop compute + // For the pre-loop limit compute: // NOT(scale*I+offset < upper_limit) // scale*I+offset >= upper_limit // scale*I+offset+1 > upper_limit @@ -2407,57 +2384,24 @@ void PhaseIdealLoop::add_constraint(int stride_con, int scale_con, Node *offset, // else /* scale > 0 and stride < 0 */ // I > (upper_limit-(offset+1))/scale // ) - // - // (upper_limit-offset-1) may underflow or overflow. - // To avoid it min(pre_limit, original_limit) is used - // in do_range_check() for stride > 0 and max() for < 0. - Node *one = _igvn.intcon(1); + Node* one = _igvn.longcon(1); set_ctrl(one, C->root()); - - Node *plus_one = new AddINode(offset, one); + Node* plus_one = new AddLNode(offset, one); register_new_node(plus_one, pre_ctrl); - // Pass (-stride) to indicate pre_loop_cond = NOT(main_loop_cond); - *pre_limit = adjust_limit((-stride_con), scale, plus_one, upper_limit, *pre_limit, pre_ctrl, - scale_con < -1 && stride_con > 0); - - if (low_limit->get_int() == -max_jint) { - // We need this guard when scale*main_limit+offset >= limit - // due to underflow. So we need execute main-loop while - // scale*I+offset+1 > min_int. But (min_int-offset-1) will - // underflow when (offset+1) > 0 and X will be < main_limit - // when scale < 0 (and stride > 0). To avoid it we replace - // positive (offset+1) with 0. - // - // Also (min_int+1 == -max_int) is used instead of min_int here - // to avoid problem with scale == -1 (min_int/(-1) == min_int). - Node* shift = _igvn.intcon(31); - set_ctrl(shift, C->root()); - Node* sign = new RShiftINode(plus_one, shift); - register_new_node(sign, pre_ctrl); - plus_one = new AndINode(plus_one, sign); - register_new_node(plus_one, pre_ctrl); - } else { - assert(low_limit->get_int() == 0, "wrong low limit for range check"); - // The only problem we have here when offset == max_int - // since (max_int+1) == min_int and (0-min_int) == min_int. - // But it is fine since main loop will either have - // less iterations or will be skipped in such case. - } - // The underflow limit: low_limit <= scale*I+offset. - // For main-loop compute + *pre_limit = adjust_limit(!is_positive_stride, scale, plus_one, upper_limit, *pre_limit, pre_ctrl, round); + + // The underflow limit: low_limit <= scale*I+offset + // For the main-loop limit compute: // scale*I+offset+1 > low_limit // ( if (scale < 0) /* and stride > 0 */ // I < (low_limit-(offset+1))/scale // else /* scale > 0 and stride < 0 */ // I > (low_limit-(offset+1))/scale // ) - - *main_limit = adjust_limit(stride_con, scale, plus_one, low_limit, *main_limit, pre_ctrl, - false); + *main_limit = adjust_limit(is_positive_stride, scale, plus_one, low_limit, *main_limit, pre_ctrl, false); } } - //------------------------------is_scaled_iv--------------------------------- // Return true if exp is a constant times an induction var bool PhaseIdealLoop::is_scaled_iv(Node* exp, Node* iv, int* p_scale) { @@ -2654,22 +2598,14 @@ int PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { // Must know if its a count-up or count-down loop int stride_con = cl->stride_con(); - Node *zero = _igvn.intcon(0); - Node *one = _igvn.intcon(1); + Node* zero = _igvn.longcon(0); + Node* one = _igvn.longcon(1); // Use symmetrical int range [-max_jint,max_jint] - Node *mini = _igvn.intcon(-max_jint); + Node* mini = _igvn.longcon(-max_jint); set_ctrl(zero, C->root()); set_ctrl(one, C->root()); set_ctrl(mini, C->root()); - // Range checks that do not dominate the loop backedge (ie. - // conditionally executed) can lengthen the pre loop limit beyond - // the original loop limit. To prevent this, the pre limit is - // (for stride > 0) MINed with the original loop limit (MAXed - // stride < 0) when some range_check (rc) is conditionally - // executed. - bool conditional_rc = false; - // Count number of range checks and reduce by load range limits, if zero, // the loop is in canonical form to multiversion. closed_range_checks = 0; @@ -2757,23 +2693,30 @@ int PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { // stride_con and scale_con can be negative which will flip about the // sense of the test. + // Perform the limit computations in jlong to avoid overflow + jlong lscale_con = scale_con; + Node* int_offset = offset; + offset = new ConvI2LNode(offset); + register_new_node(offset, pre_ctrl); + Node* int_limit = limit; + limit = new ConvI2LNode(limit); + register_new_node(limit, pre_ctrl); + // Adjust pre and main loop limits to guard the correct iteration set if (cmp->Opcode() == Op_CmpU) { // Unsigned compare is really 2 tests if (b_test._test == BoolTest::lt) { // Range checks always use lt // The underflow and overflow limits: 0 <= scale*I+offset < limit - add_constraint(stride_con, scale_con, offset, zero, limit, pre_ctrl, &pre_limit, &main_limit); - // (0-offset)/scale could be outside of loop iterations range. - conditional_rc = true; + add_constraint(stride_con, lscale_con, offset, zero, limit, pre_ctrl, &pre_limit, &main_limit); Node* init = cl->init_trip(); Node* opaque_init = new OpaqueLoopInitNode(C, init); register_new_node(opaque_init, predicate_proj); // predicate on first value of first iteration - predicate_proj = add_range_check_predicate(loop, cl, predicate_proj, scale_con, offset, limit, stride_con, init); + predicate_proj = add_range_check_predicate(loop, cl, predicate_proj, scale_con, int_offset, int_limit, stride_con, init); assert(!skeleton_predicate_has_opaque(predicate_proj->in(0)->as_If()), "unexpected"); // template predicate so it can be updated on next unrolling - predicate_proj = add_range_check_predicate(loop, cl, predicate_proj, scale_con, offset, limit, stride_con, opaque_init); + predicate_proj = add_range_check_predicate(loop, cl, predicate_proj, scale_con, int_offset, int_limit, stride_con, opaque_init); assert(skeleton_predicate_has_opaque(predicate_proj->in(0)->as_If()), "unexpected"); Node* opaque_stride = new OpaqueLoopStrideNode(C, cl->stride()); @@ -2782,7 +2725,7 @@ int PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { register_new_node(max_value, predicate_proj); max_value = new AddINode(opaque_init, max_value); register_new_node(max_value, predicate_proj); - predicate_proj = add_range_check_predicate(loop, cl, predicate_proj, scale_con, offset, limit, stride_con, max_value); + predicate_proj = add_range_check_predicate(loop, cl, predicate_proj, scale_con, int_offset, int_limit, stride_con, max_value); assert(skeleton_predicate_has_opaque(predicate_proj->in(0)->as_If()), "unexpected"); } else { @@ -2797,16 +2740,16 @@ int PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { // Fall into GE case case BoolTest::ge: // Convert (I*scale+offset) >= Limit to (I*(-scale)+(-offset)) <= -Limit - scale_con = -scale_con; - offset = new SubINode(zero, offset); + lscale_con = -lscale_con; + offset = new SubLNode(zero, offset); register_new_node(offset, pre_ctrl); - limit = new SubINode(zero, limit); + limit = new SubLNode(zero, limit); register_new_node(limit, pre_ctrl); // Fall into LE case case BoolTest::le: if (b_test._test != BoolTest::gt) { // Convert X <= Y to X < Y+1 - limit = new AddINode(limit, one); + limit = new AddLNode(limit, one); register_new_node(limit, pre_ctrl); } // Fall into LT case @@ -2814,11 +2757,7 @@ int PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { // The underflow and overflow limits: MIN_INT <= scale*I+offset < limit // Note: (MIN_INT+1 == -MAX_INT) is used instead of MIN_INT here // to avoid problem with scale == -1: MIN_INT/(-1) == MIN_INT. - add_constraint(stride_con, scale_con, offset, mini, limit, pre_ctrl, &pre_limit, &main_limit); - // ((MIN_INT+1)-offset)/scale could be outside of loop iterations range. - // Note: negative offset is replaced with 0 but (MIN_INT+1)/scale could - // still be outside of loop range. - conditional_rc = true; + add_constraint(stride_con, lscale_con, offset, mini, limit, pre_ctrl, &pre_limit, &main_limit); break; default: if (PrintOpto) { @@ -2847,7 +2786,7 @@ int PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { --imax; } } - if (limit->Opcode() == Op_LoadRange) { + if (int_limit->Opcode() == Op_LoadRange) { closed_range_checks--; } } // End of is IF @@ -2858,7 +2797,8 @@ int PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { } // Update loop limits - if (conditional_rc) { + if (pre_limit != orig_limit) { + // Computed pre-loop limit can be outside of loop iterations range. pre_limit = (stride_con > 0) ? (Node*)new MinINode(pre_limit, orig_limit) : (Node*)new MaxINode(pre_limit, orig_limit); register_new_node(pre_limit, pre_ctrl); @@ -3391,9 +3331,8 @@ bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_n uint est_peeling = estimate_peeling(phase); bool should_peel = 0 < est_peeling; - // Counted loops may be peeled, may need some iterations run up - // front for RCE, and may want to align loop refs to a cache - // line. Thus we clone a full loop up front whose trip count is + // Counted loops may be peeled, or may need some iterations run up + // front for RCE. Thus we clone a full loop up front whose trip count is // at least 1 (if peeling), but may be several more. // The main loop will start cache-line aligned with at least 1 @@ -3405,27 +3344,25 @@ bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_n bool should_unroll = policy_unroll(phase); bool should_rce = policy_range_check(phase); - // TODO: Remove align -- not used. - bool should_align = policy_align(phase); - // If not RCE'ing (iteration splitting) or Aligning, then we do not need a - // pre-loop. We may still need to peel an initial iteration but we will not + // If not RCE'ing (iteration splitting), then we do not need a pre-loop. + // We may still need to peel an initial iteration but we will not // be needing an unknown number of pre-iterations. // - // Basically, if may_rce_align reports FALSE first time through, we will not - // be able to later do RCE or Aligning on this loop. - bool may_rce_align = !policy_peel_only(phase) || should_rce || should_align; + // Basically, if peel_only reports TRUE first time through, we will not + // be able to later do RCE on this loop. + bool peel_only = policy_peel_only(phase) && !should_rce; - // If we have any of these conditions (RCE, alignment, unrolling) met, then + // If we have any of these conditions (RCE, unrolling) met, then // we switch to the pre-/main-/post-loop model. This model also covers // peeling. - if (should_rce || should_align || should_unroll) { + if (should_rce || should_unroll) { if (cl->is_normal_loop()) { // Convert to 'pre/main/post' loops uint estimate = est_loop_clone_sz(3); if (!phase->may_require_nodes(estimate)) { return false; } - phase->insert_pre_post_loops(this, old_new, !may_rce_align); + phase->insert_pre_post_loops(this, old_new, peel_only); } // Adjust the pre- and main-loop limits to let the pre and post loops run // with full checks, but the main-loop with no checks. Remove said checks @@ -3456,11 +3393,6 @@ bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_n } phase->do_unroll(this, old_new, true); } - - // Adjust the pre-loop limits to align the main body iterations. - if (should_align) { - Unimplemented(); - } } else { // Else we have an unchanged counted loop if (should_peel) { // Might want to peel but do nothing else if (phase->may_require_nodes(est_peeling)) { diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 38360bf988b..98934145eeb 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -40,8 +40,11 @@ #include "opto/loopnode.hpp" #include "opto/movenode.hpp" #include "opto/mulnode.hpp" +#include "opto/opaquenode.hpp" #include "opto/rootnode.hpp" +#include "opto/runtime.hpp" #include "opto/superword.hpp" +#include "runtime/sharedRuntime.hpp" #include "utilities/powerOfTwo.hpp" //============================================================================= @@ -501,6 +504,729 @@ static int check_stride_overflow(jint stride_con, const TypeInt* limit_t) { return 0; } +static int check_stride_overflow(jlong stride_con, const TypeLong* limit_t) { + if (stride_con > 0) { + if (limit_t->_lo > (max_jlong - stride_con)) { + return -1; + } + if (limit_t->_hi > (max_jlong - stride_con)) { + return 1; + } + } else { + if (limit_t->_hi < (min_jlong - stride_con)) { + return -1; + } + if (limit_t->_lo < (min_jlong - stride_con)) { + return 1; + } + } + return 0; +} + +static bool condition_stride_ok(BoolTest::mask bt, jlong stride_con) { + // If the condition is inverted and we will be rolling + // through MININT to MAXINT, then bail out. + if (bt == BoolTest::eq || // Bail out, but this loop trips at most twice! + // Odd stride + (bt == BoolTest::ne && stride_con != 1 && stride_con != -1) || + // Count down loop rolls through MAXINT + ((bt == BoolTest::le || bt == BoolTest::lt) && stride_con < 0) || + // Count up loop rolls through MININT + ((bt == BoolTest::ge || bt == BoolTest::gt) && stride_con > 0)) { + return false; // Bail out + } + return true; +} + +void PhaseIdealLoop::long_loop_replace_long_iv(Node* iv_to_replace, Node* inner_iv, Node* outer_phi, Node* inner_head) { + Node* iv_as_long = new ConvI2LNode(inner_iv, TypeLong::INT); + register_new_node(iv_as_long, inner_head); + Node* iv_replacement = new AddLNode(outer_phi, iv_as_long); + register_new_node(iv_replacement, inner_head); + for (DUIterator_Last imin, i = iv_to_replace->last_outs(imin); i >= imin;) { + Node* u = iv_to_replace->last_out(i); +#ifdef ASSERT + if (!is_dominator(inner_head, ctrl_or_self(u))) { + assert(u->is_Phi(), "should be a Phi"); + for (uint j = 1; j < u->req(); j++) { + if (u->in(j) == iv_to_replace) { + assert(is_dominator(inner_head, u->in(0)->in(j)), "iv use above loop?"); + } + } + } +#endif + _igvn.rehash_node_delayed(u); + int nb = u->replace_edge(iv_to_replace, iv_replacement); + i -= nb; + } +} + +void PhaseIdealLoop::add_empty_predicate(Deoptimization::DeoptReason reason, Node* inner_head, IdealLoopTree* loop, SafePointNode* sfpt) { + if (!C->too_many_traps(reason)) { + Node *cont = _igvn.intcon(1); + Node* opq = new Opaque1Node(C, cont); + _igvn.register_new_node_with_optimizer(opq); + Node *bol = new Conv2BNode(opq); + _igvn.register_new_node_with_optimizer(bol); + set_subtree_ctrl(bol); + IfNode* iff = new IfNode(inner_head->in(LoopNode::EntryControl), bol, PROB_MAX, COUNT_UNKNOWN); + register_control(iff, loop, inner_head->in(LoopNode::EntryControl)); + Node* iffalse = new IfFalseNode(iff); + register_control(iffalse, _ltree_root, iff); + Node* iftrue = new IfTrueNode(iff); + register_control(iftrue, loop, iff); + C->add_predicate_opaq(opq); + + int trap_request = Deoptimization::make_trap_request(reason, Deoptimization::Action_maybe_recompile); + address call_addr = SharedRuntime::uncommon_trap_blob()->entry_point(); + const TypePtr* no_memory_effects = NULL; + JVMState* jvms = sfpt->jvms(); + CallNode* unc = new CallStaticJavaNode(OptoRuntime::uncommon_trap_Type(), call_addr, "uncommon_trap", + jvms->bci(), no_memory_effects); + + Node* mem = NULL; + Node* i_o = NULL; + if (sfpt->is_Call()) { + mem = sfpt->proj_out(TypeFunc::Memory); + i_o = sfpt->proj_out(TypeFunc::I_O); + } else { + mem = sfpt->memory(); + i_o = sfpt->i_o(); + } + + Node *frame = new ParmNode(C->start(), TypeFunc::FramePtr); + register_new_node(frame, C->start()); + Node *ret = new ParmNode(C->start(), TypeFunc::ReturnAdr); + register_new_node(ret, C->start()); + + unc->init_req(TypeFunc::Control, iffalse); + unc->init_req(TypeFunc::I_O, i_o); + unc->init_req(TypeFunc::Memory, mem); // may gc ptrs + unc->init_req(TypeFunc::FramePtr, frame); + unc->init_req(TypeFunc::ReturnAdr, ret); + unc->init_req(TypeFunc::Parms+0, _igvn.intcon(trap_request)); + unc->set_cnt(PROB_UNLIKELY_MAG(4)); + unc->copy_call_debug_info(&_igvn, sfpt); + + for (uint i = TypeFunc::Parms; i < unc->req(); i++) { + set_subtree_ctrl(unc->in(i)); + } + register_control(unc, _ltree_root, iffalse); + + Node* ctrl = new ProjNode(unc, TypeFunc::Control); + register_control(ctrl, _ltree_root, unc); + Node* halt = new HaltNode(ctrl, frame, "uncommon trap returned which should never happen" PRODUCT_ONLY(COMMA /*reachable*/false)); + register_control(halt, _ltree_root, ctrl); + C->root()->add_req(halt); + + _igvn.replace_input_of(inner_head, LoopNode::EntryControl, iftrue); + set_idom(inner_head, iftrue, dom_depth(inner_head)); + } +} + +// Find a safepoint node that dominates the back edge. We need a +// SafePointNode so we can use its jvm state to create empty +// predicates. +SafePointNode* PhaseIdealLoop::find_safepoint(Node* back_control, Node* x, IdealLoopTree* loop) { + IfNode* exit_test = back_control->in(0)->as_If(); + SafePointNode* safepoint = NULL; + if (exit_test->in(0)->is_SafePoint() && exit_test->in(0)->outcnt() == 1) { + safepoint = exit_test->in(0)->as_SafePoint(); + } else { + Node* c = back_control; + while (c != x && c->Opcode() != Op_SafePoint) { + c = idom(c); + } + + if (c->Opcode() == Op_SafePoint) { + safepoint = c->as_SafePoint(); + } + + if (safepoint == NULL) { + return NULL; + } + + Node* mem = safepoint->in(TypeFunc::Memory); + + // We can only use that safepoint if there's not side effect + // between the backedge and the safepoint. + +#ifdef ASSERT + // mm is used for book keeping + MergeMemNode* mm = NULL; + if (mem->is_MergeMem()) { + mm = mem->clone()->as_MergeMem(); + for (MergeMemStream mms(mem->as_MergeMem()); mms.next_non_empty(); ) { + if (mms.alias_idx() != Compile::AliasIdxBot && loop != get_loop(ctrl_or_self(mms.memory()))) { + mm->set_memory_at(mms.alias_idx(), mem->as_MergeMem()->base_memory()); + } + } + } +#endif + for (DUIterator_Fast imax, i = x->fast_outs(imax); i < imax; i++) { + Node* u = x->fast_out(i); + if (u->is_Phi() && u->bottom_type() == Type::MEMORY) { + Node* m = u->in(LoopNode::LoopBackControl); + if (u->adr_type() == TypePtr::BOTTOM) { + if (m->is_MergeMem() && mem->is_MergeMem()) { + if (m != mem DEBUG_ONLY(|| true)) { + for (MergeMemStream mms(m->as_MergeMem(), mem->as_MergeMem()); mms.next_non_empty2(); ) { + if (!mms.is_empty()) { + if (mms.memory() != mms.memory2()) { + return NULL; + } +#ifdef ASSERT + if (mms.alias_idx() != Compile::AliasIdxBot) { + mm->set_memory_at(mms.alias_idx(), mem->as_MergeMem()->base_memory()); + } +#endif + } + } + } + } else if (mem->is_MergeMem()) { + if (m != mem->as_MergeMem()->base_memory()) { + return NULL; + } + } else { + return NULL; + } + } else { + if (mem->is_MergeMem()) { + if (m != mem->as_MergeMem()->memory_at(C->get_alias_index(u->adr_type()))) { + return NULL; + } +#ifdef ASSERT + mm->set_memory_at(C->get_alias_index(u->adr_type()), mem->as_MergeMem()->base_memory()); +#endif + } else { + if (m != mem) { + return NULL; + } + } + } + } + } +#ifdef ASSERT + if (mm != NULL) { + assert (_igvn.transform(mm) == mem->as_MergeMem()->base_memory(), "all memory state should have been processed"); + _igvn.remove_dead_node(mm); + } +#endif + } + return safepoint; +} + +// If the loop has the shape of a counted loop but with a long +// induction variable, transform the loop in a loop nest: an inner +// loop that iterates for at most max int iterations with an integer +// induction variable and an outer loop that iterates over the full +// range of long values from the initial loop in (at most) max int +// steps. That is: +// +// x: for (long phi = init; phi < limit; phi += stride) { +// // phi := Phi(L, init, incr) +// // incr := AddL(phi, longcon(stride)) +// // phi_incr := phi (test happens before increment) +// long incr = phi + stride; +// ... use phi and incr ... +// } +// +// OR: +// +// x: for (long phi = init; (phi += stride) < limit; ) { +// // phi := Phi(L, AddL(init, stride), incr) +// // incr := AddL(phi, longcon(stride)) +// // phi_incr := NULL (test happens after increment) +// long incr = phi + stride; +// ... use phi and (phi + stride) ... +// } +// +// ==transform=> +// +// const ulong inner_iters_limit = INT_MAX - stride - 1; //near 0x7FFFFFF0 +// assert(stride <= inner_iters_limit); // else abort transform +// assert((extralong)limit + stride <= LONG_MAX); // else deopt +// outer_head: for (long outer_phi = init;;) { +// // outer_phi := Phi(outer_head, init, AddL(outer_phi, I2L(inner_phi))) +// ulong inner_iters_max = (ulong) MAX(0, ((extralong)limit + stride - outer_phi)); +// long inner_iters_actual = MIN(inner_iters_limit, inner_iters_max); +// assert(inner_iters_actual == (int)inner_iters_actual); +// int inner_phi, inner_incr; +// x: for (inner_phi = 0;; inner_phi = inner_incr) { +// // inner_phi := Phi(x, intcon(0), inner_incr) +// // inner_incr := AddI(inner_phi, intcon(stride)) +// inner_incr = inner_phi + stride; +// if (inner_incr < inner_iters_actual) { +// ... use phi=>(outer_phi+inner_phi) and incr=>(outer_phi+inner_incr) ... +// continue; +// } +// else break; +// } +// if ((outer_phi+inner_phi) < limit) //OR (outer_phi+inner_incr) < limit +// continue; +// else break; +// } +bool PhaseIdealLoop::is_long_counted_loop(Node* x, IdealLoopTree* loop, Node_List &old_new) { + // Only for inner loops + if (loop->_child != NULL) { + return false; + } + + // Checks whether the loop has the shape of a counted loop + Node* back_control = loop_exit_control(x, loop); + if (back_control == NULL) { + return false; + } + + BoolTest::mask bt = BoolTest::illegal; + float cl_prob = 0; + Node* incr = NULL; + Node* limit = NULL; + + Node* cmp = loop_exit_test(back_control, loop, incr, limit, bt, cl_prob); + if (cmp == NULL || cmp->Opcode() != Op_CmpL) { + return false; // Avoid pointer & float & 32-bit compares + } + + Node* phi_incr = NULL; + incr = loop_iv_incr(incr, x, loop, phi_incr); + if (incr == NULL || incr->Opcode() != Op_AddL) { + return false; + } + + Node* xphi = NULL; + Node* stride = loop_iv_stride(incr, loop, xphi); + + if (stride == NULL) { + return false; + } + +#ifndef PRODUCT + Atomic::inc(&_long_loop_candidates); +#endif + + jlong stride_con = stride->get_long(); + assert(stride_con != 0, "missed some peephole opt"); + // We can't iterate for more than max int at a time. + if (stride_con != (jint)stride_con) { + return false; + } + // The number of iterations for the integer count loop: guarantee no + // overflow: max_jint - stride_con max. -1 so there's no need for a + // loop limit check if the exit test is <= or >=. + int iters_limit = max_jint - ABS(stride_con) - 1; +#ifdef ASSERT + if (StressLongCountedLoop > 0) { + iters_limit = iters_limit / StressLongCountedLoop; + } +#endif + // At least 2 iterations so counted loop construction doesn't fail + if (iters_limit/ABS(stride_con) < 2) { + return false; + } + + PhiNode* phi = loop_iv_phi(xphi, phi_incr, x, loop); + + if (phi == NULL || phi->in(LoopNode::LoopBackControl) != incr) { + return false; + } + + // Safepoint on backedge not supported + if (x->in(LoopNode::LoopBackControl)->Opcode() == Op_SafePoint) { + return false; + } + + // data nodes on back branch not supported + if (back_control->outcnt() > 1) { + return false; + } + + if (!condition_stride_ok(bt, stride_con)) { + return false; + } + + // We'll need to use the loop limit before the inner loop is entered + if (!is_dominator(get_ctrl(limit), x)) { + return false; + } + + IfNode* exit_test = back_control->in(0)->as_If(); + + // We need a safepoint to insert empty predicates for the inner loop. + SafePointNode* safepoint = find_safepoint(back_control, x, loop); + if (safepoint == NULL) { + // If exit condition is ne, then a loop limit check is likely needed + if (bt == BoolTest::ne) { + return false; + } + } else if (C->too_many_traps(safepoint->jvms()->method(), + safepoint->jvms()->bci(), + Deoptimization::Reason_loop_limit_check)) { + // We must have transformed the loop already and a loop limit + // check must have failed. + return false; + } + + Node* exit_branch = exit_test->proj_out(back_control->Opcode() == Op_IfFalse); + Node* entry_control = x->in(LoopNode::EntryControl); + + // if the loop exit test is on the IV before it is incremented: i < + // limit, we transform the exit test so it is performed on the exit + // test after it is incremented: i + stride < limit + stride. We + // need limit + stride to not overflow. See adjusted_limit below. + bool limit_check_required = false; + if (phi_incr != NULL) { + const TypeLong* limit_t = _igvn.type(limit)->is_long(); + int sov = check_stride_overflow(stride_con, limit_t); + if (sov != 0) { + if (sov < 0) { + return false; // Bailout: integer overflow is certain. + } + // Check that inserting a predicate is indeed possible + if (find_predicate_insertion_point(x->in(LoopNode::EntryControl), Deoptimization::Reason_loop_limit_check) == NULL) { + return false; + } + limit_check_required = true; + } + } + + // Clone the control flow of the loop to build an outer loop + Node* outer_back_branch = back_control->clone(); + Node* outer_exit_test = exit_test->clone(); + Node* inner_exit_branch = exit_branch->clone(); + + Node* outer_head = new LoopNode(entry_control, outer_back_branch); + IdealLoopTree* outer_ilt = insert_outer_loop(loop, outer_head->as_Loop(), outer_back_branch); + + const bool body_populated = true; + register_control(outer_head, outer_ilt, entry_control, body_populated); + + _igvn.register_new_node_with_optimizer(inner_exit_branch); + set_loop(inner_exit_branch, outer_ilt); + set_idom(inner_exit_branch, exit_test, dom_depth(exit_branch)); + + outer_exit_test->set_req(0, inner_exit_branch); + register_control(outer_exit_test, outer_ilt, inner_exit_branch, body_populated); + + _igvn.replace_input_of(exit_branch, 0, outer_exit_test); + set_idom(exit_branch, outer_exit_test, dom_depth(exit_branch)); + + outer_back_branch->set_req(0, outer_exit_test); + register_control(outer_back_branch, outer_ilt, outer_exit_test, body_populated); + + _igvn.replace_input_of(x, LoopNode::EntryControl, outer_head); + set_idom(x, outer_head, dom_depth(x)); + + // add an iv phi to the outer loop and use it to compute the inner + // loop iteration limit + Node* outer_phi = phi->clone(); + outer_phi->set_req(0, outer_head); + register_new_node(outer_phi, outer_head); + + Node* adjusted_limit = limit; + if (phi_incr != NULL) { + // If compare points directly to the phi we need to adjust the + // compare so that it points to the incr. + Node* long_stride = _igvn.longcon(stride_con); + set_ctrl(long_stride, C->root()); + adjusted_limit = new AddLNode(limit, long_stride); + _igvn.register_new_node_with_optimizer(adjusted_limit); + } + Node* inner_iters_max = NULL; + if (stride_con > 0) { + inner_iters_max = MaxNode::max_diff_with_zero(adjusted_limit, outer_phi, TypeLong::LONG, _igvn); + } else { + inner_iters_max = MaxNode::max_diff_with_zero(outer_phi, adjusted_limit, TypeLong::LONG, _igvn); + } + + Node* inner_iters_limit = _igvn.longcon(iters_limit); + // inner_iters_max may not fit in a signed integer (iterating from + // Long.MIN_VALUE to Long.MAX_VALUE for instance). Use an unsigned + // min. + Node* inner_iters_actual = MaxNode::unsigned_min(inner_iters_max, inner_iters_limit, TypeLong::make(0, iters_limit, Type::WidenMin), _igvn); + + Node* inner_iters_actual_int = new ConvL2INode(inner_iters_actual); + _igvn.register_new_node_with_optimizer(inner_iters_actual_int); + + Node* zero = _igvn.intcon(0); + set_ctrl(zero, C->root()); + if (stride_con < 0) { + inner_iters_actual_int = new SubINode(zero, inner_iters_actual_int); + _igvn.register_new_node_with_optimizer(inner_iters_actual_int); + } + + // Clone the iv data nodes as an integer iv + Node* int_stride = _igvn.intcon((int)stride_con); + set_ctrl(int_stride, C->root()); + Node* inner_phi = new PhiNode(x->in(0), TypeInt::INT); + Node* inner_incr = new AddINode(inner_phi, int_stride); + Node* inner_cmp = NULL; + if (cmp->in(1) == incr || cmp->in(1) == phi) { + inner_cmp = new CmpINode(inner_incr, inner_iters_actual_int); + } else { + assert(cmp->in(2) == incr || cmp->in(2) == phi, "bad iv shape"); + inner_cmp = new CmpINode(inner_iters_actual_int, inner_incr); + } + Node* inner_bol = new BoolNode(inner_cmp, exit_test->in(1)->as_Bool()->_test._test); + inner_phi->set_req(LoopNode::EntryControl, zero); + inner_phi->set_req(LoopNode::LoopBackControl, inner_incr); + register_new_node(inner_phi, x); + register_new_node(inner_incr, x); + register_new_node(inner_cmp, x); + register_new_node(inner_bol, x); + + _igvn.replace_input_of(exit_test, 1, inner_bol); + + // Add a predicate to guarantee limit adjustment doesn't overflow + if (limit_check_required) { + assert(phi_incr != NULL, "only when exit test must be transformed"); + ProjNode *limit_check_proj = find_predicate_insertion_point(outer_head->in(LoopNode::EntryControl), Deoptimization::Reason_loop_limit_check); + assert(limit_check_proj != NULL, "was tested before"); + IfNode* check_iff = limit_check_proj->in(0)->as_If(); + Node* cmp_limit; + Node* bol; + + if (stride_con > 0) { + cmp_limit = new CmpLNode(limit, _igvn.longcon(max_jlong - stride_con)); + bol = new BoolNode(cmp_limit, BoolTest::le); + } else { + cmp_limit = new CmpLNode(limit, _igvn.longcon(min_jlong - stride_con)); + bol = new BoolNode(cmp_limit, BoolTest::ge); + } + + insert_loop_limit_check(limit_check_proj, cmp_limit, bol); + Node* new_predicate = limit_check_proj->in(0)->in(0); + Node* above_predicate = new_predicate->in(0)->in(0); + Node* entry = outer_head->in(LoopNode::EntryControl); + _igvn.replace_input_of(limit_check_proj->in(0), 0, above_predicate); + _igvn.replace_input_of(new_predicate->in(0), 0, entry); + _igvn.replace_input_of(outer_head, LoopNode::EntryControl, new_predicate); + set_idom(new_predicate->in(0), entry, dom_depth(entry)); + set_idom(new_predicate, new_predicate->in(0), dom_depth(entry)); + Node* region = new_predicate->in(0)->as_If()->proj_out(new_predicate->Opcode() == Op_IfFalse)->unique_ctrl_out(); + assert(region->is_Region(), "should be region merging predicates"); + set_idom(region, entry, dom_depth(entry)); + set_idom(limit_check_proj->in(0), above_predicate, dom_depth(above_predicate)); + } + + LoopNode* inner_head = x->as_Loop(); + + // Clone inner loop phis to outer loop + for (uint i = 0; i < inner_head->outcnt(); i++) { + Node* u = inner_head->raw_out(i); + if (u->is_Phi() && u != inner_phi && u != phi) { + assert(u->in(0) == inner_head, "inconsistent"); + Node* clone = u->clone(); + clone->set_req(0, outer_head); + register_new_node(clone, outer_head); + _igvn.replace_input_of(u, LoopNode::EntryControl, clone); + } + } + + // Replace inner loop long iv phi as inner loop int iv phi + outer + // loop iv phi + long_loop_replace_long_iv(phi, inner_phi, outer_phi, inner_head); + + // Replace inner loop long iv incr with inner loop int incr + outer + // loop iv phi + long_loop_replace_long_iv(incr, inner_incr, outer_phi, inner_head); + + set_subtree_ctrl(inner_iters_actual_int); + + // Summary of steps from inital loop to loop nest: + // + // == old IR nodes => + // + // entry_control: {...} + // x: + // for (long phi = init;;) { + // // phi := Phi(x, init, incr) + // // incr := AddL(phi, longcon(stride)) + // exit_test: + // if (phi < limit) + // back_control: fallthrough; + // else + // exit_branch: break; + // // test happens before increment => phi == phi_incr != NULL + // long incr = phi + stride; + // ... use phi and incr ... + // phi = incr; + // } + // + // == new IR nodes (just before final peel) => + // + // entry_control: {...} + // long adjusted_limit = limit + stride; //because phi_incr != NULL + // assert(!limit_check_required || (extralong)limit + stride == adjusted_limit); // else deopt + // ulong inner_iters_limit = max_jint - ABS(stride) - 1; //near 0x7FFFFFF0 + // outer_head: + // for (long outer_phi = init;;) { + // // outer_phi := phi->clone(), in(0):=outer_head, => Phi(outer_head, init, incr) + // // REPLACE phi => AddL(outer_phi, I2L(inner_phi)) + // // REPLACE incr => AddL(outer_phi, I2L(inner_incr)) + // // SO THAT outer_phi := Phi(outer_head, init, AddL(outer_phi, I2L(inner_incr))) + // ulong inner_iters_max = (ulong) MAX(0, ((extralong)adjusted_limit - outer_phi) * SGN(stride)); + // int inner_iters_actual_int = (int) MIN(inner_iters_limit, inner_iters_max) * SGN(stride); + // inner_head: x: //in(1) := outer_head + // int inner_phi; + // for (inner_phi = 0;;) { + // // inner_phi := Phi(x, intcon(0), inner_phi + stride) + // int inner_incr = inner_phi + stride; + // bool inner_bol = (inner_incr < inner_iters_actual_int); + // exit_test: //exit_test->in(1) := inner_bol; + // if (inner_bol) // WAS (phi < limit) + // back_control: fallthrough; + // else + // inner_exit_branch: break; //exit_branch->clone() + // ... use phi=>(outer_phi+inner_phi) and incr=>(outer_phi+inner_incr) ... + // inner_phi = inner_phi + stride; // inner_incr + // } + // outer_exit_test: //exit_test->clone(), in(0):=inner_exit_branch + // if ((outer_phi+inner_phi) < limit) // WAS (phi < limit) + // outer_back_branch: fallthrough; //back_control->clone(), in(0):=outer_exit_test + // else + // exit_branch: break; //in(0) := outer_exit_test + // } + + // Peel one iteration of the loop and use the safepoint at the end + // of the peeled iteration to insert empty predicates. If no well + // positioned safepoint peel to guarantee a safepoint in the outer + // loop. + if (safepoint != NULL || !loop->_has_call) { + old_new.clear(); + do_peeling(loop, old_new); + } + + if (safepoint != NULL) { + SafePointNode* cloned_sfpt = old_new[safepoint->_idx]->as_SafePoint(); + + if (UseLoopPredicate) { + add_empty_predicate(Deoptimization::Reason_predicate, inner_head, outer_ilt, cloned_sfpt); + } + if (UseProfiledLoopPredicate) { + add_empty_predicate(Deoptimization::Reason_profile_predicate, inner_head, outer_ilt, cloned_sfpt); + } + add_empty_predicate(Deoptimization::Reason_loop_limit_check, inner_head, outer_ilt, cloned_sfpt); + } + +#ifndef PRODUCT + Atomic::inc(&_long_loop_nests); +#endif + + inner_head->mark_transformed_long_loop(); + + return true; +} + +#ifdef ASSERT +// convert an int counted loop to a long counted to stress handling of +// long counted loops +bool PhaseIdealLoop::convert_to_long_loop(Node* cmp, Node* phi, IdealLoopTree* loop) { + Unique_Node_List iv_nodes; + Node_List old_new; + iv_nodes.push(cmp); + bool failed = false; + + for (uint i = 0; i < iv_nodes.size() && !failed; i++) { + Node* n = iv_nodes.at(i); + switch(n->Opcode()) { + case Op_Phi: { + Node* clone = new PhiNode(n->in(0), TypeLong::LONG); + old_new.map(n->_idx, clone); + break; + } + case Op_CmpI: { + Node* clone = new CmpLNode(NULL, NULL); + old_new.map(n->_idx, clone); + break; + } + case Op_AddI: { + Node* clone = new AddLNode(NULL, NULL); + old_new.map(n->_idx, clone); + break; + } + case Op_CastII: { + failed = true; + break; + } + default: + DEBUG_ONLY(n->dump()); + fatal("unexpected"); + } + + for (uint i = 1; i < n->req(); i++) { + Node* in = n->in(i); + if (in == NULL) { + continue; + } + if (loop->is_member(get_loop(get_ctrl(in)))) { + iv_nodes.push(in); + } + } + } + + if (failed) { + for (uint i = 0; i < iv_nodes.size(); i++) { + Node* n = iv_nodes.at(i); + Node* clone = old_new[n->_idx]; + if (clone != NULL) { + _igvn.remove_dead_node(clone); + } + } + return false; + } + + for (uint i = 0; i < iv_nodes.size(); i++) { + Node* n = iv_nodes.at(i); + Node* clone = old_new[n->_idx]; + for (uint i = 1; i < n->req(); i++) { + Node* in = n->in(i); + if (in == NULL) { + continue; + } + Node* in_clone = old_new[in->_idx]; + if (in_clone == NULL) { + assert(_igvn.type(in)->isa_int(), ""); + in_clone = new ConvI2LNode(in); + _igvn.register_new_node_with_optimizer(in_clone); + set_subtree_ctrl(in_clone); + } + if (in_clone->in(0) == NULL) { + in_clone->set_req(0, C->top()); + clone->set_req(i, in_clone); + in_clone->set_req(0, NULL); + } else { + clone->set_req(i, in_clone); + } + } + _igvn.register_new_node_with_optimizer(clone); + } + set_ctrl(old_new[phi->_idx], phi->in(0)); + + for (uint i = 0; i < iv_nodes.size(); i++) { + Node* n = iv_nodes.at(i); + Node* clone = old_new[n->_idx]; + set_subtree_ctrl(clone); + Node* m = n->Opcode() == Op_CmpI ? clone : NULL; + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node* u = n->fast_out(i); + if (iv_nodes.member(u)) { + continue; + } + if (m == NULL) { + m = new ConvL2INode(clone); + _igvn.register_new_node_with_optimizer(m); + set_subtree_ctrl(m); + } + _igvn.rehash_node_delayed(u); + int nb = u->replace_edge(n, m); + --i, imax -= nb; + } + } + return true; +} +#endif + //------------------------------is_counted_loop-------------------------------- bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop) { PhaseGVN *gvn = &_igvn; @@ -514,7 +1240,6 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop) { float cl_prob = 0; Node* incr = NULL; Node* limit = NULL; - Node* cmp = loop_exit_test(back_control, loop, incr, limit, bt, cl_prob); if (cmp == NULL || cmp->Opcode() != Op_CmpI) { return false; // Avoid pointer & float & 64-bit compares @@ -640,16 +1365,8 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop) { assert(trunc1 == NULL && trunc2 == NULL, "no truncation for int"); } - // If the condition is inverted and we will be rolling - // through MININT to MAXINT, then bail out. - if (bt == BoolTest::eq || // Bail out, but this loop trips at most twice! - // Odd stride - (bt == BoolTest::ne && stride_con != 1 && stride_con != -1) || - // Count down loop rolls through MAXINT - ((bt == BoolTest::le || bt == BoolTest::lt) && stride_con < 0) || - // Count up loop rolls through MININT - ((bt == BoolTest::ge || bt == BoolTest::gt) && stride_con > 0)) { - return false; // Bail out + if (!condition_stride_ok(bt, stride_con)) { + return false; } const TypeInt* init_t = gvn->type(init_trip)->is_int(); @@ -683,8 +1400,6 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop) { assert(x->Opcode() == Op_Loop, "regular loops only"); C->print_method(PHASE_BEFORE_CLOOPS, 3); - Node *hook = new Node(6); - // =================================================== // Generate loop limit check to avoid integer overflow // in cases like next (cyclic loops): @@ -722,6 +1437,7 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop) { if (sov < 0) { return false; // Bailout: integer overflow is certain. } + assert(!x->as_Loop()->is_transformed_long_loop(), "long loop was transformed"); // Generate loop's limit check. // Loop limit check predicate should be near the loop. ProjNode *limit_check_proj = find_predicate_insertion_point(init_control, Deoptimization::Reason_loop_limit_check); @@ -809,6 +1525,12 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop) { } } +#ifdef ASSERT + if (!x->as_Loop()->is_transformed_long_loop() && StressLongCountedLoop > 0 && trunc1 == NULL && convert_to_long_loop(cmp, phi, loop)) { + return false; + } +#endif + if (phi_incr != NULL) { // If compare points directly to the phi we need to adjust // the compare so that it points to the incr. Limit have @@ -967,9 +1689,6 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop) { } } - // Free up intermediate goo - _igvn.remove_dead_node(hook); - #ifdef ASSERT assert(l->is_valid_counted_loop(), "counted loop shape is messed up"); assert(l == loop->_head && l->phi() == phi && l->loopexit_or_null() == lex, "" ); @@ -995,6 +1714,12 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop) { loop = outer_ilt; } +#ifndef PRODUCT + if (x->as_Loop()->is_transformed_long_loop()) { + Atomic::inc(&_long_loop_counted_loops); + } +#endif + return true; } @@ -1926,7 +2651,7 @@ void IdealLoopTree::split_fall_in( PhaseIdealLoop *phase, int fall_in_cnt ) { // with the CSE to avoid O(N^2) node blow-up. Node *p2 = igvn.hash_find_insert(p); // Look for a CSE if( p2 ) { // Found CSE - p->destruct(); // Recover useless new node + p->destruct(&igvn); // Recover useless new node p = p2; // Use old node } else { igvn.register_new_node_with_optimizer(p, old_phi); @@ -2574,11 +3299,14 @@ void IdealLoopTree::counted_loop( PhaseIdealLoop *phase ) { // Look for induction variables phase->replace_parallel_iv(this); - } else if (_parent != NULL && !_irreducible) { + } else { + assert(!_head->is_Loop() || !_head->as_Loop()->is_transformed_long_loop(), "transformation to counted loop should not fail"); + if (_parent != NULL && !_irreducible) { // Not a counted loop. Keep one safepoint. bool keep_one_sfpt = true; remove_safepoints(phase, keep_one_sfpt); } + } // Recursively assert(loop->_child != this || (loop->_head->as_Loop()->is_OuterStripMinedLoop() && _head->as_CountedLoop()->is_strip_mined()), "what kind of loop was added?"); @@ -2753,34 +3481,39 @@ void IdealLoopTree::dump() const { #endif -static void log_loop_tree(IdealLoopTree* root, IdealLoopTree* loop, CompileLog* log) { +static void log_loop_tree_helper(IdealLoopTree* root, IdealLoopTree* loop, CompileLog* log) { if (loop == root) { if (loop->_child != NULL) { log->begin_head("loop_tree"); log->end_head(); - if( loop->_child ) log_loop_tree(root, loop->_child, log); + log_loop_tree_helper(root, loop->_child, log); log->tail("loop_tree"); assert(loop->_next == NULL, "what?"); } - } else { + } else if (loop != NULL) { Node* head = loop->_head; log->begin_head("loop"); log->print(" idx='%d' ", head->_idx); if (loop->_irreducible) log->print("irreducible='1' "); if (head->is_Loop()) { - if (head->as_Loop()->is_inner_loop()) log->print("inner_loop='1' "); + if (head->as_Loop()->is_inner_loop()) log->print("inner_loop='1' "); if (head->as_Loop()->is_partial_peel_loop()) log->print("partial_peel_loop='1' "); - } - if (head->is_CountedLoop()) { + } else if (head->is_CountedLoop()) { CountedLoopNode* cl = head->as_CountedLoop(); if (cl->is_pre_loop()) log->print("pre_loop='%d' ", cl->main_idx()); if (cl->is_main_loop()) log->print("main_loop='%d' ", cl->_idx); - if (cl->is_post_loop()) log->print("post_loop='%d' ", cl->main_idx()); + if (cl->is_post_loop()) log->print("post_loop='%d' ", cl->main_idx()); } log->end_head(); - if( loop->_child ) log_loop_tree(root, loop->_child, log); + log_loop_tree_helper(root, loop->_child, log); log->tail("loop"); - if( loop->_next ) log_loop_tree(root, loop->_next, log); + log_loop_tree_helper(root, loop->_next, log); + } +} + +void PhaseIdealLoop::log_loop_tree() { + if (C->log() != NULL) { + log_loop_tree_helper(_ltree_root, _ltree_root, C->log()); } } @@ -2936,12 +3669,39 @@ bool PhaseIdealLoop::process_expensive_nodes() { return progress; } +#ifdef ASSERT +bool PhaseIdealLoop::only_has_infinite_loops() { + for (LoopTreeIterator iter(_ltree_root); !iter.done(); iter.next()) { + IdealLoopTree* lpt = iter.current(); + if (lpt->is_innermost()) { + uint i = 1; + for (; i < C->root()->req(); i++) { + Node* in = C->root()->in(i); + if (in != NULL && + in->Opcode() == Op_Halt && + in->in(0)->is_Proj() && + in->in(0)->in(0)->Opcode() == Op_NeverBranch && + in->in(0)->in(0)->in(0) == lpt->_head) { + break; + } + } + if (i == C->root()->req()) { + return false; + } + } + } + return true; +} +#endif + //============================================================================= //----------------------------build_and_optimize------------------------------- // Create a PhaseLoop. Build the ideal Loop tree. Map each Ideal Node to // its corresponding LoopNode. If 'optimize' is true, do some loop cleanups. void PhaseIdealLoop::build_and_optimize(LoopOptsMode mode) { + assert(!C->post_loop_opts_phase(), "no loop opts allowed"); + bool do_split_ifs = (mode == LoopOptsDefault); bool skip_loop_opts = (mode == LoopOptsNone); @@ -2991,6 +3751,13 @@ void PhaseIdealLoop::build_and_optimize(LoopOptsMode mode) { return; } + // Verify that the has_loops() flag set at parse time is consistent + // with the just built loop tree. With infinite loops, it could be + // that one pass of loop opts only finds infinite loops, clears the + // has_loops() flag but adds NeverBranch nodes so the next loop opts + // verification pass finds a non empty loop tree. When the back edge + // is an exception edge, parsing doesn't set has_loops(). + assert(_ltree_root->_child == NULL || C->has_loops() || only_has_infinite_loops() || C->has_exception_backedge(), "parsing found no loops but there are some"); // No loops after all if( !_ltree_root->_child && !_verify_only ) C->set_has_loops(false); @@ -3011,7 +3778,6 @@ void PhaseIdealLoop::build_and_optimize(LoopOptsMode mode) { bool do_expensive_nodes = C->should_optimize_expensive_nodes(_igvn); bool strip_mined_loops_expanded = bs->strip_mined_loops_expanded(mode); if (stop_early && !do_expensive_nodes) { - _igvn.optimize(); // Cleanup NeverBranches return; } @@ -3121,7 +3887,6 @@ void PhaseIdealLoop::build_and_optimize(LoopOptsMode mode) { // nodes again. C->set_major_progress(); } - _igvn.optimize(); return; } @@ -3144,15 +3909,7 @@ void PhaseIdealLoop::build_and_optimize(LoopOptsMode mode) { #endif if (skip_loop_opts) { - // restore major progress flag C->restore_major_progress(old_progress); - - // Cleanup any modified bits - _igvn.optimize(); - - if (C->log() != NULL) { - log_loop_tree(_ltree_root, _ltree_root, C->log()); - } return; } @@ -3174,24 +3931,19 @@ void PhaseIdealLoop::build_and_optimize(LoopOptsMode mode) { } C->restore_major_progress(old_progress); - - _igvn.optimize(); - - if (C->log() != NULL) { - log_loop_tree(_ltree_root, _ltree_root, C->log()); - } return; } if (bs->optimize_loops(this, mode, visited, nstack, worklist)) { - _igvn.optimize(); - if (C->log() != NULL) { - log_loop_tree(_ltree_root, _ltree_root, C->log()); - } return; } - if (ReassociateInvariants) { + for (LoopTreeIterator iter(_ltree_root); !iter.done(); iter.next()) { + IdealLoopTree* lpt = iter.current(); + is_long_counted_loop(lpt->_head, lpt, worklist); + } + + if (ReassociateInvariants && !C->major_progress()) { // Reassociate invariants and prep for split_thru_phi for (LoopTreeIterator iter(_ltree_root); !iter.done(); iter.next()) { IdealLoopTree* lpt = iter.current(); @@ -3219,7 +3971,7 @@ void PhaseIdealLoop::build_and_optimize(LoopOptsMode mode) { // Check for aggressive application of split-if and other transforms // that require basic-block info (like cloning through Phi's) - if( SplitIfBlocks && do_split_ifs ) { + if (!C->major_progress() && SplitIfBlocks && do_split_ifs) { visited.clear(); split_if_with_blocks( visited, nstack); NOT_PRODUCT( if( VerifyLoopOptimizations ) verify(); ); @@ -3321,24 +4073,20 @@ void PhaseIdealLoop::build_and_optimize(LoopOptsMode mode) { } } - // Cleanup any modified bits - _igvn.optimize(); - // disable assert until issue with split_flow_path is resolved (6742111) // assert(!_has_irreducible_loops || C->parsed_irreducible_loop() || C->is_osr_compilation(), // "shouldn't introduce irreducible loops"); - - if (C->log() != NULL) { - log_loop_tree(_ltree_root, _ltree_root, C->log()); - } } #ifndef PRODUCT //------------------------------print_statistics------------------------------- int PhaseIdealLoop::_loop_invokes=0;// Count of PhaseIdealLoop invokes int PhaseIdealLoop::_loop_work=0; // Sum of PhaseIdealLoop x unique +volatile int PhaseIdealLoop::_long_loop_candidates=0; // Number of long loops seen +volatile int PhaseIdealLoop::_long_loop_nests=0; // Number of long loops successfully transformed to a nest +volatile int PhaseIdealLoop::_long_loop_counted_loops=0; // Number of long loops successfully transformed to a counted loop void PhaseIdealLoop::print_statistics() { - tty->print_cr("PhaseIdealLoop=%d, sum _unique=%d", _loop_invokes, _loop_work); + tty->print_cr("PhaseIdealLoop=%d, sum _unique=%d, long loops=%d/%d/%d", _loop_invokes, _loop_work, _long_loop_counted_loops, _long_loop_nests, _long_loop_candidates); } //------------------------------verify----------------------------------------- diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 108c9df757d..ac65ffc09ed 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -76,7 +76,8 @@ class LoopNode : public RegionNode { IsMultiversioned=16384, StripMined=32768, SubwordLoop=65536, - ProfileTripFailed=131072}; + ProfileTripFailed=131072, + TransformedLongLoop=262144}; char _unswitch_count; enum { _unswitch_max=3 }; char _postloop_flags; @@ -101,6 +102,7 @@ class LoopNode : public RegionNode { bool is_strip_mined() const { return _loop_flags & StripMined; } bool is_profile_trip_failed() const { return _loop_flags & ProfileTripFailed; } bool is_subword_loop() const { return _loop_flags & SubwordLoop; } + bool is_transformed_long_loop() const { return _loop_flags & TransformedLongLoop; } void mark_partial_peel_failed() { _loop_flags |= PartialPeelFailed; } void mark_has_reductions() { _loop_flags |= HasReductions; } @@ -115,6 +117,7 @@ class LoopNode : public RegionNode { void clear_strip_mined() { _loop_flags &= ~StripMined; } void mark_profile_trip_failed() { _loop_flags |= ProfileTripFailed; } void mark_subword_loop() { _loop_flags |= SubwordLoop; } + void mark_transformed_long_loop() { _loop_flags |= TransformedLongLoop; } int unswitch_max() { return _unswitch_max; } int unswitch_count() { return _unswitch_count; } @@ -614,13 +617,6 @@ class IdealLoopTree : public ResourceObj { // also gather the end of the first split and the start of the 2nd split. bool policy_range_check( PhaseIdealLoop *phase ) const; - // Return TRUE or FALSE if the loop should be cache-line aligned. - // Gather the expression that does the alignment. Note that only - // one array base can be aligned in a loop (unless the VM guarantees - // mutual alignment). Note that if we vectorize short memory ops - // into longer memory ops, we may want to increase alignment. - bool policy_align( PhaseIdealLoop *phase ) const; - // Return TRUE if "iff" is a range check. bool is_range_check_if(IfNode *iff, PhaseIdealLoop *phase, Invariance& invar) const; @@ -805,6 +801,11 @@ class PhaseIdealLoop : public PhaseTransform { bool skeleton_predicate_has_opaque(IfNode* iff); void update_main_loop_skeleton_predicates(Node* ctrl, CountedLoopNode* loop_head, Node* init, int stride_con); void insert_loop_limit_check(ProjNode* limit_check_proj, Node* cmp_limit, Node* bol); +#ifdef ASSERT + bool only_has_infinite_loops(); +#endif + + void log_loop_tree(); public: @@ -1031,6 +1032,14 @@ class PhaseIdealLoop : public PhaseTransform { static void optimize(PhaseIterGVN &igvn, LoopOptsMode mode) { ResourceMark rm; PhaseIdealLoop v(igvn, mode); + + Compile* C = Compile::current(); + if (!C->failing()) { + // Cleanup any modified bits + igvn.optimize(); + + v.log_loop_tree(); + } } // True if the method has at least 1 irreducible loop @@ -1046,6 +1055,13 @@ class PhaseIdealLoop : public PhaseTransform { PhiNode* loop_iv_phi(Node* xphi, Node* phi_incr, Node* x, IdealLoopTree* loop); bool is_counted_loop(Node* n, IdealLoopTree* &loop); + void long_loop_replace_long_iv(Node* iv_to_replace, Node* inner_iv, Node* outer_phi, Node* inner_head); + bool is_long_counted_loop(Node* x, IdealLoopTree* loop, Node_List &old_new); +#ifdef ASSERT + bool convert_to_long_loop(Node* cmp, Node* phi, IdealLoopTree* loop); +#endif + void add_empty_predicate(Deoptimization::DeoptReason reason, Node* inner_head, IdealLoopTree* loop, SafePointNode* sfpt); + SafePointNode* find_safepoint(Node* back_control, Node* x, IdealLoopTree* loop); IdealLoopTree* insert_outer_loop(IdealLoopTree* loop, LoopNode* outer_l, Node* outer_ift); IdealLoopTree* create_outer_strip_mined_loop(BoolNode *test, Node *cmp, Node *init_control, IdealLoopTree* loop, float cl_prob, float le_fcnt, @@ -1243,9 +1259,9 @@ class PhaseIdealLoop : public PhaseTransform { // always holds true. That is, either increase the number of iterations in // the pre-loop or the post-loop until the condition holds true in the main // loop. Scale_con, offset and limit are all loop invariant. - void add_constraint( int stride_con, int scale_con, Node *offset, Node *low_limit, Node *upper_limit, Node *pre_ctrl, Node **pre_limit, Node **main_limit ); + void add_constraint(jlong stride_con, jlong scale_con, Node* offset, Node* low_limit, Node* upper_limit, Node* pre_ctrl, Node** pre_limit, Node** main_limit); // Helper function for add_constraint(). - Node* adjust_limit(int stride_con, Node * scale, Node *offset, Node *rc_limit, Node *loop_limit, Node *pre_ctrl, bool round_up); + Node* adjust_limit(bool reduce, Node* scale, Node* offset, Node* rc_limit, Node* old_limit, Node* pre_ctrl, bool round); // Partially peel loop up through last_peel node. bool partial_peel( IdealLoopTree *loop, Node_List &old_new ); @@ -1468,6 +1484,9 @@ class PhaseIdealLoop : public PhaseTransform { static void print_statistics(); static int _loop_invokes; // Count of PhaseIdealLoop invokes static int _loop_work; // Sum of PhaseIdealLoop x _unique + static volatile int _long_loop_candidates; + static volatile int _long_loop_nests; + static volatile int _long_loop_counted_loops; #endif void rpo(Node* start, Node_Stack &stk, VectorSet &visited, Node_List &rpo_list) const; diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 46a1d1ccf99..5848bae07af 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -1038,7 +1038,9 @@ Node *PhaseIdealLoop::split_if_with_blocks_pre( Node *n ) { // Do not clone the trip counter through on a CountedLoop // (messes up the canonical shape). - if( n_blk->is_CountedLoop() && n->Opcode() == Op_AddI ) return n; + if ((n_blk->is_CountedLoop() || (n_blk->is_Loop() && n_blk->as_Loop()->is_transformed_long_loop())) && n->Opcode() == Op_AddI) { + return n; + } // Check for having no control input; not pinned. Allow // dominating control. @@ -2558,7 +2560,13 @@ IfNode* PhaseIdealLoop::insert_cmpi_loop_exit(IfNode* if_cmpu, IdealLoopTree *lo ProjNode* lp_continue = lp_proj->as_Proj(); ProjNode* lp_exit = if_cmpu->proj_out(!lp_continue->is_IfTrue())->as_Proj(); - + if (!lp_exit->is_IfFalse()) { + // The loop exit condition is (i (i >= 0 && i < limit). + // We therefore can't add a single exit condition. + return NULL; + } + // The loop exit condition is !(i (i < 0 || i >= limit). + // Split out the exit condition (i < 0) for stride < 0 or (i >= limit) for stride > 0. Node* limit = NULL; if (stride > 0) { limit = cmpu->in(2); diff --git a/src/hotspot/share/opto/machnode.cpp b/src/hotspot/share/opto/machnode.cpp index c51d6117cfb..2f8bfb51abd 100644 --- a/src/hotspot/share/opto/machnode.cpp +++ b/src/hotspot/share/opto/machnode.cpp @@ -295,7 +295,7 @@ const Node* MachNode::get_base_and_disp(intptr_t &offset, const TypePtr* &adr_ty } offset = disp; - // In i486.ad, indOffset32X uses base==RegI and disp==RegP, + // In x86_32.ad, indOffset32X uses base==RegI and disp==RegP, // this will prevent alias analysis without the following support: // Lookup the TypePtr used by indOffset32X, a compile-time constant oop, // Add the offset determined by the "base", or use Type::OffsetBot. diff --git a/src/hotspot/share/opto/machnode.hpp b/src/hotspot/share/opto/machnode.hpp index cb1d6d69372..ec375abc37a 100644 --- a/src/hotspot/share/opto/machnode.hpp +++ b/src/hotspot/share/opto/machnode.hpp @@ -151,7 +151,7 @@ class MachOper : public ResourceObj { virtual int index_position() const; // index edge position, or -1 // Access the TypeKlassPtr of operands with a base==RegI and disp==RegP - // Only returns non-null value for i486.ad's indOffset32X + // Only returns non-null value for x86_32.ad's indOffset32X virtual const TypePtr *disp_as_type() const { return NULL; } // Return the label @@ -434,7 +434,6 @@ class MachConstantBaseNode : public MachIdealNode { virtual void emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const; virtual uint size(PhaseRegAlloc* ra_) const; - virtual bool pinned() const { return UseRDPCForConstantTableBase; } static const RegMask& static_out_RegMask() { return _out_RegMask; } virtual const RegMask& out_RegMask() const { return static_out_RegMask(); } @@ -820,10 +819,11 @@ class MachSafePointNode : public MachReturnNode { OopMap* _oop_map; // Array of OopMap info (8-bit char) for GC JVMState* _jvms; // Pointer to list of JVM State Objects uint _jvmadj; // Extra delta to jvms indexes (mach. args) + bool _has_ea_local_in_scope; // NoEscape or ArgEscape objects in JVM States OopMap* oop_map() const { return _oop_map; } void set_oop_map(OopMap* om) { _oop_map = om; } - MachSafePointNode() : MachReturnNode(), _oop_map(NULL), _jvms(NULL), _jvmadj(0) { + MachSafePointNode() : MachReturnNode(), _oop_map(NULL), _jvms(NULL), _jvmadj(0), _has_ea_local_in_scope(false) { init_class_id(Class_MachSafePoint); } @@ -922,6 +922,7 @@ class MachCallJavaNode : public MachCallNode { int _bci; // Byte Code index of call byte code bool _optimized_virtual; // Tells if node is a static call or an optimized virtual bool _method_handle_invoke; // Tells if the call has to preserve SP + bool _arg_escape; // ArgEscape in parameter list MachCallJavaNode() : MachCallNode(), _override_symbolic_info(false) { init_class_id(Class_MachCallJava); } diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index b82a36ea891..b51fafcb440 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -94,44 +94,6 @@ void PhaseMacroExpand::migrate_outs(Node *old, Node *target) { assert(old->outcnt() == 0, "all uses must be deleted"); } -void PhaseMacroExpand::copy_call_debug_info(CallNode *oldcall, CallNode * newcall) { - // Copy debug information and adjust JVMState information - uint old_dbg_start = oldcall->tf()->domain()->cnt(); - uint new_dbg_start = newcall->tf()->domain()->cnt(); - int jvms_adj = new_dbg_start - old_dbg_start; - assert (new_dbg_start == newcall->req(), "argument count mismatch"); - - // SafePointScalarObject node could be referenced several times in debug info. - // Use Dict to record cloned nodes. - Dict* sosn_map = new Dict(cmpkey,hashkey); - for (uint i = old_dbg_start; i < oldcall->req(); i++) { - Node* old_in = oldcall->in(i); - // Clone old SafePointScalarObjectNodes, adjusting their field contents. - if (old_in != NULL && old_in->is_SafePointScalarObject()) { - SafePointScalarObjectNode* old_sosn = old_in->as_SafePointScalarObject(); - uint old_unique = C->unique(); - Node* new_in = old_sosn->clone(sosn_map); - if (old_unique != C->unique()) { // New node? - new_in->set_req(0, C->root()); // reset control edge - new_in = transform_later(new_in); // Register new node. - } - old_in = new_in; - } - newcall->add_req(old_in); - } - - // JVMS may be shared so clone it before we modify it - newcall->set_jvms(oldcall->jvms() != NULL ? oldcall->jvms()->clone_deep(C) : NULL); - for (JVMState *jvms = newcall->jvms(); jvms != NULL; jvms = jvms->caller()) { - jvms->set_map(newcall); - jvms->set_locoff(jvms->locoff()+jvms_adj); - jvms->set_stkoff(jvms->stkoff()+jvms_adj); - jvms->set_monoff(jvms->monoff()+jvms_adj); - jvms->set_scloff(jvms->scloff()+jvms_adj); - jvms->set_endoff(jvms->endoff()+jvms_adj); - } -} - Node* PhaseMacroExpand::opt_bits_test(Node* ctrl, Node* region, int edge, Node* word, int mask, int bits, bool return_fast_path) { Node* cmp; if (mask != 0) { @@ -184,7 +146,7 @@ CallNode* PhaseMacroExpand::make_slow_call(CallNode *oldcall, const TypeFunc* sl if (parm0 != NULL) call->init_req(TypeFunc::Parms+0, parm0); if (parm1 != NULL) call->init_req(TypeFunc::Parms+1, parm1); if (parm2 != NULL) call->init_req(TypeFunc::Parms+2, parm2); - copy_call_debug_info(oldcall, call); + call->copy_call_debug_info(&_igvn, oldcall); call->set_cnt(PROB_UNLIKELY_MAG(4)); // Same effect as RC_UNCOMMON. _igvn.replace_node(oldcall, call); transform_later(call); @@ -367,7 +329,7 @@ Node* PhaseMacroExpand::make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, Node* base = ac->in(ArrayCopyNode::Src); Node* adr = _igvn.transform(new AddPNode(base, base, MakeConX(offset))); const TypePtr* adr_type = _igvn.type(base)->is_ptr()->add_offset(offset); - MergeMemNode* mergemen = MergeMemNode::make(mem); + MergeMemNode* mergemen = _igvn.transform(MergeMemNode::make(mem))->as_MergeMem(); BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); res = ArrayCopyNode::load(bs, &_igvn, ctl, mergemen, adr, adr_type, type, bt); } else { @@ -407,7 +369,7 @@ Node* PhaseMacroExpand::make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, return NULL; } } - MergeMemNode* mergemen = MergeMemNode::make(mem); + MergeMemNode* mergemen = _igvn.transform(MergeMemNode::make(mem))->as_MergeMem(); BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); res = ArrayCopyNode::load(bs, &_igvn, ctl, mergemen, adr, adr_type, type, bt); } @@ -1089,11 +1051,12 @@ void PhaseMacroExpand::process_users_of_allocation(CallNode *alloc) { } bool PhaseMacroExpand::eliminate_allocate_node(AllocateNode *alloc) { - // Don't do scalar replacement if the frame can be popped by JVMTI: - // if reallocation fails during deoptimization we'll pop all + // If reallocation fails during deoptimization we'll pop all // interpreter frames for this compiled frame and that won't play // nice with JVMTI popframe. - if (!EliminateAllocations || JvmtiExport::can_pop_frame() || !alloc->_is_non_escaping) { + // We avoid this issue by eager reallocation when the popframe request + // is received. + if (!EliminateAllocations || !alloc->_is_non_escaping) { return false; } Node* klass = alloc->in(AllocateNode::KlassNode); @@ -1473,7 +1436,7 @@ void PhaseMacroExpand::expand_allocate_common( // Copy debug information and adjust JVMState information, then replace // allocate node with the call - copy_call_debug_info((CallNode *) alloc, call); + call->copy_call_debug_info(&_igvn, alloc); if (expand_fast_path) { call->set_cnt(PROB_UNLIKELY_MAG(4)); // Same effect as RC_UNCOMMON. } else { @@ -2460,7 +2423,7 @@ void PhaseMacroExpand::expand_lock_node(LockNode *lock) { Node *slow_ctrl = _fallthroughproj->clone(); transform_later(slow_ctrl); _igvn.hash_delete(_fallthroughproj); - _fallthroughproj->disconnect_inputs(NULL, C); + _fallthroughproj->disconnect_inputs(C); region->init_req(1, slow_ctrl); // region inputs are now complete transform_later(region); @@ -2528,7 +2491,7 @@ void PhaseMacroExpand::expand_unlock_node(UnlockNode *unlock) { Node *slow_ctrl = _fallthroughproj->clone(); transform_later(slow_ctrl); _igvn.hash_delete(_fallthroughproj); - _fallthroughproj->disconnect_inputs(NULL, C); + _fallthroughproj->disconnect_inputs(C); region->init_req(1, slow_ctrl); // region inputs are now complete transform_later(region); diff --git a/src/hotspot/share/opto/macroArrayCopy.cpp b/src/hotspot/share/opto/macroArrayCopy.cpp index ad2de040926..1e1c1803662 100644 --- a/src/hotspot/share/opto/macroArrayCopy.cpp +++ b/src/hotspot/share/opto/macroArrayCopy.cpp @@ -958,7 +958,7 @@ MergeMemNode* PhaseMacroExpand::generate_slow_arraycopy(ArrayCopyNode *ac, call->init_req(TypeFunc::Parms+2, dest); call->init_req(TypeFunc::Parms+3, dest_offset); call->init_req(TypeFunc::Parms+4, copy_length); - copy_call_debug_info(ac, call); + call->copy_call_debug_info(&_igvn, ac); call->set_cnt(PROB_UNLIKELY_MAG(4)); // Same effect as RC_UNCOMMON. _igvn.replace_node(ac, call); diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index b07b4e03be9..0846cad3c3f 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -430,7 +430,7 @@ static RegMask *init_input_masks( uint size, RegMask &ret_adr, RegMask &fp ) { return rms; } -#define NOF_STACK_MASKS (3*6+6) +#define NOF_STACK_MASKS (3*12) // Create the initial stack mask used by values spilling to the stack. // Disallow any debug info in outgoing argument areas by setting the @@ -473,6 +473,20 @@ void Matcher::init_first_stack_mask() { idealreg2spillmask [Op_VecY] = &rms[22]; idealreg2spillmask [Op_VecZ] = &rms[23]; + idealreg2debugmask [Op_VecA] = &rms[24]; + idealreg2debugmask [Op_VecS] = &rms[25]; + idealreg2debugmask [Op_VecD] = &rms[26]; + idealreg2debugmask [Op_VecX] = &rms[27]; + idealreg2debugmask [Op_VecY] = &rms[28]; + idealreg2debugmask [Op_VecZ] = &rms[29]; + + idealreg2mhdebugmask[Op_VecA] = &rms[30]; + idealreg2mhdebugmask[Op_VecS] = &rms[31]; + idealreg2mhdebugmask[Op_VecD] = &rms[32]; + idealreg2mhdebugmask[Op_VecX] = &rms[33]; + idealreg2mhdebugmask[Op_VecY] = &rms[34]; + idealreg2mhdebugmask[Op_VecZ] = &rms[35]; + OptoReg::Name i; // At first, start with the empty mask @@ -520,13 +534,19 @@ void Matcher::init_first_stack_mask() { if (Matcher::vector_size_supported(T_BYTE,4)) { *idealreg2spillmask[Op_VecS] = *idealreg2regmask[Op_VecS]; idealreg2spillmask[Op_VecS]->OR(C->FIRST_STACK_mask()); + } else { + *idealreg2spillmask[Op_VecS] = RegMask::Empty; } + if (Matcher::vector_size_supported(T_FLOAT,2)) { // For VecD we need dual alignment and 8 bytes (2 slots) for spills. // RA guarantees such alignment since it is needed for Double and Long values. *idealreg2spillmask[Op_VecD] = *idealreg2regmask[Op_VecD]; idealreg2spillmask[Op_VecD]->OR(aligned_stack_mask); + } else { + *idealreg2spillmask[Op_VecD] = RegMask::Empty; } + if (Matcher::vector_size_supported(T_FLOAT,4)) { // For VecX we need quadro alignment and 16 bytes (4 slots) for spills. // @@ -544,7 +564,10 @@ void Matcher::init_first_stack_mask() { assert(aligned_stack_mask.is_AllStack(), "should be infinite stack"); *idealreg2spillmask[Op_VecX] = *idealreg2regmask[Op_VecX]; idealreg2spillmask[Op_VecX]->OR(aligned_stack_mask); + } else { + *idealreg2spillmask[Op_VecX] = RegMask::Empty; } + if (Matcher::vector_size_supported(T_FLOAT,8)) { // For VecY we need octo alignment and 32 bytes (8 slots) for spills. OptoReg::Name in = OptoReg::add(_in_arg_limit, -1); @@ -556,7 +579,10 @@ void Matcher::init_first_stack_mask() { assert(aligned_stack_mask.is_AllStack(), "should be infinite stack"); *idealreg2spillmask[Op_VecY] = *idealreg2regmask[Op_VecY]; idealreg2spillmask[Op_VecY]->OR(aligned_stack_mask); + } else { + *idealreg2spillmask[Op_VecY] = RegMask::Empty; } + if (Matcher::vector_size_supported(T_FLOAT,16)) { // For VecZ we need enough alignment and 64 bytes (16 slots) for spills. OptoReg::Name in = OptoReg::add(_in_arg_limit, -1); @@ -568,6 +594,8 @@ void Matcher::init_first_stack_mask() { assert(aligned_stack_mask.is_AllStack(), "should be infinite stack"); *idealreg2spillmask[Op_VecZ] = *idealreg2regmask[Op_VecZ]; idealreg2spillmask[Op_VecZ]->OR(aligned_stack_mask); + } else { + *idealreg2spillmask[Op_VecZ] = RegMask::Empty; } if (Matcher::supports_scalable_vector()) { @@ -622,6 +650,13 @@ void Matcher::init_first_stack_mask() { *idealreg2debugmask [Op_RegD] = *idealreg2spillmask[Op_RegD]; *idealreg2debugmask [Op_RegP] = *idealreg2spillmask[Op_RegP]; + *idealreg2debugmask [Op_VecA] = *idealreg2spillmask[Op_VecA]; + *idealreg2debugmask [Op_VecS] = *idealreg2spillmask[Op_VecS]; + *idealreg2debugmask [Op_VecD] = *idealreg2spillmask[Op_VecD]; + *idealreg2debugmask [Op_VecX] = *idealreg2spillmask[Op_VecX]; + *idealreg2debugmask [Op_VecY] = *idealreg2spillmask[Op_VecY]; + *idealreg2debugmask [Op_VecZ] = *idealreg2spillmask[Op_VecZ]; + *idealreg2mhdebugmask[Op_RegN] = *idealreg2spillmask[Op_RegN]; *idealreg2mhdebugmask[Op_RegI] = *idealreg2spillmask[Op_RegI]; *idealreg2mhdebugmask[Op_RegL] = *idealreg2spillmask[Op_RegL]; @@ -629,6 +664,13 @@ void Matcher::init_first_stack_mask() { *idealreg2mhdebugmask[Op_RegD] = *idealreg2spillmask[Op_RegD]; *idealreg2mhdebugmask[Op_RegP] = *idealreg2spillmask[Op_RegP]; + *idealreg2mhdebugmask[Op_VecA] = *idealreg2spillmask[Op_VecA]; + *idealreg2mhdebugmask[Op_VecS] = *idealreg2spillmask[Op_VecS]; + *idealreg2mhdebugmask[Op_VecD] = *idealreg2spillmask[Op_VecD]; + *idealreg2mhdebugmask[Op_VecX] = *idealreg2spillmask[Op_VecX]; + *idealreg2mhdebugmask[Op_VecY] = *idealreg2spillmask[Op_VecY]; + *idealreg2mhdebugmask[Op_VecZ] = *idealreg2spillmask[Op_VecZ]; + // Prevent stub compilations from attempting to reference // callee-saved (SOE) registers from debug info bool exclude_soe = !Compile::current()->is_method_compilation(); @@ -642,12 +684,26 @@ void Matcher::init_first_stack_mask() { idealreg2debugmask[Op_RegD]->SUBTRACT(*caller_save_mask); idealreg2debugmask[Op_RegP]->SUBTRACT(*caller_save_mask); + idealreg2debugmask[Op_VecA]->SUBTRACT(*caller_save_mask); + idealreg2debugmask[Op_VecS]->SUBTRACT(*caller_save_mask); + idealreg2debugmask[Op_VecD]->SUBTRACT(*caller_save_mask); + idealreg2debugmask[Op_VecX]->SUBTRACT(*caller_save_mask); + idealreg2debugmask[Op_VecY]->SUBTRACT(*caller_save_mask); + idealreg2debugmask[Op_VecZ]->SUBTRACT(*caller_save_mask); + idealreg2mhdebugmask[Op_RegN]->SUBTRACT(*mh_caller_save_mask); idealreg2mhdebugmask[Op_RegI]->SUBTRACT(*mh_caller_save_mask); idealreg2mhdebugmask[Op_RegL]->SUBTRACT(*mh_caller_save_mask); idealreg2mhdebugmask[Op_RegF]->SUBTRACT(*mh_caller_save_mask); idealreg2mhdebugmask[Op_RegD]->SUBTRACT(*mh_caller_save_mask); idealreg2mhdebugmask[Op_RegP]->SUBTRACT(*mh_caller_save_mask); + + idealreg2mhdebugmask[Op_VecA]->SUBTRACT(*mh_caller_save_mask); + idealreg2mhdebugmask[Op_VecS]->SUBTRACT(*mh_caller_save_mask); + idealreg2mhdebugmask[Op_VecD]->SUBTRACT(*mh_caller_save_mask); + idealreg2mhdebugmask[Op_VecX]->SUBTRACT(*mh_caller_save_mask); + idealreg2mhdebugmask[Op_VecY]->SUBTRACT(*mh_caller_save_mask); + idealreg2mhdebugmask[Op_VecZ]->SUBTRACT(*mh_caller_save_mask); } //---------------------------is_save_on_entry---------------------------------- @@ -1195,6 +1251,7 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) { is_method_handle_invoke = call_java->is_method_handle_invoke(); mcall_java->_method_handle_invoke = is_method_handle_invoke; mcall_java->_override_symbolic_info = call_java->override_symbolic_info(); + mcall_java->_arg_escape = call_java->arg_escape(); if (is_method_handle_invoke) { C->set_has_method_handle_invokes(true); } @@ -1219,6 +1276,7 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) { msfpt = mn->as_MachSafePoint(); cnt = TypeFunc::Parms; } + msfpt->_has_ea_local_in_scope = sfpt->has_ea_local_in_scope(); // Advertise the correct memory effects (for anti-dependence computation). msfpt->set_adr_type(sfpt->adr_type()); @@ -1953,7 +2011,6 @@ bool Matcher::is_vshift_con_pattern(Node *n, Node *m) { return false; } - bool Matcher::clone_node(Node* n, Node* m, Matcher::MStack& mstack) { // Must clone all producers of flags, or we will not match correctly. // Suppose a compare setting int-flags is shared (e.g., a switch-tree) @@ -2308,8 +2365,28 @@ void Matcher::find_shared_post_visit(Node* n, uint opcode) { n->del_req(3); break; } + case Op_VectorBlend: + case Op_VectorInsert: { + Node* pair = new BinaryNode(n->in(1), n->in(2)); + n->set_req(1, pair); + n->set_req(2, n->in(3)); + n->del_req(3); + break; + } + case Op_StoreVectorScatter: { + Node* pair = new BinaryNode(n->in(MemNode::ValueIn), n->in(MemNode::ValueIn+1)); + n->set_req(MemNode::ValueIn, pair); + n->del_req(MemNode::ValueIn+1); + break; + } + case Op_VectorMaskCmp: { + n->set_req(1, new BinaryNode(n->in(1), n->in(2))); + n->set_req(2, n->in(3)); + n->del_req(3); + break; default: break; + } } } diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp index c211557eacb..5409dc09bb1 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.hpp @@ -345,6 +345,9 @@ class Matcher : public PhaseTransform { // Vector ideal reg static const uint vector_ideal_reg(int len); + // Does the CPU supports vector variable shift instructions? + static bool supports_vector_variable_shifts(void); + // CPU supports misaligned vectors store/load. static const bool misaligned_vectors_ok(); @@ -415,13 +418,6 @@ class Matcher : public PhaseTransform { // The Method-klass-holder may be passed in the inline_cache_reg // and then expanded into the inline_cache_reg and a method_ptr register - static OptoReg::Name interpreter_method_reg(); - static int interpreter_method_reg_encode(); - - static OptoReg::Name compiler_method_reg(); - static const RegMask &compiler_method_reg_mask(); - static int compiler_method_reg_encode(); - // Interpreter's Frame Pointer Register static OptoReg::Name interpreter_frame_pointer_reg(); @@ -532,10 +528,6 @@ class Matcher : public PhaseTransform { DEBUG_ONLY( bool verify_after_postselect_cleanup(); ) public: - // Perform a platform dependent implicit null fixup. This is needed - // on windows95 to take care of some unusual register constraints. - void pd_implicit_null_fixup(MachNode *load, uint idx); - // Advertise here if the CPU requires explicit rounding operations to implement strictfp mode. static const bool strict_fp_requires_explicit_rounding; diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index ac43a474880..d62c187ccb0 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -641,7 +641,8 @@ Node* MemNode::find_previous_store(PhaseTransform* phase) { } if (st_offset != offset && st_offset != Type::OffsetBot) { - const int MAX_STORE = BytesPerLong; + const int MAX_STORE = MAX2(BytesPerLong, (int)MaxVectorSize); + assert(mem->as_Store()->memory_size() <= MAX_STORE, ""); if (st_offset >= offset + size_in_bytes || st_offset <= offset - MAX_STORE || st_offset <= offset - mem->as_Store()->memory_size()) { @@ -1073,7 +1074,7 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseTransform* phase) const { if (st->is_Store()) { Node* st_adr = st->in(MemNode::Address); - if (!phase->eqv(st_adr, ld_adr)) { + if (st_adr != ld_adr) { // Try harder before giving up. Unify base pointers with casts (e.g., raw/non-raw pointers). intptr_t st_off = 0; Node* st_base = AddPNode::Ideal_base_and_offset(st_adr, phase, st_off); @@ -1111,11 +1112,16 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseTransform* phase) const { // (This is one of the few places where a generic PhaseTransform // can create new nodes. Think of it as lazily manifesting // virtually pre-existing constants.) - if (ReduceBulkZeroing || find_array_copy_clone(phase, ld_alloc, in(MemNode::Memory)) == NULL) { - // If ReduceBulkZeroing is disabled, we need to check if the allocation does not belong to an - // ArrayCopyNode clone. If it does, then we cannot assume zero since the initialization is done - // by the ArrayCopyNode. - return phase->zerocon(memory_type()); + if (memory_type() != T_VOID) { + if (ReduceBulkZeroing || find_array_copy_clone(phase, ld_alloc, in(MemNode::Memory)) == NULL) { + // If ReduceBulkZeroing is disabled, we need to check if the allocation does not belong to an + // ArrayCopyNode clone. If it does, then we cannot assume zero since the initialization is done + // by the ArrayCopyNode. + return phase->zerocon(memory_type()); + } + } else { + // TODO: materialize all-zero vector constant + assert(!isa_Load() || as_Load()->type()->isa_vect(), ""); } } @@ -1262,6 +1268,59 @@ Node* LoadNode::convert_to_signed_load(PhaseGVN& gvn) { is_unaligned_access(), is_mismatched_access()); } +bool LoadNode::has_reinterpret_variant(const Type* rt) { + BasicType bt = rt->basic_type(); + switch (Opcode()) { + case Op_LoadI: return (bt == T_FLOAT); + case Op_LoadL: return (bt == T_DOUBLE); + case Op_LoadF: return (bt == T_INT); + case Op_LoadD: return (bt == T_LONG); + + default: return false; + } +} + +Node* LoadNode::convert_to_reinterpret_load(PhaseGVN& gvn, const Type* rt) { + BasicType bt = rt->basic_type(); + assert(has_reinterpret_variant(rt), "no reinterpret variant: %s %s", Name(), type2name(bt)); + bool is_mismatched = is_mismatched_access(); + const TypeRawPtr* raw_type = gvn.type(in(MemNode::Memory))->isa_rawptr(); + if (raw_type == NULL) { + is_mismatched = true; // conservatively match all non-raw accesses as mismatched + } + return LoadNode::make(gvn, in(MemNode::Control), in(MemNode::Memory), in(MemNode::Address), + raw_adr_type(), rt, bt, _mo, _control_dependency, + is_unaligned_access(), is_mismatched); +} + +bool StoreNode::has_reinterpret_variant(const Type* vt) { + BasicType bt = vt->basic_type(); + switch (Opcode()) { + case Op_StoreI: return (bt == T_FLOAT); + case Op_StoreL: return (bt == T_DOUBLE); + case Op_StoreF: return (bt == T_INT); + case Op_StoreD: return (bt == T_LONG); + + default: return false; + } +} + +Node* StoreNode::convert_to_reinterpret_store(PhaseGVN& gvn, Node* val, const Type* vt) { + BasicType bt = vt->basic_type(); + assert(has_reinterpret_variant(vt), "no reinterpret variant: %s %s", Name(), type2name(bt)); + StoreNode* st = StoreNode::make(gvn, in(MemNode::Control), in(MemNode::Memory), in(MemNode::Address), raw_adr_type(), val, bt, _mo); + + bool is_mismatched = is_mismatched_access(); + const TypeRawPtr* raw_type = gvn.type(in(MemNode::Memory))->isa_rawptr(); + if (raw_type == NULL) { + is_mismatched = true; // conservatively match all non-raw accesses as mismatched + } + if (is_mismatched) { + st->set_mismatched_access(); + } + return st; +} + // We're loading from an object which has autobox behaviour. // If this object is result of a valueOf call we'll have a phi // merging a newly allocated object and a load from the cache. @@ -1464,7 +1523,7 @@ Node *LoadNode::split_through_phi(PhaseGVN *phase) { // Do nothing here if Identity will find a value // (to avoid infinite chain of value phis generation). - if (!phase->eqv(this, this->Identity(phase))) { + if (this != Identity(phase)) { return NULL; } @@ -2542,6 +2601,7 @@ Node *StoreNode::Ideal(PhaseGVN *phase, bool can_reshape) { Node* mem = in(MemNode::Memory); Node* address = in(MemNode::Address); + Node* value = in(MemNode::ValueIn); // Back-to-back stores to same address? Fold em up. Generally // unsafe if I have intervening uses... Also disallowed for StoreCM // since they must follow each StoreP operation. Redundant StoreCMs @@ -2561,6 +2621,8 @@ Node *StoreNode::Ideal(PhaseGVN *phase, bool can_reshape) { assert(Opcode() == st->Opcode() || st->Opcode() == Op_StoreVector || Opcode() == Op_StoreVector || + st->Opcode() == Op_StoreVectorScatter || + Opcode() == Op_StoreVectorScatter || phase->C->get_alias_index(adr_type()) == Compile::AliasIdxRaw || (Opcode() == Op_StoreL && st->Opcode() == Op_StoreI) || // expanded ClearArrayNode (Opcode() == Op_StoreI && st->Opcode() == Op_StoreL) || // initialization by arraycopy @@ -2603,6 +2665,19 @@ Node *StoreNode::Ideal(PhaseGVN *phase, bool can_reshape) { } } + // Fold reinterpret cast into memory operation: + // StoreX mem (MoveY2X v) => StoreY mem v + if (value->is_Move()) { + const Type* vt = value->in(1)->bottom_type(); + if (has_reinterpret_variant(vt)) { + if (phase->C->post_loop_opts_phase()) { + return convert_to_reinterpret_store(*phase, value->in(1), vt); + } else { + phase->C->record_for_post_loop_opts_igvn(this); // attempt the transformation once loop opts are over + } + } + } + return NULL; // No further progress } @@ -2662,7 +2737,7 @@ Node* StoreNode::Identity(PhaseGVN* phase) { // Steps (a), (b): Walk past independent stores to find an exact match. if (prev_mem != NULL) { Node* prev_val = can_see_stored_value(prev_mem, phase); - if (prev_val != NULL && phase->eqv(prev_val, val)) { + if (prev_val != NULL && prev_val == val) { // prev_val and val might differ by a cast; it would be good // to keep the more informative of the two. result = mem; @@ -3744,7 +3819,7 @@ intptr_t InitializeNode::can_capture_store(StoreNode* st, PhaseGVN* phase, bool int InitializeNode::captured_store_insertion_point(intptr_t start, int size_in_bytes, PhaseTransform* phase) { - const int FAIL = 0, MAX_STORE = BytesPerLong; + const int FAIL = 0, MAX_STORE = MAX2(BytesPerLong, (int)MaxVectorSize); if (is_complete()) return FAIL; // arraycopy got here first; punt @@ -3774,6 +3849,7 @@ int InitializeNode::captured_store_insertion_point(intptr_t start, } return -(int)i; // not found; here is where to put it } else if (st_off < start) { + assert(st->as_Store()->memory_size() <= MAX_STORE, ""); if (size_in_bytes != 0 && start < st_off + MAX_STORE && start < st_off + st->as_Store()->memory_size()) { diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp index 413c64e7604..8899a8f48f6 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -282,6 +282,9 @@ class LoadNode : public MemNode { Node* convert_to_unsigned_load(PhaseGVN& gvn); Node* convert_to_signed_load(PhaseGVN& gvn); + bool has_reinterpret_variant(const Type* rt); + Node* convert_to_reinterpret_load(PhaseGVN& gvn, const Type* rt); + void pin() { _control_dependency = Pinned; } bool has_unknown_control_dependency() const { return _control_dependency == UnknownControl; } @@ -634,6 +637,9 @@ class StoreNode : public MemNode { // have all possible loads of the value stored been optimized away? bool value_never_loaded(PhaseTransform *phase) const; + bool has_reinterpret_variant(const Type* vt); + Node* convert_to_reinterpret_store(PhaseGVN& gvn, Node* val, const Type* vt); + MemBarNode* trailing_membar() const; }; diff --git a/src/hotspot/share/opto/movenode.cpp b/src/hotspot/share/opto/movenode.cpp index f7e208d8213..f9b887c3173 100644 --- a/src/hotspot/share/opto/movenode.cpp +++ b/src/hotspot/share/opto/movenode.cpp @@ -78,9 +78,9 @@ Node *CMoveNode::Ideal(PhaseGVN *phase, bool can_reshape) { if( in(0) && remove_dead_region(phase, can_reshape) ) return this; // Don't bother trying to transform a dead node if( in(0) && in(0)->is_top() ) return NULL; - assert( !phase->eqv(in(Condition), this) && - !phase->eqv(in(IfFalse), this) && - !phase->eqv(in(IfTrue), this), "dead loop in CMoveNode::Ideal" ); + assert(in(Condition) != this && + in(IfFalse) != this && + in(IfTrue) != this, "dead loop in CMoveNode::Ideal" ); if( phase->type(in(Condition)) == Type::TOP ) return NULL; // return NULL when Condition is dead @@ -98,11 +98,9 @@ Node *CMoveNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Helper function to check for CMOVE identity. Shared with PhiNode::Identity Node *CMoveNode::is_cmove_id( PhaseTransform *phase, Node *cmp, Node *t, Node *f, BoolNode *b ) { // Check for Cmp'ing and CMove'ing same values - if( (phase->eqv(cmp->in(1),f) && - phase->eqv(cmp->in(2),t)) || - // Swapped Cmp is OK - (phase->eqv(cmp->in(2),f) && - phase->eqv(cmp->in(1),t)) ) { + if ((cmp->in(1) == f && cmp->in(2) == t) || + // Swapped Cmp is OK + (cmp->in(2) == f && cmp->in(1) == t)) { // Give up this identity check for floating points because it may choose incorrect // value around 0.0 and -0.0 if ( cmp->Opcode()==Op_CmpF || cmp->Opcode()==Op_CmpD ) @@ -122,12 +120,16 @@ Node *CMoveNode::is_cmove_id( PhaseTransform *phase, Node *cmp, Node *t, Node *f // Conditional-move is an identity if both inputs are the same, or the test // true or false. Node* CMoveNode::Identity(PhaseGVN* phase) { - if( phase->eqv(in(IfFalse),in(IfTrue)) ) // C-moving identical inputs? - return in(IfFalse); // Then it doesn't matter - if( phase->type(in(Condition)) == TypeInt::ZERO ) - return in(IfFalse); // Always pick left(false) input - if( phase->type(in(Condition)) == TypeInt::ONE ) - return in(IfTrue); // Always pick right(true) input + // C-moving identical inputs? + if (in(IfFalse) == in(IfTrue)) { + return in(IfFalse); // Then it doesn't matter + } + if (phase->type(in(Condition)) == TypeInt::ZERO) { + return in(IfFalse); // Always pick left(false) input + } + if (phase->type(in(Condition)) == TypeInt::ONE) { + return in(IfTrue); // Always pick right(true) input + } // Check for CMove'ing a constant after comparing against the constant. // Happens all the time now, since if we compare equality vs a constant in @@ -352,6 +354,36 @@ Node *CMoveDNode::Ideal(PhaseGVN *phase, bool can_reshape) { return abs; } +//------------------------------MoveNode------------------------------------------ + +Node* MoveNode::Ideal(PhaseGVN* phase, bool can_reshape) { + if (can_reshape) { + // Fold reinterpret cast into memory operation: + // MoveX2Y (LoadX mem) => LoadY mem + LoadNode* ld = in(1)->isa_Load(); + if (ld != NULL && (ld->outcnt() == 1)) { // replace only + const Type* rt = bottom_type(); + if (ld->has_reinterpret_variant(rt)) { + if (phase->C->post_loop_opts_phase()) { + return ld->convert_to_reinterpret_load(*phase, rt); + } else { + phase->C->record_for_post_loop_opts_igvn(this); // attempt the transformation once loop opts are over + } + } + } + } + return NULL; +} + +Node* MoveNode::Identity(PhaseGVN* phase) { + if (in(1)->is_Move()) { + // Back-to-back moves: MoveX2Y (MoveY2X v) => v + assert(bottom_type() == in(1)->in(1)->bottom_type(), "sanity"); + return in(1)->in(1); + } + return this; +} + //------------------------------Value------------------------------------------ const Type* MoveL2DNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); @@ -363,6 +395,14 @@ const Type* MoveL2DNode::Value(PhaseGVN* phase) const { return TypeD::make( v.get_jdouble() ); } +//------------------------------Identity---------------------------------------- +Node* MoveL2DNode::Identity(PhaseGVN* phase) { + if (in(1)->Opcode() == Op_MoveD2L) { + return in(1)->in(1); + } + return this; +} + //------------------------------Value------------------------------------------ const Type* MoveI2FNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); @@ -374,6 +414,14 @@ const Type* MoveI2FNode::Value(PhaseGVN* phase) const { return TypeF::make( v.get_jfloat() ); } +//------------------------------Identity---------------------------------------- +Node* MoveI2FNode::Identity(PhaseGVN* phase) { + if (in(1)->Opcode() == Op_MoveF2I) { + return in(1)->in(1); + } + return this; +} + //------------------------------Value------------------------------------------ const Type* MoveF2INode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); @@ -385,6 +433,14 @@ const Type* MoveF2INode::Value(PhaseGVN* phase) const { return TypeInt::make( v.get_jint() ); } +//------------------------------Identity---------------------------------------- +Node* MoveF2INode::Identity(PhaseGVN* phase) { + if (in(1)->Opcode() == Op_MoveI2F) { + return in(1)->in(1); + } + return this; +} + //------------------------------Value------------------------------------------ const Type* MoveD2LNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); @@ -396,6 +452,14 @@ const Type* MoveD2LNode::Value(PhaseGVN* phase) const { return TypeLong::make( v.get_jlong() ); } +//------------------------------Identity---------------------------------------- +Node* MoveD2LNode::Identity(PhaseGVN* phase) { + if (in(1)->Opcode() == Op_MoveL2D) { + return in(1)->in(1); + } + return this; +} + #ifndef PRODUCT //----------------------------BinaryNode--------------------------------------- // The set of related nodes for a BinaryNode is all data inputs and all outputs diff --git a/src/hotspot/share/opto/movenode.hpp b/src/hotspot/share/opto/movenode.hpp index ecbccaef0a5..1e8b1b2489b 100644 --- a/src/hotspot/share/opto/movenode.hpp +++ b/src/hotspot/share/opto/movenode.hpp @@ -98,40 +98,55 @@ class CMoveNNode : public CMoveNode { }; // -class MoveI2FNode : public Node { +class MoveNode : public Node { + protected: + MoveNode(Node* value) : Node(NULL, value) { + init_class_id(Class_Move); + } + + public: + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + virtual Node* Identity(PhaseGVN* phase); +}; + +class MoveI2FNode : public MoveNode { public: - MoveI2FNode( Node *value ) : Node(0,value) {} + MoveI2FNode(Node* value) : MoveNode(value) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return Type::FLOAT; } + virtual const Type* bottom_type() const { return Type::FLOAT; } virtual uint ideal_reg() const { return Op_RegF; } virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); }; -class MoveL2DNode : public Node { +class MoveL2DNode : public MoveNode { public: - MoveL2DNode( Node *value ) : Node(0,value) {} + MoveL2DNode(Node* value) : MoveNode(value) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return Type::DOUBLE; } + virtual const Type* bottom_type() const { return Type::DOUBLE; } virtual uint ideal_reg() const { return Op_RegD; } virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); }; -class MoveF2INode : public Node { +class MoveF2INode : public MoveNode { public: - MoveF2INode( Node *value ) : Node(0,value) {} + MoveF2INode(Node* value) : MoveNode(value) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual const Type* bottom_type() const { return TypeInt::INT; } virtual uint ideal_reg() const { return Op_RegI; } virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); }; -class MoveD2LNode : public Node { +class MoveD2LNode : public MoveNode { public: - MoveD2LNode( Node *value ) : Node(0,value) {} + MoveD2LNode(Node* value) : MoveNode(value) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return TypeLong::LONG; } + virtual const Type* bottom_type() const { return TypeLong::LONG; } virtual uint ideal_reg() const { return Op_RegL; } virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); }; //------------------------------BinaryNode------------------------------------- diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index 3df3f7fa040..e1db42be167 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -87,12 +87,13 @@ Node *MulNode::Ideal(PhaseGVN *phase, bool can_reshape) { Node *mul1 = in(1); #ifdef ASSERT // Check for dead loop - int op1 = mul1->Opcode(); - if( phase->eqv( mul1, this ) || phase->eqv( in(2), this ) || - ( ( op1 == mul_opcode() || op1 == add_opcode() ) && - ( phase->eqv( mul1->in(1), this ) || phase->eqv( mul1->in(2), this ) || - phase->eqv( mul1->in(1), mul1 ) || phase->eqv( mul1->in(2), mul1 ) ) ) ) + int op1 = mul1->Opcode(); + if ((mul1 == this) || (in(2) == this) || + ((op1 == mul_opcode() || op1 == add_opcode()) && + ((mul1->in(1) == this) || (mul1->in(2) == this) || + (mul1->in(1) == mul1) || (mul1->in(2) == mul1)))) { assert(false, "dead loop in MulNode::Ideal"); + } #endif if( mul1->Opcode() == mul_opcode() ) { // Left input is a multiply? @@ -436,7 +437,9 @@ const Type *AndINode::mul_ring( const Type *t0, const Type *t1 ) const { Node* AndINode::Identity(PhaseGVN* phase) { // x & x => x - if (phase->eqv(in(1), in(2))) return in(1); + if (in(1) == in(2)) { + return in(1); + } Node* in1 = in(1); uint op = in1->Opcode(); @@ -558,7 +561,9 @@ const Type *AndLNode::mul_ring( const Type *t0, const Type *t1 ) const { Node* AndLNode::Identity(PhaseGVN* phase) { // x & x => x - if (phase->eqv(in(1), in(2))) return in(1); + if (in(1) == in(2)) { + return in(1); + } Node *usr = in(1); const TypeLong *t2 = phase->type( in(2) )->isa_long(); diff --git a/src/hotspot/share/opto/mulnode.hpp b/src/hotspot/share/opto/mulnode.hpp index fce6e02825e..b6d29f21923 100644 --- a/src/hotspot/share/opto/mulnode.hpp +++ b/src/hotspot/share/opto/mulnode.hpp @@ -259,6 +259,25 @@ class RShiftLNode : public Node { virtual uint ideal_reg() const { return Op_RegL; } }; +//------------------------------URShiftBNode----------------------------------- +// Logical shift right +class URShiftBNode : public Node { +public: + URShiftBNode( Node *in1, Node *in2 ) : Node(0,in1,in2) { + ShouldNotReachHere(); // only vector variant is used + } + virtual int Opcode() const; +}; + +//------------------------------URShiftSNode----------------------------------- +// Logical shift right +class URShiftSNode : public Node { +public: + URShiftSNode( Node *in1, Node *in2 ) : Node(0,in1,in2) { + ShouldNotReachHere(); // only vector variant is used + } + virtual int Opcode() const; +}; //------------------------------URShiftINode----------------------------------- // Logical shift right diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 635f5f04e35..7b854a2192b 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -515,20 +515,19 @@ Node *Node::clone() const { n->_in[i] = x; if (x != NULL) x->add_out(n); } - if (is_macro()) + if (is_macro()) { C->add_macro_node(n); - if (is_expensive()) + } + if (is_expensive()) { C->add_expensive_node(n); - BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); - bs->register_potential_barrier_node(n); - // If the cloned node is a range check dependent CastII, add it to the list. - CastIINode* cast = n->isa_CastII(); - if (cast != NULL && cast->has_range_check()) { - C->add_range_check_cast(cast); } - if (n->Opcode() == Op_Opaque4) { - C->add_opaque4_node(n); + if (for_post_loop_opts_igvn()) { + // Don't add cloned node to Compile::_for_post_loop_opts_igvn list automatically. + // If it is applicable, it will happen anyway when the cloned node is registered with IGVN. + n->remove_flag(Node::NodeFlags::Flag_for_post_loop_opts_igvn); } + BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); + bs->register_potential_barrier_node(n); n->set_idx(C->next_unique()); // Get new unique index as well debug_only( n->verify_construction() ); @@ -582,11 +581,17 @@ void Node::setup_is_top() { //------------------------------~Node------------------------------------------ // Fancy destructor; eagerly attempt to reclaim Node numberings and storage -void Node::destruct() { - // Eagerly reclaim unique Node numberings - Compile* compile = Compile::current(); +void Node::destruct(PhaseValues* phase) { + Compile* compile = (phase != NULL) ? phase->C : Compile::current(); + if (phase != NULL && phase->is_IterGVN()) { + phase->is_IterGVN()->_worklist.remove(this); + } + // If this is the most recently created node, reclaim its index. Otherwise, + // record the node as dead to keep liveness information accurate. if ((uint)_idx+1 == compile->unique()) { compile->set_unique(compile->unique()-1); + } else { + compile->record_dead_node(_idx); } // Clear debug info: Node_Notes* nn = compile->node_notes_at(_idx); @@ -632,12 +637,8 @@ void Node::destruct() { if (is_expensive()) { compile->remove_expensive_node(this); } - CastIINode* cast = isa_CastII(); - if (cast != NULL && cast->has_range_check()) { - compile->remove_range_check_cast(cast); - } - if (Opcode() == Op_Opaque4) { - compile->remove_opaque4_node(this); + if (for_post_loop_opts_igvn()) { + compile->remove_from_post_loop_opts_igvn(this); } if (is_SafePoint()) { @@ -891,33 +892,37 @@ int Node::replace_edges_in_range(Node* old, Node* neww, int start, int end) { //-------------------------disconnect_inputs----------------------------------- // NULL out all inputs to eliminate incoming Def-Use edges. -// Return the number of edges between 'n' and 'this' -int Node::disconnect_inputs(Node *n, Compile* C) { - int edges_to_n = 0; - - uint cnt = req(); - for( uint i = 0; i < cnt; ++i ) { - if( in(i) == 0 ) continue; - if( in(i) == n ) ++edges_to_n; - set_req(i, NULL); +void Node::disconnect_inputs(Compile* C) { + // the layout of Node::_in + // r: a required input, null is allowed + // p: a precedence, null values are all at the end + // ----------------------------------- + // |r|...|r|p|...|p|null|...|null| + // | | + // req() len() + // ----------------------------------- + for (uint i = 0; i < req(); ++i) { + if (in(i) != nullptr) { + set_req(i, nullptr); + } } + // Remove precedence edges if any exist // Note: Safepoints may have precedence edges, even during parsing - if( (req() != len()) && (in(req()) != NULL) ) { - uint max = len(); - for( uint i = 0; i < max; ++i ) { - if( in(i) == 0 ) continue; - if( in(i) == n ) ++edges_to_n; - set_prec(i, NULL); - } + for (uint i = len(); i > req(); ) { + rm_prec(--i); // no-op if _in[i] is nullptr } +#ifdef ASSERT + // sanity check + for (uint i = 0; i < len(); ++i) { + assert(_in[i] == nullptr, "disconnect_inputs() failed!"); + } +#endif + // Node::destruct requires all out edges be deleted first // debug_only(destruct();) // no reuse benefit expected - if (edges_to_n == 0) { - C->record_dead_node(_idx); - } - return edges_to_n; + C->record_dead_node(_idx); } //-----------------------------uncast--------------------------------------- @@ -1048,7 +1053,7 @@ bool Node::verify_jvms(const JVMState* using_jvms) const { //------------------------------init_NodeProperty------------------------------ void Node::init_NodeProperty() { assert(_max_classes <= max_juint, "too many NodeProperty classes"); - assert(max_flags() <= max_jushort, "too many NodeProperty flags"); + assert(max_flags() <= max_juint, "too many NodeProperty flags"); } //-----------------------------max_flags--------------------------------------- @@ -1397,7 +1402,6 @@ static void kill_dead_code( Node *dead, PhaseIterGVN *igvn ) { // Done with outputs. igvn->hash_delete(dead); igvn->_worklist.remove(dead); - igvn->C->remove_modified_node(dead); igvn->set_type(dead, Type::TOP); if (dead->is_macro()) { igvn->C->remove_macro_node(dead); @@ -1405,12 +1409,8 @@ static void kill_dead_code( Node *dead, PhaseIterGVN *igvn ) { if (dead->is_expensive()) { igvn->C->remove_expensive_node(dead); } - CastIINode* cast = dead->isa_CastII(); - if (cast != NULL && cast->has_range_check()) { - igvn->C->remove_range_check_cast(cast); - } - if (dead->Opcode() == Op_Opaque4) { - igvn->C->remove_opaque4_node(dead); + if (dead->for_post_loop_opts_igvn()) { + igvn->C->remove_from_post_loop_opts_igvn(dead); } BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); bs->unregister_potential_barrier_node(dead); @@ -1437,6 +1437,7 @@ static void kill_dead_code( Node *dead, PhaseIterGVN *igvn ) { } } } + igvn->C->remove_modified_node(dead); } // (dead->outcnt() == 0) } // while (nstack.size() > 0) for outputs return; @@ -2428,6 +2429,29 @@ void Node::ensure_control_or_add_prec(Node* c) { } } +bool Node::is_dead_loop_safe() const { + if (is_Phi()) { + return true; + } + if (is_Proj() && in(0) == NULL) { + return true; + } + if ((_flags & (Flag_is_dead_loop_safe | Flag_is_Con)) != 0) { + if (!is_Proj()) { + return true; + } + if (in(0)->is_Allocate()) { + return false; + } + // MemNode::can_see_stored_value() peeks through the boxing call + if (in(0)->is_CallStaticJava() && in(0)->as_CallStaticJava()->is_boxing_method()) { + return false; + } + return true; + } + return false; +} + //============================================================================= //------------------------------yank------------------------------------------- // Find and remove diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 899c6cd90ac..d156dd2c454 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -112,6 +112,7 @@ class MemBarNode; class MemBarStoreStoreNode; class MemNode; class MergeMemNode; +class MoveNode; class MulNode; class MultiNode; class MultiBranchNode; @@ -152,7 +153,10 @@ class TypeNode; class UnlockNode; class VectorNode; class LoadVectorNode; +class LoadVectorGatherNode; class StoreVectorNode; +class StoreVectorScatterNode; +class VectorMaskCmpNode; class VectorSet; typedef void (*NFunc)(Node&,void*); extern "C" { @@ -232,7 +236,7 @@ class Node { // Delete is a NOP void operator delete( void *ptr ) {} // Fancy destructor; eagerly attempt to reclaim Node numberings and storage - void destruct(); + void destruct(PhaseValues* phase); // Create a new Node. Required is the number is of inputs required for // semantic correctness. @@ -446,8 +450,7 @@ class Node { int replace_edge(Node* old, Node* neww); int replace_edges_in_range(Node* old, Node* neww, int start, int end); // NULL out all inputs to eliminate incoming Def-Use edges. - // Return the number of edges between 'n' and 'this' - int disconnect_inputs(Node *n, Compile *c); + void disconnect_inputs(Compile* C); // Quickly, return true if and only if I am Compile::current()->top(). bool is_top() const { @@ -517,7 +520,7 @@ class Node { // and cutting input edges of old node. void subsume_by(Node* new_node, Compile* c) { replace_by(new_node); - disconnect_inputs(NULL, c); + disconnect_inputs(c); } void set_req_X( uint i, Node *n, PhaseIterGVN *igvn ); // Find the one non-null required input. RegionNode only @@ -539,7 +542,7 @@ class Node { } if (_in[i] != NULL) _in[i]->del_out((Node *)this); _in[i] = n; - if (n != NULL) n->add_out((Node *)this); + n->add_out((Node *)this); } // Set this node's index, used by cisc_version to replace current node @@ -689,8 +692,10 @@ class Node { DEFINE_CLASS_ID(Mem, Node, 4) DEFINE_CLASS_ID(Load, Mem, 0) DEFINE_CLASS_ID(LoadVector, Load, 0) + DEFINE_CLASS_ID(LoadVectorGather, LoadVector, 0) DEFINE_CLASS_ID(Store, Mem, 1) DEFINE_CLASS_ID(StoreVector, Store, 0) + DEFINE_CLASS_ID(StoreVectorScatter, StoreVector, 0) DEFINE_CLASS_ID(LoadStore, Mem, 2) DEFINE_CLASS_ID(LoadStoreConditional, LoadStore, 0) DEFINE_CLASS_ID(CompareAndSwap, LoadStoreConditional, 0) @@ -715,39 +720,42 @@ class Node { DEFINE_CLASS_ID(Add, Node, 11) DEFINE_CLASS_ID(Mul, Node, 12) DEFINE_CLASS_ID(Vector, Node, 13) + DEFINE_CLASS_ID(VectorMaskCmp, Vector, 0) DEFINE_CLASS_ID(ClearArray, Node, 14) - DEFINE_CLASS_ID(Halt, Node, 15) - DEFINE_CLASS_ID(Opaque1, Node, 16) + DEFINE_CLASS_ID(Halt, Node, 15) + DEFINE_CLASS_ID(Opaque1, Node, 16) + DEFINE_CLASS_ID(Move, Node, 17) - _max_classes = ClassMask_Opaque1 + _max_classes = ClassMask_Move }; #undef DEFINE_CLASS_ID // Flags are sorted by usage frequency. enum NodeFlags { - Flag_is_Copy = 0x01, // should be first bit to avoid shift - Flag_rematerialize = Flag_is_Copy << 1, - Flag_needs_anti_dependence_check = Flag_rematerialize << 1, - Flag_is_macro = Flag_needs_anti_dependence_check << 1, - Flag_is_Con = Flag_is_macro << 1, - Flag_is_cisc_alternate = Flag_is_Con << 1, - Flag_is_dead_loop_safe = Flag_is_cisc_alternate << 1, - Flag_may_be_short_branch = Flag_is_dead_loop_safe << 1, - Flag_avoid_back_to_back_before = Flag_may_be_short_branch << 1, - Flag_avoid_back_to_back_after = Flag_avoid_back_to_back_before << 1, - Flag_has_call = Flag_avoid_back_to_back_after << 1, - Flag_is_reduction = Flag_has_call << 1, - Flag_is_scheduled = Flag_is_reduction << 1, - Flag_has_vector_mask_set = Flag_is_scheduled << 1, - Flag_is_expensive = Flag_has_vector_mask_set << 1, - _last_flag = Flag_is_expensive + Flag_is_Copy = 1 << 0, // should be first bit to avoid shift + Flag_rematerialize = 1 << 1, + Flag_needs_anti_dependence_check = 1 << 2, + Flag_is_macro = 1 << 3, + Flag_is_Con = 1 << 4, + Flag_is_cisc_alternate = 1 << 5, + Flag_is_dead_loop_safe = 1 << 6, + Flag_may_be_short_branch = 1 << 7, + Flag_avoid_back_to_back_before = 1 << 8, + Flag_avoid_back_to_back_after = 1 << 9, + Flag_has_call = 1 << 10, + Flag_is_reduction = 1 << 11, + Flag_is_scheduled = 1 << 12, + Flag_has_vector_mask_set = 1 << 13, + Flag_is_expensive = 1 << 14, + Flag_for_post_loop_opts_igvn = 1 << 15, + _last_flag = Flag_for_post_loop_opts_igvn }; class PD; private: juint _class_id; - jushort _flags; + juint _flags; static juint max_flags(); @@ -768,11 +776,11 @@ class Node { public: const juint class_id() const { return _class_id; } - const jushort flags() const { return _flags; } + const juint flags() const { return _flags; } - void add_flag(jushort fl) { init_flags(fl); } + void add_flag(juint fl) { init_flags(fl); } - void remove_flag(jushort fl) { clear_flag(fl); } + void remove_flag(juint fl) { clear_flag(fl); } // Return a dense integer opcode number virtual int Opcode() const; @@ -786,7 +794,7 @@ class Node { return ((_class_id & ClassMask_##type) == Class_##type); \ } \ type##Node *as_##type() const { \ - assert(is_##type(), "invalid node class"); \ + assert(is_##type(), "invalid node class: %s", Name()); \ return (type##Node*)this; \ } \ type##Node* isa_##type() const { \ @@ -864,6 +872,7 @@ class Node { DEFINE_CLASS_QUERY(MemBar) DEFINE_CLASS_QUERY(MemBarStoreStore) DEFINE_CLASS_QUERY(MergeMem) + DEFINE_CLASS_QUERY(Move) DEFINE_CLASS_QUERY(Mul) DEFINE_CLASS_QUERY(Multi) DEFINE_CLASS_QUERY(MultiBranch) @@ -885,7 +894,10 @@ class Node { DEFINE_CLASS_QUERY(Type) DEFINE_CLASS_QUERY(Vector) DEFINE_CLASS_QUERY(LoadVector) + DEFINE_CLASS_QUERY(LoadVectorGather) DEFINE_CLASS_QUERY(StoreVector) + DEFINE_CLASS_QUERY(StoreVectorScatter) + DEFINE_CLASS_QUERY(VectorMaskCmp) DEFINE_CLASS_QUERY(Unlock) #undef DEFINE_CLASS_QUERY @@ -897,11 +909,7 @@ class Node { bool is_Con () const { return (_flags & Flag_is_Con) != 0; } // The data node which is safe to leave in dead loop during IGVN optimization. - bool is_dead_loop_safe() const { - return is_Phi() || (is_Proj() && in(0) == NULL) || - ((_flags & (Flag_is_dead_loop_safe | Flag_is_Con)) != 0 && - (!is_Proj() || !in(0)->is_Allocate())); - } + bool is_dead_loop_safe() const; // is_Copy() returns copied edge index (0 or 1) uint is_Copy() const { return (_flags & Flag_is_Copy); } @@ -946,6 +954,8 @@ class Node { // Used in lcm to mark nodes that have scheduled bool is_scheduled() const { return (_flags & Flag_is_scheduled) != 0; } + bool for_post_loop_opts_igvn() const { return (_flags & Flag_for_post_loop_opts_igvn) != 0; } + //----------------- Optimization // Get the worst-case Type output for this Node. diff --git a/src/hotspot/share/opto/opaquenode.cpp b/src/hotspot/share/opto/opaquenode.cpp index fd9ad8a57e7..c1b769e2370 100644 --- a/src/hotspot/share/opto/opaquenode.cpp +++ b/src/hotspot/share/opto/opaquenode.cpp @@ -34,14 +34,14 @@ bool Opaque1Node::cmp( const Node &n ) const { } //------------------------------Identity--------------------------------------- -// If _major_progress, then more loop optimizations follow. Do NOT remove -// the opaque Node until no more loop ops can happen. Note the timing of -// _major_progress; it's set in the major loop optimizations THEN comes the -// call to IterGVN and any chance of hitting this code. Hence there's no -// phase-ordering problem with stripping Opaque1 in IGVN followed by some -// more loop optimizations that require it. +// Do NOT remove the opaque Node until no more loop ops can happen. Node* Opaque1Node::Identity(PhaseGVN* phase) { - return phase->C->major_progress() ? this : in(1); + if (phase->C->post_loop_opts_phase()) { + return in(1); + } else { + phase->C->record_for_post_loop_opts_igvn(this); + } + return this; } //============================================================================= @@ -60,6 +60,25 @@ bool Opaque2Node::cmp( const Node &n ) const { return (&n == this); // Always fail except on self } +Node* Opaque4Node::Identity(PhaseGVN* phase) { + if (phase->C->post_loop_opts_phase()) { + // With Opaque4 nodes, the expectation is that the test of input 1 + // is always equal to the constant value of input 2. So we can + // remove the Opaque4 and replace it by input 2. In debug builds, + // leave the non constant test in instead to sanity check that it + // never fails (if it does, that subgraph was constructed so, at + // runtime, a Halt node is executed). +#ifdef ASSERT + return this->in(1); +#else + return this->in(2); +#endif + } else { + phase->C->record_for_post_loop_opts_igvn(this); + } + return this; +} + const Type* Opaque4Node::Value(PhaseGVN* phase) const { return phase->type(in(1)); } diff --git a/src/hotspot/share/opto/opaquenode.hpp b/src/hotspot/share/opto/opaquenode.hpp index c6d27948748..160f814fd2d 100644 --- a/src/hotspot/share/opto/opaquenode.hpp +++ b/src/hotspot/share/opto/opaquenode.hpp @@ -114,12 +114,11 @@ class Opaque3Node : public Opaque2Node { // GraphKit::must_be_not_null(). class Opaque4Node : public Node { public: - Opaque4Node(Compile* C, Node *tst, Node* final_tst) : Node(NULL, tst, final_tst) { - // Put it on the Opaque4 nodes list to be removed after all optimizations - C->add_opaque4_node(this); - } + Opaque4Node(Compile* C, Node *tst, Node* final_tst) : Node(NULL, tst, final_tst) {} + virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeInt::BOOL; } + virtual Node* Identity(PhaseGVN* phase); virtual const Type* Value(PhaseGVN* phase) const; }; diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index d22bcb4e15f..8e78481a491 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -224,6 +224,72 @@ class Scheduling { }; +volatile int C2SafepointPollStubTable::_stub_size = 0; + +Label& C2SafepointPollStubTable::add_safepoint(uintptr_t safepoint_offset) { + C2SafepointPollStub* entry = new (Compile::current()->comp_arena()) C2SafepointPollStub(safepoint_offset); + _safepoints.append(entry); + return entry->_stub_label; +} + +void C2SafepointPollStubTable::emit(CodeBuffer& cb) { + MacroAssembler masm(&cb); + for (int i = _safepoints.length() - 1; i >= 0; i--) { + // Make sure there is enough space in the code buffer + if (cb.insts()->maybe_expand_to_ensure_remaining(PhaseOutput::MAX_inst_size) && cb.blob() == NULL) { + ciEnv::current()->record_failure("CodeCache is full"); + return; + } + + C2SafepointPollStub* entry = _safepoints.at(i); + emit_stub(masm, entry); + } +} + +int C2SafepointPollStubTable::stub_size_lazy() const { + int size = Atomic::load(&_stub_size); + + if (size != 0) { + return size; + } + + Compile* const C = Compile::current(); + BufferBlob* const blob = C->output()->scratch_buffer_blob(); + CodeBuffer cb(blob->content_begin(), C->output()->scratch_buffer_code_size()); + MacroAssembler masm(&cb); + C2SafepointPollStub* entry = _safepoints.at(0); + emit_stub(masm, entry); + size += cb.insts_size(); + + Atomic::store(&_stub_size, size); + + return size; +} + +int C2SafepointPollStubTable::estimate_stub_size() const { + if (_safepoints.length() == 0) { + return 0; + } + + int result = stub_size_lazy() * _safepoints.length(); + +#ifdef ASSERT + Compile* const C = Compile::current(); + BufferBlob* const blob = C->output()->scratch_buffer_blob(); + int size = 0; + + for (int i = _safepoints.length() - 1; i >= 0; i--) { + CodeBuffer cb(blob->content_begin(), C->output()->scratch_buffer_code_size()); + MacroAssembler masm(&cb); + C2SafepointPollStub* entry = _safepoints.at(i); + emit_stub(masm, entry); + size += cb.insts_size(); + } + assert(size == result, "stubs should not have variable size"); +#endif + + return result; +} PhaseOutput::PhaseOutput() : Phase(Phase::Output), @@ -826,6 +892,10 @@ void PhaseOutput::FillLocArray( int idx, MachSafePointNode* sfpt, Node *local, ? Location::int_in_long : Location::normal )); } else if( t->base() == Type::NarrowOop ) { array->append(new_loc_value( C->regalloc(), regnum, Location::narrowoop )); + } else if (t->base() == Type::VectorA || t->base() == Type::VectorS || + t->base() == Type::VectorD || t->base() == Type::VectorX || + t->base() == Type::VectorY || t->base() == Type::VectorZ) { + array->append(new_loc_value( C->regalloc(), regnum, Location::vector )); } else { array->append(new_loc_value( C->regalloc(), regnum, C->regalloc()->is_oop(local) ? Location::oop : Location::normal )); } @@ -933,6 +1003,8 @@ void PhaseOutput::Process_OopMap_Node(MachNode *mach, int current_offset) { int safepoint_pc_offset = current_offset; bool is_method_handle_invoke = false; bool return_oop = false; + bool has_ea_local_in_scope = sfn->_has_ea_local_in_scope; + bool arg_escape = false; // Add the safepoint in the DebugInfoRecorder if( !mach->is_MachCall() ) { @@ -947,6 +1019,7 @@ void PhaseOutput::Process_OopMap_Node(MachNode *mach, int current_offset) { assert(C->has_method_handle_invokes(), "must have been set during call generation"); is_method_handle_invoke = true; } + arg_escape = mcall->as_MachCallJava()->_arg_escape; } // Check if a call returns an object. @@ -1067,7 +1140,10 @@ void PhaseOutput::Process_OopMap_Node(MachNode *mach, int current_offset) { // Now we can describe the scope. methodHandle null_mh; bool rethrow_exception = false; - C->debug_info()->describe_scope(safepoint_pc_offset, null_mh, scope_method, jvms->bci(), jvms->should_reexecute(), rethrow_exception, is_method_handle_invoke, return_oop, locvals, expvals, monvals); + C->debug_info()->describe_scope(safepoint_pc_offset, null_mh, scope_method, jvms->bci(), + jvms->should_reexecute(), rethrow_exception, is_method_handle_invoke, + return_oop, has_ea_local_in_scope, arg_escape, + locvals, expvals, monvals); } // End jvms loop // Mark the end of the scope set. @@ -1235,6 +1311,7 @@ CodeBuffer* PhaseOutput::init_buffer() { BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); stub_req += bs->estimate_stub_size(); + stub_req += safepoint_poll_table()->estimate_stub_size(); // nmethod and CodeBuffer count stubs & constants as part of method's code. // class HandlerImpl is platform-specific and defined in the *.ad files. @@ -1295,10 +1372,10 @@ void PhaseOutput::fill_buffer(CodeBuffer* cb, uint* blk_starts) { uint nblocks = C->cfg()->number_of_blocks(); // Count and start of implicit null check instructions uint inct_cnt = 0; - uint *inct_starts = NEW_RESOURCE_ARRAY(uint, nblocks+1); + uint* inct_starts = NEW_RESOURCE_ARRAY(uint, nblocks+1); // Count and start of calls - uint *call_returns = NEW_RESOURCE_ARRAY(uint, nblocks+1); + uint* call_returns = NEW_RESOURCE_ARRAY(uint, nblocks+1); uint return_offset = 0; int nop_size = (new MachNopNode())->size(C->regalloc()); @@ -1316,7 +1393,7 @@ void PhaseOutput::fill_buffer(CodeBuffer* cb, uint* blk_starts) { // Create an array of unused labels, one for each basic block, if printing is enabled #if defined(SUPPORT_OPTO_ASSEMBLY) - int *node_offsets = NULL; + int* node_offsets = NULL; uint node_offset_limit = C->unique(); if (C->print_assembly()) { @@ -1336,15 +1413,13 @@ void PhaseOutput::fill_buffer(CodeBuffer* cb, uint* blk_starts) { } // Create an array of labels, one for each basic block - Label *blk_labels = NEW_RESOURCE_ARRAY(Label, nblocks+1); - for (uint i=0; i <= nblocks; i++) { + Label* blk_labels = NEW_RESOURCE_ARRAY(Label, nblocks+1); + for (uint i = 0; i <= nblocks; i++) { blk_labels[i].init(); } - // ------------------ // Now fill in the code buffer - Node *delay_slot = NULL; - + Node* delay_slot = NULL; for (uint i = 0; i < nblocks; i++) { Block* block = C->cfg()->get_block(i); _block = block; @@ -1601,11 +1676,12 @@ void PhaseOutput::fill_buffer(CodeBuffer* cb, uint* blk_starts) { node_offsets[n->_idx] = cb->insts_size(); } #endif + assert(!C->failing(), "Should not reach here if failing."); // "Normal" instruction case - DEBUG_ONLY( uint instr_offset = cb->insts_size(); ) + DEBUG_ONLY(uint instr_offset = cb->insts_size()); n->emit(*cb, C->regalloc()); - current_offset = cb->insts_size(); + current_offset = cb->insts_size(); // Above we only verified that there is enough space in the instruction section. // However, the instruction may emit stubs that cause code buffer expansion. @@ -1737,6 +1813,10 @@ void PhaseOutput::fill_buffer(CodeBuffer* cb, uint* blk_starts) { bs->emit_stubs(*cb); if (C->failing()) return; + // Fill in stubs for calling the runtime from safepoint polls. + safepoint_poll_table()->emit(*cb); + if (C->failing()) return; + #ifndef PRODUCT // Information on the size of the method, without the extraneous code Scheduling::increment_method_size(cb->insts_size()); @@ -1788,8 +1868,7 @@ void PhaseOutput::fill_buffer(CodeBuffer* cb, uint* blk_starts) { // be sure to tag this tty output with the compile ID. if (xtty != NULL) { xtty->head("opto_assembly compile_id='%d'%s", C->compile_id(), - C->is_osr_compilation() ? " compile_kind='osr'" : - ""); + C->is_osr_compilation() ? " compile_kind='osr'" : ""); } if (C->method() != NULL) { tty->print_cr("----------------------- MetaData before Compile_id = %d ------------------------", C->compile_id()); diff --git a/src/hotspot/share/opto/output.hpp b/src/hotspot/share/opto/output.hpp index 43f374c73e3..0b42ecfaa74 100644 --- a/src/hotspot/share/opto/output.hpp +++ b/src/hotspot/share/opto/output.hpp @@ -25,11 +25,13 @@ #ifndef SHARE_OPTO_OUTPUT_HPP #define SHARE_OPTO_OUTPUT_HPP +#include "code/debugInfo.hpp" +#include "code/exceptionHandlerTable.hpp" +#include "metaprogramming/enableIf.hpp" #include "opto/ad.hpp" #include "opto/constantTable.hpp" #include "opto/phase.hpp" -#include "code/debugInfo.hpp" -#include "code/exceptionHandlerTable.hpp" +#include "runtime/vm_version.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" @@ -70,6 +72,47 @@ class BufferSizingData { { }; }; +class C2SafepointPollStubTable { +private: + struct C2SafepointPollStub: public ResourceObj { + uintptr_t _safepoint_offset; + Label _stub_label; + Label _trampoline_label; + C2SafepointPollStub(uintptr_t safepoint_offset) : + _safepoint_offset(safepoint_offset), + _stub_label(), + _trampoline_label() {} + }; + + GrowableArray _safepoints; + + static volatile int _stub_size; + + void emit_stub_impl(MacroAssembler& masm, C2SafepointPollStub* entry) const; + + // The selection logic below relieves the need to add dummy files to unsupported platforms. + template + typename EnableIf::type + select_emit_stub(MacroAssembler& masm, C2SafepointPollStub* entry) const { + emit_stub_impl(masm, entry); + } + + template + typename EnableIf::type + select_emit_stub(MacroAssembler& masm, C2SafepointPollStub* entry) const {} + + void emit_stub(MacroAssembler& masm, C2SafepointPollStub* entry) const { + select_emit_stub(masm, entry); + } + + int stub_size_lazy() const; + +public: + Label& add_safepoint(uintptr_t safepoint_offset); + int estimate_stub_size() const; + void emit(CodeBuffer& cb); +}; + class PhaseOutput : public Phase { private: // Instruction bits passed off to the VM @@ -78,6 +121,7 @@ class PhaseOutput : public Phase { int _first_block_size; // Size of unvalidated entry point code / OSR poison code ExceptionHandlerTable _handler_table; // Table of native-code exception handlers ImplicitExceptionTable _inc_table; // Table of implicit null checks in native code + C2SafepointPollStubTable _safepoint_poll_table;// Table for safepoint polls OopMapSet* _oop_map_set; // Table of oop maps (one for each safepoint location) BufferBlob* _scratch_buffer_blob; // For temporary code buffers. relocInfo* _scratch_locs_memory; // For temporary code buffers. @@ -126,6 +170,9 @@ class PhaseOutput : public Phase { // Constant table ConstantTable& constant_table() { return _constant_table; } + // Safepoint poll table + C2SafepointPollStubTable* safepoint_poll_table() { return &_safepoint_poll_table; } + // Code emission iterator Block* block() { return _block; } int index() { return _index; } @@ -173,6 +220,7 @@ class PhaseOutput : public Phase { void set_scratch_buffer_blob(BufferBlob* b) { _scratch_buffer_blob = b; } relocInfo* scratch_locs_memory() { return _scratch_locs_memory; } void set_scratch_locs_memory(relocInfo* b) { _scratch_locs_memory = b; } + int scratch_buffer_code_size() { return (address)scratch_locs_memory() - _scratch_buffer_blob->content_begin(); } // emit to scratch blob, report resulting size uint scratch_emit_size(const Node* n); diff --git a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp index a6e7703796a..6166ede33cc 100644 --- a/src/hotspot/share/opto/parse.hpp +++ b/src/hotspot/share/opto/parse.hpp @@ -330,8 +330,6 @@ class Parse : public GraphKit { bool _wrote_volatile; // Did we write a volatile field? bool _wrote_stable; // Did we write a @Stable field? bool _wrote_fields; // Did we write any field? - bool _count_invocations; // update and test invocation counter - bool _method_data_update; // update method data oop Node* _alloc_with_final; // An allocation node with final field // Variables which track Java semantics during bytecode parsing: @@ -377,8 +375,6 @@ class Parse : public GraphKit { void set_wrote_stable(bool z) { _wrote_stable = z; } bool wrote_fields() const { return _wrote_fields; } void set_wrote_fields(bool z) { _wrote_fields = z; } - bool count_invocations() const { return _count_invocations; } - bool method_data_update() const { return _method_data_update; } Node* alloc_with_final() const { return _alloc_with_final; } void set_alloc_with_final(Node* n) { assert((_alloc_with_final == NULL) || (_alloc_with_final == n), "different init objects?"); @@ -500,9 +496,6 @@ class Parse : public GraphKit { // Helper function to uncommon-trap or bailout for non-compilable call-sites bool can_not_compile_call_site(ciMethod *dest_method, ciInstanceKlass *klass); - // Helper function to setup for type-profile based inlining - bool prepare_type_profile_inline(ciInstanceKlass* prof_klass, ciMethod* prof_method); - // Helper functions for type checking bytecodes: void do_checkcast(); void do_instanceof(); @@ -555,9 +548,9 @@ class Parse : public GraphKit { void maybe_add_predicate_after_if(Block* path); IfNode* jump_if_fork_int(Node* a, Node* b, BoolTest::mask mask, float prob, float cnt); Node* jump_if_join(Node* iffalse, Node* iftrue); - void jump_if_true_fork(IfNode *ifNode, int dest_bci_if_true, int prof_table_index, bool unc); - void jump_if_false_fork(IfNode *ifNode, int dest_bci_if_false, int prof_table_index, bool unc); - void jump_if_always_fork(int dest_bci_if_true, int prof_table_index, bool unc); + void jump_if_true_fork(IfNode *ifNode, int dest_bci_if_true, bool unc); + void jump_if_false_fork(IfNode *ifNode, int dest_bci_if_false, bool unc); + void jump_if_always_fork(int dest_bci_if_true, bool unc); friend class SwitchRange; void do_tableswitch(); @@ -567,23 +560,6 @@ class Parse : public GraphKit { void linear_search_switch_ranges(Node* key_val, SwitchRange*& lo, SwitchRange*& hi); void decrement_age(); - // helper functions for methodData style profiling - void test_counter_against_threshold(Node* cnt, int limit); - void increment_and_test_invocation_counter(int limit); - void test_for_osr_md_counter_at(ciMethodData* md, ciProfileData* data, ByteSize offset, int limit); - Node* method_data_addressing(ciMethodData* md, ciProfileData* data, ByteSize offset, Node* idx = NULL, uint stride = 0); - void increment_md_counter_at(ciMethodData* md, ciProfileData* data, ByteSize offset, Node* idx = NULL, uint stride = 0); - void set_md_flag_at(ciMethodData* md, ciProfileData* data, int flag_constant); - - void profile_method_entry(); - void profile_taken_branch(int target_bci, bool force_update = false); - void profile_not_taken_branch(bool force_update = false); - void profile_call(Node* receiver); - void profile_generic_call(); - void profile_receiver_type(Node* receiver); - void profile_ret(int target_bci); - void profile_null_checkcast(); - void profile_switch_case(int table_index); // helper function for call statistics void count_compiled_calls(bool at_method_entry, bool is_inline) PRODUCT_RETURN; diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 2cfce3c0467..73f1c4ab000 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -435,6 +435,7 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses) C->set_parsed_irreducible_loop(true); } #endif + C->set_has_loops(C->has_loops() || method()->has_loops()); if (_expected_uses <= 0) { _prof_factor = 1; @@ -482,9 +483,6 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses) // Accumulate total sum of decompilations, also. C->set_decompile_count(C->decompile_count() + md->decompile_count()); - _count_invocations = C->do_count_invocations(); - _method_data_update = C->do_method_data_update(); - if (log != NULL && method()->has_exception_handlers()) { log->elem("observe that='has_exception_handlers'"); } @@ -1227,10 +1225,6 @@ void Parse::do_method_entry() { // Feed profiling data for parameters to the type system so it can // propagate it as speculative types record_profiled_parameters_for_speculation(); - - if (depth() == 1) { - increment_and_test_invocation_counter(Tier2CompileThreshold); - } } //------------------------------init_blocks------------------------------------ @@ -1599,6 +1593,11 @@ void Parse::merge_new_path(int target_bci) { // Merge the current mapping into the basic block starting at bci // The ex_oop must be pushed on the stack, unlike throw_to_exit. void Parse::merge_exception(int target_bci) { +#ifdef ASSERT + if (target_bci < bci()) { + C->set_exception_backedge(); + } +#endif assert(sp() == 1, "must have only the throw exception on the stack"); Block* target = successor_for_bci(target_bci); if (target == NULL) { handle_missing_successor(target_bci); return; } @@ -2248,23 +2247,7 @@ void Parse::return_current(Node* value) { //------------------------------add_safepoint---------------------------------- void Parse::add_safepoint() { - // See if we can avoid this safepoint. No need for a SafePoint immediately - // after a Call (except Leaf Call) or another SafePoint. - Node *proj = control(); uint parms = TypeFunc::Parms+1; - if( proj->is_Proj() ) { - Node *n0 = proj->in(0); - if( n0->is_Catch() ) { - n0 = n0->in(0)->in(0); - assert( n0->is_Call(), "expect a call here" ); - } - if( n0->is_Call() ) { - if( n0->as_Call()->guaranteed_safepoint() ) - return; - } else if( n0->is_SafePoint() && n0->req() >= parms ) { - return; - } - } // Clear out dead values from the debug info. kill_dead_locals(); diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index 59e9ce7a9dd..6f2ae1af88a 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -232,7 +232,7 @@ Node* Parse::jump_if_join(Node* iffalse, Node* iftrue) { static const int never_reached = INT_MAX; //------------------------------helper for tableswitch------------------------- -void Parse::jump_if_true_fork(IfNode *iff, int dest_bci_if_true, int prof_table_index, bool unc) { +void Parse::jump_if_true_fork(IfNode *iff, int dest_bci_if_true, bool unc) { // True branch, use existing map info { PreserveJVMState pjvms(this); Node *iftrue = _gvn.transform( new IfTrueNode (iff) ); @@ -245,7 +245,6 @@ void Parse::jump_if_true_fork(IfNode *iff, int dest_bci_if_true, int prof_table_ "taken always"); } else { assert(dest_bci_if_true != never_reached, "inconsistent dest"); - profile_switch_case(prof_table_index); merge_new_path(dest_bci_if_true); } } @@ -255,7 +254,7 @@ void Parse::jump_if_true_fork(IfNode *iff, int dest_bci_if_true, int prof_table_ set_control( iffalse ); } -void Parse::jump_if_false_fork(IfNode *iff, int dest_bci_if_true, int prof_table_index, bool unc) { +void Parse::jump_if_false_fork(IfNode *iff, int dest_bci_if_true, bool unc) { // True branch, use existing map info { PreserveJVMState pjvms(this); Node *iffalse = _gvn.transform( new IfFalseNode (iff) ); @@ -268,7 +267,6 @@ void Parse::jump_if_false_fork(IfNode *iff, int dest_bci_if_true, int prof_table "taken never"); } else { assert(dest_bci_if_true != never_reached, "inconsistent dest"); - profile_switch_case(prof_table_index); merge_new_path(dest_bci_if_true); } } @@ -278,7 +276,7 @@ void Parse::jump_if_false_fork(IfNode *iff, int dest_bci_if_true, int prof_table set_control( iftrue ); } -void Parse::jump_if_always_fork(int dest_bci, int prof_table_index, bool unc) { +void Parse::jump_if_always_fork(int dest_bci, bool unc) { // False branch, use existing map and control() if (unc) { repush_if_args(); @@ -288,7 +286,6 @@ void Parse::jump_if_always_fork(int dest_bci, int prof_table_index, bool unc) { "taken never"); } else { assert(dest_bci != never_reached, "inconsistent dest"); - profile_switch_case(prof_table_index); merge_new_path(dest_bci); } } @@ -303,34 +300,28 @@ extern "C" { } -// Default value for methodData switch indexing. Must be a negative value to avoid -// conflict with any legal switch index. -#define NullTableIndex -1 - class SwitchRange : public StackObj { // a range of integers coupled with a bci destination jint _lo; // inclusive lower limit jint _hi; // inclusive upper limit int _dest; - int _table_index; // index into method data table float _cnt; // how many times this range was hit according to profiling public: jint lo() const { return _lo; } jint hi() const { return _hi; } int dest() const { return _dest; } - int table_index() const { return _table_index; } bool is_singleton() const { return _lo == _hi; } float cnt() const { return _cnt; } - void setRange(jint lo, jint hi, int dest, int table_index, float cnt) { + void setRange(jint lo, jint hi, int dest, float cnt) { assert(lo <= hi, "must be a non-empty range"); - _lo = lo, _hi = hi; _dest = dest; _table_index = table_index; _cnt = cnt; + _lo = lo, _hi = hi; _dest = dest; _cnt = cnt; assert(_cnt >= 0, ""); } - bool adjoinRange(jint lo, jint hi, int dest, int table_index, float cnt, bool trim_ranges) { + bool adjoinRange(jint lo, jint hi, int dest, float cnt, bool trim_ranges) { assert(lo <= hi, "must be a non-empty range"); - if (lo == _hi+1 && table_index == _table_index) { + if (lo == _hi+1) { // see merge_ranges() comment below if (trim_ranges) { if (cnt == 0) { @@ -360,14 +351,14 @@ class SwitchRange : public StackObj { return false; } - void set (jint value, int dest, int table_index, float cnt) { - setRange(value, value, dest, table_index, cnt); + void set (jint value, int dest, float cnt) { + setRange(value, value, dest, cnt); } - bool adjoin(jint value, int dest, int table_index, float cnt, bool trim_ranges) { - return adjoinRange(value, value, dest, table_index, cnt, trim_ranges); + bool adjoin(jint value, int dest, float cnt, bool trim_ranges) { + return adjoinRange(value, value, dest, cnt, trim_ranges); } bool adjoin(SwitchRange& other) { - return adjoinRange(other._lo, other._hi, other._dest, other._table_index, other._cnt, false); + return adjoinRange(other._lo, other._hi, other._dest, other._cnt, false); } void print() { @@ -418,7 +409,7 @@ static void merge_ranges(SwitchRange* ranges, int& rp) { for (int j = 0; j <= rp; j++) { SwitchRange& r = ranges[j]; if (r.cnt() == 0 && r.dest() != never_reached) { - r.setRange(r.lo(), r.hi(), never_reached, r.table_index(), r.cnt()); + r.setRange(r.lo(), r.hi(), never_reached, r.cnt()); } } } @@ -447,7 +438,7 @@ void Parse::do_tableswitch() { profile = (ciMultiBranchData*)data; } } - bool trim_ranges = !method_data_update() && !C->too_many_traps(method(), bci(), Deoptimization::Reason_unstable_if); + bool trim_ranges = !C->too_many_traps(method(), bci(), Deoptimization::Reason_unstable_if); // generate decision tree, using trichotomy when possible int rnum = len+2; @@ -459,19 +450,18 @@ void Parse::do_tableswitch() { if (profile != NULL) { cnt = profile->default_count() / (hi_index != max_jint ? 2 : 1); } - ranges[++rp].setRange(min_jint, lo_index-1, default_dest, NullTableIndex, cnt); + ranges[++rp].setRange(min_jint, lo_index-1, default_dest, cnt); } for (int j = 0; j < len; j++) { jint match_int = lo_index+j; int dest = iter().get_dest_table(j+3); makes_backward_branch |= (dest <= bci()); - int table_index = method_data_update() ? j : NullTableIndex; uint cnt = 1; if (profile != NULL) { cnt = profile->count_at(j); } - if (rp < 0 || !ranges[rp].adjoin(match_int, dest, table_index, cnt, trim_ranges)) { - ranges[++rp].set(match_int, dest, table_index, cnt); + if (rp < 0 || !ranges[rp].adjoin(match_int, dest, cnt, trim_ranges)) { + ranges[++rp].set(match_int, dest, cnt); } } jint highest = lo_index+(len-1); @@ -481,8 +471,8 @@ void Parse::do_tableswitch() { if (profile != NULL) { cnt = profile->default_count() / (lo_index != min_jint ? 2 : 1); } - if (!ranges[rp].adjoinRange(highest+1, max_jint, default_dest, NullTableIndex, cnt, trim_ranges)) { - ranges[++rp].setRange(highest+1, max_jint, default_dest, NullTableIndex, cnt); + if (!ranges[rp].adjoinRange(highest+1, max_jint, default_dest, cnt, trim_ranges)) { + ranges[++rp].setRange(highest+1, max_jint, default_dest, cnt); } } assert(rp < len+2, "not too many ranges"); @@ -520,7 +510,7 @@ void Parse::do_lookupswitch() { profile = (ciMultiBranchData*)data; } } - bool trim_ranges = !method_data_update() && !C->too_many_traps(method(), bci(), Deoptimization::Reason_unstable_if); + bool trim_ranges = !C->too_many_traps(method(), bci(), Deoptimization::Reason_unstable_if); // generate decision tree, using trichotomy when possible jint* table = NEW_RESOURCE_ARRAY(jint, len*3); @@ -560,23 +550,22 @@ void Parse::do_lookupswitch() { int dest = table[3*j+1]; int cnt = table[3*j+2]; int next_lo = rp < 0 ? min_jint : ranges[rp].hi()+1; - int table_index = method_data_update() ? j : NullTableIndex; makes_backward_branch |= (dest <= bci()); float c = default_cnt * ((float)match_int - next_lo); - if (match_int != next_lo && (rp < 0 || !ranges[rp].adjoinRange(next_lo, match_int-1, default_dest, NullTableIndex, c, trim_ranges))) { + if (match_int != next_lo && (rp < 0 || !ranges[rp].adjoinRange(next_lo, match_int-1, default_dest, c, trim_ranges))) { assert(default_dest != never_reached, "sentinel value for dead destinations"); - ranges[++rp].setRange(next_lo, match_int-1, default_dest, NullTableIndex, c); + ranges[++rp].setRange(next_lo, match_int-1, default_dest, c); } - if (rp < 0 || !ranges[rp].adjoin(match_int, dest, table_index, cnt, trim_ranges)) { + if (rp < 0 || !ranges[rp].adjoin(match_int, dest, cnt, trim_ranges)) { assert(dest != never_reached, "sentinel value for dead destinations"); - ranges[++rp].set(match_int, dest, table_index, cnt); + ranges[++rp].set(match_int, dest, cnt); } } jint highest = table[3*(len-1)]; assert(ranges[rp].hi() == highest, ""); if (highest != max_jint && - !ranges[rp].adjoinRange(highest+1, max_jint, default_dest, NullTableIndex, default_cnt * ((float)max_jint - highest), trim_ranges)) { - ranges[++rp].setRange(highest+1, max_jint, default_dest, NullTableIndex, default_cnt * ((float)max_jint - highest)); + !ranges[rp].adjoinRange(highest+1, max_jint, default_dest, default_cnt * ((float)max_jint - highest), trim_ranges)) { + ranges[++rp].setRange(highest+1, max_jint, default_dest, default_cnt * ((float)max_jint - highest)); } assert(rp < rnum, "not too many ranges"); @@ -735,7 +724,7 @@ void Parse::linear_search_switch_ranges(Node* key_val, SwitchRange*& lo, SwitchR shift++; if (i > 0 && i < nr-1) { SwitchRange prev = lo[i-1]; - prev.setRange(prev.lo(), sr->hi(), prev.dest(), prev.table_index(), prev.cnt()); + prev.setRange(prev.lo(), sr->hi(), prev.dest(), prev.cnt()); if (prev.adjoin(lo[i+1])) { shift++; i++; @@ -762,7 +751,7 @@ void Parse::linear_search_switch_ranges(Node* key_val, SwitchRange*& lo, SwitchR Node* cmp = _gvn.transform(new CmpUNode(val, _gvn.intcon(most_freq.hi() - most_freq.lo()))); Node* tst = _gvn.transform(new BoolNode(cmp, BoolTest::le)); IfNode* iff = create_and_map_if(control(), tst, if_prob(most_freq.cnt(), total_cnt), if_cnt(most_freq.cnt())); - jump_if_true_fork(iff, most_freq.dest(), most_freq.table_index(), false); + jump_if_true_fork(iff, most_freq.dest(), false); sub += most_freq.cnt() / total_cnt; extra += 1 - sub; @@ -778,9 +767,6 @@ bool Parse::create_jump_tables(Node* key_val, SwitchRange* lo, SwitchRange* hi) // Are jumptables supported if (!Matcher::has_match_rule(Op_Jump)) return false; - // Don't make jump table if profiling - if (method_data_update()) return false; - bool trim_ranges = !C->too_many_traps(method(), bci(), Deoptimization::Reason_unstable_if); // Decide if a guard is needed to lop off big ranges at either (or @@ -858,7 +844,7 @@ bool Parse::create_jump_tables(Node* key_val, SwitchRange* lo, SwitchRange* hi) Node* cmp = _gvn.transform(new CmpUNode(key_val, size)); Node* tst = _gvn.transform(new BoolNode(cmp, BoolTest::ge)); IfNode* iff = create_and_map_if(control(), tst, if_prob(trimmed_cnt, total), if_cnt(trimmed_cnt)); - jump_if_true_fork(iff, default_dest, NullTableIndex, trim_ranges && trimmed_cnt == 0); + jump_if_true_fork(iff, default_dest, trim_ranges && trimmed_cnt == 0); total -= trimmed_cnt; } @@ -918,7 +904,7 @@ bool Parse::create_jump_tables(Node* key_val, SwitchRange* lo, SwitchRange* hi) { PreserveJVMState pjvms(this); set_control(input); - jump_if_always_fork(r->dest(), r->table_index(), trim_ranges && r->cnt() == 0); + jump_if_always_fork(r->dest(), trim_ranges && r->cnt() == 0); } } } @@ -930,7 +916,7 @@ bool Parse::create_jump_tables(Node* key_val, SwitchRange* lo, SwitchRange* hi) //----------------------------jump_switch_ranges------------------------------- void Parse::jump_switch_ranges(Node* key_val, SwitchRange *lo, SwitchRange *hi, int switch_depth) { Block* switch_block = block(); - bool trim_ranges = !method_data_update() && !C->too_many_traps(method(), bci(), Deoptimization::Reason_unstable_if); + bool trim_ranges = !C->too_many_traps(method(), bci(), Deoptimization::Reason_unstable_if); if (switch_depth == 0) { // Do special processing for the top-level call. @@ -971,13 +957,13 @@ void Parse::jump_switch_ranges(Node* key_val, SwitchRange *lo, SwitchRange *hi, lo++; } if (lo->lo() < min_val) { - lo->setRange(min_val, lo->hi(), lo->dest(), lo->table_index(), lo->cnt()); + lo->setRange(min_val, lo->hi(), lo->dest(), lo->cnt()); } while (hi->lo() > max_val) { hi--; } if (hi->hi() > max_val) { - hi->setRange(hi->lo(), max_val, hi->dest(), hi->table_index(), hi->cnt()); + hi->setRange(hi->lo(), max_val, hi->dest(), hi->cnt()); } linear_search_switch_ranges(key_val, lo, hi); @@ -992,7 +978,7 @@ void Parse::jump_switch_ranges(Node* key_val, SwitchRange *lo, SwitchRange *hi, assert(lo <= hi, "must be a non-empty set of ranges"); if (lo == hi) { - jump_if_always_fork(lo->dest(), lo->table_index(), trim_ranges && lo->cnt() == 0); + jump_if_always_fork(lo->dest(), trim_ranges && lo->cnt() == 0); } else { assert(lo->hi() == (lo+1)->lo()-1, "contiguous ranges"); assert(hi->lo() == (hi-1)->hi()+1, "contiguous ranges"); @@ -1030,7 +1016,7 @@ void Parse::jump_switch_ranges(Node* key_val, SwitchRange *lo, SwitchRange *hi, if (mid->is_singleton()) { IfNode *iff_ne = jump_if_fork_int(key_val, test_val, BoolTest::ne, 1-if_prob(mid->cnt(), total_cnt), if_cnt(mid->cnt())); - jump_if_false_fork(iff_ne, mid->dest(), mid->table_index(), trim_ranges && mid->cnt() == 0); + jump_if_false_fork(iff_ne, mid->dest(), trim_ranges && mid->cnt() == 0); // Special Case: If there are exactly three ranges, and the high // and low range each go to the same place, omit the "gt" test, @@ -1059,7 +1045,7 @@ void Parse::jump_switch_ranges(Node* key_val, SwitchRange *lo, SwitchRange *hi, // if there is a higher range, test for it and process it: if (mid == hi) { - jump_if_true_fork(iff_ge, mid->dest(), mid->table_index(), trim_ranges && cnt == 0); + jump_if_true_fork(iff_ge, mid->dest(), trim_ranges && cnt == 0); } else { Node *iftrue = _gvn.transform( new IfTrueNode(iff_ge) ); Node *iffalse = _gvn.transform( new IfFalseNode(iff_ge) ); @@ -1076,7 +1062,7 @@ void Parse::jump_switch_ranges(Node* key_val, SwitchRange *lo, SwitchRange *hi, if (mid->is_singleton()) { jump_switch_ranges(key_val, lo+1, hi, switch_depth+1); } else { - jump_if_always_fork(lo->dest(), lo->table_index(), trim_ranges && lo->cnt() == 0); + jump_if_always_fork(lo->dest(), trim_ranges && lo->cnt() == 0); } } else { jump_switch_ranges(key_val, lo, mid-1, switch_depth+1); @@ -1211,9 +1197,6 @@ void Parse::do_jsr() { int return_bci = iter().next_bci(); int jsr_bci = (bc() == Bytecodes::_jsr) ? iter().get_dest() : iter().get_far_dest(); - // Update method data - profile_taken_branch(jsr_bci); - // The way we do things now, there is only one successor block // for the jsr, because the target code is cloned by ciTypeFlow. Block* target = successor_for_bci(jsr_bci); @@ -1235,7 +1218,6 @@ void Parse::do_ret() { assert(block()->num_successors() == 1, "a ret can only go one place now"); Block* target = block()->successor_at(0); assert(!target->is_ready(), "our arrival must be expected"); - profile_ret(target->flow()->start()); int pnum = target->next_path_num(); merge_common(target, pnum); } @@ -1447,11 +1429,6 @@ void Parse::do_ifnull(BoolTest::mask btest, Node *c) { tty->print_cr("Never-taken edge stops compilation at bci %d", bci()); } repush_if_args(); // to gather stats on loop - // We need to mark this branch as taken so that if we recompile we will - // see that it is possible. In the tiered system the interpreter doesn't - // do profiling and by the time we get to the lower tier from the interpreter - // the path may be cold again. Make sure it doesn't look untaken - profile_taken_branch(target_bci, !ProfileInterpreter); uncommon_trap(Deoptimization::Reason_unreached, Deoptimization::Action_reinterpret, NULL, "cold"); @@ -1485,8 +1462,6 @@ void Parse::do_ifnull(BoolTest::mask btest, Node *c) { branch_block->next_path_num(); } } else { // Path is live. - // Update method data - profile_taken_branch(target_bci); adjust_map_after_if(btest, c, prob, branch_block, next_block); if (!stopped()) { merge(target_bci); @@ -1505,8 +1480,6 @@ void Parse::do_ifnull(BoolTest::mask btest, Node *c) { next_block->next_path_num(); } } else { // Path is live. - // Update method data - profile_not_taken_branch(); adjust_map_after_if(BoolTest(btest).negate(), c, 1.0-prob, next_block, branch_block); } @@ -1528,11 +1501,6 @@ void Parse::do_if(BoolTest::mask btest, Node* c) { tty->print_cr("Never-taken edge stops compilation at bci %d", bci()); } repush_if_args(); // to gather stats on loop - // We need to mark this branch as taken so that if we recompile we will - // see that it is possible. In the tiered system the interpreter doesn't - // do profiling and by the time we get to the lower tier from the interpreter - // the path may be cold again. Make sure it doesn't look untaken - profile_taken_branch(target_bci, !ProfileInterpreter); uncommon_trap(Deoptimization::Reason_unreached, Deoptimization::Action_reinterpret, NULL, "cold"); @@ -1607,8 +1575,6 @@ void Parse::do_if(BoolTest::mask btest, Node* c) { branch_block->next_path_num(); } } else { - // Update method data - profile_taken_branch(target_bci); adjust_map_after_if(taken_btest, c, prob, branch_block, next_block); if (!stopped()) { merge(target_bci); @@ -1626,8 +1592,6 @@ void Parse::do_if(BoolTest::mask btest, Node* c) { next_block->next_path_num(); } } else { - // Update method data - profile_not_taken_branch(); adjust_map_after_if(untaken_btest, c, untaken_prob, next_block, branch_block); } @@ -2373,7 +2337,7 @@ void Parse::do_one_bytecode() { if (Matcher::convL2FSupported()) { a = pop_pair(); b = _gvn.transform( new ConvL2FNode(a)); - // For i486.ad, FILD doesn't restrict precision to 24 or 53 bits. + // For x86_32.ad, FILD doesn't restrict precision to 24 or 53 bits. // Rather than storing the result into an FP register then pushing // out to memory to round, the machine instruction that implements // ConvL2D is responsible for rounding. @@ -2388,7 +2352,7 @@ void Parse::do_one_bytecode() { case Bytecodes::_l2d: a = pop_pair(); b = _gvn.transform( new ConvL2DNode(a)); - // For i486.ad, rounding is always necessary (see _l2f above). + // For x86_32.ad, rounding is always necessary (see _l2f above). // c = dprecision_rounding(b); c = _gvn.transform(b); push_pair(c); @@ -2691,9 +2655,6 @@ void Parse::do_one_bytecode() { // If this is a backwards branch in the bytecodes, add Safepoint maybe_add_safepoint(target_bci); - // Update method data - profile_taken_branch(target_bci); - // Merge the current control into the target basic block merge(target_bci); diff --git a/src/hotspot/share/opto/parseHelper.cpp b/src/hotspot/share/opto/parseHelper.cpp index d214c2a97b3..c885f7806b1 100644 --- a/src/hotspot/share/opto/parseHelper.cpp +++ b/src/hotspot/share/opto/parseHelper.cpp @@ -86,9 +86,6 @@ void Parse::do_checkcast() { } null_assert(obj); assert( stopped() || _gvn.type(peek())->higher_equal(TypePtr::NULL_PTR), "what's left behind is null" ); - if (!stopped()) { - profile_null_checkcast(); - } return; } @@ -299,287 +296,3 @@ void Parse::dump_map_adr_mem() const { #endif - -//============================================================================= -// -// parser methods for profiling - - -//----------------------test_counter_against_threshold ------------------------ -void Parse::test_counter_against_threshold(Node* cnt, int limit) { - // Test the counter against the limit and uncommon trap if greater. - - // This code is largely copied from the range check code in - // array_addressing() - - // Test invocation count vs threshold - Node *threshold = makecon(TypeInt::make(limit)); - Node *chk = _gvn.transform( new CmpUNode( cnt, threshold) ); - BoolTest::mask btest = BoolTest::lt; - Node *tst = _gvn.transform( new BoolNode( chk, btest) ); - // Branch to failure if threshold exceeded - { BuildCutout unless(this, tst, PROB_ALWAYS); - uncommon_trap(Deoptimization::Reason_age, - Deoptimization::Action_maybe_recompile); - } -} - -//----------------------increment_and_test_invocation_counter------------------- -void Parse::increment_and_test_invocation_counter(int limit) { - if (!count_invocations()) return; - - // Get the Method* node. - ciMethod* m = method(); - MethodCounters* counters_adr = m->ensure_method_counters(); - if (counters_adr == NULL) { - C->record_failure("method counters allocation failed"); - return; - } - - Node* ctrl = control(); - const TypePtr* adr_type = TypeRawPtr::make((address) counters_adr); - Node *counters_node = makecon(adr_type); - Node* adr_iic_node = basic_plus_adr(counters_node, counters_node, - MethodCounters::interpreter_invocation_counter_offset_in_bytes()); - Node* cnt = make_load(ctrl, adr_iic_node, TypeInt::INT, T_INT, adr_type, MemNode::unordered); - - test_counter_against_threshold(cnt, limit); - - // Add one to the counter and store - Node* incr = _gvn.transform(new AddINode(cnt, _gvn.intcon(1))); - store_to_memory(ctrl, adr_iic_node, incr, T_INT, adr_type, MemNode::unordered); -} - -//----------------------------method_data_addressing--------------------------- -Node* Parse::method_data_addressing(ciMethodData* md, ciProfileData* data, ByteSize counter_offset, Node* idx, uint stride) { - // Get offset within MethodData* of the data array - ByteSize data_offset = MethodData::data_offset(); - - // Get cell offset of the ProfileData within data array - int cell_offset = md->dp_to_di(data->dp()); - - // Add in counter_offset, the # of bytes into the ProfileData of counter or flag - int offset = in_bytes(data_offset) + cell_offset + in_bytes(counter_offset); - - const TypePtr* adr_type = TypeMetadataPtr::make(md); - Node* mdo = makecon(adr_type); - Node* ptr = basic_plus_adr(mdo, mdo, offset); - - if (stride != 0) { - Node* str = _gvn.MakeConX(stride); - Node* scale = _gvn.transform( new MulXNode( idx, str ) ); - ptr = _gvn.transform( new AddPNode( mdo, ptr, scale ) ); - } - - return ptr; -} - -//--------------------------increment_md_counter_at---------------------------- -void Parse::increment_md_counter_at(ciMethodData* md, ciProfileData* data, ByteSize counter_offset, Node* idx, uint stride) { - Node* adr_node = method_data_addressing(md, data, counter_offset, idx, stride); - - const TypePtr* adr_type = _gvn.type(adr_node)->is_ptr(); - Node* cnt = make_load(NULL, adr_node, TypeInt::INT, T_INT, adr_type, MemNode::unordered); - Node* incr = _gvn.transform(new AddINode(cnt, _gvn.intcon(DataLayout::counter_increment))); - store_to_memory(NULL, adr_node, incr, T_INT, adr_type, MemNode::unordered); -} - -//--------------------------test_for_osr_md_counter_at------------------------- -void Parse::test_for_osr_md_counter_at(ciMethodData* md, ciProfileData* data, ByteSize counter_offset, int limit) { - Node* adr_node = method_data_addressing(md, data, counter_offset); - - const TypePtr* adr_type = _gvn.type(adr_node)->is_ptr(); - Node* cnt = make_load(NULL, adr_node, TypeInt::INT, T_INT, adr_type, MemNode::unordered); - - test_counter_against_threshold(cnt, limit); -} - -//-------------------------------set_md_flag_at-------------------------------- -void Parse::set_md_flag_at(ciMethodData* md, ciProfileData* data, int flag_constant) { - Node* adr_node = method_data_addressing(md, data, DataLayout::flags_offset()); - - const TypePtr* adr_type = _gvn.type(adr_node)->is_ptr(); - Node* flags = make_load(NULL, adr_node, TypeInt::INT, T_INT, adr_type, MemNode::unordered); - Node* incr = _gvn.transform(new OrINode(flags, _gvn.intcon(flag_constant))); - store_to_memory(NULL, adr_node, incr, T_INT, adr_type, MemNode::unordered); -} - -//----------------------------profile_taken_branch----------------------------- -void Parse::profile_taken_branch(int target_bci, bool force_update) { - // This is a potential osr_site if we have a backedge. - int cur_bci = bci(); - bool osr_site = - (target_bci <= cur_bci) && count_invocations() && UseOnStackReplacement; - - // If we are going to OSR, restart at the target bytecode. - set_bci(target_bci); - - // To do: factor out the the limit calculations below. These duplicate - // the similar limit calculations in the interpreter. - - if (method_data_update() || force_update) { - ciMethodData* md = method()->method_data(); - assert(md != NULL, "expected valid ciMethodData"); - ciProfileData* data = md->bci_to_data(cur_bci); - assert(data != NULL && data->is_JumpData(), "need JumpData for taken branch"); - increment_md_counter_at(md, data, JumpData::taken_offset()); - } - - // In the new tiered system this is all we need to do. In the old - // (c2 based) tiered sytem we must do the code below. -#ifndef TIERED - if (method_data_update()) { - ciMethodData* md = method()->method_data(); - if (osr_site) { - ciProfileData* data = md->bci_to_data(cur_bci); - assert(data != NULL && data->is_JumpData(), "need JumpData for taken branch"); - int limit = (int)((int64_t)CompileThreshold - * (OnStackReplacePercentage - InterpreterProfilePercentage) / 100); - test_for_osr_md_counter_at(md, data, JumpData::taken_offset(), limit); - } - } else { - // With method data update off, use the invocation counter to trigger an - // OSR compilation, as done in the interpreter. - if (osr_site) { - int limit = (int)((int64_t)CompileThreshold * OnStackReplacePercentage / 100); - increment_and_test_invocation_counter(limit); - } - } -#endif // TIERED - - // Restore the original bytecode. - set_bci(cur_bci); -} - -//--------------------------profile_not_taken_branch--------------------------- -void Parse::profile_not_taken_branch(bool force_update) { - - if (method_data_update() || force_update) { - ciMethodData* md = method()->method_data(); - assert(md != NULL, "expected valid ciMethodData"); - ciProfileData* data = md->bci_to_data(bci()); - assert(data != NULL && data->is_BranchData(), "need BranchData for not taken branch"); - increment_md_counter_at(md, data, BranchData::not_taken_offset()); - } - -} - -//---------------------------------profile_call-------------------------------- -void Parse::profile_call(Node* receiver) { - if (!method_data_update()) return; - - switch (bc()) { - case Bytecodes::_invokevirtual: - case Bytecodes::_invokeinterface: - profile_receiver_type(receiver); - break; - case Bytecodes::_invokestatic: - case Bytecodes::_invokedynamic: - case Bytecodes::_invokespecial: - profile_generic_call(); - break; - default: fatal("unexpected call bytecode"); - } -} - -//------------------------------profile_generic_call--------------------------- -void Parse::profile_generic_call() { - assert(method_data_update(), "must be generating profile code"); - - ciMethodData* md = method()->method_data(); - assert(md != NULL, "expected valid ciMethodData"); - ciProfileData* data = md->bci_to_data(bci()); - assert(data != NULL && data->is_CounterData(), "need CounterData for not taken branch"); - increment_md_counter_at(md, data, CounterData::count_offset()); -} - -//-----------------------------profile_receiver_type--------------------------- -void Parse::profile_receiver_type(Node* receiver) { - assert(method_data_update(), "must be generating profile code"); - - ciMethodData* md = method()->method_data(); - assert(md != NULL, "expected valid ciMethodData"); - ciProfileData* data = md->bci_to_data(bci()); - assert(data != NULL && data->is_ReceiverTypeData(), "need ReceiverTypeData here"); - - // Skip if we aren't tracking receivers - if (TypeProfileWidth < 1) { - increment_md_counter_at(md, data, CounterData::count_offset()); - return; - } - ciReceiverTypeData* rdata = (ciReceiverTypeData*)data->as_ReceiverTypeData(); - - Node* method_data = method_data_addressing(md, rdata, in_ByteSize(0)); - - // Using an adr_type of TypePtr::BOTTOM to work around anti-dep problems. - // A better solution might be to use TypeRawPtr::BOTTOM with RC_NARROW_MEM. - make_runtime_call(RC_LEAF, OptoRuntime::profile_receiver_type_Type(), - CAST_FROM_FN_PTR(address, - OptoRuntime::profile_receiver_type_C), - "profile_receiver_type_C", - TypePtr::BOTTOM, - method_data, receiver); -} - -//---------------------------------profile_ret--------------------------------- -void Parse::profile_ret(int target_bci) { - if (!method_data_update()) return; - - // Skip if we aren't tracking ret targets - if (TypeProfileWidth < 1) return; - - ciMethodData* md = method()->method_data(); - assert(md != NULL, "expected valid ciMethodData"); - ciProfileData* data = md->bci_to_data(bci()); - assert(data != NULL && data->is_RetData(), "need RetData for ret"); - ciRetData* ret_data = (ciRetData*)data->as_RetData(); - - // Look for the target_bci is already in the table - uint row; - bool table_full = true; - for (row = 0; row < ret_data->row_limit(); row++) { - int key = ret_data->bci(row); - table_full &= (key != RetData::no_bci); - if (key == target_bci) break; - } - - if (row >= ret_data->row_limit()) { - // The target_bci was not found in the table. - if (!table_full) { - // XXX: Make slow call to update RetData - } - return; - } - - // the target_bci is already in the table - increment_md_counter_at(md, data, RetData::bci_count_offset(row)); -} - -//--------------------------profile_null_checkcast---------------------------- -void Parse::profile_null_checkcast() { - // Set the null-seen flag, done in conjunction with the usual null check. We - // never unset the flag, so this is a one-way switch. - if (!method_data_update()) return; - - ciMethodData* md = method()->method_data(); - assert(md != NULL, "expected valid ciMethodData"); - ciProfileData* data = md->bci_to_data(bci()); - assert(data != NULL && data->is_BitData(), "need BitData for checkcast"); - set_md_flag_at(md, data, BitData::null_seen_byte_constant()); -} - -//-----------------------------profile_switch_case----------------------------- -void Parse::profile_switch_case(int table_index) { - if (!method_data_update()) return; - - ciMethodData* md = method()->method_data(); - assert(md != NULL, "expected valid ciMethodData"); - - ciProfileData* data = md->bci_to_data(bci()); - assert(data != NULL && data->is_MultiBranchData(), "need MultiBranchData for switch case"); - if (table_index >= 0) { - increment_md_counter_at(md, data, MultiBranchData::case_count_offset(table_index)); - } else { - increment_md_counter_at(md, data, MultiBranchData::default_count_offset()); - } -} diff --git a/src/hotspot/share/opto/phase.cpp b/src/hotspot/share/opto/phase.cpp index 01eff476703..9759c593eee 100644 --- a/src/hotspot/share/opto/phase.cpp +++ b/src/hotspot/share/opto/phase.cpp @@ -78,6 +78,10 @@ void Phase::print_timers() { } } tty->print_cr (" Renumber Live: %7.3f s", timers[_t_renumberLive].seconds()); + tty->print_cr (" Vector: %7.3f s", timers[_t_vector].seconds()); + tty->print_cr (" Box elimination: %7.3f s", timers[_t_vector_elimination].seconds()); + tty->print_cr (" IGVN: %7.3f s", timers[_t_vector_igvn].seconds()); + tty->print_cr (" Prune Useless: %7.3f s", timers[_t_vector_pru].seconds()); tty->print_cr (" IdealLoop: %7.3f s", timers[_t_idealLoop].seconds()); tty->print_cr (" IdealLoop Verify: %7.3f s", timers[_t_idealLoopVerify].seconds()); tty->print_cr (" Cond Const Prop: %7.3f s", timers[_t_ccp].seconds()); diff --git a/src/hotspot/share/opto/phase.hpp b/src/hotspot/share/opto/phase.hpp index 87fe19a780d..32944f0747e 100644 --- a/src/hotspot/share/opto/phase.hpp +++ b/src/hotspot/share/opto/phase.hpp @@ -59,6 +59,7 @@ class Phase : public StackObj { Ideal_Loop, // Find idealized trip-counted loops Macro_Expand, // Expand macro nodes Peephole, // Apply peephole optimizations + Vector, Output, last_phase }; @@ -75,6 +76,10 @@ class Phase : public StackObj { _t_incrInline_igvn, _t_incrInline_pru, _t_incrInline_inline, + _t_vector, + _t_vector_elimination, + _t_vector_igvn, + _t_vector_pru, _t_renumberLive, _t_idealLoop, _t_idealLoopVerify, diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 08855d21367..1d144b1fbcd 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -439,6 +439,7 @@ PhaseRemoveUseless::PhaseRemoveUseless(PhaseGVN* gvn, Unique_Node_List* worklist // The PhaseRenumberLive phase updates two data structures with the new node IDs. // (1) The worklist is used by the PhaseIterGVN phase to identify nodes that must be // processed. A new worklist (with the updated node IDs) is returned in 'new_worklist'. +// 'worklist' is cleared upon returning. // (2) Type information (the field PhaseGVN::_types) maps type information to each // node ID. The mapping is updated to use the new node IDs as well. Updated type // information is returned in PhaseGVN::_types. @@ -510,6 +511,9 @@ PhaseRenumberLive::PhaseRenumberLive(PhaseGVN* gvn, // Set the dead node count to 0 and reset dead node list. C->reset_dead_node_list(); + + // Clear the original worklist + worklist->clear(); } int PhaseRenumberLive::new_index(int old_idx) { @@ -745,7 +749,7 @@ ConNode* PhaseValues::uncached_makecon(const Type *t) { loc->clear(); // do not put debug info on constants } } else { - x->destruct(); // Hit, destroy duplicate constant + x->destruct(this); // Hit, destroy duplicate constant x = k; // use existing constant } return x; @@ -1065,9 +1069,9 @@ void PhaseIterGVN::init_verifyPhaseIterGVN() { Unique_Node_List* modified_list = C->modified_nodes(); while (modified_list != NULL && modified_list->size()) { Node* n = modified_list->pop(); - if (n->outcnt() != 0 && !n->is_Con() && !_worklist.member(n)) { + if (!n->is_Con() && !_worklist.member(n)) { n->dump(); - assert(false, "modified node is not on IGVN._worklist"); + fatal("modified node is not on IGVN._worklist"); } } #endif @@ -1079,9 +1083,9 @@ void PhaseIterGVN::verify_PhaseIterGVN() { Unique_Node_List* modified_list = C->modified_nodes(); while (modified_list != NULL && modified_list->size()) { Node* n = modified_list->pop(); - if (n->outcnt() != 0 && !n->is_Con()) { // skip dead and Con nodes + if (!n->is_Con()) { // skip Con nodes n->dump(); - assert(false, "modified node was not processed by IGVN.transform_old()"); + fatal("modified node was not processed by IGVN.transform_old()"); } } #endif @@ -1405,12 +1409,8 @@ void PhaseIterGVN::remove_globally_dead_node( Node *dead ) { if (dead->is_expensive()) { C->remove_expensive_node(dead); } - CastIINode* cast = dead->isa_CastII(); - if (cast != NULL && cast->has_range_check()) { - C->remove_range_check_cast(cast); - } - if (dead->Opcode() == Op_Opaque4) { - C->remove_opaque4_node(dead); + if (dead->for_post_loop_opts_igvn()) { + C->remove_from_post_loop_opts_igvn(dead); } BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); bs->unregister_potential_barrier_node(dead); @@ -1472,8 +1472,7 @@ void PhaseIterGVN::subsume_node( Node *old, Node *nn ) { } } #endif - _worklist.remove(temp); // this can be necessary - temp->destruct(); // reuse the _idx of this little guy + temp->destruct(this); // reuse the _idx of this little guy } //------------------------------add_users_to_worklist-------------------------- diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp index c297bb76978..4a5805093f9 100644 --- a/src/hotspot/share/opto/phaseX.hpp +++ b/src/hotspot/share/opto/phaseX.hpp @@ -282,11 +282,6 @@ class PhaseTransform : public Phase { // in a faster or cheaper fashion. virtual Node *transform( Node *n ) = 0; - // Return whether two Nodes are equivalent. - // Must not be recursive, since the recursive version is built from this. - // For pessimistic optimizations this is simply pointer equivalence. - bool eqv(const Node* n1, const Node* n2) const { return n1 == n2; } - // For pessimistic passes, the return type must monotonically narrow. // For optimistic passes, the return type must monotonically widen. // It is possible to get into a "death march" in either type of pass, diff --git a/src/hotspot/share/opto/phasetype.hpp b/src/hotspot/share/opto/phasetype.hpp index fcfe894182a..4883ee39e26 100644 --- a/src/hotspot/share/opto/phasetype.hpp +++ b/src/hotspot/share/opto/phasetype.hpp @@ -31,7 +31,14 @@ enum CompilerPhaseType { PHASE_BEFORE_REMOVEUSELESS, PHASE_AFTER_PARSING, PHASE_ITER_GVN1, + PHASE_EXPAND_VUNBOX, + PHASE_SCALARIZE_VBOX, + PHASE_INLINE_VECTOR_REBOX, + PHASE_EXPAND_VBOX, + PHASE_ELIMINATE_VBOX_ALLOC, PHASE_PHASEIDEAL_BEFORE_EA, + PHASE_ITER_GVN_AFTER_VECTOR, + PHASE_ITER_GVN_BEFORE_EA, PHASE_ITER_GVN_AFTER_EA, PHASE_ITER_GVN_AFTER_ELIMINATION, PHASE_PHASEIDEALLOOP1, @@ -41,6 +48,7 @@ enum CompilerPhaseType { PHASE_ITER_GVN2, PHASE_PHASEIDEALLOOP_ITERATIONS, PHASE_OPTIMIZE_FINISHED, + PHASE_AFTER_MATCHING, PHASE_GLOBAL_CODE_MOTION, PHASE_FINAL_CODE, PHASE_AFTER_EA, @@ -51,6 +59,7 @@ enum CompilerPhaseType { PHASE_BEFORE_MATCHING, PHASE_MATCHING, PHASE_INCREMENTAL_INLINE, + PHASE_INCREMENTAL_INLINE_STEP, PHASE_INCREMENTAL_BOXING_INLINE, PHASE_CALL_CATCH_CLEANUP, PHASE_INSERT_BARRIER, @@ -73,7 +82,14 @@ class CompilerPhaseTypeHelper { case PHASE_BEFORE_REMOVEUSELESS: return "Before RemoveUseless"; case PHASE_AFTER_PARSING: return "After Parsing"; case PHASE_ITER_GVN1: return "Iter GVN 1"; + case PHASE_EXPAND_VUNBOX: return "Expand VectorUnbox"; + case PHASE_SCALARIZE_VBOX: return "Scalarize VectorBox"; + case PHASE_INLINE_VECTOR_REBOX: return "Inline Vector Rebox Calls"; + case PHASE_EXPAND_VBOX: return "Expand VectorBox"; + case PHASE_ELIMINATE_VBOX_ALLOC: return "Eliminate VectorBoxAllocate"; case PHASE_PHASEIDEAL_BEFORE_EA: return "PhaseIdealLoop before EA"; + case PHASE_ITER_GVN_AFTER_VECTOR: return "Iter GVN after vector box elimination"; + case PHASE_ITER_GVN_BEFORE_EA: return "Iter GVN before EA"; case PHASE_ITER_GVN_AFTER_EA: return "Iter GVN after EA"; case PHASE_ITER_GVN_AFTER_ELIMINATION: return "Iter GVN after eliminating allocations and locks"; case PHASE_PHASEIDEALLOOP1: return "PhaseIdealLoop 1"; @@ -83,6 +99,7 @@ class CompilerPhaseTypeHelper { case PHASE_ITER_GVN2: return "Iter GVN 2"; case PHASE_PHASEIDEALLOOP_ITERATIONS: return "PhaseIdealLoop iterations"; case PHASE_OPTIMIZE_FINISHED: return "Optimize finished"; + case PHASE_AFTER_MATCHING: return "After Matching"; case PHASE_GLOBAL_CODE_MOTION: return "Global code motion"; case PHASE_FINAL_CODE: return "Final Code"; case PHASE_AFTER_EA: return "After Escape Analysis"; @@ -93,6 +110,7 @@ class CompilerPhaseTypeHelper { case PHASE_BEFORE_MATCHING: return "Before matching"; case PHASE_MATCHING: return "After matching"; case PHASE_INCREMENTAL_INLINE: return "Incremental Inline"; + case PHASE_INCREMENTAL_INLINE_STEP: return "Incremental Inline Step"; case PHASE_INCREMENTAL_BOXING_INLINE: return "Incremental Boxing Inline"; case PHASE_CALL_CATCH_CLEANUP: return "Call catch cleanup"; case PHASE_INSERT_BARRIER: return "Insert barrier"; diff --git a/src/hotspot/share/opto/postaloc.cpp b/src/hotspot/share/opto/postaloc.cpp index 3514b37bc83..73bf1276371 100644 --- a/src/hotspot/share/opto/postaloc.cpp +++ b/src/hotspot/share/opto/postaloc.cpp @@ -152,7 +152,7 @@ int PhaseChaitin::yank_if_dead_recurse(Node *old, Node *orig_old, Block *current } } // Disconnect control and remove precedence edges if any exist - old->disconnect_inputs(NULL, C); + old->disconnect_inputs(C); } return blk_adjust; } diff --git a/src/hotspot/share/opto/reg_split.cpp b/src/hotspot/share/opto/reg_split.cpp index 761e0c94b71..a3c39cbfcae 100644 --- a/src/hotspot/share/opto/reg_split.cpp +++ b/src/hotspot/share/opto/reg_split.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -785,7 +785,7 @@ uint PhaseChaitin::Split(uint maxlrg, ResourceArea* split_arena) { if (i >= cnt) { // Found one unique input assert(_lrg_map.find_id(n) == _lrg_map.find_id(u), "should be the same lrg"); n->replace_by(u); // Then replace with unique input - n->disconnect_inputs(NULL, C); + n->disconnect_inputs(C); b->remove_node(insidx); insidx--; b->_ihrp_index--; diff --git a/src/hotspot/share/opto/regmask.cpp b/src/hotspot/share/opto/regmask.cpp index c09ed814bb0..f024387c985 100644 --- a/src/hotspot/share/opto/regmask.cpp +++ b/src/hotspot/share/opto/regmask.cpp @@ -32,8 +32,6 @@ #include "utilities/population_count.hpp" #include "utilities/powerOfTwo.hpp" -#define RM_SIZE _RM_SIZE /* a constant private to the class RegMask */ - //------------------------------dump------------------------------------------- #ifndef PRODUCT @@ -65,27 +63,29 @@ bool RegMask::is_vector(uint ireg) { } int RegMask::num_registers(uint ireg) { - switch(ireg) { - case Op_VecZ: - return SlotsPerVecZ; - case Op_VecY: - return SlotsPerVecY; - case Op_VecX: - return SlotsPerVecX; - case Op_VecD: - return SlotsPerVecD; - case Op_RegD: - case Op_RegL: + switch(ireg) { + case Op_VecZ: + return SlotsPerVecZ; + case Op_VecY: + return SlotsPerVecY; + case Op_VecX: + return SlotsPerVecX; + case Op_VecD: + return SlotsPerVecD; + case Op_RegD: + case Op_RegL: #ifdef _LP64 - case Op_RegP: + case Op_RegP: #endif - return 2; - case Op_VecA: - assert(Matcher::supports_scalable_vector(), "does not support scalable vector"); - return SlotsPerVecA; - } - // Op_VecS and the rest ideal registers. - return 1; + return 2; + case Op_VecA: + assert(Matcher::supports_scalable_vector(), "does not support scalable vector"); + return SlotsPerVecA; + default: + // Op_VecS and the rest ideal registers. + assert(ireg == Op_VecS || !is_vector(ireg), "unexpected, possibly multi-slot register"); + return 1; + } } int RegMask::num_registers(uint ireg, LRG &lrg) { @@ -101,14 +101,25 @@ int RegMask::num_registers(uint ireg, LRG &lrg) { return n_regs; } +static const uintptr_t zero = uintptr_t(0); // 0x00..00 +static const uintptr_t all = ~uintptr_t(0); // 0xFF..FF +static const uintptr_t fives = all/3; // 0x5555..55 + +// only indices of power 2 are accessed, so index 3 is only filled in for storage. +static const uintptr_t low_bits[5] = { fives, // 0x5555..55 + all/0xF, // 0x1111..11, + all/0xFF, // 0x0101..01, + zero, // 0x0000..00 + all/0xFFFF }; // 0x0001..01 + // Clear out partial bits; leave only bit pairs void RegMask::clear_to_pairs() { assert(valid_watermarks(), "sanity"); - for (int i = _lwm; i <= _hwm; i++) { - int bits = _A[i]; - bits &= ((bits & 0x55555555)<<1); // 1 hi-bit set for each pair - bits |= (bits>>1); // Smear 1 hi-bit into a pair - _A[i] = bits; + for (unsigned i = _lwm; i <= _hwm; i++) { + uintptr_t bits = _RM_UP[i]; + bits &= ((bits & fives) << 1U); // 1 hi-bit set for each pair + bits |= (bits >> 1U); // Smear 1 hi-bit into a pair + _RM_UP[i] = bits; } assert(is_aligned_pairs(), "mask is not aligned, adjacent pairs"); } @@ -120,16 +131,16 @@ bool RegMask::is_misaligned_pair() const { bool RegMask::is_aligned_pairs() const { // Assert that the register mask contains only bit pairs. assert(valid_watermarks(), "sanity"); - for (int i = _lwm; i <= _hwm; i++) { - int bits = _A[i]; + for (unsigned i = _lwm; i <= _hwm; i++) { + uintptr_t bits = _RM_UP[i]; while (bits) { // Check bits for pairing - int bit = bits & -bits; // Extract low bit + uintptr_t bit = uintptr_t(1) << find_lowest_bit(bits); // Extract low bit // Low bit is not odd means its mis-aligned. - if ((bit & 0x55555555) == 0) return false; + if ((bit & fives) == 0) return false; bits -= bit; // Remove bit from mask // Check for aligned adjacent bit - if ((bits & (bit<<1)) == 0) return false; - bits -= (bit<<1); // Remove other halve of pair + if ((bits & (bit << 1U)) == 0) return false; + bits -= (bit << 1U); // Remove other halve of pair } } return true; @@ -144,19 +155,19 @@ bool RegMask::is_bound1() const { // Return TRUE if the mask contains an adjacent pair of bits and no other bits. bool RegMask::is_bound_pair() const { if (is_AllStack()) return false; - int bit = -1; // Set to hold the one bit allowed + uintptr_t bit = all; // Set to hold the one bit allowed assert(valid_watermarks(), "sanity"); - for (int i = _lwm; i <= _hwm; i++) { - if (_A[i]) { // Found some bits - if (bit != -1) return false; // Already had bits, so fail - bit = _A[i] & -(_A[i]); // Extract 1 bit from mask - if ((bit << 1) != 0) { // Bit pair stays in same word? - if ((bit | (bit<<1)) != _A[i]) + for (unsigned i = _lwm; i <= _hwm; i++) { + if (_RM_UP[i]) { // Found some bits + if (bit != all) return false; // Already had bits, so fail + bit = uintptr_t(1) << find_lowest_bit(_RM_UP[i]); // Extract lowest bit from mask + if ((bit << 1U) != 0) { // Bit pair stays in same word? + if ((bit | (bit << 1U)) != _RM_UP[i]) return false; // Require adjacent bit pair and no more bits } else { // Else its a split-pair case - if(bit != _A[i]) return false; // Found many bits, so fail + if (bit != _RM_UP[i]) return false; // Found many bits, so fail i++; // Skip iteration forward - if (i > _hwm || _A[i] != 1) + if (i > _hwm || _RM_UP[i] != 1) return false; // Require 1 lo bit in next word } } @@ -186,9 +197,6 @@ bool RegMask::is_valid_reg(OptoReg::Name reg, const int size) const { return true; } -// only indicies of power 2 are accessed, so index 3 is only filled in for storage. -static int low_bits[5] = { 0x55555555, 0x11111111, 0x01010101, 0x00000000, 0x00010001 }; - // Find the lowest-numbered register set in the mask. Return the // HIGHEST register number in the set, or BAD if no sets. // Works also for size 1. @@ -200,90 +208,90 @@ OptoReg::Name RegMask::find_first_set(LRG &lrg, const int size) const { assert(is_aligned_sets(size), "mask is not aligned, adjacent sets"); } assert(valid_watermarks(), "sanity"); - for (int i = _lwm; i <= _hwm; i++) { - if (_A[i]) { // Found some bits + for (unsigned i = _lwm; i <= _hwm; i++) { + if (_RM_UP[i]) { // Found some bits // Convert to bit number, return hi bit in pair - return OptoReg::Name((i<<_LogWordBits) + find_lowest_bit(_A[i]) + (size - 1)); + return OptoReg::Name((i<<_LogWordBits) + find_lowest_bit(_RM_UP[i]) + (size - 1)); } } return OptoReg::Bad; } // Clear out partial bits; leave only aligned adjacent bit pairs -void RegMask::clear_to_sets(const int size) { +void RegMask::clear_to_sets(const unsigned int size) { if (size == 1) return; assert(2 <= size && size <= 16, "update low bits table"); assert(is_power_of_2(size), "sanity"); assert(valid_watermarks(), "sanity"); - int low_bits_mask = low_bits[size>>2]; - for (int i = _lwm; i <= _hwm; i++) { - int bits = _A[i]; - int sets = (bits & low_bits_mask); - for (int j = 1; j < size; j++) { - sets = (bits & (sets<<1)); // filter bits which produce whole sets + uintptr_t low_bits_mask = low_bits[size >> 2U]; + for (unsigned i = _lwm; i <= _hwm; i++) { + uintptr_t bits = _RM_UP[i]; + uintptr_t sets = (bits & low_bits_mask); + for (unsigned j = 1U; j < size; j++) { + sets = (bits & (sets << 1U)); // filter bits which produce whole sets } - sets |= (sets>>1); // Smear 1 hi-bit into a set + sets |= (sets >> 1U); // Smear 1 hi-bit into a set if (size > 2) { - sets |= (sets>>2); // Smear 2 hi-bits into a set + sets |= (sets >> 2U); // Smear 2 hi-bits into a set if (size > 4) { - sets |= (sets>>4); // Smear 4 hi-bits into a set + sets |= (sets >> 4U); // Smear 4 hi-bits into a set if (size > 8) { - sets |= (sets>>8); // Smear 8 hi-bits into a set + sets |= (sets >> 8U); // Smear 8 hi-bits into a set } } } - _A[i] = sets; + _RM_UP[i] = sets; } assert(is_aligned_sets(size), "mask is not aligned, adjacent sets"); } // Smear out partial bits to aligned adjacent bit sets -void RegMask::smear_to_sets(const int size) { +void RegMask::smear_to_sets(const unsigned int size) { if (size == 1) return; assert(2 <= size && size <= 16, "update low bits table"); assert(is_power_of_2(size), "sanity"); assert(valid_watermarks(), "sanity"); - int low_bits_mask = low_bits[size>>2]; - for (int i = _lwm; i <= _hwm; i++) { - int bits = _A[i]; - int sets = 0; - for (int j = 0; j < size; j++) { + uintptr_t low_bits_mask = low_bits[size >> 2U]; + for (unsigned i = _lwm; i <= _hwm; i++) { + uintptr_t bits = _RM_UP[i]; + uintptr_t sets = 0; + for (unsigned j = 0; j < size; j++) { sets |= (bits & low_bits_mask); // collect partial bits - bits = bits>>1; + bits = bits >> 1U; } - sets |= (sets<<1); // Smear 1 lo-bit into a set + sets |= (sets << 1U); // Smear 1 lo-bit into a set if (size > 2) { - sets |= (sets<<2); // Smear 2 lo-bits into a set + sets |= (sets << 2U); // Smear 2 lo-bits into a set if (size > 4) { - sets |= (sets<<4); // Smear 4 lo-bits into a set + sets |= (sets << 4U); // Smear 4 lo-bits into a set if (size > 8) { - sets |= (sets<<8); // Smear 8 lo-bits into a set + sets |= (sets << 8U); // Smear 8 lo-bits into a set } } } - _A[i] = sets; + _RM_UP[i] = sets; } assert(is_aligned_sets(size), "mask is not aligned, adjacent sets"); } // Assert that the register mask contains only bit sets. -bool RegMask::is_aligned_sets(const int size) const { +bool RegMask::is_aligned_sets(const unsigned int size) const { if (size == 1) return true; assert(2 <= size && size <= 16, "update low bits table"); assert(is_power_of_2(size), "sanity"); - int low_bits_mask = low_bits[size>>2]; + uintptr_t low_bits_mask = low_bits[size >> 2U]; assert(valid_watermarks(), "sanity"); - for (int i = _lwm; i <= _hwm; i++) { - int bits = _A[i]; + for (unsigned i = _lwm; i <= _hwm; i++) { + uintptr_t bits = _RM_UP[i]; while (bits) { // Check bits for pairing - int bit = bits & -bits; // Extract low bit + uintptr_t bit = uintptr_t(1) << find_lowest_bit(bits); // Low bit is not odd means its mis-aligned. if ((bit & low_bits_mask) == 0) { return false; } // Do extra work since (bit << size) may overflow. - int hi_bit = bit << (size-1); // high bit - int set = hi_bit + ((hi_bit-1) & ~(bit-1)); + uintptr_t hi_bit = bit << (size-1); // high bit + uintptr_t set = hi_bit + ((hi_bit-1) & ~(bit-1)); // Check for aligned adjacent bits in this set if ((bits & set) != set) { return false; @@ -296,32 +304,29 @@ bool RegMask::is_aligned_sets(const int size) const { // Return TRUE if the mask contains one adjacent set of bits and no other bits. // Works also for size 1. -int RegMask::is_bound_set(const int size) const { +bool RegMask::is_bound_set(const unsigned int size) const { if (is_AllStack()) return false; assert(1 <= size && size <= 16, "update low bits table"); assert(valid_watermarks(), "sanity"); - int bit = -1; // Set to hold the one bit allowed - for (int i = _lwm; i <= _hwm; i++) { - if (_A[i] ) { // Found some bits - if (bit != -1) - return false; // Already had bits, so fail - bit = _A[i] & -_A[i]; // Extract low bit from mask - int hi_bit = bit << (size-1); // high bit + uintptr_t bit = all; // Set to hold the one bit allowed + for (unsigned i = _lwm; i <= _hwm; i++) { + if (_RM_UP[i] ) { // Found some bits + if (bit != all) + return false; // Already had bits, so fail + unsigned bit_index = find_lowest_bit(_RM_UP[i]); + bit = uintptr_t(1) << bit_index; + uintptr_t hi_bit = bit << (size - 1); // high bit if (hi_bit != 0) { // Bit set stays in same word? - int set = hi_bit + ((hi_bit-1) & ~(bit-1)); - if (set != _A[i]) + uintptr_t set = hi_bit + ((hi_bit-1) & ~(bit-1)); + if (set != _RM_UP[i]) return false; // Require adjacent bit set and no more bits } else { // Else its a split-set case - if (((-1) & ~(bit-1)) != _A[i]) + if ((all & ~(bit-1)) != _RM_UP[i]) return false; // Found many bits, so fail i++; // Skip iteration forward and check high part - // The lower (32-size) bits should be 0 since it is split case. - int clear_bit_size = 32-size; - int shift_back_size = 32-clear_bit_size; - int set = bit>>clear_bit_size; - set = set & -set; // Remove sign extension. - set = (((set << size) - 1) >> shift_back_size); - if (i > _hwm || _A[i] != set) + // The lower (BitsPerWord - size) bits should be 1 since it is split case. + uintptr_t set = (bit >> (BitsPerWord - bit_index)) - 1; + if (i > _hwm || _RM_UP[i] != set) return false; // Require expected low bits in next word } } @@ -346,8 +351,8 @@ bool RegMask::is_UP() const { uint RegMask::Size() const { uint sum = 0; assert(valid_watermarks(), "sanity"); - for (int i = _lwm; i <= _hwm; i++) { - sum += population_count((unsigned)_A[i]); + for (unsigned i = _lwm; i <= _hwm; i++) { + sum += population_count(_RM_UP[i]); } return sum; } diff --git a/src/hotspot/share/opto/regmask.hpp b/src/hotspot/share/opto/regmask.hpp index d9165da6c90..bed1c416e1a 100644 --- a/src/hotspot/share/opto/regmask.hpp +++ b/src/hotspot/share/opto/regmask.hpp @@ -34,12 +34,12 @@ class LRG; //-------------Non-zero bit search methods used by RegMask--------------------- // Find lowest 1, undefined if empty/0 -static int find_lowest_bit(uint32_t mask) { +static unsigned int find_lowest_bit(uintptr_t mask) { return count_trailing_zeros(mask); } // Find highest 1, undefined if empty/0 -static int find_highest_bit(uint32_t mask) { - return count_leading_zeros(mask) ^ 31; +static unsigned int find_highest_bit(uintptr_t mask) { + return count_leading_zeros(mask) ^ (BitsPerWord - 1U); } //------------------------------RegMask---------------------------------------- @@ -48,37 +48,39 @@ static int find_highest_bit(uint32_t mask) { // just a collection of Register numbers. // The ADLC defines 2 macros, RM_SIZE and FORALL_BODY. -// RM_SIZE is the size of a register mask in words. +// RM_SIZE is the size of a register mask in 32-bit words. // FORALL_BODY replicates a BODY macro once per word in the register mask. // The usage is somewhat clumsy and limited to the regmask.[h,c]pp files. // However, it means the ADLC can redefine the unroll macro and all loops // over register masks will be unrolled by the correct amount. class RegMask { + + enum { + _WordBits = BitsPerWord, + _LogWordBits = LogBitsPerWord, + _RM_SIZE = LP64_ONLY(align_up(RM_SIZE, 2) >> 1) NOT_LP64(RM_SIZE) + }; + union { - double _dummy_force_double_alignment[RM_SIZE>>1]; // Array of Register Mask bits. This array is large enough to cover // all the machine registers and all parameters that need to be passed // on the stack (stack registers) up to some interesting limit. Methods // that need more parameters will NOT be compiled. On Intel, the limit // is something like 90+ parameters. - int _A[RM_SIZE]; + int _RM_I[RM_SIZE]; + uintptr_t _RM_UP[_RM_SIZE]; }; + // The low and high water marks represents the lowest and highest word // that might contain set register mask bits, respectively. We guarantee // that there are no bits in words outside this range, but any word at // and between the two marks can still be 0. - int _lwm; - int _hwm; - - enum { - _WordBits = BitsPerInt, - _LogWordBits = LogBitsPerInt, - _RM_SIZE = RM_SIZE // local constant, imported, then hidden by #undef - }; + unsigned int _lwm; + unsigned int _hwm; public: - enum { CHUNK_SIZE = RM_SIZE*_WordBits }; + enum { CHUNK_SIZE = RM_SIZE*BitsPerInt }; // SlotsPerLong is 2, since slots are 32 bits and longs are 64 bits. // Also, consider the maximum alignment size for a normally allocated @@ -108,13 +110,18 @@ class RegMask { FORALL_BODY # undef BODY int dummy = 0) { -# define BODY(I) _A[I] = a##I; +#if defined(VM_LITTLE_ENDIAN) || !defined(_LP64) +# define BODY(I) _RM_I[I] = a##I; +#else + // We need to swap ints. +# define BODY(I) _RM_I[I ^ 1] = a##I; +#endif FORALL_BODY # undef BODY _lwm = 0; - _hwm = RM_SIZE - 1; - while (_hwm > 0 && _A[_hwm] == 0) _hwm--; - while ((_lwm < _hwm) && _A[_lwm] == 0) _lwm++; + _hwm = _RM_SIZE - 1; + while (_hwm > 0 && _RM_UP[_hwm] == 0) _hwm--; + while ((_lwm < _hwm) && _RM_UP[_lwm] == 0) _lwm++; assert(valid_watermarks(), "post-condition"); } @@ -122,48 +129,41 @@ class RegMask { RegMask(RegMask *rm) { _hwm = rm->_hwm; _lwm = rm->_lwm; - for (int i = 0; i < RM_SIZE; i++) { - _A[i] = rm->_A[i]; + for (unsigned i = 0; i < _RM_SIZE; i++) { + _RM_UP[i] = rm->_RM_UP[i]; } assert(valid_watermarks(), "post-condition"); } // Construct an empty mask - RegMask() { - Clear(); - } + RegMask() : _RM_UP(), _lwm(_RM_SIZE - 1), _hwm(0) {} // Construct a mask with a single bit - RegMask(OptoReg::Name reg) { - Clear(); + RegMask(OptoReg::Name reg) : RegMask() { Insert(reg); } // Check for register being in mask - int Member(OptoReg::Name reg) const { + bool Member(OptoReg::Name reg) const { assert(reg < CHUNK_SIZE, ""); - return _A[reg>>_LogWordBits] & (1<<(reg&(_WordBits-1))); + + unsigned r = (unsigned)reg; + return _RM_UP[r >> _LogWordBits] & (uintptr_t(1) <<(r & (_WordBits - 1U))); } // The last bit in the register mask indicates that the mask should repeat // indefinitely with ONE bits. Returns TRUE if mask is infinite or // unbounded in size. Returns FALSE if mask is finite size. - int is_AllStack() const { return _A[RM_SIZE-1] >> (_WordBits-1); } - - // Work around an -xO3 optimization problme in WS6U1. The old way: - // void set_AllStack() { _A[RM_SIZE-1] |= (1<<(_WordBits-1)); } - // will cause _A[RM_SIZE-1] to be clobbered, not updated when set_AllStack() - // follows an Insert() loop, like the one found in init_spill_mask(). Using - // Insert() instead works because the index into _A in computed instead of - // constant. See bug 4665841. + bool is_AllStack() const { return _RM_UP[_RM_SIZE - 1U] >> (_WordBits - 1U); } + void set_AllStack() { Insert(OptoReg::Name(CHUNK_SIZE-1)); } // Test for being a not-empty mask. - int is_NotEmpty() const { + bool is_NotEmpty() const { assert(valid_watermarks(), "sanity"); - int tmp = 0; - for (int i = _lwm; i <= _hwm; i++) { - tmp |= _A[i]; + uintptr_t tmp = 0; + for (unsigned i = _lwm; i <= _hwm; i++) { + tmp |= _RM_UP[i]; } return tmp; } @@ -171,8 +171,8 @@ class RegMask { // Find lowest-numbered register from mask, or BAD if mask is empty. OptoReg::Name find_first_elem() const { assert(valid_watermarks(), "sanity"); - for (int i = _lwm; i <= _hwm; i++) { - int bits = _A[i]; + for (unsigned i = _lwm; i <= _hwm; i++) { + uintptr_t bits = _RM_UP[i]; if (bits) { return OptoReg::Name((i<<_LogWordBits) + find_lowest_bit(bits)); } @@ -183,8 +183,10 @@ class RegMask { // Get highest-numbered register from mask, or BAD if mask is empty. OptoReg::Name find_last_elem() const { assert(valid_watermarks(), "sanity"); - for (int i = _hwm; i >= _lwm; i--) { - int bits = _A[i]; + // Careful not to overflow if _lwm == 0 + unsigned i = _hwm + 1; + while (i > _lwm) { + uintptr_t bits = _RM_UP[--i]; if (bits) { return OptoReg::Name((i<<_LogWordBits) + find_highest_bit(bits)); } @@ -199,13 +201,13 @@ class RegMask { // Verify watermarks are sane, i.e., within bounds and that no // register words below or above the watermarks have bits set. bool valid_watermarks() const { - assert(_hwm >= 0 && _hwm < RM_SIZE, "_hwm out of range: %d", _hwm); - assert(_lwm >= 0 && _lwm < RM_SIZE, "_lwm out of range: %d", _lwm); - for (int i = 0; i < _lwm; i++) { - assert(_A[i] == 0, "_lwm too high: %d regs at: %d", _lwm, i); + assert(_hwm < _RM_SIZE, "_hwm out of range: %d", _hwm); + assert(_lwm < _RM_SIZE, "_lwm out of range: %d", _lwm); + for (unsigned i = 0; i < _lwm; i++) { + assert(_RM_UP[i] == 0, "_lwm too high: %d regs at: %d", _lwm, i); } - for (int i = _hwm + 1; i < RM_SIZE; i++) { - assert(_A[i] == 0, "_hwm too low: %d regs at: %d", _hwm, i); + for (unsigned i = _hwm + 1; i < _RM_SIZE; i++) { + assert(_RM_UP[i] == 0, "_hwm too low: %d regs at: %d", _hwm, i); } return true; } @@ -233,27 +235,27 @@ class RegMask { OptoReg::Name find_first_set(LRG &lrg, const int size) const; // Clear out partial bits; leave only aligned adjacent bit sets of size. - void clear_to_sets(const int size); + void clear_to_sets(const unsigned int size); // Smear out partial bits to aligned adjacent bit sets. - void smear_to_sets(const int size); + void smear_to_sets(const unsigned int size); // Test that the mask contains only aligned adjacent bit sets - bool is_aligned_sets(const int size) const; + bool is_aligned_sets(const unsigned int size) const; // Test for a single adjacent set - int is_bound_set(const int size) const; + bool is_bound_set(const unsigned int size) const; static bool is_vector(uint ireg); static int num_registers(uint ireg); static int num_registers(uint ireg, LRG &lrg); // Fast overlap test. Non-zero if any registers in common. - int overlap(const RegMask &rm) const { + bool overlap(const RegMask &rm) const { assert(valid_watermarks() && rm.valid_watermarks(), "sanity"); - int hwm = MIN2(_hwm, rm._hwm); - int lwm = MAX2(_lwm, rm._lwm); - int result = 0; - for (int i = lwm; i <= hwm; i++) { - result |= _A[i] & rm._A[i]; + unsigned hwm = MIN2(_hwm, rm._hwm); + unsigned lwm = MAX2(_lwm, rm._lwm); + uintptr_t result = 0; + for (unsigned i = lwm; i <= hwm; i++) { + result |= _RM_UP[i] & rm._RM_UP[i]; } return result; } @@ -264,35 +266,39 @@ class RegMask { // Clear a register mask void Clear() { - _lwm = RM_SIZE - 1; + _lwm = _RM_SIZE - 1; _hwm = 0; - memset(_A, 0, sizeof(int)*RM_SIZE); + memset(_RM_UP, 0, sizeof(uintptr_t)*_RM_SIZE); assert(valid_watermarks(), "sanity"); } // Fill a register mask with 1's void Set_All() { _lwm = 0; - _hwm = RM_SIZE - 1; - memset(_A, 0xFF, sizeof(int)*RM_SIZE); + _hwm = _RM_SIZE - 1; + memset(_RM_UP, 0xFF, sizeof(uintptr_t)*_RM_SIZE); assert(valid_watermarks(), "sanity"); } // Insert register into mask void Insert(OptoReg::Name reg) { + assert(reg != OptoReg::Bad, "sanity"); + assert(reg != OptoReg::Special, "sanity"); assert(reg < CHUNK_SIZE, "sanity"); assert(valid_watermarks(), "pre-condition"); - int index = reg>>_LogWordBits; + unsigned r = (unsigned)reg; + unsigned index = r >> _LogWordBits; if (index > _hwm) _hwm = index; if (index < _lwm) _lwm = index; - _A[index] |= (1<<(reg&(_WordBits-1))); + _RM_UP[index] |= (uintptr_t(1) << (r & (_WordBits - 1U))); assert(valid_watermarks(), "post-condition"); } // Remove register from mask void Remove(OptoReg::Name reg) { assert(reg < CHUNK_SIZE, ""); - _A[reg>>_LogWordBits] &= ~(1<<(reg&(_WordBits-1))); + unsigned r = (unsigned)reg; + _RM_UP[r >> _LogWordBits] &= ~(uintptr_t(1) << (r & (_WordBits-1U))); } // OR 'rm' into 'this' @@ -301,8 +307,8 @@ class RegMask { // OR widens the live range if (_lwm > rm._lwm) _lwm = rm._lwm; if (_hwm < rm._hwm) _hwm = rm._hwm; - for (int i = _lwm; i <= _hwm; i++) { - _A[i] |= rm._A[i]; + for (unsigned i = _lwm; i <= _hwm; i++) { + _RM_UP[i] |= rm._RM_UP[i]; } assert(valid_watermarks(), "sanity"); } @@ -312,8 +318,8 @@ class RegMask { assert(valid_watermarks() && rm.valid_watermarks(), "sanity"); // Do not evaluate words outside the current watermark range, as they are // already zero and an &= would not change that - for (int i = _lwm; i <= _hwm; i++) { - _A[i] &= rm._A[i]; + for (unsigned i = _lwm; i <= _hwm; i++) { + _RM_UP[i] &= rm._RM_UP[i]; } // Narrow the watermarks if &rm spans a narrower range. // Update after to ensure non-overlapping words are zeroed out. @@ -324,10 +330,10 @@ class RegMask { // Subtract 'rm' from 'this' void SUBTRACT(const RegMask &rm) { assert(valid_watermarks() && rm.valid_watermarks(), "sanity"); - int hwm = MIN2(_hwm, rm._hwm); - int lwm = MAX2(_lwm, rm._lwm); - for (int i = lwm; i <= hwm; i++) { - _A[i] &= ~rm._A[i]; + unsigned hwm = MIN2(_hwm, rm._hwm); + unsigned lwm = MAX2(_lwm, rm._lwm); + for (unsigned i = lwm; i <= hwm; i++) { + _RM_UP[i] &= ~rm._RM_UP[i]; } } diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index 1171dc9db99..f38a3a205d6 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -68,6 +68,7 @@ #include "runtime/javaCalls.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/signature.hpp" +#include "runtime/stackWatermarkSet.hpp" #include "runtime/threadCritical.hpp" #include "runtime/vframe.hpp" #include "runtime/vframeArray.hpp" @@ -960,14 +961,15 @@ const TypeFunc* OptoRuntime::counterMode_aescrypt_Type() { /* * void implCompress(byte[] buf, int ofs) */ -const TypeFunc* OptoRuntime::digestBase_implCompress_Type() { +const TypeFunc* OptoRuntime::digestBase_implCompress_Type(bool is_sha3) { // create input type (domain) - int num_args = 2; + int num_args = is_sha3 ? 3 : 2; int argcnt = num_args; const Type** fields = TypeTuple::fields(argcnt); int argp = TypeFunc::Parms; fields[argp++] = TypePtr::NOTNULL; // buf fields[argp++] = TypePtr::NOTNULL; // state + if (is_sha3) fields[argp++] = TypeInt::INT; // digest_length assert(argp == TypeFunc::Parms+argcnt, "correct decoding"); const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+argcnt, fields); @@ -981,14 +983,15 @@ const TypeFunc* OptoRuntime::digestBase_implCompress_Type() { /* * int implCompressMultiBlock(byte[] b, int ofs, int limit) */ -const TypeFunc* OptoRuntime::digestBase_implCompressMB_Type() { +const TypeFunc* OptoRuntime::digestBase_implCompressMB_Type(bool is_sha3) { // create input type (domain) - int num_args = 4; + int num_args = is_sha3 ? 5 : 4; int argcnt = num_args; const Type** fields = TypeTuple::fields(argcnt); int argp = TypeFunc::Parms; fields[argp++] = TypePtr::NOTNULL; // buf fields[argp++] = TypePtr::NOTNULL; // state + if (is_sha3) fields[argp++] = TypeInt::INT; // digest_length fields[argp++] = TypeInt::INT; // ofs fields[argp++] = TypeInt::INT; // limit assert(argp == TypeFunc::Parms+argcnt, "correct decoding"); @@ -1207,60 +1210,6 @@ const TypeFunc* OptoRuntime::osr_end_Type() { return TypeFunc::make(domain, range); } -//-------------- methodData update helpers - -const TypeFunc* OptoRuntime::profile_receiver_type_Type() { - // create input type (domain) - const Type **fields = TypeTuple::fields(2); - fields[TypeFunc::Parms+0] = TypeAryPtr::NOTNULL; // methodData pointer - fields[TypeFunc::Parms+1] = TypeInstPtr::BOTTOM; // receiver oop - const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+2, fields); - - // create result type - fields = TypeTuple::fields(1); - fields[TypeFunc::Parms+0] = NULL; // void - const TypeTuple *range = TypeTuple::make(TypeFunc::Parms, fields); - return TypeFunc::make(domain,range); -} - -JRT_LEAF(void, OptoRuntime::profile_receiver_type_C(DataLayout* data, oopDesc* receiver)) - if (receiver == NULL) return; - Klass* receiver_klass = receiver->klass(); - - intptr_t* mdp = ((intptr_t*)(data)) + DataLayout::header_size_in_cells(); - int empty_row = -1; // free row, if any is encountered - - // ReceiverTypeData* vc = new ReceiverTypeData(mdp); - for (uint row = 0; row < ReceiverTypeData::row_limit(); row++) { - // if (vc->receiver(row) == receiver_klass) - int receiver_off = ReceiverTypeData::receiver_cell_index(row); - intptr_t row_recv = *(mdp + receiver_off); - if (row_recv == (intptr_t) receiver_klass) { - // vc->set_receiver_count(row, vc->receiver_count(row) + DataLayout::counter_increment); - int count_off = ReceiverTypeData::receiver_count_cell_index(row); - *(mdp + count_off) += DataLayout::counter_increment; - return; - } else if (row_recv == 0) { - // else if (vc->receiver(row) == NULL) - empty_row = (int) row; - } - } - - if (empty_row != -1) { - int receiver_off = ReceiverTypeData::receiver_cell_index(empty_row); - // vc->set_receiver(empty_row, receiver_klass); - *(mdp + receiver_off) = (intptr_t) receiver_klass; - // vc->set_receiver_count(empty_row, DataLayout::counter_increment); - int count_off = ReceiverTypeData::receiver_count_cell_index(empty_row); - *(mdp + count_off) = DataLayout::counter_increment; - } else { - // Receiver did not match any saved receiver and there is no empty row for it. - // Increment total counter to indicate polymorphic case. - intptr_t* count_p = (intptr_t*)(((uint8_t*)(data)) + in_bytes(CounterData::count_offset())); - *count_p += DataLayout::counter_increment; - } -JRT_END - //------------------------------------------------------------------------------------- // register policy @@ -1286,7 +1235,6 @@ static void trace_exception(outputStream* st, oop exception_oop, address excepti // directly from compiled code. Compiled code will call the C++ method following. // We can't allow async exception to be installed during exception processing. JRT_ENTRY_NO_ASYNC(address, OptoRuntime::handle_exception_C_helper(JavaThread* thread, nmethod* &nm)) - // Do not confuse exception_oop with pending_exception. The exception_oop // is only used to pass arguments into the method. Not for general // exception handling. DO NOT CHANGE IT to use pending_exception, since @@ -1464,6 +1412,11 @@ address OptoRuntime::handle_exception_C(JavaThread* thread) { // *THIS IS NOT RECOMMENDED PROGRAMMING STYLE* // address OptoRuntime::rethrow_C(oopDesc* exception, JavaThread* thread, address ret_pc) { + // The frame we rethrow the exception to might not have been processed by the GC yet. + // The stack watermark barrier takes care of detecting that and ensuring the frame + // has updated oops. + StackWatermarkSet::after_unwind(thread); + #ifndef PRODUCT SharedRuntime::_rethrow_ctr++; // count rethrows #endif diff --git a/src/hotspot/share/opto/runtime.hpp b/src/hotspot/share/opto/runtime.hpp index c0985c6958a..8f07b71de33 100644 --- a/src/hotspot/share/opto/runtime.hpp +++ b/src/hotspot/share/opto/runtime.hpp @@ -229,9 +229,6 @@ class OptoRuntime : public AllStatic { static ExceptionBlob* exception_blob() { return _exception_blob; } - // Leaf routines helping with method data update - static void profile_receiver_type_C(DataLayout* data, oopDesc* receiver); - // Implicit exception support static void throw_div0_exception_C (JavaThread* thread); static void throw_stack_overflow_error_C(JavaThread* thread); @@ -278,8 +275,8 @@ class OptoRuntime : public AllStatic { static const TypeFunc* electronicCodeBook_aescrypt_Type(); static const TypeFunc* counterMode_aescrypt_Type(); - static const TypeFunc* digestBase_implCompress_Type(); - static const TypeFunc* digestBase_implCompressMB_Type(); + static const TypeFunc* digestBase_implCompress_Type(bool is_sha3); + static const TypeFunc* digestBase_implCompressMB_Type(bool is_sha3); static const TypeFunc* multiplyToLen_Type(); static const TypeFunc* montgomeryMultiply_Type(); @@ -304,9 +301,6 @@ class OptoRuntime : public AllStatic { // leaf on stack replacement interpreter accessor types static const TypeFunc* osr_end_Type(); - // leaf methodData routine types - static const TypeFunc* profile_receiver_type_Type(); - // leaf on stack replacement interpreter accessor types static const TypeFunc* fetch_int_Type(); static const TypeFunc* fetch_long_Type(); diff --git a/src/hotspot/share/opto/split_if.cpp b/src/hotspot/share/opto/split_if.cpp index f761bdc0c1e..6bf835926e5 100644 --- a/src/hotspot/share/opto/split_if.cpp +++ b/src/hotspot/share/opto/split_if.cpp @@ -337,7 +337,7 @@ Node *PhaseIdealLoop::spinup( Node *iff_dom, Node *new_false, Node *new_true, No if( t ) { // See if we already have this one // phi_post will not be used, so kill it _igvn.remove_dead_node(phi_post); - phi_post->destruct(); + phi_post->destruct(&_igvn); phi_post = t; } else { register_new_node( phi_post, prior_n ); diff --git a/src/hotspot/share/opto/stringopts.cpp b/src/hotspot/share/opto/stringopts.cpp index bbb1147fe13..42e0f1c7c6b 100644 --- a/src/hotspot/share/opto/stringopts.cpp +++ b/src/hotspot/share/opto/stringopts.cpp @@ -249,13 +249,13 @@ class StringConcat : public ResourceObj { _stringopts->gvn()->transform(call); C->gvn_replace_by(uct, call); - uct->disconnect_inputs(NULL, C); + uct->disconnect_inputs(C); } } void cleanup() { // disconnect the hook node - _arguments->disconnect_inputs(NULL, _stringopts->C); + _arguments->disconnect_inputs(_stringopts->C); } }; @@ -373,7 +373,7 @@ void StringConcat::eliminate_initialize(InitializeNode* init) { C->gvn_replace_by(mem_proj, mem); } C->gvn_replace_by(init, C->top()); - init->disconnect_inputs(NULL, C); + init->disconnect_inputs(C); } Node_List PhaseStringOpts::collect_toString_calls() { @@ -1210,6 +1210,7 @@ Node* PhaseStringOpts::int_stringSize(GraphKit& kit, Node* arg) { // Add loop predicate first. kit.add_empty_predicates(); + C->set_has_loops(true); RegionNode *loop = new RegionNode(3); loop->init_req(1, kit.control()); @@ -1287,6 +1288,7 @@ void PhaseStringOpts::getChars(GraphKit& kit, Node* arg, Node* dst_array, BasicT // Add loop predicate first. kit.add_empty_predicates(); + C->set_has_loops(true); RegionNode* head = new RegionNode(3); head->init_req(1, kit.control()); @@ -1981,6 +1983,6 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) { kit.replace_call(sc->end(), result); // Unhook any hook nodes - string_sizes->disconnect_inputs(NULL, C); + string_sizes->disconnect_inputs(C); sc->cleanup(); } diff --git a/src/hotspot/share/opto/subnode.cpp b/src/hotspot/share/opto/subnode.cpp index 1bc8e7fe78f..eaa007d35cb 100644 --- a/src/hotspot/share/opto/subnode.cpp +++ b/src/hotspot/share/opto/subnode.cpp @@ -61,20 +61,22 @@ Node* SubNode::Identity(PhaseGVN* phase) { } // Convert "(X+Y) - Y" into X and "(X+Y) - X" into Y - if( in(1)->Opcode() == Op_AddI ) { - if( phase->eqv(in(1)->in(2),in(2)) ) + if (in(1)->Opcode() == Op_AddI) { + if (in(1)->in(2) == in(2)) { return in(1)->in(1); - if (phase->eqv(in(1)->in(1),in(2))) + } + if (in(1)->in(1) == in(2)) { return in(1)->in(2); + } // Also catch: "(X + Opaque2(Y)) - Y". In this case, 'Y' is a loop-varying // trip counter and X is likely to be loop-invariant (that's how O2 Nodes // are originally used, although the optimizer sometimes jiggers things). // This folding through an O2 removes a loop-exit use of a loop-varying // value and generally lowers register pressure in and around the loop. - if( in(1)->in(2)->Opcode() == Op_Opaque2 && - phase->eqv(in(1)->in(2)->in(1),in(2)) ) + if (in(1)->in(2)->Opcode() == Op_Opaque2 && in(1)->in(2)->in(1) == in(2)) { return in(1)->in(1); + } } return ( phase->type( in(2) )->higher_equal( zero ) ) ? in(1) : this; @@ -154,11 +156,12 @@ Node *SubINode::Ideal(PhaseGVN *phase, bool can_reshape){ #ifdef ASSERT // Check for dead loop - if( phase->eqv( in1, this ) || phase->eqv( in2, this ) || - ( ( op1 == Op_AddI || op1 == Op_SubI ) && - ( phase->eqv( in1->in(1), this ) || phase->eqv( in1->in(2), this ) || - phase->eqv( in1->in(1), in1 ) || phase->eqv( in1->in(2), in1 ) ) ) ) + if ((in1 == this) || (in2 == this) || + ((op1 == Op_AddI || op1 == Op_SubI) && + ((in1->in(1) == this) || (in1->in(2) == this) || + (in1->in(1) == in1) || (in1->in(2) == in1)))) { assert(false, "dead loop in SubINode::Ideal"); + } #endif const Type *t2 = phase->type( in2 ); @@ -200,24 +203,25 @@ Node *SubINode::Ideal(PhaseGVN *phase, bool can_reshape){ #ifdef ASSERT // Check for dead loop - if( ( op2 == Op_AddI || op2 == Op_SubI ) && - ( phase->eqv( in2->in(1), this ) || phase->eqv( in2->in(2), this ) || - phase->eqv( in2->in(1), in2 ) || phase->eqv( in2->in(2), in2 ) ) ) + if ((op2 == Op_AddI || op2 == Op_SubI) && + ((in2->in(1) == this) || (in2->in(2) == this) || + (in2->in(1) == in2) || (in2->in(2) == in2))) { assert(false, "dead loop in SubINode::Ideal"); + } #endif // Convert "x - (x+y)" into "-y" - if( op2 == Op_AddI && - phase->eqv( in1, in2->in(1) ) ) - return new SubINode( phase->intcon(0),in2->in(2)); + if (op2 == Op_AddI && in1 == in2->in(1)) { + return new SubINode(phase->intcon(0), in2->in(2)); + } // Convert "(x-y) - x" into "-y" - if( op1 == Op_SubI && - phase->eqv( in1->in(1), in2 ) ) - return new SubINode( phase->intcon(0),in1->in(2)); + if (op1 == Op_SubI && in1->in(1) == in2) { + return new SubINode(phase->intcon(0), in1->in(2)); + } // Convert "x - (y+x)" into "-y" - if( op2 == Op_AddI && - phase->eqv( in1, in2->in(2) ) ) - return new SubINode( phase->intcon(0),in2->in(1)); + if (op2 == Op_AddI && in1 == in2->in(2)) { + return new SubINode(phase->intcon(0), in2->in(1)); + } // Convert "0 - (x-y)" into "y-x" if( t1 == TypeInt::ZERO && op2 == Op_SubI ) @@ -296,11 +300,12 @@ Node *SubLNode::Ideal(PhaseGVN *phase, bool can_reshape) { #ifdef ASSERT // Check for dead loop - if( phase->eqv( in1, this ) || phase->eqv( in2, this ) || - ( ( op1 == Op_AddL || op1 == Op_SubL ) && - ( phase->eqv( in1->in(1), this ) || phase->eqv( in1->in(2), this ) || - phase->eqv( in1->in(1), in1 ) || phase->eqv( in1->in(2), in1 ) ) ) ) + if ((in1 == this) || (in2 == this) || + ((op1 == Op_AddL || op1 == Op_SubL) && + ((in1->in(1) == this) || (in1->in(2) == this) || + (in1->in(1) == in1) || (in1->in(2) == in1)))) { assert(false, "dead loop in SubLNode::Ideal"); + } #endif if( phase->type( in2 ) == Type::TOP ) return NULL; @@ -340,20 +345,21 @@ Node *SubLNode::Ideal(PhaseGVN *phase, bool can_reshape) { #ifdef ASSERT // Check for dead loop - if( ( op2 == Op_AddL || op2 == Op_SubL ) && - ( phase->eqv( in2->in(1), this ) || phase->eqv( in2->in(2), this ) || - phase->eqv( in2->in(1), in2 ) || phase->eqv( in2->in(2), in2 ) ) ) + if ((op2 == Op_AddL || op2 == Op_SubL) && + ((in2->in(1) == this) || (in2->in(2) == this) || + (in2->in(1) == in2) || (in2->in(2) == in2))) { assert(false, "dead loop in SubLNode::Ideal"); + } #endif // Convert "x - (x+y)" into "-y" - if( op2 == Op_AddL && - phase->eqv( in1, in2->in(1) ) ) - return new SubLNode( phase->makecon(TypeLong::ZERO), in2->in(2)); + if (op2 == Op_AddL && in1 == in2->in(1)) { + return new SubLNode(phase->makecon(TypeLong::ZERO), in2->in(2)); + } // Convert "x - (y+x)" into "-y" - if( op2 == Op_AddL && - phase->eqv( in1, in2->in(2) ) ) - return new SubLNode( phase->makecon(TypeLong::ZERO),in2->in(1)); + if (op2 == Op_AddL && in1 == in2->in(2)) { + return new SubLNode(phase->makecon(TypeLong::ZERO), in2->in(1)); + } // Convert "0 - (x-y)" into "y-x" if( phase->type( in1 ) == TypeLong::ZERO && op2 == Op_SubL ) @@ -421,8 +427,8 @@ const Type* SubFPNode::Value(PhaseGVN* phase) const { // if both operands are infinity of same sign, the result is NaN; do // not replace with zero - if( (t1->is_finite() && t2->is_finite()) ) { - if( phase->eqv(in1, in2) ) return add_id(); + if (t1->is_finite() && t2->is_finite() && in1 == in2) { + return add_id(); } // Either input is BOTTOM ==> the result is the local BOTTOM @@ -445,11 +451,10 @@ Node *SubFNode::Ideal(PhaseGVN *phase, bool can_reshape) { } // Not associative because of boundary conditions (infinity) - if( IdealizedNumerics && !phase->C->method()->is_strict() ) { + if (IdealizedNumerics && !phase->C->method()->is_strict() && + in(2)->is_Add() && in(1) == in(2)->in(1)) { // Convert "x - (x+y)" into "-y" - if( in(2)->is_Add() && - phase->eqv(in(1),in(2)->in(1) ) ) - return new SubFNode( phase->makecon(TypeF::ZERO),in(2)->in(2)); + return new SubFNode(phase->makecon(TypeF::ZERO), in(2)->in(2)); } // Cannot replace 0.0-X with -X because a 'fsub' bytecode computes @@ -488,11 +493,10 @@ Node *SubDNode::Ideal(PhaseGVN *phase, bool can_reshape){ } // Not associative because of boundary conditions (infinity) - if( IdealizedNumerics && !phase->C->method()->is_strict() ) { + if (IdealizedNumerics && !phase->C->method()->is_strict() && + in(2)->is_Add() && in(1) == in(2)->in(1)) { // Convert "x - (x+y)" into "-y" - if( in(2)->is_Add() && - phase->eqv(in(1),in(2)->in(1) ) ) - return new SubDNode( phase->makecon(TypeD::ZERO),in(2)->in(2)); + return new SubDNode(phase->makecon(TypeD::ZERO), in(2)->in(2)); } // Cannot replace 0.0-X with -X because a 'dsub' bytecode computes diff --git a/src/hotspot/share/opto/subnode.hpp b/src/hotspot/share/opto/subnode.hpp index 0976f09d4c0..99be190c1d2 100644 --- a/src/hotspot/share/opto/subnode.hpp +++ b/src/hotspot/share/opto/subnode.hpp @@ -404,6 +404,28 @@ class NegNode : public Node { NegNode( Node *in1 ) : Node(0,in1) {} }; +//------------------------------NegINode--------------------------------------- +// Negate value an int. For int values, negation is the same as subtraction +// from zero +class NegINode : public NegNode { +public: + NegINode(Node *in1) : NegNode(in1) {} + virtual int Opcode() const; + const Type *bottom_type() const { return TypeInt::INT; } + virtual uint ideal_reg() const { return Op_RegI; } +}; + +//------------------------------NegLNode--------------------------------------- +// Negate value an int. For int values, negation is the same as subtraction +// from zero +class NegLNode : public NegNode { +public: + NegLNode(Node *in1) : NegNode(in1) {} + virtual int Opcode() const; + const Type *bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } +}; + //------------------------------NegFNode--------------------------------------- // Negate value a float. Negating 0.0 returns -0.0, but subtracting from // zero returns +0.0 (per JVM spec on 'fneg' bytecode). As subtraction diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index 8dfed327bf7..a7515d3603e 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -91,6 +91,8 @@ SuperWord::SuperWord(PhaseIdealLoop* phase) : #endif } +static const bool _do_vector_loop_experimental = false; // Experimental vectorization which uses data from loop unrolling. + //------------------------------transform_loop--------------------------- void SuperWord::transform_loop(IdealLoopTree* lpt, bool do_optimization) { assert(UseSuperWord, "should be"); @@ -470,7 +472,7 @@ void SuperWord::SLP_extract() { CountedLoopNode *cl = lpt()->_head->as_CountedLoop(); bool post_loop_allowed = (PostLoopMultiversioning && Matcher::has_predicated_vectors() && cl->is_post_loop()); if (cl->is_main_loop()) { - if (_do_vector_loop) { + if (_do_vector_loop_experimental) { if (mark_generations() != -1) { hoist_loads_in_graph(); // this only rebuild the graph; all basic structs need rebuild explicitly @@ -508,11 +510,13 @@ void SuperWord::SLP_extract() { extend_packlist(); - if (_do_vector_loop) { + if (_do_vector_loop_experimental) { if (_packset.length() == 0) { +#ifndef PRODUCT if (TraceSuperWord) { tty->print_cr("\nSuperWord::_do_vector_loop DFA could not build packset, now trying to build anyway"); } +#endif pack_parallel(); } } @@ -1723,7 +1727,14 @@ void SuperWord::construct_my_pack_map() { Node_List* p = _packset.at(i); for (uint j = 0; j < p->size(); j++) { Node* s = p->at(j); - assert(my_pack(s) == NULL, "only in one pack"); +#ifdef ASSERT + if (my_pack(s) != NULL) { + s->dump(1); + tty->print_cr("packs[%d]:", i); + print_pack(p); + assert(false, "only in one pack"); + } +#endif set_my_pack(s, p); } } @@ -1738,7 +1749,7 @@ void SuperWord::filter_packs() { bool impl = implemented(pk); if (!impl) { #ifndef PRODUCT - if (TraceSuperWord && Verbose) { + if ((TraceSuperWord && Verbose) || _vector_loop_debug) { tty->print_cr("Unimplemented"); pk->at(0)->dump(); } @@ -1762,7 +1773,7 @@ void SuperWord::filter_packs() { bool prof = profitable(pk); if (!prof) { #ifndef PRODUCT - if (TraceSuperWord && Verbose) { + if ((TraceSuperWord && Verbose) || _vector_loop_debug) { tty->print_cr("Unprofitable"); pk->at(0)->dump(); } @@ -2767,7 +2778,7 @@ Node* SuperWord::vector_opd(Node_List* p, int opd_idx) { } } // Move shift count into vector register. - cnt = VectorNode::shift_count(p0, cnt, vlen, velt_basic_type(p0)); + cnt = VectorNode::shift_count(p0->Opcode(), cnt, vlen, velt_basic_type(p0)); _igvn.register_new_node_with_optimizer(cnt); _phase->set_ctrl(cnt, _phase->get_ctrl(opd)); return cnt; @@ -3052,12 +3063,13 @@ bool SuperWord::construct_bb() { int ii_current = -1; unsigned int load_idx = (unsigned int)-1; - _ii_order.clear(); + // Build iterations order if needed + bool build_ii_order = _do_vector_loop_experimental && _ii_order.is_empty(); // Create real map of block indices for nodes for (int j = 0; j < _block.length(); j++) { Node* n = _block.at(j); set_bb_idx(n, j); - if (_do_vector_loop && n->is_Load()) { + if (build_ii_order && n->is_Load()) { if (ii_current == -1) { ii_current = _clone_map.gen(n->_idx); _ii_order.push(ii_current); @@ -4700,6 +4712,15 @@ bool SuperWord::pack_parallel() { _packset.clear(); + if (_ii_order.is_empty()) { +#ifndef PRODUCT + if (_vector_loop_debug) { + tty->print_cr("SuperWord::pack_parallel: EMPTY"); + } +#endif + return false; + } + for (int ii = 0; ii < _iteration_first.length(); ii++) { Node* nd = _iteration_first.at(ii); if (in_bb(nd) && (nd->is_Load() || nd->is_Store() || nd->is_Add() || nd->is_Mul())) { diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index dc7af3d4b29..ff26780f786 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -439,16 +439,22 @@ void Type::Initialize_shared(Compile* current) { BOTTOM = make(Bottom); // Everything HALF = make(Half); // Placeholder half of doublewide type + TypeF::MAX = TypeF::make(max_jfloat); // Float MAX + TypeF::MIN = TypeF::make(min_jfloat); // Float MIN TypeF::ZERO = TypeF::make(0.0); // Float 0 (positive zero) TypeF::ONE = TypeF::make(1.0); // Float 1 TypeF::POS_INF = TypeF::make(jfloat_cast(POSITIVE_INFINITE_F)); TypeF::NEG_INF = TypeF::make(-jfloat_cast(POSITIVE_INFINITE_F)); + TypeD::MAX = TypeD::make(max_jdouble); // Double MAX + TypeD::MIN = TypeD::make(min_jdouble); // Double MIN TypeD::ZERO = TypeD::make(0.0); // Double 0 (positive zero) TypeD::ONE = TypeD::make(1.0); // Double 1 TypeD::POS_INF = TypeD::make(jdouble_cast(POSITIVE_INFINITE_D)); TypeD::NEG_INF = TypeD::make(-jdouble_cast(POSITIVE_INFINITE_D)); + TypeInt::MAX = TypeInt::make(max_jint); // Int MAX + TypeInt::MIN = TypeInt::make(min_jint); // Int MIN TypeInt::MINUS_1 = TypeInt::make(-1); // -1 TypeInt::ZERO = TypeInt::make( 0); // 0 TypeInt::ONE = TypeInt::make( 1); // 1 @@ -477,6 +483,8 @@ void Type::Initialize_shared(Compile* current) { assert( TypeInt::CC_GE == TypeInt::BOOL, "types must match for CmpL to work" ); assert( (juint)(TypeInt::CC->_hi - TypeInt::CC->_lo) <= SMALLINT, "CC is truly small"); + TypeLong::MAX = TypeLong::make(max_jlong); // Long MAX + TypeLong::MIN = TypeLong::make(min_jlong); // Long MIN TypeLong::MINUS_1 = TypeLong::make(-1); // -1 TypeLong::ZERO = TypeLong::make( 0); // 0 TypeLong::ONE = TypeLong::make( 1); // 1 @@ -1119,6 +1127,8 @@ void Type::typerr( const Type *t ) const { //============================================================================= // Convenience common pre-built types. +const TypeF *TypeF::MAX; // Floating point max +const TypeF *TypeF::MIN; // Floating point min const TypeF *TypeF::ZERO; // Floating point zero const TypeF *TypeF::ONE; // Floating point one const TypeF *TypeF::POS_INF; // Floating point positive infinity @@ -1229,6 +1239,8 @@ bool TypeF::empty(void) const { //============================================================================= // Convenience common pre-built types. +const TypeD *TypeD::MAX; // Floating point max +const TypeD *TypeD::MIN; // Floating point min const TypeD *TypeD::ZERO; // Floating point zero const TypeD *TypeD::ONE; // Floating point one const TypeD *TypeD::POS_INF; // Floating point positive infinity @@ -1335,6 +1347,8 @@ bool TypeD::empty(void) const { //============================================================================= // Convience common pre-built types. +const TypeInt *TypeInt::MAX; // INT_MAX +const TypeInt *TypeInt::MIN; // INT_MIN const TypeInt *TypeInt::MINUS_1;// -1 const TypeInt *TypeInt::ZERO; // 0 const TypeInt *TypeInt::ONE; // 1 @@ -1604,6 +1618,8 @@ bool TypeInt::empty(void) const { //============================================================================= // Convenience common pre-built types. +const TypeLong *TypeLong::MAX; +const TypeLong *TypeLong::MIN; const TypeLong *TypeLong::MINUS_1;// -1 const TypeLong *TypeLong::ZERO; // 0 const TypeLong *TypeLong::ONE; // 1 @@ -3030,9 +3046,11 @@ TypeOopPtr::TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, bool xk, ciObject* o, int o } else if (klass() == ciEnv::current()->Class_klass() && _offset >= InstanceMirrorKlass::offset_of_static_fields()) { // Static fields - assert(o != NULL, "must be constant"); - ciInstanceKlass* k = o->as_instance()->java_lang_Class_klass()->as_instance_klass(); - ciField* field = k->get_field_by_offset(_offset, true); + ciField* field = NULL; + if (const_oop() != NULL) { + ciInstanceKlass* k = const_oop()->as_instance()->java_lang_Class_klass()->as_instance_klass(); + field = k->get_field_by_offset(_offset, true); + } if (field != NULL) { BasicType basic_elem_type = field->layout_type(); _is_ptr_to_narrowoop = UseCompressedOops && is_reference_type(basic_elem_type); diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 989630b17a8..51a48c04e4b 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -483,6 +483,8 @@ class TypeF : public Type { virtual const Type *xmeet( const Type *t ) const; virtual const Type *xdual() const; // Compute dual right now. // Convenience common pre-built types. + static const TypeF *MAX; + static const TypeF *MIN; static const TypeF *ZERO; // positive zero only static const TypeF *ONE; static const TypeF *POS_INF; @@ -512,6 +514,8 @@ class TypeD : public Type { virtual const Type *xmeet( const Type *t ) const; virtual const Type *xdual() const; // Compute dual right now. // Convenience common pre-built types. + static const TypeD *MAX; + static const TypeD *MIN; static const TypeD *ZERO; // positive zero only static const TypeD *ONE; static const TypeD *POS_INF; @@ -555,6 +559,8 @@ class TypeInt : public Type { virtual const Type *narrow( const Type *t ) const; // Do not kill _widen bits. // Convenience common pre-built types. + static const TypeInt *MAX; + static const TypeInt *MIN; static const TypeInt *MINUS_1; static const TypeInt *ZERO; static const TypeInt *ONE; @@ -620,6 +626,8 @@ class TypeLong : public Type { virtual const Type *widen( const Type *t, const Type* limit_type ) const; virtual const Type *narrow( const Type *t ) const; // Convenience common pre-built types. + static const TypeLong *MAX; + static const TypeLong *MIN; static const TypeLong *MINUS_1; static const TypeLong *ZERO; static const TypeLong *ONE; diff --git a/src/hotspot/share/opto/vector.cpp b/src/hotspot/share/opto/vector.cpp new file mode 100644 index 00000000000..2bae87acf7d --- /dev/null +++ b/src/hotspot/share/opto/vector.cpp @@ -0,0 +1,466 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "opto/castnode.hpp" +#include "opto/graphKit.hpp" +#include "opto/phaseX.hpp" +#include "opto/rootnode.hpp" +#include "opto/vector.hpp" +#include "utilities/macros.hpp" + +void PhaseVector::optimize_vector_boxes() { + Compile::TracePhase tp("vector_elimination", &timers[_t_vector_elimination]); + + // Signal GraphKit it's post-parse phase. + assert(C->inlining_incrementally() == false, "sanity"); + C->set_inlining_incrementally(true); + + C->for_igvn()->clear(); + C->initial_gvn()->replace_with(&_igvn); + + expand_vunbox_nodes(); + scalarize_vbox_nodes(); + + C->inline_vector_reboxing_calls(); + + expand_vbox_nodes(); + eliminate_vbox_alloc_nodes(); + + C->set_inlining_incrementally(false); + + do_cleanup(); +} + +void PhaseVector::do_cleanup() { + if (C->failing()) return; + { + Compile::TracePhase tp("vector_pru", &timers[_t_vector_pru]); + ResourceMark rm; + PhaseRemoveUseless pru(C->initial_gvn(), C->for_igvn()); + if (C->failing()) return; + } + { + Compile::TracePhase tp("incrementalInline_igvn", &timers[_t_vector_igvn]); + _igvn = PhaseIterGVN(C->initial_gvn()); + _igvn.optimize(); + if (C->failing()) return; + } + C->print_method(PHASE_ITER_GVN_BEFORE_EA, 3); +} + +void PhaseVector::scalarize_vbox_nodes() { + if (C->failing()) return; + + if (!EnableVectorReboxing) { + return; // don't scalarize vector boxes + } + + int macro_idx = C->macro_count() - 1; + while (macro_idx >= 0) { + Node * n = C->macro_node(macro_idx); + assert(n->is_macro(), "only macro nodes expected here"); + if (n->Opcode() == Op_VectorBox) { + VectorBoxNode* vbox = static_cast(n); + scalarize_vbox_node(vbox); + if (C->failing()) return; + C->print_method(PHASE_SCALARIZE_VBOX, vbox, 3); + } + if (C->failing()) return; + macro_idx = MIN2(macro_idx - 1, C->macro_count() - 1); + } +} + +void PhaseVector::expand_vbox_nodes() { + if (C->failing()) return; + + int macro_idx = C->macro_count() - 1; + while (macro_idx >= 0) { + Node * n = C->macro_node(macro_idx); + assert(n->is_macro(), "only macro nodes expected here"); + if (n->Opcode() == Op_VectorBox) { + VectorBoxNode* vbox = static_cast(n); + expand_vbox_node(vbox); + if (C->failing()) return; + } + if (C->failing()) return; + macro_idx = MIN2(macro_idx - 1, C->macro_count() - 1); + } +} + +void PhaseVector::expand_vunbox_nodes() { + if (C->failing()) return; + + int macro_idx = C->macro_count() - 1; + while (macro_idx >= 0) { + Node * n = C->macro_node(macro_idx); + assert(n->is_macro(), "only macro nodes expected here"); + if (n->Opcode() == Op_VectorUnbox) { + VectorUnboxNode* vec_unbox = static_cast(n); + expand_vunbox_node(vec_unbox); + if (C->failing()) return; + C->print_method(PHASE_EXPAND_VUNBOX, vec_unbox, 3); + } + if (C->failing()) return; + macro_idx = MIN2(macro_idx - 1, C->macro_count() - 1); + } +} + +void PhaseVector::eliminate_vbox_alloc_nodes() { + if (C->failing()) return; + + int macro_idx = C->macro_count() - 1; + while (macro_idx >= 0) { + Node * n = C->macro_node(macro_idx); + assert(n->is_macro(), "only macro nodes expected here"); + if (n->Opcode() == Op_VectorBoxAllocate) { + VectorBoxAllocateNode* vbox_alloc = static_cast(n); + eliminate_vbox_alloc_node(vbox_alloc); + if (C->failing()) return; + C->print_method(PHASE_ELIMINATE_VBOX_ALLOC, vbox_alloc, 3); + } + if (C->failing()) return; + macro_idx = MIN2(macro_idx - 1, C->macro_count() - 1); + } +} + +static JVMState* clone_jvms(Compile* C, SafePointNode* sfpt) { + JVMState* new_jvms = sfpt->jvms()->clone_shallow(C); + uint size = sfpt->req(); + SafePointNode* map = new SafePointNode(size, new_jvms); + for (uint i = 0; i < size; i++) { + map->init_req(i, sfpt->in(i)); + } + new_jvms->set_map(map); + return new_jvms; +} + +void PhaseVector::scalarize_vbox_node(VectorBoxNode* vec_box) { + Node* vec_value = vec_box->in(VectorBoxNode::Value); + PhaseGVN& gvn = *C->initial_gvn(); + + // Process merged VBAs + + if (EnableVectorAggressiveReboxing) { + Unique_Node_List calls(C->comp_arena()); + for (DUIterator_Fast imax, i = vec_box->fast_outs(imax); i < imax; i++) { + Node* use = vec_box->fast_out(i); + if (use->is_CallJava()) { + CallJavaNode* call = use->as_CallJava(); + if (call->has_non_debug_use(vec_box) && vec_box->in(VectorBoxNode::Box)->is_Phi()) { + calls.push(call); + } + } + } + + while (calls.size() > 0) { + CallJavaNode* call = calls.pop()->as_CallJava(); + // Attach new VBA to the call and use it instead of Phi (VBA ... VBA). + + JVMState* jvms = clone_jvms(C, call); + GraphKit kit(jvms); + PhaseGVN& gvn = kit.gvn(); + + // Adjust JVMS from post-call to pre-call state: put args on stack + uint nargs = call->method()->arg_size(); + kit.ensure_stack(kit.sp() + nargs); + for (uint i = TypeFunc::Parms; i < call->tf()->domain()->cnt(); i++) { + kit.push(call->in(i)); + } + jvms = kit.sync_jvms(); + + Node* new_vbox = NULL; + { + PreserveReexecuteState prs(&kit); + + kit.jvms()->set_should_reexecute(true); + + const TypeInstPtr* vbox_type = vec_box->box_type(); + const TypeVect* vect_type = vec_box->vec_type(); + Node* vect = vec_box->in(VectorBoxNode::Value); + + VectorBoxAllocateNode* alloc = new VectorBoxAllocateNode(C, vbox_type); + kit.set_edges_for_java_call(alloc, /*must_throw=*/false, /*separate_io_proj=*/true); + kit.make_slow_call_ex(alloc, C->env()->Throwable_klass(), /*separate_io_proj=*/true, /*deoptimize=*/true); + kit.set_i_o(gvn.transform( new ProjNode(alloc, TypeFunc::I_O) )); + kit.set_all_memory(gvn.transform( new ProjNode(alloc, TypeFunc::Memory) )); + Node* ret = gvn.transform(new ProjNode(alloc, TypeFunc::Parms)); + + new_vbox = gvn.transform(new VectorBoxNode(C, ret, vect, vbox_type, vect_type)); + + kit.replace_in_map(vec_box, new_vbox); + } + + kit.dec_sp(nargs); + jvms = kit.sync_jvms(); + + call->set_req(TypeFunc::Control , kit.control()); + call->set_req(TypeFunc::I_O , kit.i_o()); + call->set_req(TypeFunc::Memory , kit.reset_memory()); + call->set_req(TypeFunc::FramePtr, kit.frameptr()); + call->replace_edge(vec_box, new_vbox); + + C->record_for_igvn(call); + } + } + + // Process debug uses at safepoints + Unique_Node_List safepoints(C->comp_arena()); + + for (DUIterator_Fast imax, i = vec_box->fast_outs(imax); i < imax; i++) { + Node* use = vec_box->fast_out(i); + if (use->is_SafePoint()) { + SafePointNode* sfpt = use->as_SafePoint(); + if (!sfpt->is_Call() || !sfpt->as_Call()->has_non_debug_use(vec_box)) { + safepoints.push(sfpt); + } + } + } + + while (safepoints.size() > 0) { + SafePointNode* sfpt = safepoints.pop()->as_SafePoint(); + + uint first_ind = (sfpt->req() - sfpt->jvms()->scloff()); + Node* sobj = new SafePointScalarObjectNode(vec_box->box_type(), +#ifdef ASSERT + NULL, +#endif // ASSERT + first_ind, /*n_fields=*/1); + sobj->init_req(0, C->root()); + sfpt->add_req(vec_value); + + sobj = gvn.transform(sobj); + + JVMState *jvms = sfpt->jvms(); + + jvms->set_endoff(sfpt->req()); + // Now make a pass over the debug information replacing any references + // to the allocated object with "sobj" + int start = jvms->debug_start(); + int end = jvms->debug_end(); + sfpt->replace_edges_in_range(vec_box, sobj, start, end); + + C->record_for_igvn(sfpt); + } +} + +void PhaseVector::expand_vbox_node(VectorBoxNode* vec_box) { + if (vec_box->outcnt() > 0) { + Node* vbox = vec_box->in(VectorBoxNode::Box); + Node* vect = vec_box->in(VectorBoxNode::Value); + Node* result = expand_vbox_node_helper(vbox, vect, vec_box->box_type(), vec_box->vec_type()); + C->gvn_replace_by(vec_box, result); + C->print_method(PHASE_EXPAND_VBOX, vec_box, 3); + } + C->remove_macro_node(vec_box); +} + +Node* PhaseVector::expand_vbox_node_helper(Node* vbox, + Node* vect, + const TypeInstPtr* box_type, + const TypeVect* vect_type) { + if (vbox->is_Phi() && vect->is_Phi()) { + assert(vbox->as_Phi()->region() == vect->as_Phi()->region(), ""); + Node* new_phi = new PhiNode(vbox->as_Phi()->region(), box_type); + for (uint i = 1; i < vbox->req(); i++) { + Node* new_box = expand_vbox_node_helper(vbox->in(i), vect->in(i), box_type, vect_type); + new_phi->set_req(i, new_box); + } + new_phi = C->initial_gvn()->transform(new_phi); + return new_phi; + } else if (vbox->is_Proj() && vbox->in(0)->Opcode() == Op_VectorBoxAllocate) { + VectorBoxAllocateNode* vbox_alloc = static_cast(vbox->in(0)); + return expand_vbox_alloc_node(vbox_alloc, vect, box_type, vect_type); + } else { + assert(!vbox->is_Phi(), ""); + // TODO: assert that expanded vbox is initialized with the same value (vect). + return vbox; // already expanded + } +} + +static bool is_vector_mask(ciKlass* klass) { + return klass->is_subclass_of(ciEnv::current()->vector_VectorMask_klass()); +} + +static bool is_vector_shuffle(ciKlass* klass) { + return klass->is_subclass_of(ciEnv::current()->vector_VectorShuffle_klass()); +} + +Node* PhaseVector::expand_vbox_alloc_node(VectorBoxAllocateNode* vbox_alloc, + Node* value, + const TypeInstPtr* box_type, + const TypeVect* vect_type) { + JVMState* jvms = clone_jvms(C, vbox_alloc); + GraphKit kit(jvms); + PhaseGVN& gvn = kit.gvn(); + + ciInstanceKlass* box_klass = box_type->klass()->as_instance_klass(); + BasicType bt = vect_type->element_basic_type(); + int num_elem = vect_type->length(); + + bool is_mask = is_vector_mask(box_klass); + if (is_mask && bt != T_BOOLEAN) { + value = gvn.transform(VectorStoreMaskNode::make(gvn, value, bt, num_elem)); + // Although type of mask depends on its definition, in terms of storage everything is stored in boolean array. + bt = T_BOOLEAN; + assert(value->as_Vector()->bottom_type()->is_vect()->element_basic_type() == bt, + "must be consistent with mask representation"); + } + + // Generate array allocation for the field which holds the values. + const TypeKlassPtr* array_klass = TypeKlassPtr::make(ciTypeArrayKlass::make(bt)); + Node* arr = kit.new_array(kit.makecon(array_klass), kit.intcon(num_elem), 1); + + // Store the vector value into the array. + // (The store should be captured by InitializeNode and turned into initialized store later.) + Node* arr_adr = kit.array_element_address(arr, kit.intcon(0), bt); + const TypePtr* arr_adr_type = arr_adr->bottom_type()->is_ptr(); + Node* arr_mem = kit.memory(arr_adr); + Node* vstore = gvn.transform(StoreVectorNode::make(0, + kit.control(), + arr_mem, + arr_adr, + arr_adr_type, + value, + num_elem)); + kit.set_memory(vstore, arr_adr_type); + + C->set_max_vector_size(MAX2(C->max_vector_size(), vect_type->length_in_bytes())); + + // Generate the allocate for the Vector object. + const TypeKlassPtr* klass_type = box_type->as_klass_type(); + Node* klass_node = kit.makecon(klass_type); + Node* vec_obj = kit.new_instance(klass_node); + + // Store the allocated array into object. + ciField* field = ciEnv::current()->vector_VectorPayload_klass()->get_field_by_name(ciSymbol::payload_name(), + ciSymbol::object_signature(), + false); + assert(field != NULL, ""); + Node* vec_field = kit.basic_plus_adr(vec_obj, field->offset_in_bytes()); + const TypePtr* vec_adr_type = vec_field->bottom_type()->is_ptr(); + + // The store should be captured by InitializeNode and turned into initialized store later. + Node* field_store = gvn.transform(kit.access_store_at(vec_obj, + vec_field, + vec_adr_type, + arr, + TypeOopPtr::make_from_klass(field->type()->as_klass()), + T_OBJECT, + IN_HEAP)); + kit.set_memory(field_store, vec_adr_type); + + kit.replace_call(vbox_alloc, vec_obj, true); + C->remove_macro_node(vbox_alloc); + + return vec_obj; +} + +void PhaseVector::expand_vunbox_node(VectorUnboxNode* vec_unbox) { + if (vec_unbox->outcnt() > 0) { + GraphKit kit; + PhaseGVN& gvn = kit.gvn(); + + Node* obj = vec_unbox->obj(); + const TypeInstPtr* tinst = gvn.type(obj)->isa_instptr(); + ciInstanceKlass* from_kls = tinst->klass()->as_instance_klass(); + BasicType bt = vec_unbox->vect_type()->element_basic_type(); + BasicType masktype = bt; + BasicType elem_bt; + + if (is_vector_mask(from_kls)) { + bt = T_BOOLEAN; + } else if (is_vector_shuffle(from_kls)) { + if (vec_unbox->is_shuffle_to_vector() == true) { + elem_bt = bt; + } + bt = T_BYTE; + } + + ciField* field = ciEnv::current()->vector_VectorPayload_klass()->get_field_by_name(ciSymbol::payload_name(), + ciSymbol::object_signature(), + false); + assert(field != NULL, ""); + int offset = field->offset_in_bytes(); + Node* vec_adr = kit.basic_plus_adr(obj, offset); + + Node* mem = vec_unbox->mem(); + Node* ctrl = vec_unbox->in(0); + Node* vec_field_ld = LoadNode::make(gvn, + ctrl, + mem, + vec_adr, + vec_adr->bottom_type()->is_ptr(), + TypeOopPtr::make_from_klass(field->type()->as_klass()), + T_OBJECT, + MemNode::unordered); + vec_field_ld = gvn.transform(vec_field_ld); + + // For proper aliasing, attach concrete payload type. + ciKlass* payload_klass = ciTypeArrayKlass::make(bt); + const Type* payload_type = TypeAryPtr::make_from_klass(payload_klass)->cast_to_ptr_type(TypePtr::NotNull); + vec_field_ld = gvn.transform(new CastPPNode(vec_field_ld, payload_type)); + + Node* adr = kit.array_element_address(vec_field_ld, gvn.intcon(0), bt); + const TypePtr* adr_type = adr->bottom_type()->is_ptr(); + const TypeVect* vt = vec_unbox->bottom_type()->is_vect(); + int num_elem = vt->length(); + Node* vec_val_load = LoadVectorNode::make(0, + ctrl, + mem, + adr, + adr_type, + num_elem, + bt); + vec_val_load = gvn.transform(vec_val_load); + + C->set_max_vector_size(MAX2(C->max_vector_size(), vt->length_in_bytes())); + + if (is_vector_mask(from_kls) && masktype != T_BOOLEAN) { + assert(vec_unbox->bottom_type()->is_vect()->element_basic_type() == masktype, "expect mask type consistency"); + vec_val_load = gvn.transform(new VectorLoadMaskNode(vec_val_load, TypeVect::make(masktype, num_elem))); + } else if (is_vector_shuffle(from_kls)) { + if (vec_unbox->is_shuffle_to_vector() == false) { + assert(vec_unbox->bottom_type()->is_vect()->element_basic_type() == masktype, "expect shuffle type consistency"); + vec_val_load = gvn.transform(new VectorLoadShuffleNode(vec_val_load, TypeVect::make(masktype, num_elem))); + } else if (elem_bt != T_BYTE) { + vec_val_load = gvn.transform(VectorCastNode::make(Op_VectorCastB2X, vec_val_load, elem_bt, num_elem)); + } + } + + gvn.hash_delete(vec_unbox); + vec_unbox->disconnect_inputs(C); + C->gvn_replace_by(vec_unbox, vec_val_load); + } + C->remove_macro_node(vec_unbox); +} + +void PhaseVector::eliminate_vbox_alloc_node(VectorBoxAllocateNode* vbox_alloc) { + JVMState* jvms = clone_jvms(C, vbox_alloc); + GraphKit kit(jvms); + // Remove VBA, but leave a safepoint behind. + // Otherwise, it may end up with a loop without any safepoint polls. + kit.replace_call(vbox_alloc, kit.map(), true); + C->remove_macro_node(vbox_alloc); +} diff --git a/src/hotspot/share/opto/vector.hpp b/src/hotspot/share/opto/vector.hpp new file mode 100644 index 00000000000..067a2280d30 --- /dev/null +++ b/src/hotspot/share/opto/vector.hpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_OPTO_VECTOR_HPP +#define SHARE_OPTO_VECTOR_HPP + +#include "opto/node.hpp" +#include "opto/phaseX.hpp" +#include "opto/type.hpp" +#include "opto/vectornode.hpp" + +class PhaseVector : public Phase { + private: + PhaseIterGVN& _igvn; + + void expand_vbox_nodes(); + void expand_vbox_node(VectorBoxNode* vec_box); + Node* expand_vbox_node_helper(Node* vbox, + Node* vect, + const TypeInstPtr* box_type, + const TypeVect* vect_type); + Node* expand_vbox_alloc_node(VectorBoxAllocateNode* vbox_alloc, + Node* value, + const TypeInstPtr* box_type, + const TypeVect* vect_type); + void scalarize_vbox_nodes(); + void scalarize_vbox_node(VectorBoxNode* vec_box); + void expand_vunbox_nodes(); + void expand_vunbox_node(VectorUnboxNode* vec_box); + void eliminate_vbox_alloc_nodes(); + void eliminate_vbox_alloc_node(VectorBoxAllocateNode* vbox_alloc); + void do_cleanup(); + void scalarize_vector_boxes(); + void expand_vector_boxes(); + + public: + PhaseVector(PhaseIterGVN& igvn) : Phase(Vector), _igvn(igvn) {} + void optimize_vector_boxes(); +}; + +#endif // SHARE_OPTO_VECTOR_HPP diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp new file mode 100644 index 00000000000..d06c8348907 --- /dev/null +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -0,0 +1,1598 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "classfile/vmSymbols.hpp" +#include "opto/library_call.hpp" +#include "opto/runtime.hpp" +#include "opto/vectornode.hpp" +#include "prims/vectorSupport.hpp" + +bool LibraryCallKit::arch_supports_vector(int sopc, int num_elem, BasicType type, VectorMaskUseType mask_use_type, bool has_scalar_args) { + // Check that the operation is valid. + if (sopc <= 0) { +#ifndef PRODUCT + if (C->print_intrinsics()) { + tty->print_cr(" ** Rejected intrinsification because no valid vector op could be extracted"); + } +#endif + return false; + } + + // Check that architecture supports this op-size-type combination. + if (!Matcher::match_rule_supported_vector(sopc, num_elem, type)) { +#ifndef PRODUCT + if (C->print_intrinsics()) { + tty->print_cr(" ** Rejected vector op (%s,%s,%d) because architecture does not support it", + NodeClassNames[sopc], type2name(type), num_elem); + } +#endif + return false; + } else { + assert(Matcher::match_rule_supported(sopc), "must be supported"); + } + + if (!has_scalar_args && VectorNode::is_vector_shift(sopc) && + Matcher::supports_vector_variable_shifts() == false) { + if (C->print_intrinsics()) { + tty->print_cr(" ** Rejected vector op (%s,%s,%d) because architecture does not support variable vector shifts", + NodeClassNames[sopc], type2name(type), num_elem); + } + return false; + } + + // Check whether mask unboxing is supported. + if (mask_use_type == VecMaskUseAll || mask_use_type == VecMaskUseLoad) { + if (!Matcher::match_rule_supported_vector(Op_VectorLoadMask, num_elem, type)) { + #ifndef PRODUCT + if (C->print_intrinsics()) { + tty->print_cr(" ** Rejected vector mask loading (%s,%s,%d) because architecture does not support it", + NodeClassNames[Op_VectorLoadMask], type2name(type), num_elem); + } + #endif + return false; + } + } + + // Check whether mask boxing is supported. + if (mask_use_type == VecMaskUseAll || mask_use_type == VecMaskUseStore) { + if (!Matcher::match_rule_supported_vector(Op_VectorStoreMask, num_elem, type)) { + #ifndef PRODUCT + if (C->print_intrinsics()) { + tty->print_cr("Rejected vector mask storing (%s,%s,%d) because architecture does not support it", + NodeClassNames[Op_VectorStoreMask], type2name(type), num_elem); + } + #endif + return false; + } + } + + return true; +} + +static bool is_vector_mask(ciKlass* klass) { + return klass->is_subclass_of(ciEnv::current()->vector_VectorMask_klass()); +} + +static bool is_vector_shuffle(ciKlass* klass) { + return klass->is_subclass_of(ciEnv::current()->vector_VectorShuffle_klass()); +} + +static bool is_klass_initialized(const TypeInstPtr* vec_klass) { + if (vec_klass->const_oop() == NULL) { + return false; // uninitialized or some kind of unsafe access + } + assert(vec_klass->const_oop()->as_instance()->java_lang_Class_klass() != NULL, "klass instance expected"); + ciInstanceKlass* klass = vec_klass->const_oop()->as_instance()->java_lang_Class_klass()->as_instance_klass(); + return klass->is_initialized(); +} + +#ifdef ASSERT +static bool is_vector(ciKlass* klass) { + return klass->is_subclass_of(ciEnv::current()->vector_VectorPayload_klass()); +} + +static bool check_vbox(const TypeInstPtr* vbox_type) { + assert(vbox_type->klass_is_exact(), ""); + + ciInstanceKlass* ik = vbox_type->klass()->as_instance_klass(); + assert(is_vector(ik), "not a vector"); + + ciField* fd1 = ik->get_field_by_name(ciSymbol::ETYPE_name(), ciSymbol::class_signature(), /* is_static */ true); + assert(fd1 != NULL, "element type info is missing"); + + ciConstant val1 = fd1->constant_value(); + BasicType elem_bt = val1.as_object()->as_instance()->java_mirror_type()->basic_type(); + assert(is_java_primitive(elem_bt), "element type info is missing"); + + ciField* fd2 = ik->get_field_by_name(ciSymbol::VLENGTH_name(), ciSymbol::int_signature(), /* is_static */ true); + assert(fd2 != NULL, "vector length info is missing"); + + ciConstant val2 = fd2->constant_value(); + assert(val2.as_int() > 0, "vector length info is missing"); + + return true; +} +#endif + +Node* LibraryCallKit::box_vector(Node* vector, const TypeInstPtr* vbox_type, + BasicType elem_bt, int num_elem) { + assert(EnableVectorSupport, ""); + const TypeVect* vec_type = TypeVect::make(elem_bt, num_elem); + + VectorBoxAllocateNode* alloc = new VectorBoxAllocateNode(C, vbox_type); + set_edges_for_java_call(alloc, /*must_throw=*/false, /*separate_io_proj=*/true); + make_slow_call_ex(alloc, env()->Throwable_klass(), /*separate_io_proj=*/true); + set_i_o(gvn().transform( new ProjNode(alloc, TypeFunc::I_O) )); + set_all_memory(gvn().transform( new ProjNode(alloc, TypeFunc::Memory) )); + Node* ret = gvn().transform(new ProjNode(alloc, TypeFunc::Parms)); + + assert(check_vbox(vbox_type), ""); + VectorBoxNode* vbox = new VectorBoxNode(C, ret, vector, vbox_type, vec_type); + return gvn().transform(vbox); +} + +Node* LibraryCallKit::unbox_vector(Node* v, const TypeInstPtr* vbox_type, BasicType elem_bt, int num_elem, bool shuffle_to_vector) { + assert(EnableVectorSupport, ""); + const TypeInstPtr* vbox_type_v = gvn().type(v)->is_instptr(); + if (vbox_type->klass() != vbox_type_v->klass()) { + return NULL; // arguments don't agree on vector shapes + } + if (vbox_type_v->maybe_null()) { + return NULL; // no nulls are allowed + } + assert(check_vbox(vbox_type), ""); + const TypeVect* vec_type = TypeVect::make(elem_bt, num_elem); + Node* unbox = gvn().transform(new VectorUnboxNode(C, vec_type, v, merged_memory(), shuffle_to_vector)); + return unbox; +} + +// public static +// +// VM unaryOp(int oprId, Class vmClass, Class elementType, int length, +// VM vm, +// Function defaultImpl) { +// +// public static +// +// VM binaryOp(int oprId, Class vmClass, Class elementType, int length, +// VM vm1, VM vm2, +// BiFunction defaultImpl) { +// +// public static +// +// VM ternaryOp(int oprId, Class vmClass, Class elementType, int length, +// VM vm1, VM vm2, VM vm3, +// TernaryOperation defaultImpl) { +// +bool LibraryCallKit::inline_vector_nary_operation(int n) { + const TypeInt* opr = gvn().type(argument(0))->is_int(); + const TypeInstPtr* vector_klass = gvn().type(argument(1))->is_instptr(); + const TypeInstPtr* elem_klass = gvn().type(argument(2))->is_instptr(); + const TypeInt* vlen = gvn().type(argument(3))->is_int(); + + if (!opr->is_con() || vector_klass->const_oop() == NULL || elem_klass->const_oop() == NULL || !vlen->is_con()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** missing constant: opr=%s vclass=%s etype=%s vlen=%s", + NodeClassNames[argument(0)->Opcode()], + NodeClassNames[argument(1)->Opcode()], + NodeClassNames[argument(2)->Opcode()], + NodeClassNames[argument(3)->Opcode()]); + } + return false; // not enough info for intrinsification + } + ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); + if (!elem_type->is_primitive_type()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not a primitive bt=%d", elem_type->basic_type()); + } + return false; // should be primitive type + } + if (!is_klass_initialized(vector_klass)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** klass argument not initialized"); + } + return false; + } + BasicType elem_bt = elem_type->basic_type(); + int num_elem = vlen->get_con(); + int opc = VectorSupport::vop2ideal(opr->get_con(), elem_bt); + int sopc = VectorNode::opcode(opc, elem_bt); + ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); + + // TODO When mask usage is supported, VecMaskNotUsed needs to be VecMaskUseLoad. + if (!arch_supports_vector(sopc, num_elem, elem_bt, is_vector_mask(vbox_klass) ? VecMaskUseAll : VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=%d opc=%d vlen=%d etype=%s ismask=%d", + n, sopc, num_elem, type2name(elem_bt), + is_vector_mask(vbox_klass) ? 1 : 0); + } + return false; // not supported + } + + Node* opd1 = NULL; Node* opd2 = NULL; Node* opd3 = NULL; + switch (n) { + case 3: { + opd3 = unbox_vector(argument(6), vbox_type, elem_bt, num_elem); + if (opd3 == NULL) { + if (C->print_intrinsics()) { + tty->print_cr(" ** unbox failed v3=%s", + NodeClassNames[argument(6)->Opcode()]); + } + return false; + } + // fall-through + } + case 2: { + opd2 = unbox_vector(argument(5), vbox_type, elem_bt, num_elem); + if (opd2 == NULL) { + if (C->print_intrinsics()) { + tty->print_cr(" ** unbox failed v2=%s", + NodeClassNames[argument(5)->Opcode()]); + } + return false; + } + // fall-through + } + case 1: { + opd1 = unbox_vector(argument(4), vbox_type, elem_bt, num_elem); + if (opd1 == NULL) { + if (C->print_intrinsics()) { + tty->print_cr(" ** unbox failed v1=%s", + NodeClassNames[argument(4)->Opcode()]); + } + return false; + } + break; + } + default: fatal("unsupported arity: %d", n); + } + + Node* operation = NULL; + const TypeVect* vt = TypeVect::make(elem_bt, num_elem); + switch (n) { + case 1: + case 2: { + operation = gvn().transform(VectorNode::make(sopc, opd1, opd2, vt)); + break; + } + case 3: { + operation = gvn().transform(VectorNode::make(sopc, opd1, opd2, opd3, vt)); + break; + } + default: fatal("unsupported arity: %d", n); + } + // Wrap it up in VectorBox to keep object type information. + Node* vbox = box_vector(operation, vbox_type, elem_bt, num_elem); + set_result(vbox); + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); + return true; +} + +// , E> +// Sh ShuffleIota(Class E, Class ShuffleClass, Vector.Species s, int length, +// int start, int step, int wrap, ShuffleIotaOperation defaultImpl) +bool LibraryCallKit::inline_vector_shuffle_iota() { + const TypeInstPtr* shuffle_klass = gvn().type(argument(1))->is_instptr(); + const TypeInt* vlen = gvn().type(argument(3))->is_int(); + Node* start = argument(4); + const TypeInt* start_val = gvn().type(start)->is_int(); + Node* step = argument(5); + const TypeInt* step_val = gvn().type(step)->is_int(); + const TypeInt* wrap = gvn().type(argument(6))->is_int(); + + if (!vlen->is_con() || !is_power_of_2(vlen->get_con()) || + shuffle_klass->const_oop() == NULL || !wrap->is_con()) { + return false; // not enough info for intrinsification + } + if (!is_klass_initialized(shuffle_klass)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** klass argument not initialized"); + } + return false; + } + + int do_wrap = wrap->get_con(); + int num_elem = vlen->get_con(); + BasicType elem_bt = T_BYTE; + + if (num_elem < 4) + return false; + + if (!arch_supports_vector(VectorNode::replicate_opcode(elem_bt), num_elem, elem_bt, VecMaskNotUsed)) { + return false; + } + if (!arch_supports_vector(Op_AddVB, num_elem, elem_bt, VecMaskNotUsed)) { + return false; + } + if (!arch_supports_vector(Op_AndV, num_elem, elem_bt, VecMaskNotUsed)) { + return false; + } + if (!arch_supports_vector(Op_VectorBlend, num_elem, elem_bt, VecMaskUseLoad)) { + return false; + } + if (!arch_supports_vector(Op_VectorMaskCmp, num_elem, elem_bt, VecMaskUseStore)) { + return false; + } + + const Type * type_bt = Type::get_const_basic_type(elem_bt); + const TypeVect * vt = TypeVect::make(type_bt, num_elem); + + Node* res = gvn().transform(new VectorLoadConstNode(gvn().makecon(TypeInt::ZERO), vt)); + + if(!step_val->is_con() || !is_power_of_2(step_val->get_con())) { + Node* bcast_step = gvn().transform(VectorNode::scalar2vector(step, num_elem, type_bt)); + res = gvn().transform(VectorNode::make(Op_MulI, res, bcast_step, num_elem, elem_bt)); + } else if (step_val->get_con() > 1) { + Node* cnt = gvn().makecon(TypeInt::make(log2_int(step_val->get_con()))); + res = gvn().transform(VectorNode::make(Op_LShiftVB, res, cnt, vt)); + } + + if (!start_val->is_con() || start_val->get_con() != 0) { + Node* bcast_start = gvn().transform(VectorNode::scalar2vector(start, num_elem, type_bt)); + res = gvn().transform(VectorNode::make(Op_AddI, res, bcast_start, num_elem, elem_bt)); + } + + Node * mod_val = gvn().makecon(TypeInt::make(num_elem-1)); + Node * bcast_mod = gvn().transform(VectorNode::scalar2vector(mod_val, num_elem, type_bt)); + if(do_wrap) { + // Wrap the indices greater than lane count. + res = gvn().transform(VectorNode::make(Op_AndI, res, bcast_mod, num_elem, elem_bt)); + } else { + ConINode* pred_node = (ConINode*)gvn().makecon(TypeInt::make(1)); + Node * lane_cnt = gvn().makecon(TypeInt::make(num_elem)); + Node * bcast_lane_cnt = gvn().transform(VectorNode::scalar2vector(lane_cnt, num_elem, type_bt)); + Node* mask = gvn().transform(new VectorMaskCmpNode(BoolTest::ge, bcast_lane_cnt, res, pred_node, vt)); + + // Make the indices greater than lane count as -ve values. This matches the java side implementation. + res = gvn().transform(VectorNode::make(Op_AndI, res, bcast_mod, num_elem, elem_bt)); + Node * biased_val = gvn().transform(VectorNode::make(Op_SubI, res, bcast_lane_cnt, num_elem, elem_bt)); + res = gvn().transform(new VectorBlendNode(biased_val, res, mask)); + } + + ciKlass* sbox_klass = shuffle_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* shuffle_box_type = TypeInstPtr::make_exact(TypePtr::NotNull, sbox_klass); + + // Wrap it up in VectorBox to keep object type information. + res = box_vector(res, shuffle_box_type, elem_bt, num_elem); + set_result(res); + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); + return true; +} + +// , E> +// VM shuffleToVector(Class VecClass, ClassE , Class ShuffleClass, Sh s, int length, +// ShuffleToVectorOperation defaultImpl) +bool LibraryCallKit::inline_vector_shuffle_to_vector() { + const TypeInstPtr* vector_klass = gvn().type(argument(0))->is_instptr(); + const TypeInstPtr* elem_klass = gvn().type(argument(1))->is_instptr(); + const TypeInstPtr* shuffle_klass = gvn().type(argument(2))->is_instptr(); + Node* shuffle = argument(3); + const TypeInt* vlen = gvn().type(argument(4))->is_int(); + + if (!vlen->is_con() || vector_klass->const_oop() == NULL || shuffle_klass->const_oop() == NULL) { + return false; // not enough info for intrinsification + } + if (!is_klass_initialized(shuffle_klass) || !is_klass_initialized(vector_klass) ) { + if (C->print_intrinsics()) { + tty->print_cr(" ** klass argument not initialized"); + } + return false; + } + + int num_elem = vlen->get_con(); + ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); + BasicType elem_bt = elem_type->basic_type(); + + if (num_elem < 4) { + return false; + } + + int cast_vopc = VectorCastNode::opcode(T_BYTE); // from shuffle of type T_BYTE + // Make sure that cast is implemented to particular type/size combination. + if (!arch_supports_vector(cast_vopc, num_elem, elem_bt, VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=1 op=cast#%d/3 vlen2=%d etype2=%s", + cast_vopc, num_elem, type2name(elem_bt)); + } + return false; + } + + ciKlass* sbox_klass = shuffle_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* shuffle_box_type = TypeInstPtr::make_exact(TypePtr::NotNull, sbox_klass); + + // Unbox shuffle with true flag to indicate its load shuffle to vector + Node* shuffle_vec = unbox_vector(shuffle, shuffle_box_type, elem_bt, num_elem, true); + + ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* vec_box_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); + + // Box vector + Node* res = box_vector(shuffle_vec, vec_box_type, elem_bt, num_elem); + set_result(res); + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); + return true; +} + +// > +// V broadcastCoerced(Class vectorClass, Class elementType, int vlen, +// long bits, +// LongFunction defaultImpl) +bool LibraryCallKit::inline_vector_broadcast_coerced() { + const TypeInstPtr* vector_klass = gvn().type(argument(0))->is_instptr(); + const TypeInstPtr* elem_klass = gvn().type(argument(1))->is_instptr(); + const TypeInt* vlen = gvn().type(argument(2))->is_int(); + + if (vector_klass->const_oop() == NULL || elem_klass->const_oop() == NULL || !vlen->is_con()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** missing constant: vclass=%s etype=%s vlen=%s", + NodeClassNames[argument(0)->Opcode()], + NodeClassNames[argument(1)->Opcode()], + NodeClassNames[argument(2)->Opcode()]); + } + return false; // not enough info for intrinsification + } + + if (!is_klass_initialized(vector_klass)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** klass argument not initialized"); + } + return false; + } + ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); + if (!elem_type->is_primitive_type()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not a primitive bt=%d", elem_type->basic_type()); + } + return false; // should be primitive type + } + BasicType elem_bt = elem_type->basic_type(); + int num_elem = vlen->get_con(); + ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); + + // TODO When mask usage is supported, VecMaskNotUsed needs to be VecMaskUseLoad. + if (!arch_supports_vector(VectorNode::replicate_opcode(elem_bt), num_elem, elem_bt, + (is_vector_mask(vbox_klass) ? VecMaskUseStore : VecMaskNotUsed), true /*has_scalar_args*/)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=0 op=broadcast vlen=%d etype=%s ismask=%d", + num_elem, type2name(elem_bt), + is_vector_mask(vbox_klass) ? 1 : 0); + } + return false; // not supported + } + + Node* bits = argument(3); // long + + Node* elem = NULL; + switch (elem_bt) { + case T_BOOLEAN: // fall-through + case T_BYTE: // fall-through + case T_SHORT: // fall-through + case T_CHAR: // fall-through + case T_INT: { + elem = gvn().transform(new ConvL2INode(bits)); + break; + } + case T_DOUBLE: { + elem = gvn().transform(new MoveL2DNode(bits)); + break; + } + case T_FLOAT: { + bits = gvn().transform(new ConvL2INode(bits)); + elem = gvn().transform(new MoveI2FNode(bits)); + break; + } + case T_LONG: { + elem = bits; // no conversion needed + break; + } + default: fatal("%s", type2name(elem_bt)); + } + + Node* broadcast = VectorNode::scalar2vector(elem, num_elem, Type::get_const_basic_type(elem_bt)); + broadcast = gvn().transform(broadcast); + + Node* box = box_vector(broadcast, vbox_type, elem_bt, num_elem); + set_result(box); + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); + return true; +} + +// > +// V load(Class vectorClass, Class elementType, int vlen, +// Object base, long offset, +// /* Vector.Mask m*/ +// Object container, int index, +// LoadOperation defaultImpl) { +// +// > +// void store(Class vectorClass, Class elementType, int vlen, +// Object base, long offset, +// V v, /*Vector.Mask m*/ +// Object container, int index, +// StoreVectorOperation defaultImpl) { + +bool LibraryCallKit::inline_vector_mem_operation(bool is_store) { + const TypeInstPtr* vector_klass = gvn().type(argument(0))->is_instptr(); + const TypeInstPtr* elem_klass = gvn().type(argument(1))->is_instptr(); + const TypeInt* vlen = gvn().type(argument(2))->is_int(); + + if (vector_klass->const_oop() == NULL || elem_klass->const_oop() == NULL || !vlen->is_con()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** missing constant: vclass=%s etype=%s vlen=%s", + NodeClassNames[argument(0)->Opcode()], + NodeClassNames[argument(1)->Opcode()], + NodeClassNames[argument(2)->Opcode()]); + } + return false; // not enough info for intrinsification + } + if (!is_klass_initialized(vector_klass)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** klass argument not initialized"); + } + return false; + } + + ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); + if (!elem_type->is_primitive_type()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not a primitive bt=%d", elem_type->basic_type()); + } + return false; // should be primitive type + } + BasicType elem_bt = elem_type->basic_type(); + int num_elem = vlen->get_con(); + + // TODO When mask usage is supported, VecMaskNotUsed needs to be VecMaskUseLoad. + if (!arch_supports_vector(is_store ? Op_StoreVector : Op_LoadVector, num_elem, elem_bt, VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=%d op=%s vlen=%d etype=%s ismask=no", + is_store, is_store ? "store" : "load", + num_elem, type2name(elem_bt)); + } + return false; // not supported + } + + ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); + bool is_mask = is_vector_mask(vbox_klass); + + Node* base = argument(3); + Node* offset = ConvL2X(argument(4)); + DecoratorSet decorators = C2_UNSAFE_ACCESS; + Node* addr = make_unsafe_address(base, offset, decorators, (is_mask ? T_BOOLEAN : elem_bt), true); + + // Can base be NULL? Otherwise, always on-heap access. + bool can_access_non_heap = TypePtr::NULL_PTR->higher_equal(gvn().type(base)); + + const TypePtr *addr_type = gvn().type(addr)->isa_ptr(); + const TypeAryPtr* arr_type = addr_type->isa_aryptr(); + + // Now handle special case where load/store happens from/to byte array but element type is not byte. + bool using_byte_array = arr_type != NULL && arr_type->elem()->array_element_basic_type() == T_BYTE && elem_bt != T_BYTE; + // Handle loading masks. + // If there is no consistency between array and vector element types, it must be special byte array case or loading masks + if (arr_type != NULL && !using_byte_array && elem_bt != arr_type->elem()->array_element_basic_type() && !is_mask) { + return false; + } + // Since we are using byte array, we need to double check that the byte operations are supported by backend. + if (using_byte_array) { + int byte_num_elem = num_elem * type2aelembytes(elem_bt); + if (!arch_supports_vector(is_store ? Op_StoreVector : Op_LoadVector, byte_num_elem, T_BYTE, VecMaskNotUsed) + || !arch_supports_vector(Op_VectorReinterpret, byte_num_elem, T_BYTE, VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=%d op=%s vlen=%d*8 etype=%s/8 ismask=no", + is_store, is_store ? "store" : "load", + byte_num_elem, type2name(elem_bt)); + } + return false; // not supported + } + } + if (is_mask) { + if (!arch_supports_vector(Op_LoadVector, num_elem, T_BOOLEAN, VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=%d op=%s/mask vlen=%d etype=bit ismask=no", + is_store, is_store ? "store" : "load", + num_elem); + } + return false; // not supported + } + if (!is_store) { + if (!arch_supports_vector(Op_LoadVector, num_elem, elem_bt, VecMaskUseLoad)) { + return false; // not supported + } + } else { + if (!arch_supports_vector(Op_StoreVector, num_elem, elem_bt, VecMaskUseStore)) { + return false; // not supported + } + } + } + + const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); + + if (can_access_non_heap) { + insert_mem_bar(Op_MemBarCPUOrder); + } + + if (is_store) { + Node* val = unbox_vector(argument(6), vbox_type, elem_bt, num_elem); + if (val == NULL) { + return false; // operand unboxing failed + } + set_all_memory(reset_memory()); + + // In case the store needs to happen to byte array, reinterpret the incoming vector to byte vector. + int store_num_elem = num_elem; + if (using_byte_array) { + store_num_elem = num_elem * type2aelembytes(elem_bt); + const TypeVect* to_vect_type = TypeVect::make(T_BYTE, store_num_elem); + val = gvn().transform(new VectorReinterpretNode(val, val->bottom_type()->is_vect(), to_vect_type)); + } + + Node* vstore = gvn().transform(StoreVectorNode::make(0, control(), memory(addr), addr, addr_type, val, store_num_elem)); + set_memory(vstore, addr_type); + } else { + // When using byte array, we need to load as byte then reinterpret the value. Otherwise, do a simple vector load. + Node* vload = NULL; + if (using_byte_array) { + int load_num_elem = num_elem * type2aelembytes(elem_bt); + vload = gvn().transform(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, load_num_elem, T_BYTE)); + const TypeVect* to_vect_type = TypeVect::make(elem_bt, num_elem); + vload = gvn().transform(new VectorReinterpretNode(vload, vload->bottom_type()->is_vect(), to_vect_type)); + } else { + // Special handle for masks + if (is_mask) { + vload = gvn().transform(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, num_elem, T_BOOLEAN)); + const TypeVect* to_vect_type = TypeVect::make(elem_bt, num_elem); + vload = gvn().transform(new VectorLoadMaskNode(vload, to_vect_type)); + } else { + vload = gvn().transform(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, num_elem, elem_bt)); + } + } + Node* box = box_vector(vload, vbox_type, elem_bt, num_elem); + set_result(box); + } + + if (can_access_non_heap) { + insert_mem_bar(Op_MemBarCPUOrder); + } + + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); + return true; +} + +// , W extends IntVector, E, S extends VectorSpecies> +// void loadWithMap(Class vectorClass, Class E, int length, Class vectorIndexClass, +// Object base, long offset, // Unsafe addressing +// W index_vector, +// C container, int index, int[] indexMap, int indexM, S s, // Arguments for default implementation +// LoadVectorOperationWithMap defaultImpl) +// +// , W extends IntVector> +// void storeWithMap(Class vectorClass, Class elementType, int length, Class vectorIndexClass, +// Object base, long offset, // Unsafe addressing +// W index_vector, V v, +// C container, int index, int[] indexMap, int indexM, // Arguments for default implementation +// StoreVectorOperationWithMap defaultImpl) { +// +bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) { + const TypeInstPtr* vector_klass = gvn().type(argument(0))->is_instptr(); + const TypeInstPtr* elem_klass = gvn().type(argument(1))->is_instptr(); + const TypeInt* vlen = gvn().type(argument(2))->is_int(); + const TypeInstPtr* vector_idx_klass = gvn().type(argument(3))->is_instptr(); + + if (vector_klass->const_oop() == NULL || elem_klass->const_oop() == NULL || vector_idx_klass->const_oop() == NULL || !vlen->is_con()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** missing constant: vclass=%s etype=%s vlen=%s viclass=%s", + NodeClassNames[argument(0)->Opcode()], + NodeClassNames[argument(1)->Opcode()], + NodeClassNames[argument(2)->Opcode()], + NodeClassNames[argument(3)->Opcode()]); + } + return false; // not enough info for intrinsification + } + + if (!is_klass_initialized(vector_klass) || !is_klass_initialized(vector_idx_klass)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** klass argument not initialized"); + } + return false; + } + ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); + if (!elem_type->is_primitive_type()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not a primitive bt=%d", elem_type->basic_type()); + } + return false; // should be primitive type + } + BasicType elem_bt = elem_type->basic_type(); + int num_elem = vlen->get_con(); + + if (!arch_supports_vector(is_scatter ? Op_StoreVectorScatter : Op_LoadVectorGather, num_elem, elem_bt, VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=%d op=%s vlen=%d etype=%s ismask=no", + is_scatter, is_scatter ? "scatter" : "gather", + num_elem, type2name(elem_bt)); + } + return false; // not supported + } + + // Check that the vector holding indices is supported by architecture + if (!arch_supports_vector(Op_LoadVector, num_elem, T_INT, VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=%d op=%s/loadindex vlen=%d etype=int ismask=no", + is_scatter, is_scatter ? "scatter" : "gather", + num_elem); + } + return false; // not supported + } + + Node* base = argument(4); + Node* offset = ConvL2X(argument(5)); + Node* addr = make_unsafe_address(base, offset, C2_UNSAFE_ACCESS, elem_bt, true); + + const TypePtr *addr_type = gvn().type(addr)->isa_ptr(); + const TypeAryPtr* arr_type = addr_type->isa_aryptr(); + + // The array must be consistent with vector type + if (arr_type == NULL || (arr_type != NULL && elem_bt != arr_type->elem()->array_element_basic_type())) { + return false; + } + ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); + + ciKlass* vbox_idx_klass = vector_idx_klass->const_oop()->as_instance()->java_lang_Class_klass(); + + if (vbox_idx_klass == NULL) { + return false; + } + + const TypeInstPtr* vbox_idx_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_idx_klass); + + Node* index_vect = unbox_vector(argument(7), vbox_idx_type, T_INT, num_elem); + if (index_vect == NULL) { + return false; + } + const TypeVect* vector_type = TypeVect::make(elem_bt, num_elem); + if (is_scatter) { + Node* val = unbox_vector(argument(8), vbox_type, elem_bt, num_elem); + if (val == NULL) { + return false; // operand unboxing failed + } + set_all_memory(reset_memory()); + + Node* vstore = gvn().transform(new StoreVectorScatterNode(control(), memory(addr), addr, addr_type, val, index_vect)); + set_memory(vstore, addr_type); + } else { + Node* vload = gvn().transform(new LoadVectorGatherNode(control(), memory(addr), addr, addr_type, vector_type, index_vect)); + + Node* box = box_vector(vload, vbox_type, elem_bt, num_elem); + set_result(box); + } + + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); + return true; +} + +// > +// long reductionCoerced(int oprId, Class vectorClass, Class elementType, int vlen, +// V v, +// Function defaultImpl) + +bool LibraryCallKit::inline_vector_reduction() { + const TypeInt* opr = gvn().type(argument(0))->is_int(); + const TypeInstPtr* vector_klass = gvn().type(argument(1))->is_instptr(); + const TypeInstPtr* elem_klass = gvn().type(argument(2))->is_instptr(); + const TypeInt* vlen = gvn().type(argument(3))->is_int(); + + if (!opr->is_con() || vector_klass->const_oop() == NULL || elem_klass->const_oop() == NULL || !vlen->is_con()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** missing constant: opr=%s vclass=%s etype=%s vlen=%s", + NodeClassNames[argument(0)->Opcode()], + NodeClassNames[argument(1)->Opcode()], + NodeClassNames[argument(2)->Opcode()], + NodeClassNames[argument(3)->Opcode()]); + } + return false; // not enough info for intrinsification + } + if (!is_klass_initialized(vector_klass)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** klass argument not initialized"); + } + return false; + } + ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); + if (!elem_type->is_primitive_type()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not a primitive bt=%d", elem_type->basic_type()); + } + return false; // should be primitive type + } + BasicType elem_bt = elem_type->basic_type(); + int num_elem = vlen->get_con(); + + int opc = VectorSupport::vop2ideal(opr->get_con(), elem_bt); + int sopc = ReductionNode::opcode(opc, elem_bt); + + // TODO When mask usage is supported, VecMaskNotUsed needs to be VecMaskUseLoad. + if (!arch_supports_vector(sopc, num_elem, elem_bt, VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=1 op=%d/reduce vlen=%d etype=%s ismask=no", + sopc, num_elem, type2name(elem_bt)); + } + return false; + } + + ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); + + Node* opd = unbox_vector(argument(4), vbox_type, elem_bt, num_elem); + if (opd == NULL) { + return false; // operand unboxing failed + } + + Node* init = ReductionNode::make_reduction_input(gvn(), opc, elem_bt); + Node* rn = gvn().transform(ReductionNode::make(opc, NULL, init, opd, elem_bt)); + + Node* bits = NULL; + switch (elem_bt) { + case T_BYTE: + case T_SHORT: + case T_INT: { + bits = gvn().transform(new ConvI2LNode(rn)); + break; + } + case T_FLOAT: { + rn = gvn().transform(new MoveF2INode(rn)); + bits = gvn().transform(new ConvI2LNode(rn)); + break; + } + case T_DOUBLE: { + bits = gvn().transform(new MoveD2LNode(rn)); + break; + } + case T_LONG: { + bits = rn; // no conversion needed + break; + } + default: fatal("%s", type2name(elem_bt)); + } + set_result(bits); + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); + return true; +} + +// public static boolean test(int cond, Class vectorClass, Class elementType, int vlen, +// V v1, V v2, +// BiFunction defaultImpl) { +// +bool LibraryCallKit::inline_vector_test() { + const TypeInt* cond = gvn().type(argument(0))->is_int(); + const TypeInstPtr* vector_klass = gvn().type(argument(1))->is_instptr(); + const TypeInstPtr* elem_klass = gvn().type(argument(2))->is_instptr(); + const TypeInt* vlen = gvn().type(argument(3))->is_int(); + + if (!cond->is_con() || vector_klass->const_oop() == NULL || elem_klass->const_oop() == NULL || !vlen->is_con()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** missing constant: cond=%s vclass=%s etype=%s vlen=%s", + NodeClassNames[argument(0)->Opcode()], + NodeClassNames[argument(1)->Opcode()], + NodeClassNames[argument(2)->Opcode()], + NodeClassNames[argument(3)->Opcode()]); + } + return false; // not enough info for intrinsification + } + if (!is_klass_initialized(vector_klass)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** klass argument not initialized"); + } + return false; + } + ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); + if (!elem_type->is_primitive_type()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not a primitive bt=%d", elem_type->basic_type()); + } + return false; // should be primitive type + } + BasicType elem_bt = elem_type->basic_type(); + int num_elem = vlen->get_con(); + BoolTest::mask booltest = (BoolTest::mask)cond->get_con(); + ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); + + if (!arch_supports_vector(Op_VectorTest, num_elem, elem_bt, is_vector_mask(vbox_klass) ? VecMaskUseLoad : VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=2 op=test/%d vlen=%d etype=%s ismask=%d", + cond->get_con(), num_elem, type2name(elem_bt), + is_vector_mask(vbox_klass)); + } + return false; + } + + Node* opd1 = unbox_vector(argument(4), vbox_type, elem_bt, num_elem); + Node* opd2 = unbox_vector(argument(5), vbox_type, elem_bt, num_elem); + if (opd1 == NULL || opd2 == NULL) { + return false; // operand unboxing failed + } + Node* test = new VectorTestNode(opd1, opd2, booltest); + test = gvn().transform(test); + + set_result(test); + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); + return true; +} + +// public static +// +// V blend(Class vectorClass, Class maskClass, Class elementType, int vlen, +// V v1, V v2, M m, +// VectorBlendOp defaultImpl) { ... +// +bool LibraryCallKit::inline_vector_blend() { + const TypeInstPtr* vector_klass = gvn().type(argument(0))->is_instptr(); + const TypeInstPtr* mask_klass = gvn().type(argument(1))->is_instptr(); + const TypeInstPtr* elem_klass = gvn().type(argument(2))->is_instptr(); + const TypeInt* vlen = gvn().type(argument(3))->is_int(); + + if (mask_klass->const_oop() == NULL || vector_klass->const_oop() == NULL || + elem_klass->const_oop() == NULL || !vlen->is_con()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** missing constant: vclass=%s mclass=%s etype=%s vlen=%s", + NodeClassNames[argument(0)->Opcode()], + NodeClassNames[argument(1)->Opcode()], + NodeClassNames[argument(2)->Opcode()], + NodeClassNames[argument(3)->Opcode()]); + } + return false; // not enough info for intrinsification + } + if (!is_klass_initialized(vector_klass) || !is_klass_initialized(mask_klass)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** klass argument not initialized"); + } + return false; + } + ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); + if (!elem_type->is_primitive_type()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not a primitive bt=%d", elem_type->basic_type()); + } + return false; // should be primitive type + } + BasicType elem_bt = elem_type->basic_type(); + BasicType mask_bt = elem_bt; + int num_elem = vlen->get_con(); + + if (!arch_supports_vector(Op_VectorBlend, num_elem, elem_bt, VecMaskUseLoad)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=2 op=blend vlen=%d etype=%s ismask=useload", + num_elem, type2name(elem_bt)); + } + return false; // not supported + } + ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); + + ciKlass* mbox_klass = mask_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* mbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, mbox_klass); + + Node* v1 = unbox_vector(argument(4), vbox_type, elem_bt, num_elem); + Node* v2 = unbox_vector(argument(5), vbox_type, elem_bt, num_elem); + Node* mask = unbox_vector(argument(6), mbox_type, mask_bt, num_elem); + + if (v1 == NULL || v2 == NULL || mask == NULL) { + return false; // operand unboxing failed + } + + Node* blend = gvn().transform(new VectorBlendNode(v1, v2, mask)); + + Node* box = box_vector(blend, vbox_type, elem_bt, num_elem); + set_result(box); + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); + return true; +} + +// public static , +// M extends Vector.Mask, +// S extends Vector.Shape, E> +// M compare(int cond, Class vectorClass, Class maskClass, Class elementType, int vlen, +// V v1, V v2, +// VectorCompareOp defaultImpl) { ... +// +bool LibraryCallKit::inline_vector_compare() { + const TypeInt* cond = gvn().type(argument(0))->is_int(); + const TypeInstPtr* vector_klass = gvn().type(argument(1))->is_instptr(); + const TypeInstPtr* mask_klass = gvn().type(argument(2))->is_instptr(); + const TypeInstPtr* elem_klass = gvn().type(argument(3))->is_instptr(); + const TypeInt* vlen = gvn().type(argument(4))->is_int(); + + if (!cond->is_con() || vector_klass->const_oop() == NULL || mask_klass->const_oop() == NULL || + elem_klass->const_oop() == NULL || !vlen->is_con()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** missing constant: cond=%s vclass=%s mclass=%s etype=%s vlen=%s", + NodeClassNames[argument(0)->Opcode()], + NodeClassNames[argument(1)->Opcode()], + NodeClassNames[argument(2)->Opcode()], + NodeClassNames[argument(3)->Opcode()], + NodeClassNames[argument(4)->Opcode()]); + } + return false; // not enough info for intrinsification + } + if (!is_klass_initialized(vector_klass) || !is_klass_initialized(mask_klass)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** klass argument not initialized"); + } + return false; + } + ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); + if (!elem_type->is_primitive_type()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not a primitive bt=%d", elem_type->basic_type()); + } + return false; // should be primitive type + } + + int num_elem = vlen->get_con(); + BasicType elem_bt = elem_type->basic_type(); + BasicType mask_bt = elem_bt; + + if (!arch_supports_vector(Op_VectorMaskCmp, num_elem, elem_bt, VecMaskUseStore)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=2 op=comp/%d vlen=%d etype=%s ismask=usestore", + cond->get_con(), num_elem, type2name(elem_bt)); + } + return false; + } + + ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); + + ciKlass* mbox_klass = mask_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* mbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, mbox_klass); + + Node* v1 = unbox_vector(argument(5), vbox_type, elem_bt, num_elem); + Node* v2 = unbox_vector(argument(6), vbox_type, elem_bt, num_elem); + + if (v1 == NULL || v2 == NULL) { + return false; // operand unboxing failed + } + BoolTest::mask pred = (BoolTest::mask)cond->get_con(); + ConINode* pred_node = (ConINode*)gvn().makecon(cond); + + const TypeVect* vt = TypeVect::make(mask_bt, num_elem); + Node* operation = gvn().transform(new VectorMaskCmpNode(pred, v1, v2, pred_node, vt)); + + Node* box = box_vector(operation, mbox_type, mask_bt, num_elem); + set_result(box); + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); + return true; +} + +// public static +// +// V rearrangeOp(Class vectorClass, Class shuffleClass, Class< ? > elementType, int vlen, +// V v1, Sh sh, +// VectorSwizzleOp defaultImpl) { ... + +bool LibraryCallKit::inline_vector_rearrange() { + const TypeInstPtr* vector_klass = gvn().type(argument(0))->is_instptr(); + const TypeInstPtr* shuffle_klass = gvn().type(argument(1))->is_instptr(); + const TypeInstPtr* elem_klass = gvn().type(argument(2))->is_instptr(); + const TypeInt* vlen = gvn().type(argument(3))->is_int(); + + if (shuffle_klass->const_oop() == NULL || vector_klass->const_oop() == NULL || + elem_klass->const_oop() == NULL || !vlen->is_con()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** missing constant: vclass=%s sclass=%s etype=%s vlen=%s", + NodeClassNames[argument(0)->Opcode()], + NodeClassNames[argument(1)->Opcode()], + NodeClassNames[argument(2)->Opcode()], + NodeClassNames[argument(3)->Opcode()]); + } + return false; // not enough info for intrinsification + } + if (!is_klass_initialized(vector_klass) || !is_klass_initialized(shuffle_klass)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** klass argument not initialized"); + } + return false; + } + ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); + if (!elem_type->is_primitive_type()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not a primitive bt=%d", elem_type->basic_type()); + } + return false; // should be primitive type + } + BasicType elem_bt = elem_type->basic_type(); + BasicType shuffle_bt = elem_bt; + int num_elem = vlen->get_con(); + + if (!arch_supports_vector(Op_VectorLoadShuffle, num_elem, elem_bt, VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=0 op=load/shuffle vlen=%d etype=%s ismask=no", + num_elem, type2name(elem_bt)); + } + return false; // not supported + } + if (!arch_supports_vector(Op_VectorRearrange, num_elem, elem_bt, VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=2 op=shuffle/rearrange vlen=%d etype=%s ismask=no", + num_elem, type2name(elem_bt)); + } + return false; // not supported + } + ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); + + ciKlass* shbox_klass = shuffle_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* shbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, shbox_klass); + + Node* v1 = unbox_vector(argument(4), vbox_type, elem_bt, num_elem); + Node* shuffle = unbox_vector(argument(5), shbox_type, shuffle_bt, num_elem); + + if (v1 == NULL || shuffle == NULL) { + return false; // operand unboxing failed + } + + Node* rearrange = gvn().transform(new VectorRearrangeNode(v1, shuffle)); + + Node* box = box_vector(rearrange, vbox_type, elem_bt, num_elem); + set_result(box); + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); + return true; +} + +Node* LibraryCallKit::shift_count(Node* cnt, int shift_op, BasicType bt, int num_elem) { + assert(bt == T_INT || bt == T_LONG || bt == T_SHORT || bt == T_BYTE, "byte, short, long and int are supported"); + juint mask = (type2aelembytes(bt) * BitsPerByte - 1); + Node* nmask = gvn().transform(ConNode::make(TypeInt::make(mask))); + Node* mcnt = gvn().transform(new AndINode(cnt, nmask)); + return gvn().transform(VectorNode::shift_count(shift_op, mcnt, num_elem, bt)); +} + +// public static +// > +// V broadcastInt(int opr, Class vectorClass, Class elementType, int vlen, +// V v, int i, +// VectorBroadcastIntOp defaultImpl) { +// +bool LibraryCallKit::inline_vector_broadcast_int() { + const TypeInt* opr = gvn().type(argument(0))->is_int(); + const TypeInstPtr* vector_klass = gvn().type(argument(1))->is_instptr(); + const TypeInstPtr* elem_klass = gvn().type(argument(2))->is_instptr(); + const TypeInt* vlen = gvn().type(argument(3))->is_int(); + + if (!opr->is_con() || vector_klass->const_oop() == NULL || elem_klass->const_oop() == NULL || !vlen->is_con()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** missing constant: opr=%s vclass=%s etype=%s vlen=%s", + NodeClassNames[argument(0)->Opcode()], + NodeClassNames[argument(1)->Opcode()], + NodeClassNames[argument(2)->Opcode()], + NodeClassNames[argument(3)->Opcode()]); + } + return false; // not enough info for intrinsification + } + if (!is_klass_initialized(vector_klass)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** klass argument not initialized"); + } + return false; + } + ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); + if (!elem_type->is_primitive_type()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not a primitive bt=%d", elem_type->basic_type()); + } + return false; // should be primitive type + } + BasicType elem_bt = elem_type->basic_type(); + int num_elem = vlen->get_con(); + int opc = VectorSupport::vop2ideal(opr->get_con(), elem_bt); + int sopc = VectorNode::opcode(opc, elem_bt); + ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); + + if (!arch_supports_vector(sopc, num_elem, elem_bt, VecMaskNotUsed, true /*has_scalar_args*/)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=0 op=int/%d vlen=%d etype=%s ismask=no", + sopc, num_elem, type2name(elem_bt)); + } + return false; // not supported + } + Node* opd1 = unbox_vector(argument(4), vbox_type, elem_bt, num_elem); + Node* opd2 = shift_count(argument(5), opc, elem_bt, num_elem); + if (opd1 == NULL || opd2 == NULL) { + return false; + } + Node* operation = gvn().transform(VectorNode::make(opc, opd1, opd2, num_elem, elem_bt)); + + Node* vbox = box_vector(operation, vbox_type, elem_bt, num_elem); + set_result(vbox); + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); + return true; +} + +// public static +// VOUT convert(int oprId, +// Class fromVectorClass, Class fromElementType, int fromVLen, +// Class toVectorClass, Class toElementType, int toVLen, +// VIN v, S s, +// VectorConvertOp defaultImpl) { +// +bool LibraryCallKit::inline_vector_convert() { + const TypeInt* opr = gvn().type(argument(0))->is_int(); + + const TypeInstPtr* vector_klass_from = gvn().type(argument(1))->is_instptr(); + const TypeInstPtr* elem_klass_from = gvn().type(argument(2))->is_instptr(); + const TypeInt* vlen_from = gvn().type(argument(3))->is_int(); + + const TypeInstPtr* vector_klass_to = gvn().type(argument(4))->is_instptr(); + const TypeInstPtr* elem_klass_to = gvn().type(argument(5))->is_instptr(); + const TypeInt* vlen_to = gvn().type(argument(6))->is_int(); + + if (!opr->is_con() || + vector_klass_from->const_oop() == NULL || elem_klass_from->const_oop() == NULL || !vlen_from->is_con() || + vector_klass_to->const_oop() == NULL || elem_klass_to->const_oop() == NULL || !vlen_to->is_con()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** missing constant: opr=%s vclass_from=%s etype_from=%s vlen_from=%s vclass_to=%s etype_to=%s vlen_to=%s", + NodeClassNames[argument(0)->Opcode()], + NodeClassNames[argument(1)->Opcode()], + NodeClassNames[argument(2)->Opcode()], + NodeClassNames[argument(3)->Opcode()], + NodeClassNames[argument(4)->Opcode()], + NodeClassNames[argument(5)->Opcode()], + NodeClassNames[argument(6)->Opcode()]); + } + return false; // not enough info for intrinsification + } + if (!is_klass_initialized(vector_klass_from) || !is_klass_initialized(vector_klass_to)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** klass argument not initialized"); + } + return false; + } + + assert(opr->get_con() == VectorSupport::VECTOR_OP_CAST || + opr->get_con() == VectorSupport::VECTOR_OP_REINTERPRET, "wrong opcode"); + bool is_cast = (opr->get_con() == VectorSupport::VECTOR_OP_CAST); + + ciKlass* vbox_klass_from = vector_klass_from->const_oop()->as_instance()->java_lang_Class_klass(); + ciKlass* vbox_klass_to = vector_klass_to->const_oop()->as_instance()->java_lang_Class_klass(); + if (is_vector_shuffle(vbox_klass_from) || is_vector_shuffle(vbox_klass_to)) { + return false; // vector shuffles aren't supported + } + bool is_mask = is_vector_mask(vbox_klass_from); + + ciType* elem_type_from = elem_klass_from->const_oop()->as_instance()->java_mirror_type(); + if (!elem_type_from->is_primitive_type()) { + return false; // should be primitive type + } + BasicType elem_bt_from = elem_type_from->basic_type(); + ciType* elem_type_to = elem_klass_to->const_oop()->as_instance()->java_mirror_type(); + if (!elem_type_to->is_primitive_type()) { + return false; // should be primitive type + } + BasicType elem_bt_to = elem_type_to->basic_type(); + if (is_mask && elem_bt_from != elem_bt_to) { + return false; // type mismatch + } + int num_elem_from = vlen_from->get_con(); + int num_elem_to = vlen_to->get_con(); + + // Check whether we can unbox to appropriate size. Even with casting, checking for reinterpret is needed + // since we may need to change size. + if (!arch_supports_vector(Op_VectorReinterpret, + num_elem_from, + elem_bt_from, + is_mask ? VecMaskUseAll : VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=1 op=%s/1 vlen1=%d etype1=%s ismask=%d", + is_cast ? "cast" : "reinterpret", + num_elem_from, type2name(elem_bt_from), is_mask); + } + return false; + } + + // Check whether we can support resizing/reinterpreting to the new size. + if (!arch_supports_vector(Op_VectorReinterpret, + num_elem_to, + elem_bt_to, + is_mask ? VecMaskUseAll : VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=1 op=%s/2 vlen2=%d etype2=%s ismask=%d", + is_cast ? "cast" : "reinterpret", + num_elem_to, type2name(elem_bt_to), is_mask); + } + return false; + } + + // At this point, we know that both input and output vector registers are supported + // by the architecture. Next check if the casted type is simply to same type - which means + // that it is actually a resize and not a cast. + if (is_cast && elem_bt_from == elem_bt_to) { + is_cast = false; + } + + const TypeInstPtr* vbox_type_from = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass_from); + + Node* opd1 = unbox_vector(argument(7), vbox_type_from, elem_bt_from, num_elem_from); + if (opd1 == NULL) { + return false; + } + + const TypeVect* src_type = TypeVect::make(elem_bt_from, num_elem_from); + const TypeVect* dst_type = TypeVect::make(elem_bt_to, num_elem_to); + + Node* op = opd1; + if (is_cast) { + assert(!is_mask, "masks cannot be casted"); + int cast_vopc = VectorCastNode::opcode(elem_bt_from); + // Make sure that cast is implemented to particular type/size combination. + if (!arch_supports_vector(cast_vopc, num_elem_to, elem_bt_to, VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=1 op=cast#%d/3 vlen2=%d etype2=%s ismask=%d", + cast_vopc, + num_elem_to, type2name(elem_bt_to), is_mask); + } + return false; + } + + if (num_elem_from < num_elem_to) { + // Since input and output number of elements are not consistent, we need to make sure we + // properly size. Thus, first make a cast that retains the number of elements from source. + // In case the size exceeds the arch size, we do the minimum. + int num_elem_for_cast = MIN2(num_elem_from, Matcher::max_vector_size(elem_bt_to)); + + // It is possible that arch does not support this intermediate vector size + // TODO More complex logic required here to handle this corner case for the sizes. + if (!arch_supports_vector(cast_vopc, num_elem_for_cast, elem_bt_to, VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=1 op=cast#%d/4 vlen1=%d etype2=%s ismask=%d", + cast_vopc, + num_elem_for_cast, type2name(elem_bt_to), is_mask); + } + return false; + } + + op = gvn().transform(VectorCastNode::make(cast_vopc, op, elem_bt_to, num_elem_for_cast)); + // Now ensure that the destination gets properly resized to needed size. + op = gvn().transform(new VectorReinterpretNode(op, op->bottom_type()->is_vect(), dst_type)); + } else if (num_elem_from > num_elem_to) { + // Since number elements from input is larger than output, simply reduce size of input (we are supposed to + // drop top elements anyway). + int num_elem_for_resize = MAX2(num_elem_to, Matcher::min_vector_size(elem_bt_to)); + + // It is possible that arch does not support this intermediate vector size + // TODO More complex logic required here to handle this corner case for the sizes. + if (!arch_supports_vector(Op_VectorReinterpret, + num_elem_for_resize, + elem_bt_from, + VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=1 op=cast/5 vlen2=%d etype1=%s ismask=%d", + num_elem_for_resize, type2name(elem_bt_from), is_mask); + } + return false; + } + + op = gvn().transform(new VectorReinterpretNode(op, + src_type, + TypeVect::make(elem_bt_from, + num_elem_for_resize))); + op = gvn().transform(VectorCastNode::make(cast_vopc, op, elem_bt_to, num_elem_to)); + } else { + // Since input and output number of elements match, and since we know this vector size is + // supported, simply do a cast with no resize needed. + op = gvn().transform(VectorCastNode::make(cast_vopc, op, elem_bt_to, num_elem_to)); + } + } else if (Type::cmp(src_type, dst_type) != 0) { + assert(!is_cast, "must be reinterpret"); + op = gvn().transform(new VectorReinterpretNode(op, src_type, dst_type)); + } + + const TypeInstPtr* vbox_type_to = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass_to); + Node* vbox = box_vector(op, vbox_type_to, elem_bt_to, num_elem_to); + set_result(vbox); + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem_to * type2aelembytes(elem_bt_to)))); + return true; +} + +// public static +// > +// V insert(Class vectorClass, Class elementType, int vlen, +// V vec, int ix, long val, +// VecInsertOp defaultImpl) { +// +bool LibraryCallKit::inline_vector_insert() { + const TypeInstPtr* vector_klass = gvn().type(argument(0))->is_instptr(); + const TypeInstPtr* elem_klass = gvn().type(argument(1))->is_instptr(); + const TypeInt* vlen = gvn().type(argument(2))->is_int(); + const TypeInt* idx = gvn().type(argument(4))->is_int(); + + if (vector_klass->const_oop() == NULL || elem_klass->const_oop() == NULL || !vlen->is_con() || !idx->is_con()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** missing constant: vclass=%s etype=%s vlen=%s idx=%s", + NodeClassNames[argument(0)->Opcode()], + NodeClassNames[argument(1)->Opcode()], + NodeClassNames[argument(2)->Opcode()], + NodeClassNames[argument(4)->Opcode()]); + } + return false; // not enough info for intrinsification + } + if (!is_klass_initialized(vector_klass)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** klass argument not initialized"); + } + return false; + } + ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); + if (!elem_type->is_primitive_type()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not a primitive bt=%d", elem_type->basic_type()); + } + return false; // should be primitive type + } + BasicType elem_bt = elem_type->basic_type(); + int num_elem = vlen->get_con(); + if (!arch_supports_vector(Op_VectorInsert, num_elem, elem_bt, VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=1 op=insert vlen=%d etype=%s ismask=no", + num_elem, type2name(elem_bt)); + } + return false; // not supported + } + + ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); + + Node* opd = unbox_vector(argument(3), vbox_type, elem_bt, num_elem); + if (opd == NULL) { + return false; + } + + Node* insert_val = argument(5); + assert(gvn().type(insert_val)->isa_long() != NULL, "expected to be long"); + + // Convert insert value back to its appropriate type. + switch (elem_bt) { + case T_BYTE: + insert_val = gvn().transform(new ConvL2INode(insert_val)); + insert_val = gvn().transform(new CastIINode(insert_val, TypeInt::BYTE)); + break; + case T_SHORT: + insert_val = gvn().transform(new ConvL2INode(insert_val)); + insert_val = gvn().transform(new CastIINode(insert_val, TypeInt::SHORT)); + break; + case T_INT: + insert_val = gvn().transform(new ConvL2INode(insert_val)); + break; + case T_FLOAT: + insert_val = gvn().transform(new ConvL2INode(insert_val)); + insert_val = gvn().transform(new MoveI2FNode(insert_val)); + break; + case T_DOUBLE: + insert_val = gvn().transform(new MoveL2DNode(insert_val)); + break; + case T_LONG: + // no conversion needed + break; + default: fatal("%s", type2name(elem_bt)); break; + } + + Node* operation = gvn().transform(VectorInsertNode::make(opd, insert_val, idx->get_con())); + + Node* vbox = box_vector(operation, vbox_type, elem_bt, num_elem); + set_result(vbox); + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); + return true; +} + +// public static +// > +// long extract(Class vectorClass, Class elementType, int vlen, +// V vec, int ix, +// VecExtractOp defaultImpl) { +// +bool LibraryCallKit::inline_vector_extract() { + const TypeInstPtr* vector_klass = gvn().type(argument(0))->is_instptr(); + const TypeInstPtr* elem_klass = gvn().type(argument(1))->is_instptr(); + const TypeInt* vlen = gvn().type(argument(2))->is_int(); + const TypeInt* idx = gvn().type(argument(4))->is_int(); + + if (vector_klass->const_oop() == NULL || elem_klass->const_oop() == NULL || !vlen->is_con() || !idx->is_con()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** missing constant: vclass=%s etype=%s vlen=%s idx=%s", + NodeClassNames[argument(0)->Opcode()], + NodeClassNames[argument(1)->Opcode()], + NodeClassNames[argument(2)->Opcode()], + NodeClassNames[argument(4)->Opcode()]); + } + return false; // not enough info for intrinsification + } + if (!is_klass_initialized(vector_klass)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** klass argument not initialized"); + } + return false; + } + ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); + if (!elem_type->is_primitive_type()) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not a primitive bt=%d", elem_type->basic_type()); + } + return false; // should be primitive type + } + BasicType elem_bt = elem_type->basic_type(); + int num_elem = vlen->get_con(); + int vopc = ExtractNode::opcode(elem_bt); + if (!arch_supports_vector(vopc, num_elem, elem_bt, VecMaskNotUsed)) { + if (C->print_intrinsics()) { + tty->print_cr(" ** not supported: arity=1 op=extract vlen=%d etype=%s ismask=no", + num_elem, type2name(elem_bt)); + } + return false; // not supported + } + + ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); + + Node* opd = unbox_vector(argument(3), vbox_type, elem_bt, num_elem); + if (opd == NULL) { + return false; + } + + Node* operation = gvn().transform(ExtractNode::make(opd, idx->get_con(), elem_bt)); + + Node* bits = NULL; + switch (elem_bt) { + case T_BYTE: + case T_SHORT: + case T_INT: { + bits = gvn().transform(new ConvI2LNode(operation)); + break; + } + case T_FLOAT: { + bits = gvn().transform(new MoveF2INode(operation)); + bits = gvn().transform(new ConvI2LNode(bits)); + break; + } + case T_DOUBLE: { + bits = gvn().transform(new MoveD2LNode(operation)); + break; + } + case T_LONG: { + bits = operation; // no conversion needed + break; + } + default: fatal("%s", type2name(elem_bt)); + } + + set_result(bits); + return true; +} + diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index b3703e11403..e8d3da07894 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -120,12 +120,51 @@ int VectorNode::opcode(int sopc, BasicType bt) { case Op_AbsL: assert(bt == T_LONG, "must be"); return Op_AbsVL; + case Op_MinI: + switch (bt) { + case T_BOOLEAN: + case T_CHAR: return 0; + case T_BYTE: + case T_SHORT: + case T_INT: return Op_MinV; + default: ShouldNotReachHere(); return 0; + } + case Op_MinL: + assert(bt == T_LONG, "must be"); + return Op_MinV; + case Op_MinF: + assert(bt == T_FLOAT, "must be"); + return Op_MinV; + case Op_MinD: + assert(bt == T_DOUBLE, "must be"); + return Op_MinV; + case Op_MaxI: + switch (bt) { + case T_BOOLEAN: + case T_CHAR: return 0; + case T_BYTE: + case T_SHORT: + case T_INT: return Op_MaxV; + default: ShouldNotReachHere(); return 0; + } + case Op_MaxL: + assert(bt == T_LONG, "must be"); + return Op_MaxV; + case Op_MaxF: + assert(bt == T_FLOAT, "must be"); + return Op_MaxV; + case Op_MaxD: + assert(bt == T_DOUBLE, "must be"); + return Op_MaxV; case Op_AbsF: assert(bt == T_FLOAT, "must be"); return Op_AbsVF; case Op_AbsD: assert(bt == T_DOUBLE, "must be"); return Op_AbsVD; + case Op_NegI: + assert(bt == T_INT, "must be"); + return Op_NegVI; case Op_NegF: assert(bt == T_FLOAT, "must be"); return Op_NegVF; @@ -178,6 +217,12 @@ int VectorNode::opcode(int sopc, BasicType bt) { case Op_RShiftL: assert(bt == T_LONG, "must be"); return Op_RShiftVL; + case Op_URShiftB: + assert(bt == T_BYTE, "must be"); + return Op_URShiftVB; + case Op_URShiftS: + assert(bt == T_SHORT, "must be"); + return Op_URShiftVS; case Op_URShiftI: switch (bt) { case T_BOOLEAN:return Op_URShiftVB; @@ -203,18 +248,6 @@ int VectorNode::opcode(int sopc, BasicType bt) { case Op_XorI: case Op_XorL: return Op_XorV; - case Op_MinF: - assert(bt == T_FLOAT, "must be"); - return Op_MinV; - case Op_MinD: - assert(bt == T_DOUBLE, "must be"); - return Op_MinV; - case Op_MaxF: - assert(bt == T_FLOAT, "must be"); - return Op_MaxV; - case Op_MaxD: - assert(bt == T_DOUBLE, "must be"); - return Op_MaxV; case Op_LoadB: case Op_LoadUB: @@ -241,6 +274,28 @@ int VectorNode::opcode(int sopc, BasicType bt) { } } +int VectorNode::replicate_opcode(BasicType bt) { + switch(bt) { + case T_BOOLEAN: + case T_BYTE: + return Op_ReplicateB; + case T_SHORT: + case T_CHAR: + return Op_ReplicateS; + case T_INT: + return Op_ReplicateI; + case T_LONG: + return Op_ReplicateL; + case T_FLOAT: + return Op_ReplicateF; + case T_DOUBLE: + return Op_ReplicateD; + default: + assert(false, "wrong type: %s", type2name(bt)); + return 0; + } +} + // Also used to check if the code generator // supports the vector operation. bool VectorNode::implemented(int opc, uint vlen, BasicType bt) { @@ -331,6 +386,16 @@ bool VectorNode::is_shift(Node* n) { } } +bool VectorNode::is_vshift_cnt(Node* n) { + switch (n->Opcode()) { + case Op_LShiftCntV: + case Op_RShiftCntV: + return true; + default: + return false; + } +} + // Check if input is loop invariant vector. bool VectorNode::is_invariant_vector(Node* n) { // Only Replicate vector nodes are loop invariant for now. @@ -397,12 +462,10 @@ void VectorNode::vector_operands(Node* n, uint* start, uint* end) { } } -// Return the vector version of a scalar operation node. -VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, uint vlen, BasicType bt) { - const TypeVect* vt = TypeVect::make(bt, vlen); - int vopc = VectorNode::opcode(opc, bt); +// Make a vector node for binary operation +VectorNode* VectorNode::make(int vopc, Node* n1, Node* n2, const TypeVect* vt) { // This method should not be called for unimplemented vectors. - guarantee(vopc > 0, "Vector for '%s' is not implemented", NodeClassNames[opc]); + guarantee(vopc > 0, "vopc must be > 0"); switch (vopc) { case Op_AddVB: return new AddVBNode(n1, n2, vt); case Op_AddVS: return new AddVSNode(n1, n2, vt); @@ -428,13 +491,17 @@ VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, uint vlen, BasicType b case Op_DivVF: return new DivVFNode(n1, n2, vt); case Op_DivVD: return new DivVDNode(n1, n2, vt); + case Op_MinV: return new MinVNode(n1, n2, vt); + case Op_MaxV: return new MaxVNode(n1, n2, vt); + + case Op_AbsVF: return new AbsVFNode(n1, vt); + case Op_AbsVD: return new AbsVDNode(n1, vt); case Op_AbsVB: return new AbsVBNode(n1, vt); case Op_AbsVS: return new AbsVSNode(n1, vt); case Op_AbsVI: return new AbsVINode(n1, vt); case Op_AbsVL: return new AbsVLNode(n1, vt); - case Op_AbsVF: return new AbsVFNode(n1, vt); - case Op_AbsVD: return new AbsVDNode(n1, vt); + case Op_NegVI: return new NegVINode(n1, vt); case Op_NegVF: return new NegVFNode(n1, vt); case Op_NegVD: return new NegVDNode(n1, vt); @@ -464,9 +531,6 @@ VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, uint vlen, BasicType b case Op_OrV: return new OrVNode (n1, n2, vt); case Op_XorV: return new XorVNode(n1, n2, vt); - case Op_MinV: return new MinVNode(n1, n2, vt); - case Op_MaxV: return new MaxVNode(n1, n2, vt); - case Op_RoundDoubleModeV: return new RoundDoubleModeVNode(n1, n2, vt); case Op_MulAddVS2VI: return new MulAddVS2VINode(n1, n2, vt); @@ -476,11 +540,19 @@ VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, uint vlen, BasicType b } } -VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, Node* n3, uint vlen, BasicType bt) { +// Return the vector version of a scalar binary operation node. +VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, uint vlen, BasicType bt) { const TypeVect* vt = TypeVect::make(bt, vlen); int vopc = VectorNode::opcode(opc, bt); // This method should not be called for unimplemented vectors. guarantee(vopc > 0, "Vector for '%s' is not implemented", NodeClassNames[opc]); + return make(vopc, n1, n2, vt); +} + +// Make a vector node for ternary operation +VectorNode* VectorNode::make(int vopc, Node* n1, Node* n2, Node* n3, const TypeVect* vt) { + // This method should not be called for unimplemented vectors. + guarantee(vopc > 0, "vopc must be > 0"); switch (vopc) { case Op_FmaVD: return new FmaVDNode(n1, n2, n3, vt); case Op_FmaVF: return new FmaVFNode(n1, n2, n3, vt); @@ -490,6 +562,15 @@ VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, Node* n3, uint vlen, B } } +// Return the vector version of a scalar ternary operation node. +VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, Node* n3, uint vlen, BasicType bt) { + const TypeVect* vt = TypeVect::make(bt, vlen); + int vopc = VectorNode::opcode(opc, bt); + // This method should not be called for unimplemented vectors. + guarantee(vopc > 0, "Vector for '%s' is not implemented", NodeClassNames[opc]); + return make(vopc, n1, n2, n3, vt); +} + // Scalar promotion VectorNode* VectorNode::scalar2vector(Node* s, uint vlen, const Type* opd_t) { BasicType bt = opd_t->array_element_basic_type(); @@ -516,21 +597,22 @@ VectorNode* VectorNode::scalar2vector(Node* s, uint vlen, const Type* opd_t) { } } -VectorNode* VectorNode::shift_count(Node* shift, Node* cnt, uint vlen, BasicType bt) { - assert(VectorNode::is_shift(shift), "sanity"); +VectorNode* VectorNode::shift_count(int opc, Node* cnt, uint vlen, BasicType bt) { // Match shift count type with shift vector type. const TypeVect* vt = TypeVect::make(bt, vlen); - switch (shift->Opcode()) { + switch (opc) { case Op_LShiftI: case Op_LShiftL: return new LShiftCntVNode(cnt, vt); case Op_RShiftI: case Op_RShiftL: + case Op_URShiftB: + case Op_URShiftS: case Op_URShiftI: case Op_URShiftL: return new RShiftCntVNode(cnt, vt); default: - fatal("Missed vector creation for '%s'", NodeClassNames[shift->Opcode()]); + fatal("Missed vector creation for '%s'", NodeClassNames[opc]); return NULL; } } @@ -677,29 +759,37 @@ StoreVectorNode* StoreVectorNode::make(int opc, Node* ctl, Node* mem, return new StoreVectorNode(ctl, mem, adr, atyp, val); } +int ExtractNode::opcode(BasicType bt) { + switch (bt) { + case T_BOOLEAN: return Op_ExtractUB; + case T_BYTE: return Op_ExtractB; + case T_CHAR: return Op_ExtractC; + case T_SHORT: return Op_ExtractS; + case T_INT: return Op_ExtractI; + case T_LONG: return Op_ExtractL; + case T_FLOAT: return Op_ExtractF; + case T_DOUBLE: return Op_ExtractD; + default: + assert(false, "wrong type: %s", type2name(bt)); + return 0; + } +} + // Extract a scalar element of vector. Node* ExtractNode::make(Node* v, uint position, BasicType bt) { assert((int)position < Matcher::max_vector_size(bt), "pos in range"); ConINode* pos = ConINode::make((int)position); switch (bt) { - case T_BOOLEAN: - return new ExtractUBNode(v, pos); - case T_BYTE: - return new ExtractBNode(v, pos); - case T_CHAR: - return new ExtractCNode(v, pos); - case T_SHORT: - return new ExtractSNode(v, pos); - case T_INT: - return new ExtractINode(v, pos); - case T_LONG: - return new ExtractLNode(v, pos); - case T_FLOAT: - return new ExtractFNode(v, pos); - case T_DOUBLE: - return new ExtractDNode(v, pos); + case T_BOOLEAN: return new ExtractUBNode(v, pos); + case T_BYTE: return new ExtractBNode(v, pos); + case T_CHAR: return new ExtractCNode(v, pos); + case T_SHORT: return new ExtractSNode(v, pos); + case T_INT: return new ExtractINode(v, pos); + case T_LONG: return new ExtractLNode(v, pos); + case T_FLOAT: return new ExtractFNode(v, pos); + case T_DOUBLE: return new ExtractDNode(v, pos); default: - fatal("Type '%s' is not supported for vectors", type2name(bt)); + assert(false, "wrong type: %s", type2name(bt)); return NULL; } } @@ -708,8 +798,16 @@ int ReductionNode::opcode(int opc, BasicType bt) { int vopc = opc; switch (opc) { case Op_AddI: - assert(bt == T_INT, "must be"); - vopc = Op_AddReductionVI; + switch (bt) { + case T_BOOLEAN: + case T_CHAR: return 0; + case T_BYTE: + case T_SHORT: + case T_INT: + vopc = Op_AddReductionVI; + break; + default: ShouldNotReachHere(); return 0; + } break; case Op_AddL: assert(bt == T_LONG, "must be"); @@ -724,8 +822,16 @@ int ReductionNode::opcode(int opc, BasicType bt) { vopc = Op_AddReductionVD; break; case Op_MulI: - assert(bt == T_INT, "must be"); - vopc = Op_MulReductionVI; + switch (bt) { + case T_BOOLEAN: + case T_CHAR: return 0; + case T_BYTE: + case T_SHORT: + case T_INT: + vopc = Op_MulReductionVI; + break; + default: ShouldNotReachHere(); return 0; + } break; case Op_MulL: assert(bt == T_LONG, "must be"); @@ -739,6 +845,22 @@ int ReductionNode::opcode(int opc, BasicType bt) { assert(bt == T_DOUBLE, "must be"); vopc = Op_MulReductionVD; break; + case Op_MinI: + switch (bt) { + case T_BOOLEAN: + case T_CHAR: return 0; + case T_BYTE: + case T_SHORT: + case T_INT: + vopc = Op_MinReductionV; + break; + default: ShouldNotReachHere(); return 0; + } + break; + case Op_MinL: + assert(bt == T_LONG, "must be"); + vopc = Op_MinReductionV; + break; case Op_MinF: assert(bt == T_FLOAT, "must be"); vopc = Op_MinReductionV; @@ -747,6 +869,22 @@ int ReductionNode::opcode(int opc, BasicType bt) { assert(bt == T_DOUBLE, "must be"); vopc = Op_MinReductionV; break; + case Op_MaxI: + switch (bt) { + case T_BOOLEAN: + case T_CHAR: return 0; + case T_BYTE: + case T_SHORT: + case T_INT: + vopc = Op_MaxReductionV; + break; + default: ShouldNotReachHere(); return 0; + } + break; + case Op_MaxL: + assert(bt == T_LONG, "must be"); + vopc = Op_MaxReductionV; + break; case Op_MaxF: assert(bt == T_FLOAT, "must be"); vopc = Op_MaxReductionV; @@ -756,24 +894,48 @@ int ReductionNode::opcode(int opc, BasicType bt) { vopc = Op_MaxReductionV; break; case Op_AndI: - assert(bt == T_INT, "must be"); - vopc = Op_AndReductionV; + switch (bt) { + case T_BOOLEAN: + case T_CHAR: return 0; + case T_BYTE: + case T_SHORT: + case T_INT: + vopc = Op_AndReductionV; + break; + default: ShouldNotReachHere(); return 0; + } break; case Op_AndL: assert(bt == T_LONG, "must be"); vopc = Op_AndReductionV; break; case Op_OrI: - assert(bt == T_INT, "must be"); - vopc = Op_OrReductionV; + switch(bt) { + case T_BOOLEAN: + case T_CHAR: return 0; + case T_BYTE: + case T_SHORT: + case T_INT: + vopc = Op_OrReductionV; + break; + default: ShouldNotReachHere(); return 0; + } break; case Op_OrL: assert(bt == T_LONG, "must be"); vopc = Op_OrReductionV; break; case Op_XorI: - assert(bt == T_INT, "must be"); - vopc = Op_XorReductionV; + switch(bt) { + case T_BOOLEAN: + case T_CHAR: return 0; + case T_BYTE: + case T_SHORT: + case T_INT: + vopc = Op_XorReductionV; + break; + default: ShouldNotReachHere(); return 0; + } break; case Op_XorL: assert(bt == T_LONG, "must be"); @@ -808,11 +970,116 @@ ReductionNode* ReductionNode::make(int opc, Node *ctrl, Node* n1, Node* n2, Basi case Op_OrReductionV: return new OrReductionVNode(ctrl, n1, n2); case Op_XorReductionV: return new XorReductionVNode(ctrl, n1, n2); default: - fatal("Missed vector creation for '%s'", NodeClassNames[vopc]); + assert(false, "unknown node: %s", NodeClassNames[vopc]); return NULL; } } +VectorStoreMaskNode* VectorStoreMaskNode::make(PhaseGVN& gvn, Node* in, BasicType in_type, uint num_elem) { + assert(in->bottom_type()->isa_vect(), "sanity"); + const TypeVect* vt = TypeVect::make(T_BOOLEAN, num_elem); + int elem_size = type2aelembytes(in_type); + return new VectorStoreMaskNode(in, gvn.intcon(elem_size), vt); +} + +VectorCastNode* VectorCastNode::make(int vopc, Node* n1, BasicType bt, uint vlen) { + const TypeVect* vt = TypeVect::make(bt, vlen); + switch (vopc) { + case Op_VectorCastB2X: return new VectorCastB2XNode(n1, vt); + case Op_VectorCastS2X: return new VectorCastS2XNode(n1, vt); + case Op_VectorCastI2X: return new VectorCastI2XNode(n1, vt); + case Op_VectorCastL2X: return new VectorCastL2XNode(n1, vt); + case Op_VectorCastF2X: return new VectorCastF2XNode(n1, vt); + case Op_VectorCastD2X: return new VectorCastD2XNode(n1, vt); + default: + assert(false, "unknown node: %s", NodeClassNames[vopc]); + return NULL; + } +} + +int VectorCastNode::opcode(BasicType bt) { + switch (bt) { + case T_BYTE: return Op_VectorCastB2X; + case T_SHORT: return Op_VectorCastS2X; + case T_INT: return Op_VectorCastI2X; + case T_LONG: return Op_VectorCastL2X; + case T_FLOAT: return Op_VectorCastF2X; + case T_DOUBLE: return Op_VectorCastD2X; + default: + assert(false, "unknown type: %s", type2name(bt)); + return 0; + } +} + +Node* ReductionNode::make_reduction_input(PhaseGVN& gvn, int opc, BasicType bt) { + int vopc = opcode(opc, bt); + guarantee(vopc != opc, "Vector reduction for '%s' is not implemented", NodeClassNames[opc]); + + switch (vopc) { + case Op_AndReductionV: + switch (bt) { + case T_BYTE: + case T_SHORT: + case T_INT: + return gvn.makecon(TypeInt::MINUS_1); + case T_LONG: + return gvn.makecon(TypeLong::MINUS_1); + default: + fatal("Missed vector creation for '%s' as the basic type is not correct.", NodeClassNames[vopc]); + return NULL; + } + break; + case Op_AddReductionVI: // fallthrough + case Op_AddReductionVL: // fallthrough + case Op_AddReductionVF: // fallthrough + case Op_AddReductionVD: + case Op_OrReductionV: + case Op_XorReductionV: + return gvn.zerocon(bt); + case Op_MulReductionVI: + return gvn.makecon(TypeInt::ONE); + case Op_MulReductionVL: + return gvn.makecon(TypeLong::ONE); + case Op_MulReductionVF: + return gvn.makecon(TypeF::ONE); + case Op_MulReductionVD: + return gvn.makecon(TypeD::ONE); + case Op_MinReductionV: + switch (bt) { + case T_BYTE: + case T_SHORT: + case T_INT: + return gvn.makecon(TypeInt::MAX); + case T_LONG: + return gvn.makecon(TypeLong::MAX); + case T_FLOAT: + return gvn.makecon(TypeF::POS_INF); + case T_DOUBLE: + return gvn.makecon(TypeD::POS_INF); + default: Unimplemented(); return NULL; + } + break; + case Op_MaxReductionV: + switch (bt) { + case T_BYTE: + case T_SHORT: + case T_INT: + return gvn.makecon(TypeInt::MIN); + case T_LONG: + return gvn.makecon(TypeLong::MIN); + case T_FLOAT: + return gvn.makecon(TypeF::NEG_INF); + case T_DOUBLE: + return gvn.makecon(TypeD::NEG_INF); + default: Unimplemented(); return NULL; + } + break; + default: + fatal("Missed vector creation for '%s'", NodeClassNames[vopc]); + return NULL; + } +} + bool ReductionNode::implemented(int opc, uint vlen, BasicType bt) { if (is_java_primitive(bt) && (vlen > 1) && is_power_of_2(vlen) && @@ -824,7 +1091,7 @@ bool ReductionNode::implemented(int opc, uint vlen, BasicType bt) { } MacroLogicVNode* MacroLogicVNode::make(PhaseGVN& gvn, Node* in1, Node* in2, Node* in3, - uint truth_table, const TypeVect* vt) { + uint truth_table, const TypeVect* vt) { assert(truth_table <= 0xFF, "invalid"); assert(in1->bottom_type()->is_vect()->length_in_bytes() == vt->length_in_bytes(), "mismatch"); assert(in2->bottom_type()->is_vect()->length_in_bytes() == vt->length_in_bytes(), "mismatch"); @@ -895,3 +1162,51 @@ Node* RotateRightVNode::Ideal(PhaseGVN* phase, bool can_reshape) { return NULL; } +#ifndef PRODUCT +void VectorMaskCmpNode::dump_spec(outputStream *st) const { + st->print(" %d #", _predicate); _type->dump_on(st); +} +#endif // PRODUCT + +Node* VectorReinterpretNode::Identity(PhaseGVN *phase) { + Node* n = in(1); + if (n->Opcode() == Op_VectorReinterpret) { + if (Type::cmp(bottom_type(), n->in(1)->bottom_type()) == 0) { + return n->in(1); + } + } + return this; +} + +Node* VectorInsertNode::make(Node* vec, Node* new_val, int position) { + assert(position < (int)vec->bottom_type()->is_vect()->length(), "pos in range"); + ConINode* pos = ConINode::make(position); + return new VectorInsertNode(vec, new_val, pos, vec->bottom_type()->is_vect()); +} + +Node* VectorUnboxNode::Identity(PhaseGVN *phase) { + Node* n = obj()->uncast(); + if (EnableVectorReboxing && n->Opcode() == Op_VectorBox) { + if (Type::cmp(bottom_type(), n->in(VectorBoxNode::Value)->bottom_type()) == 0) { + return n->in(VectorBoxNode::Value); + } + } + return this; +} + +const TypeFunc* VectorBoxNode::vec_box_type(const TypeInstPtr* box_type) { + const Type** fields = TypeTuple::fields(0); + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms, fields); + + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = box_type; + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+1, fields); + + return TypeFunc::make(domain, range); +} + +#ifndef PRODUCT +void VectorBoxAllocateNode::dump_spec(outputStream *st) const { + CallStaticJavaNode::dump_spec(st); +} +#endif // !PRODUCT diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index 719070f8ed2..1a365a81e14 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -24,6 +24,7 @@ #ifndef SHARE_OPTO_VECTORNODE_HPP #define SHARE_OPTO_VECTORNODE_HPP +#include "opto/callnode.hpp" #include "opto/matcher.hpp" #include "opto/memnode.hpp" #include "opto/node.hpp" @@ -68,13 +69,17 @@ class VectorNode : public TypeNode { virtual uint ideal_reg() const { return Matcher::vector_ideal_reg(vect_type()->length_in_bytes()); } static VectorNode* scalar2vector(Node* s, uint vlen, const Type* opd_t); - static VectorNode* shift_count(Node* shift, Node* cnt, uint vlen, BasicType bt); + static VectorNode* shift_count(int opc, Node* cnt, uint vlen, BasicType bt); static VectorNode* make(int opc, Node* n1, Node* n2, uint vlen, BasicType bt); + static VectorNode* make(int vopc, Node* n1, Node* n2, const TypeVect* vt); static VectorNode* make(int opc, Node* n1, Node* n2, Node* n3, uint vlen, BasicType bt); + static VectorNode* make(int vopc, Node* n1, Node* n2, Node* n3, const TypeVect* vt); static int opcode(int opc, BasicType bt); + static int replicate_opcode(BasicType bt); static bool implemented(int opc, uint vlen, BasicType bt); static bool is_shift(Node* n); + static bool is_vshift_cnt(Node* n); static bool is_type_transition_short_to_int(Node* n); static bool is_type_transition_to_int(Node* n); static bool is_muladds2i(Node* n); @@ -160,9 +165,10 @@ class ReductionNode : public Node { static ReductionNode* make(int opc, Node *ctrl, Node* in1, Node* in2, BasicType bt); static int opcode(int opc, BasicType bt); static bool implemented(int opc, uint vlen, BasicType bt); + static Node* make_reduction_input(PhaseGVN& gvn, int opc, BasicType bt); virtual const Type* bottom_type() const { - BasicType vbt = in(2)->bottom_type()->is_vect()->element_basic_type(); + BasicType vbt = in(1)->bottom_type()->basic_type(); return Type::get_const_basic_type(vbt); } @@ -172,13 +178,11 @@ class ReductionNode : public Node { }; //------------------------------AddReductionVINode-------------------------------------- -// Vector add int as a reduction +// Vector add byte, short and int as a reduction class AddReductionVINode : public ReductionNode { public: AddReductionVINode(Node * ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} virtual int Opcode() const; - virtual const Type* bottom_type() const { return TypeInt::INT; } - virtual uint ideal_reg() const { return Op_RegI; } }; //------------------------------AddReductionVLNode-------------------------------------- @@ -187,8 +191,6 @@ class AddReductionVLNode : public ReductionNode { public: AddReductionVLNode(Node *ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} virtual int Opcode() const; - virtual const Type* bottom_type() const { return TypeLong::LONG; } - virtual uint ideal_reg() const { return Op_RegL; } }; //------------------------------AddReductionVFNode-------------------------------------- @@ -197,8 +199,6 @@ class AddReductionVFNode : public ReductionNode { public: AddReductionVFNode(Node *ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} virtual int Opcode() const; - virtual const Type* bottom_type() const { return Type::FLOAT; } - virtual uint ideal_reg() const { return Op_RegF; } }; //------------------------------AddReductionVDNode-------------------------------------- @@ -207,8 +207,6 @@ class AddReductionVDNode : public ReductionNode { public: AddReductionVDNode(Node *ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} virtual int Opcode() const; - virtual const Type* bottom_type() const { return Type::DOUBLE; } - virtual uint ideal_reg() const { return Op_RegD; } }; //------------------------------SubVBNode-------------------------------------- @@ -348,13 +346,11 @@ class CMoveVDNode : public VectorNode { }; //------------------------------MulReductionVINode-------------------------------------- -// Vector multiply int as a reduction +// Vector multiply byte, short and int as a reduction class MulReductionVINode : public ReductionNode { public: MulReductionVINode(Node *ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} virtual int Opcode() const; - virtual const Type* bottom_type() const { return TypeInt::INT; } - virtual uint ideal_reg() const { return Op_RegI; } }; //------------------------------MulReductionVLNode-------------------------------------- @@ -363,8 +359,6 @@ class MulReductionVLNode : public ReductionNode { public: MulReductionVLNode(Node *ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} virtual int Opcode() const; - virtual const Type* bottom_type() const { return TypeLong::LONG; } - virtual uint ideal_reg() const { return Op_RegI; } }; //------------------------------MulReductionVFNode-------------------------------------- @@ -373,8 +367,6 @@ class MulReductionVFNode : public ReductionNode { public: MulReductionVFNode(Node *ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} virtual int Opcode() const; - virtual const Type* bottom_type() const { return Type::FLOAT; } - virtual uint ideal_reg() const { return Op_RegF; } }; //------------------------------MulReductionVDNode-------------------------------------- @@ -383,8 +375,6 @@ class MulReductionVDNode : public ReductionNode { public: MulReductionVDNode(Node *ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} virtual int Opcode() const; - virtual const Type* bottom_type() const { return Type::DOUBLE; } - virtual uint ideal_reg() const { return Op_RegD; } }; //------------------------------DivVFNode-------------------------------------- @@ -419,10 +409,26 @@ class AbsVSNode : public VectorNode { virtual int Opcode() const; }; +//------------------------------MinVNode-------------------------------------- +// Vector Min +class MinVNode : public VectorNode { +public: + MinVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) {} + virtual int Opcode() const; +}; + +//------------------------------MaxVNode-------------------------------------- +// Vector Max +class MaxVNode : public VectorNode { + public: + MaxVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) {} + virtual int Opcode() const; +}; + //------------------------------AbsVINode-------------------------------------- // Vector Abs int class AbsVINode : public VectorNode { -public: + public: AbsVINode(Node* in, const TypeVect* vt) : VectorNode(in, vt) {} virtual int Opcode() const; }; @@ -451,6 +457,14 @@ class AbsVDNode : public VectorNode { virtual int Opcode() const; }; +//------------------------------NegVINode-------------------------------------- +// Vector Neg int +class NegVINode : public VectorNode { + public: + NegVINode(Node* in, const TypeVect* vt) : VectorNode(in, vt) {} + virtual int Opcode() const; +}; + //------------------------------NegVFNode-------------------------------------- // Vector Neg float class NegVFNode : public VectorNode { @@ -618,64 +632,48 @@ class AndVNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------OrVNode--------------------------------------- -// Vector or integer -class OrVNode : public VectorNode { +//------------------------------AndReductionVNode-------------------------------------- +// Vector and byte, short, int, long as a reduction +class AndReductionVNode : public ReductionNode { public: - OrVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1,in2,vt) {} + AndReductionVNode(Node *ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} virtual int Opcode() const; }; -//------------------------------XorVNode--------------------------------------- -// Vector xor integer -class XorVNode : public VectorNode { +//------------------------------OrVNode--------------------------------------- +// Vector or byte, short, int, long as a reduction +class OrVNode : public VectorNode { public: - XorVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1,in2,vt) {} - virtual int Opcode() const; -}; - -//------------------------------AndReductionVNode-------------------------------------- -// Vector and int, long as a reduction -class AndReductionVNode : public ReductionNode { -public: - AndReductionVNode(Node *ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} + OrVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1,in2,vt) {} virtual int Opcode() const; }; //------------------------------OrReductionVNode-------------------------------------- -// Vector or int, long as a reduction +// Vector xor byte, short, int, long as a reduction class OrReductionVNode : public ReductionNode { -public: + public: OrReductionVNode(Node *ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} virtual int Opcode() const; }; //------------------------------XorReductionVNode-------------------------------------- -// Vector xor int, long as a reduction +// Vector and int, long as a reduction class XorReductionVNode : public ReductionNode { -public: + public: XorReductionVNode(Node *ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} virtual int Opcode() const; }; -//------------------------------MinVNode-------------------------------------- -// Vector min -class MinVNode : public VectorNode { -public: - MinVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) {} - virtual int Opcode() const; -}; - -//------------------------------MaxVNode-------------------------------------- -// Vector max -class MaxVNode : public VectorNode { -public: - MaxVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) {} +//------------------------------XorVNode--------------------------------------- +// Vector xor integer +class XorVNode : public VectorNode { + public: + XorVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1,in2,vt) {} virtual int Opcode() const; }; //------------------------------MinReductionVNode-------------------------------------- -// Vector min as a reduction +// Vector min byte, short, int, long, float, double as a reduction class MinReductionVNode : public ReductionNode { public: MinReductionVNode(Node *ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} @@ -683,7 +681,7 @@ class MinReductionVNode : public ReductionNode { }; //------------------------------MaxReductionVNode-------------------------------------- -// Vector max as a reduction +// Vector min byte, short, int, long, float, double as a reduction class MaxReductionVNode : public ReductionNode { public: MaxReductionVNode(Node *ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} @@ -720,13 +718,28 @@ class LoadVectorNode : public LoadNode { uint element_size(void) { return type2aelembytes(vect_type()->element_basic_type()); } }; +//------------------------------LoadVectorGatherNode------------------------------ +// Load Vector from memory via index map +class LoadVectorGatherNode : public LoadVectorNode { + public: + LoadVectorGatherNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const TypeVect* vt, Node* indices) + : LoadVectorNode(c, mem, adr, at, vt) { + init_class_id(Class_LoadVectorGather); + assert(indices->bottom_type()->is_vect(), "indices must be in vector"); + add_req(indices); + assert(req() == MemNode::ValueIn + 1, "match_edge expects that last input is in MemNode::ValueIn"); + } + + virtual int Opcode() const; + virtual uint match_edge(uint idx) const { return idx == MemNode::Address || idx == MemNode::ValueIn; } +}; + //------------------------------StoreVectorNode-------------------------------- // Store Vector to memory class StoreVectorNode : public StoreNode { public: StoreVectorNode(Node* c, Node* mem, Node* adr, const TypePtr* at, Node* val) : StoreNode(c, mem, adr, at, val, MemNode::unordered) { - assert(val->is_Vector() || val->is_LoadVector(), "sanity"); init_class_id(Class_StoreVector); set_mismatched_access(); } @@ -747,6 +760,23 @@ class StoreVectorNode : public StoreNode { uint element_size(void) { return type2aelembytes(vect_type()->element_basic_type()); } }; +//------------------------------StoreVectorScatterNode------------------------------ +// Store Vector into memory via index map + + class StoreVectorScatterNode : public StoreVectorNode { + public: + StoreVectorScatterNode(Node* c, Node* mem, Node* adr, const TypePtr* at, Node* val, Node* indices) + : StoreVectorNode(c, mem, adr, at, val) { + init_class_id(Class_StoreVectorScatter); + assert(indices->bottom_type()->is_vect(), "indices must be in vector"); + add_req(indices); + assert(req() == MemNode::ValueIn + 2, "match_edge expects that last input is in MemNode::ValueIn+1"); + } + virtual int Opcode() const; + virtual uint match_edge(uint idx) const { return idx == MemNode::Address || + idx == MemNode::ValueIn || + idx == MemNode::ValueIn + 1; } +}; //=========================Promote_Scalar_to_Vector============================ @@ -888,6 +918,12 @@ class Pack2DNode : public PackNode { }; +class VectorLoadConstNode : public VectorNode { + public: + VectorLoadConstNode(Node* in1, const TypeVect* vt) : VectorNode(in1, vt) {} + virtual int Opcode() const; +}; + //========================Extract_Scalar_from_Vector=========================== //------------------------------ExtractNode------------------------------------ @@ -901,6 +937,7 @@ class ExtractNode : public Node { uint pos() const { return in(2)->get_int(); } static Node* make(Node* v, uint position, BasicType bt); + static int opcode(BasicType bt); }; //------------------------------ExtractBNode----------------------------------- @@ -929,7 +966,7 @@ class ExtractCNode : public ExtractNode { public: ExtractCNode(Node* src, ConINode* pos) : ExtractNode(src, pos) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual const Type *bottom_type() const { return TypeInt::CHAR; } virtual uint ideal_reg() const { return Op_RegI; } }; @@ -939,7 +976,7 @@ class ExtractSNode : public ExtractNode { public: ExtractSNode(Node* src, ConINode* pos) : ExtractNode(src, pos) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual const Type *bottom_type() const { return TypeInt::SHORT; } virtual uint ideal_reg() const { return Op_RegI; } }; @@ -1007,6 +1044,286 @@ class MacroLogicVNode : public VectorNode { static MacroLogicVNode* make(PhaseGVN& igvn, Node* in1, Node* in2, Node* in3, uint truth_table, const TypeVect* vt); }; +class VectorMaskCmpNode : public VectorNode { + private: + BoolTest::mask _predicate; + + protected: + uint size_of() const { return sizeof(*this); } + + public: + VectorMaskCmpNode(BoolTest::mask predicate, Node* in1, Node* in2, ConINode* predicate_node, const TypeVect* vt) : + VectorNode(in1, in2, predicate_node, vt), + _predicate(predicate) { + assert(in1->bottom_type()->is_vect()->element_basic_type() == in2->bottom_type()->is_vect()->element_basic_type(), + "VectorMaskCmp inputs must have same type for elements"); + assert(in1->bottom_type()->is_vect()->length() == in2->bottom_type()->is_vect()->length(), + "VectorMaskCmp inputs must have same number of elements"); + init_class_id(Class_VectorMaskCmp); + } + + virtual int Opcode() const; + virtual uint hash() const { return VectorNode::hash() + _predicate; } + virtual bool cmp( const Node &n ) const { + return VectorNode::cmp(n) && _predicate == ((VectorMaskCmpNode&)n)._predicate; + } + BoolTest::mask get_predicate() { return _predicate; } +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif // !PRODUCT +}; + +// Used to wrap other vector nodes in order to add masking functionality. +class VectorMaskWrapperNode : public VectorNode { + public: + VectorMaskWrapperNode(Node* vector, Node* mask) + : VectorNode(vector, mask, vector->bottom_type()->is_vect()) { + assert(mask->is_VectorMaskCmp(), "VectorMaskWrapper requires that second argument be a mask"); + } + + virtual int Opcode() const; + Node* vector_val() const { return in(1); } + Node* vector_mask() const { return in(2); } +}; + +class VectorTestNode : public Node { + private: + BoolTest::mask _predicate; + + protected: + uint size_of() const { return sizeof(*this); } + + public: + VectorTestNode( Node *in1, Node *in2, BoolTest::mask predicate) : Node(NULL, in1, in2), _predicate(predicate) { + assert(in1->is_Vector() || in1->is_LoadVector(), "must be vector"); + assert(in2->is_Vector() || in2->is_LoadVector(), "must be vector"); + assert(in1->bottom_type()->is_vect()->element_basic_type() == in2->bottom_type()->is_vect()->element_basic_type(), + "same type elements are needed"); + assert(in1->bottom_type()->is_vect()->length() == in2->bottom_type()->is_vect()->length(), + "same number of elements is needed"); + } + virtual int Opcode() const; + virtual uint hash() const { return Node::hash() + _predicate; } + virtual bool cmp( const Node &n ) const { + return Node::cmp(n) && _predicate == ((VectorTestNode&)n)._predicate; + } + virtual const Type *bottom_type() const { return TypeInt::BOOL; } + virtual uint ideal_reg() const { return Op_RegI; } // TODO Should be RegFlags but due to missing comparison flags for BoolTest + // in middle-end, we make it boolean result directly. + BoolTest::mask get_predicate() const { return _predicate; } +}; + +class VectorBlendNode : public VectorNode { + public: + VectorBlendNode(Node* vec1, Node* vec2, Node* mask) + : VectorNode(vec1, vec2, mask, vec1->bottom_type()->is_vect()) { + // assert(mask->is_VectorMask(), "VectorBlendNode requires that third argument be a mask"); + } + + virtual int Opcode() const; + Node* vec1() const { return in(1); } + Node* vec2() const { return in(2); } + Node* vec_mask() const { return in(3); } +}; + +class VectorRearrangeNode : public VectorNode { + public: + VectorRearrangeNode(Node* vec1, Node* shuffle) + : VectorNode(vec1, shuffle, vec1->bottom_type()->is_vect()) { + // assert(mask->is_VectorMask(), "VectorBlendNode requires that third argument be a mask"); + } + + virtual int Opcode() const; + Node* vec1() const { return in(1); } + Node* vec_shuffle() const { return in(2); } +}; + + +class VectorLoadMaskNode : public VectorNode { + public: + VectorLoadMaskNode(Node* in, const TypeVect* vt) + : VectorNode(in, vt) { + assert(in->is_LoadVector(), "expected load vector"); + assert(in->as_LoadVector()->vect_type()->element_basic_type() == T_BOOLEAN, "must be boolean"); + } + + virtual int Opcode() const; +}; + +class VectorLoadShuffleNode : public VectorNode { + public: + VectorLoadShuffleNode(Node* in, const TypeVect* vt) + : VectorNode(in, vt) { + assert(in->is_LoadVector(), "expected load vector"); + assert(in->as_LoadVector()->vect_type()->element_basic_type() == T_BYTE, "must be BYTE"); + } + + int GetOutShuffleSize() const { return type2aelembytes(vect_type()->element_basic_type()); } + virtual int Opcode() const; +}; + +class VectorStoreMaskNode : public VectorNode { + protected: + VectorStoreMaskNode(Node* in1, ConINode* in2, const TypeVect* vt) + : VectorNode(in1, in2, vt) { } + + public: + virtual int Opcode() const; + + static VectorStoreMaskNode* make(PhaseGVN& gvn, Node* in, BasicType in_type, uint num_elem); +}; + +// This is intended for use as a simple reinterpret node that has no cast. +class VectorReinterpretNode : public VectorNode { + private: + const TypeVect* _src_vt; + protected: + uint size_of() const { return sizeof(*this); } + public: + VectorReinterpretNode(Node* in, const TypeVect* src_vt, const TypeVect* dst_vt) + : VectorNode(in, dst_vt), _src_vt(src_vt) { } + + virtual uint hash() const { return VectorNode::hash() + _src_vt->hash(); } + virtual bool cmp( const Node &n ) const { + return VectorNode::cmp(n) && !Type::cmp(_src_vt,((VectorReinterpretNode&)n)._src_vt); + } + virtual Node *Identity(PhaseGVN *phase); + + virtual int Opcode() const; +}; + +class VectorCastNode : public VectorNode { + public: + VectorCastNode(Node* in, const TypeVect* vt) : VectorNode(in, vt) {} + virtual int Opcode() const; + + static VectorCastNode* make(int vopc, Node* n1, BasicType bt, uint vlen); + static int opcode(BasicType bt); + static bool implemented(BasicType bt, uint vlen); +}; + +class VectorCastB2XNode : public VectorCastNode { + public: + VectorCastB2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { + assert(in->bottom_type()->is_vect()->element_basic_type() == T_BYTE, "must be byte"); + } + virtual int Opcode() const; +}; + +class VectorCastS2XNode : public VectorCastNode { + public: + VectorCastS2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { + assert(in->bottom_type()->is_vect()->element_basic_type() == T_SHORT, "must be short"); + } + virtual int Opcode() const; +}; + +class VectorCastI2XNode : public VectorCastNode { + public: + VectorCastI2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { + assert(in->bottom_type()->is_vect()->element_basic_type() == T_INT, "must be int"); + } + virtual int Opcode() const; +}; + +class VectorCastL2XNode : public VectorCastNode { + public: + VectorCastL2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { + assert(in->bottom_type()->is_vect()->element_basic_type() == T_LONG, "must be long"); + } + virtual int Opcode() const; +}; + +class VectorCastF2XNode : public VectorCastNode { + public: + VectorCastF2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { + assert(in->bottom_type()->is_vect()->element_basic_type() == T_FLOAT, "must be float"); + } + virtual int Opcode() const; +}; + +class VectorCastD2XNode : public VectorCastNode { + public: + VectorCastD2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { + assert(in->bottom_type()->is_vect()->element_basic_type() == T_DOUBLE, "must be double"); + } + virtual int Opcode() const; +}; + +class VectorInsertNode : public VectorNode { + public: + VectorInsertNode(Node* vsrc, Node* new_val, ConINode* pos, const TypeVect* vt) : VectorNode(vsrc, new_val, (Node*)pos, vt) { + assert(pos->get_int() >= 0, "positive constants"); + assert(pos->get_int() < (int)vt->length(), "index must be less than vector length"); + assert(Type::cmp(vt, vsrc->bottom_type()) == 0, "input and output must be same type"); + } + virtual int Opcode() const; + uint pos() const { return in(3)->get_int(); } + + static Node* make(Node* vec, Node* new_val, int position); +}; + +class VectorBoxNode : public Node { + private: + const TypeInstPtr* const _box_type; + const TypeVect* const _vec_type; + public: + enum { + Box = 1, + Value = 2 + }; + VectorBoxNode(Compile* C, Node* box, Node* val, + const TypeInstPtr* box_type, const TypeVect* vt) + : Node(NULL, box, val), _box_type(box_type), _vec_type(vt) { + init_flags(Flag_is_macro); + C->add_macro_node(this); + } + + const TypeInstPtr* box_type() const { assert(_box_type != NULL, ""); return _box_type; }; + const TypeVect* vec_type() const { assert(_vec_type != NULL, ""); return _vec_type; }; + + virtual int Opcode() const; + virtual const Type* bottom_type() const { return _box_type; } + virtual uint ideal_reg() const { return box_type()->ideal_reg(); } + virtual uint size_of() const { return sizeof(*this); } + + static const TypeFunc* vec_box_type(const TypeInstPtr* box_type); +}; + +class VectorBoxAllocateNode : public CallStaticJavaNode { + public: + VectorBoxAllocateNode(Compile* C, const TypeInstPtr* vbox_type) + : CallStaticJavaNode(C, VectorBoxNode::vec_box_type(vbox_type), NULL, NULL, -1) { + init_flags(Flag_is_macro); + C->add_macro_node(this); + } + + virtual int Opcode() const; +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif // !PRODUCT +}; + +class VectorUnboxNode : public VectorNode { + private: + bool _shuffle_to_vector; + protected: + uint size_of() const { return sizeof(*this); } + public: + VectorUnboxNode(Compile* C, const TypeVect* vec_type, Node* obj, Node* mem, bool shuffle_to_vector) + : VectorNode(mem, obj, vec_type) { + _shuffle_to_vector = shuffle_to_vector; + init_flags(Flag_is_macro); + C->add_macro_node(this); + } + + virtual int Opcode() const; + Node* obj() const { return in(2); } + Node* mem() const { return in(1); } + virtual Node *Identity(PhaseGVN *phase); + bool is_shuffle_to_vector() { return _shuffle_to_vector; } +}; + class RotateRightVNode : public VectorNode { public: RotateRightVNode(Node* in1, Node* in2, const TypeVect* vt) diff --git a/src/hotspot/share/prims/forte.cpp b/src/hotspot/share/prims/forte.cpp index c80343eb2f6..140b024cffa 100644 --- a/src/hotspot/share/prims/forte.cpp +++ b/src/hotspot/share/prims/forte.cpp @@ -91,7 +91,7 @@ static bool is_decipherable_interpreted_frame(JavaThread* thread, vframeStreamForte::vframeStreamForte(JavaThread *jt, frame fr, - bool stop_at_java_call_stub) : vframeStreamCommon(jt) { + bool stop_at_java_call_stub) : vframeStreamCommon(jt, false /* process_frames */) { _stop_at_java_call_stub = stop_at_java_call_stub; _frame = fr; diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index c9dd3c34674..4705b6a935c 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -25,6 +25,8 @@ #include "precompiled.hpp" #include "jvm.h" #include "classfile/classFileStream.hpp" +#include "classfile/classListParser.hpp" +#include "classfile/classListWriter.hpp" #include "classfile/classLoader.hpp" #include "classfile/classLoaderData.hpp" #include "classfile/classLoaderData.inline.hpp" @@ -3437,6 +3439,24 @@ JVM_ENTRY(void, JVM_WaitForReferencePendingList(JNIEnv* env)) } JVM_END +JVM_ENTRY(jboolean, JVM_ReferenceRefersTo(JNIEnv* env, jobject ref, jobject o)) + JVMWrapper("JVM_ReferenceRefersTo"); + oop ref_oop = JNIHandles::resolve_non_null(ref); + oop referent = java_lang_ref_Reference::weak_referent_no_keepalive(ref_oop); + return referent == JNIHandles::resolve(o); +JVM_END + + +// java.lang.ref.PhantomReference ////////////////////////////////////////////////// + + +JVM_ENTRY(jboolean, JVM_PhantomReferenceRefersTo(JNIEnv* env, jobject ref, jobject o)) + JVMWrapper("JVM_PhantomReferenceRefersTo"); + oop ref_oop = JNIHandles::resolve_non_null(ref); + oop referent = java_lang_ref_Reference::phantom_referent_no_keepalive(ref_oop); + return referent == JNIHandles::resolve(o); +JVM_END + // ObjectInputStream /////////////////////////////////////////////////////////////// @@ -3741,7 +3761,7 @@ JVM_ENTRY(void, JVM_RegisterLambdaProxyClassForArchiving(JNIEnv* env, jclass lambdaProxyClass)) JVMWrapper("JVM_RegisterLambdaProxyClassForArchiving"); #if INCLUDE_CDS - if (!DynamicDumpSharedSpaces) { + if (!Arguments::is_dumping_archive()) { return; } @@ -3776,7 +3796,7 @@ JVM_ENTRY(void, JVM_RegisterLambdaProxyClassForArchiving(JNIEnv* env, Symbol* instantiated_method_type = java_lang_invoke_MethodType::as_signature(instantiated_method_type_oop(), true); SystemDictionaryShared::add_lambda_proxy_class(caller_ik, lambda_ik, invoked_name, invoked_type, - method_type, m, instantiated_method_type); + method_type, m, instantiated_method_type, THREAD); #endif // INCLUDE_CDS JVM_END @@ -3790,9 +3810,6 @@ JVM_ENTRY(jclass, JVM_LookupLambdaProxyClassFromArchive(JNIEnv* env, jboolean initialize)) JVMWrapper("JVM_LookupLambdaProxyClassFromArchive"); #if INCLUDE_CDS - if (!DynamicArchive::is_mapped()) { - return NULL; - } if (invokedName == NULL || invokedType == NULL || methodType == NULL || implMethodMember == NULL || instantiatedMethodType == NULL) { @@ -3833,9 +3850,9 @@ JVM_ENTRY(jclass, JVM_LookupLambdaProxyClassFromArchive(JNIEnv* env, #endif // INCLUDE_CDS JVM_END -JVM_ENTRY(jboolean, JVM_IsDynamicDumpingEnabled(JNIEnv* env)) - JVMWrapper("JVM_IsDynamicDumpingEnable"); - return DynamicDumpSharedSpaces; +JVM_ENTRY(jboolean, JVM_IsCDSDumpingEnabled(JNIEnv* env)) + JVMWrapper("JVM_IsCDSDumpingEnabled"); + return Arguments::is_dumping_archive(); JVM_END JVM_ENTRY(jboolean, JVM_IsSharingEnabled(JNIEnv* env)) @@ -3866,6 +3883,29 @@ JVM_ENTRY_NO_ENV(jlong, JVM_GetRandomSeedForDumping()) } JVM_END +JVM_ENTRY(jboolean, JVM_IsDumpingClassList(JNIEnv *env)) + JVMWrapper("JVM_IsDumpingClassList"); +#if INCLUDE_CDS + return ClassListWriter::is_enabled(); +#else + return false; +#endif // INCLUDE_CDS +JVM_END + +JVM_ENTRY(void, JVM_LogLambdaFormInvoker(JNIEnv *env, jstring line)) + JVMWrapper("JVM_LogLambdaFormInvoker"); +#if INCLUDE_CDS + assert(ClassListWriter::is_enabled(), "Should be set and open"); + if (line != NULL) { + ResourceMark rm(THREAD); + Handle h_line (THREAD, JNIHandles::resolve_non_null(line)); + char* c_line = java_lang_String::as_utf8_string(h_line()); + ClassListWriter w; + w.stream()->print_cr("%s %s", LAMBDA_FORM_TAG, c_line); + } +#endif // INCLUDE_CDS +JVM_END + // Returns an array of all live Thread objects (VM internal JavaThreads, // jvmti agent threads, and JNI attaching threads are skipped) // See CR 6404306 regarding JNI attaching threads diff --git a/src/hotspot/share/prims/jvmti.xml b/src/hotspot/share/prims/jvmti.xml index 7d3316291ed..44553b8065f 100644 --- a/src/hotspot/share/prims/jvmti.xml +++ b/src/hotspot/share/prims/jvmti.xml @@ -9949,9 +9949,11 @@ myInit() { there is a jint parameter, the event handler should be declared: - void JNICALL myHandler(jvmtiEnv* jvmti_env, jint myInt, ...) + void JNICALL myHandler(jvmtiEnv* jvmti_env, ...) Note the terminal "..." which indicates varargs. + The jint argument inside myHandler needs to be extracted using + the va_* syntax of the C programming language. diff --git a/src/hotspot/share/prims/jvmtiCodeBlobEvents.cpp b/src/hotspot/share/prims/jvmtiCodeBlobEvents.cpp index 02dff355b96..3b290cb0ae2 100644 --- a/src/hotspot/share/prims/jvmtiCodeBlobEvents.cpp +++ b/src/hotspot/share/prims/jvmtiCodeBlobEvents.cpp @@ -275,7 +275,7 @@ void JvmtiCodeBlobEvents::build_jvmti_addr_location_map(nmethod *nm, address scopes_data = nm->scopes_data_begin(); for( pcd = nm->scopes_pcs_begin(); pcd < nm->scopes_pcs_end(); ++pcd ) { - ScopeDesc sc0(nm, pcd->scope_decode_offset(), pcd->should_reexecute(), pcd->rethrow_exception(), pcd->return_oop()); + ScopeDesc sc0(nm, pcd, true); ScopeDesc *sd = &sc0; while( !sd->is_top() ) { sd = sd->sender(); } int bci = sd->bci(); diff --git a/src/hotspot/share/prims/jvmtiDeferredUpdates.cpp b/src/hotspot/share/prims/jvmtiDeferredUpdates.cpp new file mode 100644 index 00000000000..36dbd06879d --- /dev/null +++ b/src/hotspot/share/prims/jvmtiDeferredUpdates.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "prims/jvmtiDeferredUpdates.hpp" + +void JvmtiDeferredUpdates::create_for(JavaThread* thread) { + assert(thread->deferred_updates() == NULL, "already allocated"); + thread->set_deferred_updates(new JvmtiDeferredUpdates()); +} + +JvmtiDeferredUpdates::~JvmtiDeferredUpdates() { + while (_deferred_locals_updates.length() != 0) { + jvmtiDeferredLocalVariableSet* dlv = _deferred_locals_updates.pop(); + // individual jvmtiDeferredLocalVariableSet are CHeapObj's + delete dlv; + } +} + +void JvmtiDeferredUpdates::inc_relock_count_after_wait(JavaThread* thread) { + if (thread->deferred_updates() == NULL) { + create_for(thread); + } + thread->deferred_updates()->inc_relock_count_after_wait(); +} + +int JvmtiDeferredUpdates::get_and_reset_relock_count_after_wait(JavaThread* jt) { + JvmtiDeferredUpdates* updates = jt->deferred_updates(); + int result = 0; + if (updates != NULL) { + result = updates->get_and_reset_relock_count_after_wait(); + if (updates->count() == 0) { + delete updates; + jt->set_deferred_updates(NULL); + } + } + return result; +} + +void JvmtiDeferredUpdates::delete_updates_for_frame(JavaThread* jt, intptr_t* frame_id) { + JvmtiDeferredUpdates* updates = jt->deferred_updates(); + if (updates != NULL) { + GrowableArray* list = updates->deferred_locals(); + assert(list->length() > 0, "Updates holder not deleted"); + int i = 0; + do { + // Because of inlining we could have multiple vframes for a single frame + // and several of the vframes could have deferred writes. Find them all. + jvmtiDeferredLocalVariableSet* dlv = list->at(i); + if (dlv->id() == frame_id) { + list->remove_at(i); + // individual jvmtiDeferredLocalVariableSet are CHeapObj's + delete dlv; + } else { + i++; + } + } while ( i < list->length() ); + if (updates->count() == 0) { + jt->set_deferred_updates(NULL); + // Free deferred updates. + // Note, the 'list' of local variable updates is embedded in 'updates'. + delete updates; + } + } +} diff --git a/src/hotspot/share/prims/jvmtiDeferredUpdates.hpp b/src/hotspot/share/prims/jvmtiDeferredUpdates.hpp new file mode 100644 index 00000000000..d990d9011de --- /dev/null +++ b/src/hotspot/share/prims/jvmtiDeferredUpdates.hpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_PRIMS_JVMTIDEFERREDUPDATES_HPP +#define SHARE_PRIMS_JVMTIDEFERREDUPDATES_HPP + +#include "runtime/thread.inline.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/growableArray.hpp" + +class jvmtiDeferredLocalVariable : public CHeapObj { + + private: + + BasicType _type; + jvalue _value; + int _index; + + public: + + jvmtiDeferredLocalVariable(int index, BasicType type, jvalue value); + + BasicType type(void) { return _type; } + int index(void) { return _index; } + jvalue value(void) { return _value; } + + // Only mutator is for value as only it can change + void set_value(jvalue value) { _value = value; } + + // For gc + oop* oop_addr(void) { return (oop*) &_value.l; } +}; + +// In order to implement set_locals for compiled vframes we must +// store updated locals in a data structure that contains enough +// information to recognize equality with a vframe and to store +// any updated locals. + +class StackValueCollection; + +class jvmtiDeferredLocalVariableSet : public CHeapObj { + friend class compiledVFrame; + +private: + + Method* _method; + int _bci; + intptr_t* _id; + int _vframe_id; + GrowableArray* _locals; + bool _objects_are_deoptimized; + + void update_value(StackValueCollection* locals, BasicType type, int index, jvalue value); + + void set_value_at(int idx, BasicType typ, jvalue val); + + public: + // JVM state + Method* method() const { return _method; } + int bci() const { return _bci; } + intptr_t* id() const { return _id; } + int vframe_id() const { return _vframe_id; } + bool objects_are_deoptimized() const { return _objects_are_deoptimized; } + + void update_locals(StackValueCollection* locals); + void update_stack(StackValueCollection* locals); + void update_monitors(GrowableArray* monitors); + void set_objs_are_deoptimized() { _objects_are_deoptimized = true; } + + // Does the vframe match this jvmtiDeferredLocalVariableSet + bool matches(const vframe* vf); + + // Does the underlying physical frame match this jvmtiDeferredLocalVariableSet + bool matches(intptr_t* fr_id) { return id() == fr_id; } + + // GC + void oops_do(OopClosure* f); + + // constructor + jvmtiDeferredLocalVariableSet(Method* method, int bci, intptr_t* id, int vframe_id); + + // destructor + ~jvmtiDeferredLocalVariableSet(); +}; + +// Holds updates for compiled frames by JVMTI agents that cannot be performed immediately. + +class JvmtiDeferredUpdates : public CHeapObj { + + // Relocking has to be deferred if the lock owning thread is currently waiting on the monitor. + int _relock_count_after_wait; + + // Deferred updates of locals, expressions, and monitors + GrowableArray _deferred_locals_updates; + + void inc_relock_count_after_wait() { + _relock_count_after_wait++; + } + + int get_and_reset_relock_count_after_wait() { + int result = _relock_count_after_wait; + _relock_count_after_wait = 0; + return result; + } + + GrowableArray* deferred_locals() { return &_deferred_locals_updates; } + + JvmtiDeferredUpdates() : + _relock_count_after_wait(0), + _deferred_locals_updates((ResourceObj::set_allocation_type((address) &_deferred_locals_updates, + ResourceObj::C_HEAP), 1), mtCompiler) { } + +public: + ~JvmtiDeferredUpdates(); + + static void create_for(JavaThread* thread); + + static GrowableArray* deferred_locals(JavaThread* jt) { + return jt->deferred_updates() == NULL ? NULL : jt->deferred_updates()->deferred_locals(); + } + + // Relocking has to be deferred if the lock owning thread is currently waiting on the monitor. + static int get_and_reset_relock_count_after_wait(JavaThread* jt); + static void inc_relock_count_after_wait(JavaThread* thread); + + // Delete deferred updates for the compiled frame with id 'frame_id' on the + // given thread's stack. The thread's JvmtiDeferredUpdates instance will be + // deleted too if no updates remain. + static void delete_updates_for_frame(JavaThread* jt, intptr_t* frame_id); + + // Number of deferred updates + int count() const { + return _deferred_locals_updates.length() + (_relock_count_after_wait > 0 ? 1 : 0); + } +}; + +#endif // SHARE_PRIMS_JVMTIDEFERREDUPDATES_HPP diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp index 139788f902f..85dfd406622 100644 --- a/src/hotspot/share/prims/jvmtiEnv.cpp +++ b/src/hotspot/share/prims/jvmtiEnv.cpp @@ -1206,6 +1206,11 @@ JvmtiEnv::GetOwnedMonitorInfo(JavaThread* java_thread, jint* owned_monitor_count GrowableArray *owned_monitors_list = new (ResourceObj::C_HEAP, mtServiceability) GrowableArray(1, mtServiceability); + EscapeBarrier eb(true, calling_thread, java_thread); + if (!eb.deoptimize_objects(MaxJavaStackTraceDepth)) { + return JVMTI_ERROR_OUT_OF_MEMORY; + } + // It is only safe to perform the direct operation on the current // thread. All other usage needs to use a direct handshake for safety. if (java_thread == calling_thread) { @@ -1251,6 +1256,11 @@ JvmtiEnv::GetOwnedMonitorStackDepthInfo(JavaThread* java_thread, jint* monitor_i GrowableArray *owned_monitors_list = new (ResourceObj::C_HEAP, mtServiceability) GrowableArray(1, mtServiceability); + EscapeBarrier eb(true, calling_thread, java_thread); + if (!eb.deoptimize_objects(MaxJavaStackTraceDepth)) { + return JVMTI_ERROR_OUT_OF_MEMORY; + } + // It is only safe to perform the direct operation on the current // thread. All other usage needs to use a direct handshake for safety. if (java_thread == calling_thread) { @@ -1634,107 +1644,28 @@ JvmtiEnv::GetFrameCount(JavaThread* java_thread, jint* count_ptr) { // java_thread - pre-checked jvmtiError JvmtiEnv::PopFrame(JavaThread* java_thread) { - JavaThread* current_thread = JavaThread::current(); - HandleMark hm(current_thread); - uint32_t debug_bits = 0; - // retrieve or create the state JvmtiThreadState* state = JvmtiThreadState::state_for(java_thread); if (state == NULL) { return JVMTI_ERROR_THREAD_NOT_ALIVE; } - // Check if java_thread is fully suspended - if (!java_thread->is_thread_fully_suspended(true /* wait for suspend completion */, &debug_bits)) { - return JVMTI_ERROR_THREAD_NOT_SUSPENDED; - } - // Check to see if a PopFrame was already in progress - if (java_thread->popframe_condition() != JavaThread::popframe_inactive) { - // Probably possible for JVMTI clients to trigger this, but the - // JPDA backend shouldn't allow this to happen - return JVMTI_ERROR_INTERNAL; - } - - { - // Was workaround bug - // 4812902: popFrame hangs if the method is waiting at a synchronize - // Catch this condition and return an error to avoid hanging. - // Now JVMTI spec allows an implementation to bail out with an opaque frame error. - OSThread* osThread = java_thread->osthread(); - if (osThread->get_state() == MONITOR_WAIT) { - return JVMTI_ERROR_OPAQUE_FRAME; - } + // Eagerly reallocate scalar replaced objects. + JavaThread* current_thread = JavaThread::current(); + EscapeBarrier eb(true, current_thread, java_thread); + if (!eb.deoptimize_objects(1)) { + // Reallocation of scalar replaced objects failed -> return with error + return JVMTI_ERROR_OUT_OF_MEMORY; } - { - ResourceMark rm(current_thread); - // Check if there are more than one Java frame in this thread, that the top two frames - // are Java (not native) frames, and that there is no intervening VM frame - int frame_count = 0; - bool is_interpreted[2]; - intptr_t *frame_sp[2]; - // The 2-nd arg of constructor is needed to stop iterating at java entry frame. - for (vframeStream vfs(java_thread, true); !vfs.at_end(); vfs.next()) { - methodHandle mh(current_thread, vfs.method()); - if (mh->is_native()) return(JVMTI_ERROR_OPAQUE_FRAME); - is_interpreted[frame_count] = vfs.is_interpreted_frame(); - frame_sp[frame_count] = vfs.frame_id(); - if (++frame_count > 1) break; - } - if (frame_count < 2) { - // We haven't found two adjacent non-native Java frames on the top. - // There can be two situations here: - // 1. There are no more java frames - // 2. Two top java frames are separated by non-java native frames - if(vframeFor(java_thread, 1) == NULL) { - return JVMTI_ERROR_NO_MORE_FRAMES; - } else { - // Intervening non-java native or VM frames separate java frames. - // Current implementation does not support this. See bug #5031735. - // In theory it is possible to pop frames in such cases. - return JVMTI_ERROR_OPAQUE_FRAME; - } - } - - // If any of the top 2 frames is a compiled one, need to deoptimize it - for (int i = 0; i < 2; i++) { - if (!is_interpreted[i]) { - Deoptimization::deoptimize_frame(java_thread, frame_sp[i]); - } - } - - // Update the thread state to reflect that the top frame is popped - // so that cur_stack_depth is maintained properly and all frameIDs - // are invalidated. - // The current frame will be popped later when the suspended thread - // is resumed and right before returning from VM to Java. - // (see call_VM_base() in assembler_.cpp). - - // It's fine to update the thread state here because no JVMTI events - // shall be posted for this PopFrame. - - // It is only safe to perform the direct operation on the current - // thread. All other usage needs to use a handshake for safety. - { - MutexLocker mu(JvmtiThreadState_lock); - if (java_thread == JavaThread::current()) { - state->update_for_pop_top_frame(); - } else { - UpdateForPopTopFrameClosure op(state); - Handshake::execute(&op, java_thread); - if (op.result() != JVMTI_ERROR_NONE) { - return op.result(); - } - } - } - - java_thread->set_popframe_condition(JavaThread::popframe_pending_bit); - // Set pending step flag for this popframe and it is cleared when next - // step event is posted. - state->set_pending_step_for_popframe(); + MutexLocker mu(JvmtiThreadState_lock); + UpdateForPopTopFrameClosure op(state); + if (java_thread == current_thread) { + op.doit(java_thread, true /* self */); + } else { + Handshake::execute(&op, java_thread); } - - return JVMTI_ERROR_NONE; + return op.result(); } /* end PopFrame */ @@ -1768,46 +1699,19 @@ JvmtiEnv::GetFrameLocation(JavaThread* java_thread, jint depth, jmethodID* metho // depth - pre-checked as non-negative jvmtiError JvmtiEnv::NotifyFramePop(JavaThread* java_thread, jint depth) { - jvmtiError err = JVMTI_ERROR_NONE; - ResourceMark rm; - uint32_t debug_bits = 0; - JvmtiThreadState *state = JvmtiThreadState::state_for(java_thread); if (state == NULL) { return JVMTI_ERROR_THREAD_NOT_ALIVE; } - if (!java_thread->is_thread_fully_suspended(true, &debug_bits)) { - return JVMTI_ERROR_THREAD_NOT_SUSPENDED; - } - - if (TraceJVMTICalls) { - JvmtiSuspendControl::print(); - } - - vframe *vf = vframeFor(java_thread, depth); - if (vf == NULL) { - return JVMTI_ERROR_NO_MORE_FRAMES; - } - - if (!vf->is_java_frame() || ((javaVFrame*) vf)->method()->is_native()) { - return JVMTI_ERROR_OPAQUE_FRAME; - } - - assert(vf->frame_pointer() != NULL, "frame pointer mustn't be NULL"); - - // It is only safe to perform the direct operation on the current - // thread. All other usage needs to use a vm-safepoint-op for safety. + SetFramePopClosure op(this, state, depth); MutexLocker mu(JvmtiThreadState_lock); if (java_thread == JavaThread::current()) { - int frame_number = state->count_frames() - depth; - state->env_thread_state(this)->set_frame_pop(frame_number); + op.doit(java_thread, true /* self */); } else { - SetFramePopClosure op(this, state, depth); Handshake::execute(&op, java_thread); - err = op.result(); } - return err; + return op.result(); } /* end NotifyFramePop */ diff --git a/src/hotspot/share/prims/jvmtiEnvBase.cpp b/src/hotspot/share/prims/jvmtiEnvBase.cpp index 6dfde0054dd..2065aa68f8c 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.cpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp @@ -47,16 +47,16 @@ #include "runtime/interfaceSupport.inline.hpp" #include "runtime/jfieldIDWorkaround.hpp" #include "runtime/jniHandles.inline.hpp" -#include "runtime/objectMonitor.hpp" #include "runtime/objectMonitor.inline.hpp" #include "runtime/signature.hpp" #include "runtime/thread.inline.hpp" #include "runtime/threadSMR.hpp" -#include "runtime/vframe.hpp" +#include "runtime/vframe.inline.hpp" #include "runtime/vframe_hp.hpp" #include "runtime/vmThread.hpp" #include "runtime/vmOperations.hpp" + /////////////////////////////////////////////////////////////// // // JvmtiEnvBase @@ -556,12 +556,13 @@ JvmtiEnvBase::new_jthreadGroupArray(int length, Handle *handles) { } // return the vframe on the specified thread and depth, NULL if no such frame +// The thread and the oops in the returned vframe might not have been process. vframe* -JvmtiEnvBase::vframeFor(JavaThread* java_thread, jint depth) { +JvmtiEnvBase::vframeForNoProcess(JavaThread* java_thread, jint depth) { if (!java_thread->has_last_Java_frame()) { return NULL; } - RegisterMap reg_map(java_thread); + RegisterMap reg_map(java_thread, true /* update_map */, false /* process_frames */); vframe *vf = java_thread->last_java_vframe(®_map); int d = 0; while ((vf != NULL) && (d < depth)) { @@ -909,7 +910,7 @@ JvmtiEnvBase::get_frame_location(JavaThread *java_thread, jint depth, "call by myself or at handshake"); ResourceMark rm(current_thread); - vframe *vf = vframeFor(java_thread, depth); + vframe *vf = vframeForNoProcess(java_thread, depth); if (vf == NULL) { return JVMTI_ERROR_NO_MORE_FRAMES; } @@ -1309,7 +1310,7 @@ JvmtiEnvBase::check_top_frame(Thread* current_thread, JavaThread* java_thread, jvalue value, TosState tos, Handle* ret_ob_h) { ResourceMark rm(current_thread); - vframe *vf = vframeFor(java_thread, 0); + vframe *vf = vframeForNoProcess(java_thread, 0); NULL_CHECK(vf, JVMTI_ERROR_NO_MORE_FRAMES); javaVFrame *jvf = (javaVFrame*) vf; @@ -1368,26 +1369,48 @@ JvmtiEnvBase::check_top_frame(Thread* current_thread, JavaThread* java_thread, jvmtiError JvmtiEnvBase::force_early_return(JavaThread* java_thread, jvalue value, TosState tos) { - Thread* current_thread = Thread::current(); - HandleMark hm(current_thread); - uint32_t debug_bits = 0; - // retrieve or create the state JvmtiThreadState* state = JvmtiThreadState::state_for(java_thread); if (state == NULL) { return JVMTI_ERROR_THREAD_NOT_ALIVE; } - // Check if java_thread is fully suspended - if (!java_thread->is_thread_fully_suspended(true /* wait for suspend completion */, &debug_bits)) { - return JVMTI_ERROR_THREAD_NOT_SUSPENDED; + // Eagerly reallocate scalar replaced objects. + JavaThread* current_thread = JavaThread::current(); + EscapeBarrier eb(true, current_thread, java_thread); + if (!eb.deoptimize_objects(0)) { + // Reallocation of scalar replaced objects failed -> return with error + return JVMTI_ERROR_OUT_OF_MEMORY; + } + + SetForceEarlyReturn op(state, value, tos); + if (java_thread == current_thread) { + op.doit(java_thread, true /* self */); + } else { + Handshake::execute(&op, java_thread); + } + return op.result(); +} + +void +SetForceEarlyReturn::doit(Thread *target, bool self) { + JavaThread* java_thread = target->as_Java_thread(); + Thread* current_thread = Thread::current(); + HandleMark hm(current_thread); + + if (!self) { + if (!java_thread->is_external_suspend()) { + _result = JVMTI_ERROR_THREAD_NOT_SUSPENDED; + return; + } } // Check to see if a ForceEarlyReturn was already in progress - if (state->is_earlyret_pending()) { + if (_state->is_earlyret_pending()) { // Probably possible for JVMTI clients to trigger this, but the // JPDA backend shouldn't allow this to happen - return JVMTI_ERROR_INTERNAL; + _result = JVMTI_ERROR_INTERNAL; + return; } { // The same as for PopFrame. Workaround bug: @@ -1397,15 +1420,17 @@ JvmtiEnvBase::force_early_return(JavaThread* java_thread, jvalue value, TosState // frame error. OSThread* osThread = java_thread->osthread(); if (osThread->get_state() == MONITOR_WAIT) { - return JVMTI_ERROR_OPAQUE_FRAME; + _result = JVMTI_ERROR_OPAQUE_FRAME; + return; } } + Handle ret_ob_h; - jvmtiError err = check_top_frame(current_thread, java_thread, value, tos, &ret_ob_h); - if (err != JVMTI_ERROR_NONE) { - return err; + _result = JvmtiEnvBase::check_top_frame(current_thread, java_thread, _value, _tos, &ret_ob_h); + if (_result != JVMTI_ERROR_NONE) { + return; } - assert(tos != atos || value.l == NULL || ret_ob_h() != NULL, + assert(_tos != atos || _value.l == NULL || ret_ob_h() != NULL, "return object oop must not be NULL if jobject is not NULL"); // Update the thread state to reflect that the top frame must be @@ -1414,16 +1439,14 @@ JvmtiEnvBase::force_early_return(JavaThread* java_thread, jvalue value, TosState // thread is resumed and right before returning from VM to Java. // (see call_VM_base() in assembler_.cpp). - state->set_earlyret_pending(); - state->set_earlyret_oop(ret_ob_h()); - state->set_earlyret_value(value, tos); + _state->set_earlyret_pending(); + _state->set_earlyret_oop(ret_ob_h()); + _state->set_earlyret_value(_value, _tos); // Set pending step flag for this early return. // It is cleared when next step event is posted. - state->set_pending_step_for_earlyret(); - - return JVMTI_ERROR_NONE; -} /* end force_early_return */ + _state->set_pending_step_for_earlyret(); +} void JvmtiMonitorClosure::do_monitor(ObjectMonitor* mon) { @@ -1503,24 +1526,126 @@ JvmtiModuleClosure::get_all_modules(JvmtiEnv* env, jint* module_count_ptr, jobje } void -UpdateForPopTopFrameClosure::do_thread(Thread *target) { - JavaThread* jt = _state->get_thread(); - assert(jt == target, "just checking"); - if (!jt->is_exiting() && jt->threadObj() != NULL) { +UpdateForPopTopFrameClosure::doit(Thread *target, bool self) { + Thread* current_thread = Thread::current(); + HandleMark hm(current_thread); + JavaThread* java_thread = target->as_Java_thread(); + assert(java_thread == _state->get_thread(), "Must be"); + + if (!self && !java_thread->is_external_suspend()) { + _result = JVMTI_ERROR_THREAD_NOT_SUSPENDED; + return; + } + + // Check to see if a PopFrame was already in progress + if (java_thread->popframe_condition() != JavaThread::popframe_inactive) { + // Probably possible for JVMTI clients to trigger this, but the + // JPDA backend shouldn't allow this to happen + _result = JVMTI_ERROR_INTERNAL; + return; + } + + // Was workaround bug + // 4812902: popFrame hangs if the method is waiting at a synchronize + // Catch this condition and return an error to avoid hanging. + // Now JVMTI spec allows an implementation to bail out with an opaque frame error. + OSThread* osThread = java_thread->osthread(); + if (osThread->get_state() == MONITOR_WAIT) { + _result = JVMTI_ERROR_OPAQUE_FRAME; + return; + } + + ResourceMark rm(current_thread); + // Check if there is more than one Java frame in this thread, that the top two frames + // are Java (not native) frames, and that there is no intervening VM frame + int frame_count = 0; + bool is_interpreted[2]; + intptr_t *frame_sp[2]; + // The 2-nd arg of constructor is needed to stop iterating at java entry frame. + for (vframeStream vfs(java_thread, true, false /* process_frames */); !vfs.at_end(); vfs.next()) { + methodHandle mh(current_thread, vfs.method()); + if (mh->is_native()) { + _result = JVMTI_ERROR_OPAQUE_FRAME; + return; + } + is_interpreted[frame_count] = vfs.is_interpreted_frame(); + frame_sp[frame_count] = vfs.frame_id(); + if (++frame_count > 1) break; + } + if (frame_count < 2) { + // We haven't found two adjacent non-native Java frames on the top. + // There can be two situations here: + // 1. There are no more java frames + // 2. Two top java frames are separated by non-java native frames + if(JvmtiEnvBase::vframeForNoProcess(java_thread, 1) == NULL) { + _result = JVMTI_ERROR_NO_MORE_FRAMES; + return; + } else { + // Intervening non-java native or VM frames separate java frames. + // Current implementation does not support this. See bug #5031735. + // In theory it is possible to pop frames in such cases. + _result = JVMTI_ERROR_OPAQUE_FRAME; + return; + } + } + + // If any of the top 2 frames is a compiled one, need to deoptimize it + for (int i = 0; i < 2; i++) { + if (!is_interpreted[i]) { + Deoptimization::deoptimize_frame(java_thread, frame_sp[i]); + } + } + + // Update the thread state to reflect that the top frame is popped + // so that cur_stack_depth is maintained properly and all frameIDs + // are invalidated. + // The current frame will be popped later when the suspended thread + // is resumed and right before returning from VM to Java. + // (see call_VM_base() in assembler_.cpp). + + // It's fine to update the thread state here because no JVMTI events + // shall be posted for this PopFrame. + + if (!java_thread->is_exiting() && java_thread->threadObj() != NULL) { _state->update_for_pop_top_frame(); + java_thread->set_popframe_condition(JavaThread::popframe_pending_bit); + // Set pending step flag for this popframe and it is cleared when next + // step event is posted. + _state->set_pending_step_for_popframe(); _result = JVMTI_ERROR_NONE; } } void -SetFramePopClosure::do_thread(Thread *target) { - JavaThread* jt = _state->get_thread(); - assert(jt == target, "just checking"); - if (!jt->is_exiting() && jt->threadObj() != NULL) { - int frame_number = _state->count_frames() - _depth; - _state->env_thread_state((JvmtiEnvBase*)_env)->set_frame_pop(frame_number); - _result = JVMTI_ERROR_NONE; +SetFramePopClosure::doit(Thread *target, bool self) { + ResourceMark rm; + JavaThread* java_thread = target->as_Java_thread(); + + assert(_state->get_thread() == java_thread, "Must be"); + + if (!self && !java_thread->is_external_suspend()) { + _result = JVMTI_ERROR_THREAD_NOT_SUSPENDED; + return; + } + + vframe *vf = JvmtiEnvBase::vframeForNoProcess(java_thread, _depth); + if (vf == NULL) { + _result = JVMTI_ERROR_NO_MORE_FRAMES; + return; + } + + if (!vf->is_java_frame() || ((javaVFrame*) vf)->method()->is_native()) { + _result = JVMTI_ERROR_OPAQUE_FRAME; + return; + } + + assert(vf->frame_pointer() != NULL, "frame pointer mustn't be NULL"); + if (java_thread->is_exiting() || java_thread->threadObj() == NULL) { + return; /* JVMTI_ERROR_THREAD_NOT_ALIVE (default) */ } + int frame_number = _state->count_frames() - _depth; + _state->env_thread_state((JvmtiEnvBase*)_env)->set_frame_pop(frame_number); + _result = JVMTI_ERROR_NONE; } void diff --git a/src/hotspot/share/prims/jvmtiEnvBase.hpp b/src/hotspot/share/prims/jvmtiEnvBase.hpp index 7d3db079957..fa516fe568a 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.hpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.hpp @@ -286,9 +286,9 @@ class JvmtiEnvBase : public CHeapObj { javaVFrame *jvf, GrowableArray* owned_monitors_list, jint depth); - vframe* vframeFor(JavaThread* java_thread, jint depth); - public: + static vframe* vframeForNoProcess(JavaThread* java_thread, jint depth); + // get a field descriptor for the specified class and field static bool get_field_descriptor(Klass* k, jfieldID field, fieldDescriptor* fd); @@ -306,8 +306,8 @@ class JvmtiEnvBase : public CHeapObj { jobject *monitor_ptr); jvmtiError get_owned_monitors(JavaThread *calling_thread, JavaThread* java_thread, GrowableArray *owned_monitors_list); - jvmtiError check_top_frame(Thread* current_thread, JavaThread* java_thread, - jvalue value, TosState tos, Handle* ret_ob_h); + static jvmtiError check_top_frame(Thread* current_thread, JavaThread* java_thread, + jvalue value, TosState tos, Handle* ret_ob_h); jvmtiError force_early_return(JavaThread* java_thread, jvalue value, TosState tos); }; @@ -346,6 +346,23 @@ class JvmtiHandshakeClosure : public HandshakeClosure { jvmtiError result() { return _result; } }; +class SetForceEarlyReturn : public JvmtiHandshakeClosure { +private: + JvmtiThreadState* _state; + jvalue _value; + TosState _tos; +public: + SetForceEarlyReturn(JvmtiThreadState* state, jvalue value, TosState tos) + : JvmtiHandshakeClosure("SetForceEarlyReturn"), + _state(state), + _value(value), + _tos(tos) {} + void do_thread(Thread *target) { + doit(target, false /* self */); + } + void doit(Thread *target, bool self); +}; + // HandshakeClosure to update for pop top frame. class UpdateForPopTopFrameClosure : public JvmtiHandshakeClosure { private: @@ -354,8 +371,11 @@ class UpdateForPopTopFrameClosure : public JvmtiHandshakeClosure { public: UpdateForPopTopFrameClosure(JvmtiThreadState* state) : JvmtiHandshakeClosure("UpdateForPopTopFrame"), - _state(state) {} - void do_thread(Thread *target); + _state(state) {} + void do_thread(Thread *target) { + doit(target, false /* self */); + } + void doit(Thread *target, bool self); }; // HandshakeClosure to set frame pop. @@ -371,7 +391,10 @@ class SetFramePopClosure : public JvmtiHandshakeClosure { _env(env), _state(state), _depth(depth) {} - void do_thread(Thread *target); + void do_thread(Thread *target) { + doit(target, false /* self */); + } + void doit(Thread *target, bool self); }; // HandshakeClosure to get monitor information with stack depth. diff --git a/src/hotspot/share/prims/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp index ace58f4a3e0..d6a3000c931 100644 --- a/src/hotspot/share/prims/jvmtiEventController.cpp +++ b/src/hotspot/share/prims/jvmtiEventController.cpp @@ -215,7 +215,7 @@ class EnterInterpOnlyModeClosure : public HandshakeClosure { // interpreted-only mode is enabled the first time for a given // thread (nothing to do if no Java frames yet). ResourceMark resMark; - for (StackFrameStream fst(jt, false); !fst.is_done(); fst.next()) { + for (StackFrameStream fst(jt, false /* update */, false /* process_frames */); !fst.is_done(); fst.next()) { if (fst.current()->can_be_deoptimized()) { Deoptimization::deoptimize(jt, *fst.current()); } @@ -224,7 +224,7 @@ class EnterInterpOnlyModeClosure : public HandshakeClosure { _completed = true; } bool completed() { - return _completed = true; + return _completed; } }; diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index bfe5d574a7c..e2878eeaabb 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -1560,16 +1560,12 @@ void JvmtiExport::post_method_entry(JavaThread *thread, Method* method, frame cu } } -void JvmtiExport::post_method_exit(JavaThread *thread, Method* method, frame current_frame) { +void JvmtiExport::post_method_exit(JavaThread* thread, Method* method, frame current_frame) { HandleMark hm(thread); methodHandle mh(thread, method); - EVT_TRIG_TRACE(JVMTI_EVENT_METHOD_EXIT, ("[%s] Trg Method Exit triggered %s.%s", - JvmtiTrace::safe_get_thread_name(thread), - (mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(), - (mh() == NULL) ? "NULL" : mh()->name()->as_C_string() )); - JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL || !state->is_interp_only_mode()) { // for any thread that actually wants method exit, interp_only_mode is set return; @@ -1578,13 +1574,11 @@ void JvmtiExport::post_method_exit(JavaThread *thread, Method* method, frame cur // return a flag when a method terminates by throwing an exception // i.e. if an exception is thrown and it's not caught by the current method bool exception_exit = state->is_exception_detected() && !state->is_exception_caught(); - + Handle result; + jvalue value; + value.j = 0L; if (state->is_enabled(JVMTI_EVENT_METHOD_EXIT)) { - Handle result; - jvalue value; - value.j = 0L; - // if the method hasn't been popped because of an exception then we populate // the return_value parameter for the callback. At this point we only have // the address of a "raw result" and we just call into the interpreter to @@ -1594,9 +1588,36 @@ void JvmtiExport::post_method_exit(JavaThread *thread, Method* method, frame cur BasicType type = current_frame.interpreter_frame_result(&oop_result, &value); if (is_reference_type(type)) { result = Handle(thread, oop_result); + value.l = JNIHandles::make_local(thread, result()); } } + } + // Deferred transition to VM, so we can stash away the return oop before GC + // Note that this transition is not needed when throwing an exception, because + // there is no oop to retain. + JRT_BLOCK + post_method_exit_inner(thread, mh, state, exception_exit, current_frame, value); + JRT_BLOCK_END + + if (result.not_null() && !mh->is_native()) { + // We have to restore the oop on the stack for interpreter frames + *(oop*)current_frame.interpreter_frame_tos_address() = result(); + } +} + +void JvmtiExport::post_method_exit_inner(JavaThread* thread, + methodHandle& mh, + JvmtiThreadState *state, + bool exception_exit, + frame current_frame, + jvalue& value) { + EVT_TRIG_TRACE(JVMTI_EVENT_METHOD_EXIT, ("[%s] Trg Method Exit triggered %s.%s", + JvmtiTrace::safe_get_thread_name(thread), + (mh() == NULL) ? "NULL" : mh()->klass_name()->as_C_string(), + (mh() == NULL) ? "NULL" : mh()->name()->as_C_string() )); + + if (state->is_enabled(JVMTI_EVENT_METHOD_EXIT)) { JvmtiEnvThreadStateIterator it(state); for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { if (ets->is_enabled(JVMTI_EVENT_METHOD_EXIT)) { @@ -1607,9 +1628,6 @@ void JvmtiExport::post_method_exit(JavaThread *thread, Method* method, frame cur JvmtiEnv *env = ets->get_env(); JvmtiMethodEventMark jem(thread, mh); - if (result.not_null()) { - value.l = JNIHandles::make_local(thread, result()); - } JvmtiJavaThreadEventTransition jet(thread); jvmtiEventMethodExit callback = env->callbacks()->MethodExit; if (callback != NULL) { @@ -1801,7 +1819,9 @@ void JvmtiExport::notice_unwind_due_to_exception(JavaThread *thread, Method* met if(state->is_interp_only_mode()) { // method exit and frame pop events are posted only in interp mode. // When these events are enabled code should be in running in interp mode. - JvmtiExport::post_method_exit(thread, method, thread->last_frame()); + jvalue no_value; + no_value.j = 0L; + JvmtiExport::post_method_exit_inner(thread, mh, state, true, thread->last_frame(), no_value); // The cached cur_stack_depth might have changed from the // operations of frame pop or method exit. We are not 100% sure // the cached cur_stack_depth is still valid depth so invalidate diff --git a/src/hotspot/share/prims/jvmtiExport.hpp b/src/hotspot/share/prims/jvmtiExport.hpp index ce7f4100a5a..64e9d92e8b0 100644 --- a/src/hotspot/share/prims/jvmtiExport.hpp +++ b/src/hotspot/share/prims/jvmtiExport.hpp @@ -193,6 +193,13 @@ class JvmtiExport : public AllStatic { // dependency information is complete or not. static bool _all_dependencies_are_recorded; + static void post_method_exit_inner(JavaThread* thread, + methodHandle& mh, + JvmtiThreadState *state, + bool exception_exit, + frame current_frame, + jvalue& value); + public: inline static bool has_redefined_a_class() { JVMTI_ONLY(return _redefinition_count != 0); diff --git a/src/hotspot/share/prims/jvmtiExtensions.cpp b/src/hotspot/share/prims/jvmtiExtensions.cpp index fab91559b1a..a3fc430e1d1 100644 --- a/src/hotspot/share/prims/jvmtiExtensions.cpp +++ b/src/hotspot/share/prims/jvmtiExtensions.cpp @@ -34,7 +34,14 @@ GrowableArray* JvmtiExtensions::_ext_events; // extension function -static jvmtiError JNICALL IsClassUnloadingEnabled(const jvmtiEnv* env, jboolean* enabled, ...) { +static jvmtiError JNICALL IsClassUnloadingEnabled(const jvmtiEnv* env, ...) { + jboolean* enabled = NULL; + va_list ap; + + va_start(ap, env); + enabled = va_arg(ap, jboolean *); + va_end(ap); + if (enabled == NULL) { return JVMTI_ERROR_NULL_POINTER; } diff --git a/src/hotspot/share/prims/jvmtiH.xsl b/src/hotspot/share/prims/jvmtiH.xsl index 6a98b4cf32b..975758b4edb 100644 --- a/src/hotspot/share/prims/jvmtiH.xsl +++ b/src/hotspot/share/prims/jvmtiH.xsl @@ -1,6 +1,6 @@ @@ -90,7 +90,7 @@

    Color Conversion

    Color Spaces

    The raw color space assigned by default, i.e., in the absence of a -user-supplied ImageTypeSpecifier, +user-supplied {@link javax.imageio.ImageTypeSpecifier ImageTypeSpecifier}, will be the first among the following which applies:
      @@ -158,8 +158,8 @@

      Color Spaces

      ICC Profiles

      If an ICC profile is contained in the image metadata -( -BaselineTIFFTagSet.TAG_ICC_PROFILE, tag number 34675), +({@link javax.imageio.plugins.tiff.BaselineTIFFTagSet BaselineTIFFTagSet}. +TAG_ICC_PROFILE, tag number 34675), an attempt will be made to use it to create the color space of the loaded image. It will be used if the data layout is of component type and the number of samples per pixel equals or is one greater than the number @@ -174,15 +174,13 @@

      ICC Profiles

    • Obtain the image metadata from ImageReader.getImageMetadata
    • Extract the ICC profile field and its value.
    • -
    • Create an -ICC_ColorSpace from an - -ICC_Profile created from the ICC profile field data +
    • Create an {@link java.awt.color.ICC_ColorSpace ICC_ColorSpace} from an +{@link java.awt.color.ICC_Profile ICC_Profile} created from the ICC profile field data using ICC_Profile.getInstance(byte[]).
    • Create an ImageTypeSpecifier from the new color space using one of its factory methods which accepts an ICC_ColorSpace. -
    • Create a compatible ImageReadParam +
    • Create a compatible {@link javax.imageio.ImageReadParam ImageReadParam} and set the ImageTypeSpecifier using ImageReadParam.setDestinationType.
    • Pass the parameter object to the appropriate read method.
    • @@ -206,7 +204,7 @@

      Metadata Issues

      metadata. The reader is informed to disregard all metadata as usual via the ignoreMetadata parameter of ImageReader.setInput(Object,boolean,boolean). It is -informed of which TIFFTags to +informed of which {@link javax.imageio.plugins.tiff.TIFFTag TIFFTag}s to recognize or not to recognize via TIFFImageReadParam.addAllowedTagSet(TIFFTagSet) and TIFFImageReadParam.removeAllowedTagSet(TIFFTagSet). @@ -221,7 +219,7 @@

      Metadata Issues

      TIFFImageReadParam.setReadUnknownTags(boolean) has been invoked with parameter true. -

      Use of a TIFFDirectory +

      Use of a {@link javax.imageio.plugins.tiff.TIFFDirectory TIFFDirectory} object may simplify gaining access to metadata values. An instance of TIFFDirectory may be created from the IIOMetadata object returned by the TIFF reader using the @@ -481,9 +479,9 @@

      Reading Compressed Exif Images

      Writing Images

      -TIFF images are written by a ImageWriter which may be +TIFF images are written by a {@link javax.imageio.ImageWriter ImageWriter} which may be controlled by its public interface as well as via a supplied -ImageWriteParam. For an ImageWriteParam returned +{@link javax.imageio.ImageWriteParam ImageWriteParam}. For an ImageWriteParam returned by the getDefaultWriteParam() method of the TIFF ImageWriter, the canWriteTiles() and canWriteCompressed() methods will return true; the canOffsetTiles() and @@ -631,9 +629,9 @@

      ICC Profiles

      An ICC Profile field will be written if either:
      • one is present in the native image metadata -IIOMetadata instance supplied to the writer, +{@link javax.imageio.metadata.IIOMetadata IIOMetadata} instance supplied to the writer, or
      • -
      • the ColorSpace +
      • the {@link java.awt.color.ColorSpace ColorSpace} of the destination ImageTypeSpecifier is an instance of ICC_ColorSpace which is not one of the standard color spaces defined by the CS_* constants in the @@ -748,7 +746,7 @@

        Metadata Issues

        Setting up the image metadata to write to a TIFF stream may be simplified by using the TIFFDirectory class which represents a TIFF IFD. A field in a TIFF IFD is represented by an -instance of TIFFField. For each +instance of {@link javax.imageio.plugins.tiff.TIFFField TIFFField}. For each field to be written a TIFFField may be added to the TIFFDirectory and the latter converted to an IIOMetadata object by invoking diff --git a/src/java.desktop/share/classes/javax/print/DocFlavor.java b/src/java.desktop/share/classes/javax/print/DocFlavor.java index a52b0e18852..da3f4f18762 100644 --- a/src/java.desktop/share/classes/javax/print/DocFlavor.java +++ b/src/java.desktop/share/classes/javax/print/DocFlavor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -156,9 +156,9 @@ * the primary IANA name but is guaranteed to be understood by this VM. For * common flavors, the pre-defined *HOST {@code DocFlavors} may be used. *

        - * See character - * encodings for more information on the character encodings supported on - * the Java platform. + * See + * character encodings for more information on the character encodings + * supported on the Java platform. * *


        *

        Recommended DocFlavors

        diff --git a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java index 9dcb5d5ece8..db8b71e3b2c 100644 --- a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java +++ b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,7 +50,6 @@ /** * A collection of utility methods for Swing. * - * @author unknown * @since 1.2 */ public class SwingUtilities implements SwingConstants diff --git a/src/java.desktop/share/classes/javax/swing/TimerQueue.java b/src/java.desktop/share/classes/javax/swing/TimerQueue.java index 9160b956e95..85e5b3a6aa1 100644 --- a/src/java.desktop/share/classes/javax/swing/TimerQueue.java +++ b/src/java.desktop/share/classes/javax/swing/TimerQueue.java @@ -248,7 +248,7 @@ static class DelayedTimer implements Delayed { * Sequence number to break scheduling ties, and in turn to * guarantee FIFO order among tied entries. */ - private static final AtomicLong sequencer = new AtomicLong(0); + private static final AtomicLong sequencer = new AtomicLong(); /** Sequence number to break ties FIFO */ private final long sequenceNumber; diff --git a/src/java.desktop/share/classes/javax/swing/UnsupportedLookAndFeelException.java b/src/java.desktop/share/classes/javax/swing/UnsupportedLookAndFeelException.java index a82c97d9940..02910664ea5 100644 --- a/src/java.desktop/share/classes/javax/swing/UnsupportedLookAndFeelException.java +++ b/src/java.desktop/share/classes/javax/swing/UnsupportedLookAndFeelException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,6 @@ * has been added to the java.beans package. * Please see {@link java.beans.XMLEncoder}. * - * @author unattributed * @since 1.2 */ @SuppressWarnings("serial") // Same-version serialization only diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java index 8c709c72070..d893f62bbd1 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -100,8 +100,6 @@ * of all JavaBeans * has been added to the java.beans package. * Please see {@link java.beans.XMLEncoder}. - * - * @author unattributed */ @SuppressWarnings("serial") // Same-version serialization only public abstract class BasicLookAndFeel extends LookAndFeel implements Serializable diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java index 243d465ac3e..a26f0511432 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1226,6 +1226,7 @@ public void stateChanged(ChangeEvent ev) { // menu hidden -- return focus to where it had been before // and uninstall menu keybindings removeItems(); + menuInputMap = null; } else { if (popup != lastPopup) { receivedKeyPressed = false; diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java index 5102e241e08..452707fd126 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,14 +25,37 @@ package javax.swing.plaf.synth; -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -import javax.swing.plaf.*; -import javax.swing.event.*; -import javax.swing.plaf.basic.*; -import java.beans.PropertyChangeListener; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import javax.swing.ComboBoxEditor; +import javax.swing.DefaultButtonModel; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.ListCellRenderer; +import javax.swing.SwingConstants; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicComboBoxEditor; +import javax.swing.plaf.basic.BasicComboBoxUI; +import javax.swing.plaf.basic.ComboPopup; /** * Provides the Synth L&F UI delegate for @@ -730,9 +753,9 @@ private EditorFocusHandler(JComboBox comboBox) { comboBox.addPropertyChangeListener("editor",this); } - public void unregister(){ - comboBox.removePropertyChangeListener(this); - if (editorComponent!=null){ + public void unregister() { + comboBox.removePropertyChangeListener("editor", this); + if (editorComponent != null) { editorComponent.removeFocusListener(this); } } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthDesktopIconUI.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthDesktopIconUI.java index 3c2b5122085..0b82788032e 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthDesktopIconUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthDesktopIconUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,13 +25,23 @@ package javax.swing.plaf.synth; -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.BasicDesktopIconUI; -import java.beans.*; +import java.awt.BorderLayout; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyVetoException; +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JInternalFrame; +import javax.swing.JPopupMenu; +import javax.swing.JToggleButton; +import javax.swing.ToolTipManager; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicDesktopIconUI; /** * Provides the Synth L&F UI delegate for a minimized internal frame on a desktop. @@ -89,6 +99,20 @@ protected void installComponents() { desktopIcon.add(iconPane, BorderLayout.CENTER); } + @Override + protected void uninstallComponents() { + // Uninstall the listeners here because the iconPane will be set to null + // in the super.uninstallComponents() + if (iconPane instanceof JToggleButton) { + ((JToggleButton) iconPane).removeActionListener(handler); + frame.removePropertyChangeListener(this); + } else if (iconPane instanceof SynthInternalFrameTitlePane) { + // Uninstall the listeners added by the SynthInternalFrameTitlePane + ((SynthInternalFrameTitlePane) iconPane).uninstallListeners(); + } + super.uninstallComponents(); + } + /** * {@inheritDoc} */ @@ -108,10 +132,6 @@ protected void installListeners() { */ @Override protected void uninstallListeners() { - if (iconPane instanceof JToggleButton) { - ((JToggleButton)iconPane).removeActionListener(handler); - frame.removePropertyChangeListener(this); - } desktopIcon.removePropertyChangeListener(this); super.uninstallListeners(); } diff --git a/src/java.desktop/share/classes/javax/swing/text/html/HTMLDocument.java b/src/java.desktop/share/classes/javax/swing/text/html/HTMLDocument.java index 1d1be4b484f..a100581567f 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/HTMLDocument.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/HTMLDocument.java @@ -194,7 +194,7 @@ *
    * * - * + * * + * Syntax Value Start End + * + * + * Setting value: + * kern 1 0 Turn feature on + * +kern 1 0 Turn feature on + * -kern 0 0 Turn feature off + * kern=0 0 0 Turn feature off + * kern=1 1 0 Turn feature on + * aalt=2 2 0 Choose 2nd alternate + * Setting index: + * kern[] 1 0 Turn feature on + * kern[:] 1 0 Turn feature on + * kern[5:] 1 5 Turn feature on, partial + * kern[:5] 1 0 5 Turn feature on, partial + * kern[3:5] 1 3 5 Turn feature on, range + * kern[3] 1 3 3+1 Turn feature on, single char + * Mixing it all: + * aalt[3:5]=2 2 3 5 Turn 2nd alternate on for range + * + * + * * * Return value: * %true if @str is successfully parsed, %false otherwise. @@ -1028,25 +934,25 @@ hb_feature_to_string (hb_feature_t *feature, len += 4; while (len && s[len - 1] == ' ') len--; - if (feature->start != 0 || feature->end != (unsigned int) -1) + if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END) { s[len++] = '['; if (feature->start) - len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); if (feature->end != feature->start + 1) { s[len++] = ':'; - if (feature->end != (unsigned int) -1) - len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); + if (feature->end != HB_FEATURE_GLOBAL_END) + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); } s[len++] = ']'; } if (feature->value > 1) { s[len++] = '='; - len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); } assert (len < ARRAY_LENGTH (s)); - len = MIN (len, size - 1); + len = hb_min (len, size - 1); memcpy (buf, s, len); buf[len] = '\0'; } @@ -1057,7 +963,11 @@ static bool parse_variation_value (const char **pp, const char *end, hb_variation_t *variation) { parse_char (pp, end, '='); /* Optional. */ - return parse_float (pp, end, &variation->value); + double v; + if (unlikely (!hb_parse_double (pp, end, &v))) return false; + + variation->value = v; + return true; } static bool @@ -1113,14 +1023,71 @@ hb_variation_to_string (hb_variation_t *variation, while (len && s[len - 1] == ' ') len--; s[len++] = '='; - len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value)); + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value)); assert (len < ARRAY_LENGTH (s)); - len = MIN (len, size - 1); + len = hb_min (len, size - 1); memcpy (buf, s, len); buf[len] = '\0'; } +/** + * hb_color_get_alpha: + * color: a #hb_color_t we are interested in its channels. + * + * Return value: Alpha channel value of the given color + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_alpha) (hb_color_t color) +{ + return hb_color_get_alpha (color); +} + +/** + * hb_color_get_red: + * color: a #hb_color_t we are interested in its channels. + * + * Return value: Red channel value of the given color + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_red) (hb_color_t color) +{ + return hb_color_get_red (color); +} + +/** + * hb_color_get_green: + * color: a #hb_color_t we are interested in its channels. + * + * Return value: Green channel value of the given color + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_green) (hb_color_t color) +{ + return hb_color_get_green (color); +} + +/** + * hb_color_get_blue: + * color: a #hb_color_t we are interested in its channels. + * + * Return value: Blue channel value of the given color + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_blue) (hb_color_t color) +{ + return hb_color_get_blue (color); +} + + /* If there is no visibility control, then hb-static.cc will NOT * define anything. Instead, we get it to define one set in here * only, so only libharfbuzz.so defines them, not other libs. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-common.h b/src/java.desktop/share/native/libharfbuzz/hb-common.h index fea193ada71..9614e720b32 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-common.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-common.h @@ -63,6 +63,8 @@ typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; +#elif defined (__KERNEL__) +# include #else # include #endif @@ -357,6 +359,22 @@ typedef enum /*11.0*/HB_SCRIPT_OLD_SOGDIAN = HB_TAG ('S','o','g','o'), /*11.0*/HB_SCRIPT_SOGDIAN = HB_TAG ('S','o','g','d'), + /* + * Since 2.4.0 + */ + /*12.0*/HB_SCRIPT_ELYMAIC = HB_TAG ('E','l','y','m'), + /*12.0*/HB_SCRIPT_NANDINAGARI = HB_TAG ('N','a','n','d'), + /*12.0*/HB_SCRIPT_NYIAKENG_PUACHUE_HMONG = HB_TAG ('H','m','n','p'), + /*12.0*/HB_SCRIPT_WANCHO = HB_TAG ('W','c','h','o'), + + /* + * Since 2.6.7 + */ + /*13.0*/HB_SCRIPT_CHORASMIAN = HB_TAG ('C','h','r','s'), + /*13.0*/HB_SCRIPT_DIVES_AKURU = HB_TAG ('D','i','a','k'), + /*13.0*/HB_SCRIPT_KHITAN_SMALL_SCRIPT = HB_TAG ('K','i','t','s'), + /*13.0*/HB_SCRIPT_YEZIDI = HB_TAG ('Y','e','z','i'), + /* No script set. */ HB_SCRIPT_INVALID = HB_TAG_NONE, @@ -415,6 +433,21 @@ typedef void (*hb_destroy_func_t) (void *user_data); */ #define HB_FEATURE_GLOBAL_END ((unsigned int) -1) +/** + * hb_feature_t: + * @tag: a feature tag + * @value: 0 disables the feature, non-zero (usually 1) enables the feature. + * For features implemented as lookup type 3 (like 'salt') the @value is a one + * based index into the alternates. + * @start: the cluster to start applying this feature setting (inclusive). + * @end: the cluster to end applying this feature setting (exclusive). + * + * The #hb_feature_t is the structure that holds information about requested + * feature application. The feature will be applied with the given value to all + * glyphs which are in clusters between @start (inclusive) and @end (exclusive). + * Setting start to @HB_FEATURE_GLOBAL_START and end to @HB_FEATURE_GLOBAL_END + * specifies that the feature always applies to the entire buffer. + */ typedef struct hb_feature_t { hb_tag_t tag; uint32_t value; @@ -459,39 +492,21 @@ typedef uint32_t hb_color_t; #define HB_COLOR(b,g,r,a) ((hb_color_t) HB_TAG ((b),(g),(r),(a))) -/** - * hb_color_get_alpha: - * - * - * - * Since: 2.1.0 - */ +HB_EXTERN uint8_t +hb_color_get_alpha (hb_color_t color); #define hb_color_get_alpha(color) ((color) & 0xFF) -/** - * hb_color_get_red: - * - * - * - * Since: 2.1.0 - */ + +HB_EXTERN uint8_t +hb_color_get_red (hb_color_t color); #define hb_color_get_red(color) (((color) >> 8) & 0xFF) -/** - * hb_color_get_green: - * - * - * - * Since: 2.1.0 - */ + +HB_EXTERN uint8_t +hb_color_get_green (hb_color_t color); #define hb_color_get_green(color) (((color) >> 16) & 0xFF) -/** - * hb_color_get_blue: - * - * - * - * Since: 2.1.0 - */ -#define hb_color_get_blue(color) (((color) >> 24) & 0xFF) +HB_EXTERN uint8_t +hb_color_get_blue (hb_color_t color); +#define hb_color_get_blue(color) (((color) >> 24) & 0xFF) HB_END_DECLS diff --git a/src/java.desktop/share/native/libharfbuzz/hb-config.hh b/src/java.desktop/share/native/libharfbuzz/hb-config.hh new file mode 100644 index 00000000000..fc8d424bfb0 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-config.hh @@ -0,0 +1,163 @@ +/* + * Copyright © 2019 Facebook, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_CONFIG_HH +#define HB_CONFIG_HH + +#if 0 /* Make test happy. */ +#include "hb.hh" +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#ifdef HB_TINY +#define HB_LEAN +#define HB_MINI +#define HB_NO_MT +#define HB_NO_UCD_UNASSIGNED +#ifndef NDEBUG +#define NDEBUG +#endif +#ifndef __OPTIMIZE_SIZE__ +#define __OPTIMIZE_SIZE__ +#endif +#endif + +#ifdef HB_LEAN +#define HB_DISABLE_DEPRECATED +#define HB_NDEBUG +#define HB_NO_ATEXIT +#define HB_NO_BUFFER_MESSAGE +#define HB_NO_BUFFER_SERIALIZE +#define HB_NO_BITMAP +#define HB_NO_CFF +#define HB_NO_COLOR +#define HB_NO_DRAW +#define HB_NO_ERRNO +#define HB_NO_FACE_COLLECT_UNICODES +#define HB_NO_GETENV +#define HB_NO_HINTING +#define HB_NO_LANGUAGE_PRIVATE_SUBTAG +#define HB_NO_LAYOUT_FEATURE_PARAMS +#define HB_NO_LAYOUT_COLLECT_GLYPHS +#define HB_NO_LAYOUT_UNUSED +#define HB_NO_MATH +#define HB_NO_META +#define HB_NO_METRICS +#define HB_NO_MMAP +#define HB_NO_NAME +#define HB_NO_OPEN +#define HB_NO_SETLOCALE +#define HB_NO_OT_FONT_GLYPH_NAMES +#define HB_NO_OT_SHAPE_FRACTIONS +#define HB_NO_STYLE +#define HB_NO_SUBSET_LAYOUT +#define HB_NO_VAR +#endif + +#ifdef HB_MINI +#define HB_NO_AAT +#define HB_NO_LEGACY +#endif + + +/* Closure of options. */ + +#ifdef HB_DISABLE_DEPRECATED +#define HB_IF_NOT_DEPRECATED(x) +#else +#define HB_IF_NOT_DEPRECATED(x) x +#endif + +#ifdef HB_NO_AAT +#define HB_NO_OT_NAME_LANGUAGE_AAT +#define HB_NO_AAT_SHAPE +#endif + +#ifdef HB_NO_BITMAP +#define HB_NO_OT_FONT_BITMAP +#endif + +#ifdef HB_NO_CFF +#define HB_NO_OT_FONT_CFF +#define HB_NO_SUBSET_CFF +#endif + +#ifdef HB_NO_GETENV +#define HB_NO_UNISCRIBE_BUG_COMPATIBLE +#endif + +#ifdef HB_NO_LEGACY +#define HB_NO_CMAP_LEGACY_SUBTABLES +#define HB_NO_FALLBACK_SHAPE +#define HB_NO_OT_KERN +#define HB_NO_OT_LAYOUT_BLACKLIST +#define HB_NO_OT_SHAPE_FALLBACK +#endif + +#ifdef HB_NO_NAME +#define HB_NO_OT_NAME_LANGUAGE +#endif + +#ifdef HB_NO_OT +#define HB_NO_OT_FONT +#define HB_NO_OT_LAYOUT +#define HB_NO_OT_TAG +#define HB_NO_OT_SHAPE +#endif + +#ifdef HB_NO_OT_SHAPE +#define HB_NO_AAT_SHAPE +#endif + +#ifdef HB_NO_OT_SHAPE_FALLBACK +#define HB_NO_OT_SHAPE_COMPLEX_ARABIC_FALLBACK +#define HB_NO_OT_SHAPE_COMPLEX_HEBREW_FALLBACK +#define HB_NO_OT_SHAPE_COMPLEX_THAI_FALLBACK +#define HB_NO_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS +#endif + +#ifdef NDEBUG +#ifndef HB_NDEBUG +#define HB_NDEBUG +#endif +#endif + +#ifdef __OPTIMIZE_SIZE__ +#ifndef HB_OPTIMIZE_SIZE +#define HB_OPTIMIZE_SIZE +#endif +#endif + +#ifdef HAVE_CONFIG_OVERRIDE_H +#include "config-override.h" +#endif + + +#endif /* HB_CONFIG_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-coretext.cc b/src/java.desktop/share/native/libharfbuzz/hb-coretext.cc index f8d03085bd9..a382228f20d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-coretext.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-coretext.cc @@ -27,6 +27,9 @@ */ #include "hb.hh" + +#ifdef HAVE_CORETEXT + #include "hb-shaper-impl.hh" #include "hb-coretext.h" @@ -46,24 +49,6 @@ /* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */ #define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f -static CGFloat -coretext_font_size_from_ptem (float ptem) -{ - /* CoreText points are CSS pixels (96 per inch), - * NOT typographic points (72 per inch). - * - * https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html - */ - ptem *= 96.f / 72.f; - return ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : ptem; -} -static float -coretext_font_size_to_ptem (CGFloat size) -{ - size *= 72.f / 96.f; - return size <= 0.f ? 0 : size; -} - static void release_table_data (void *user_data) { @@ -72,7 +57,7 @@ release_table_data (void *user_data) } static hb_blob_t * -reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +_hb_cg_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) { CGFontRef cg_font = reinterpret_cast (user_data); CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag); @@ -171,7 +156,7 @@ create_ct_font (CGFontRef cg_font, CGFloat font_size) if (CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSText")) || CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSDisplay"))) { -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080 +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1080 # define kCTFontUIFontSystem kCTFontSystemFontType # define kCTFontUIFontEmphasizedSystem kCTFontEmphasizedSystemFontType #endif @@ -214,7 +199,7 @@ create_ct_font (CGFontRef cg_font, CGFloat font_size) } CFURLRef original_url = nullptr; -#if TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 ATSFontRef atsFont; FSRef fsref; OSStatus status; @@ -244,7 +229,7 @@ create_ct_font (CGFontRef cg_font, CGFloat font_size) * process in Blink. This can be detected by the new file URL location * that the newly found font points to. */ CFURLRef new_url = nullptr; -#if TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 atsFont = CTFontGetPlatformFont (new_ct_font, NULL); status = ATSFontGetFileReference (atsFont, &fsref); if (status == noErr) @@ -293,13 +278,32 @@ _hb_coretext_shaper_face_data_destroy (hb_coretext_face_data_t *data) CFRelease ((CGFontRef) data); } +/** + * hb_coretext_face_create: + * @cg_font: The CGFontRef to work upon + * + * Creates an #hb_face_t face object from the specified + * CGFontRef. + * + * Return value: the new #hb_face_t face object + * + * Since: 0.9.10 + */ hb_face_t * hb_coretext_face_create (CGFontRef cg_font) { - return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), _hb_cg_font_release); + return hb_face_create_for_tables (_hb_cg_reference_table, CGFontRetain (cg_font), _hb_cg_font_release); } -/* +/** + * hb_coretext_face_get_cg_font: + * @face: The #hb_face_t to work upon + * + * Fetches the CGFontRef associated with an #hb_face_t + * face object + * + * Return value: the CGFontRef found + * * Since: 0.9.10 */ CGFontRef @@ -317,7 +321,8 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font) if (unlikely (!face_data)) return nullptr; CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext; - CTFontRef ct_font = create_ct_font (cg_font, coretext_font_size_from_ptem (font->ptem)); + CGFloat font_size = (CGFloat) (font->ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : font->ptem); + CTFontRef ct_font = create_ct_font (cg_font, font_size); if (unlikely (!ct_font)) { @@ -341,7 +346,7 @@ hb_coretext_font_data_sync (hb_font_t *font) const hb_coretext_font_data_t *data = font->data.coretext; if (unlikely (!data)) return nullptr; - if (fabs (CTFontGetSize((CTFontRef) data) - coretext_font_size_from_ptem (font->ptem)) > .5) + if (fabs (CTFontGetSize ((CTFontRef) data) - (CGFloat) font->ptem) > .5) { /* XXX-MT-bug * Note that evaluating condition above can be dangerous if another thread @@ -365,10 +370,17 @@ hb_coretext_font_data_sync (hb_font_t *font) return font->data.coretext; } - -/* +/** + * hb_coretext_font_create: + * @ct_font: The CTFontRef to work upon + * + * Creates an #hb_font_t font object from the specified + * CTFontRef. + * + * Return value: the new #hb_font_t font object + * * Since: 1.7.2 - */ + **/ hb_font_t * hb_coretext_font_create (CTFontRef ct_font) { @@ -381,7 +393,7 @@ hb_coretext_font_create (CTFontRef ct_font) if (unlikely (hb_object_is_immutable (font))) return font; - hb_font_set_ptem (font, coretext_font_size_to_ptem (CTFontGetSize(ct_font))); + hb_font_set_ptem (font, CTFontGetSize (ct_font)); /* Let there be dragons here... */ font->data.coretext.cmpexch (nullptr, (hb_coretext_font_data_t *) CFRetain (ct_font)); @@ -389,6 +401,17 @@ hb_coretext_font_create (CTFontRef ct_font) return font; } +/** + * hb_coretext_face_get_ct_font: + * @font: #hb_font_t to work upon + * + * Fetches the CTFontRef associated with the specified + * #hb_font_t font object. + * + * Return value: the CTFontRef found + * + * Since: 0.9.10 + */ CTFontRef hb_coretext_font_get_ct_font (hb_font_t *font) { @@ -410,7 +433,7 @@ struct active_feature_t { feature_record_t rec; unsigned int order; - static int cmp (const void *pa, const void *pb) { + HB_INTERNAL static int cmp (const void *pa, const void *pb) { const active_feature_t *a = (const active_feature_t *) pa; const active_feature_t *b = (const active_feature_t *) pb; return a->rec.feature < b->rec.feature ? -1 : a->rec.feature > b->rec.feature ? 1 : @@ -428,7 +451,7 @@ struct feature_event_t { bool start; active_feature_t feature; - static int cmp (const void *pa, const void *pb) { + HB_INTERNAL static int cmp (const void *pa, const void *pb) { const feature_event_t *a = (const feature_event_t *) pa; const feature_event_t *b = (const feature_event_t *) pb; return a->index < b->index ? -1 : a->index > b->index ? 1 : @@ -489,13 +512,19 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, hb_vector_t feature_events; for (unsigned int i = 0; i < num_features; i++) { + active_feature_t feature; + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 const hb_aat_feature_mapping_t * mapping = hb_aat_layout_find_feature_mapping (features[i].tag); if (!mapping) continue; - active_feature_t feature; feature.rec.feature = mapping->aatFeatureType; feature.rec.setting = features[i].value ? mapping->selectorToEnable : mapping->selectorToDisable; +#else + feature.rec.feature = features[i].tag; + feature.rec.setting = features[i].value; +#endif feature.order = i; feature_event_t *event; @@ -544,6 +573,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, /* active_features.qsort (); */ for (unsigned int j = 0; j < active_features.length; j++) { +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 CFStringRef keys[] = { kCTFontFeatureTypeIdentifierKey, kCTFontFeatureSelectorIdentifierKey @@ -552,6 +582,17 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature), CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) }; +#else + char tag[5] = {HB_UNTAG (active_features[j].rec.feature)}; + CFTypeRef keys[] = { + kCTFontOpenTypeFeatureTag, + kCTFontOpenTypeFeatureValue + }; + CFTypeRef values[] = { + CFStringCreateWithCString (kCFAllocatorDefault, tag, kCFStringEncodingASCII), + CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) + }; +#endif static_assert ((ARRAY_LENGTH_CONST (keys) == ARRAY_LENGTH_CONST (values)), ""); CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault, (const void **) keys, @@ -598,7 +639,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, } else { active_feature_t *feature = active_features.find (&event->feature); if (feature) - active_features.remove (feature - active_features.arrayZ ()); + active_features.remove (feature - active_features.arrayZ); } } } @@ -608,7 +649,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, #define ALLOCATE_ARRAY(Type, name, len, on_no_room) \ Type *name = (Type *) scratch; \ - { \ + do { \ unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ if (unlikely (_consumed > scratch_size)) \ { \ @@ -617,9 +658,9 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, } \ scratch += _consumed; \ scratch_size -= _consumed; \ - } + } while (0) - ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, /*nothing*/); + ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, ((void)nullptr) /*nothing*/); unsigned int chars_len = 0; for (unsigned int i = 0; i < buffer->len; i++) { hb_codepoint_t c = buffer->info[i].codepoint; @@ -633,7 +674,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, } } - ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, /*nothing*/); + ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, ((void)nullptr) /*nothing*/); chars_len = 0; for (unsigned int i = 0; i < buffer->len; i++) { @@ -649,7 +690,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, DEBUG_MSG (CORETEXT, nullptr, __VA_ARGS__); \ ret = false; \ goto fail; \ - } HB_STMT_END; + } HB_STMT_END bool ret = true; CFStringRef string_ref = nullptr; @@ -711,7 +752,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, /* What's the iOS equivalent of this check? * The symbols was introduced in iOS 7.0. * At any rate, our fallback is safe and works fine. */ -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1090 +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1090 # define kCTLanguageAttributeName CFSTR ("NSLanguage") #endif CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault, @@ -771,7 +812,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, feature.start < chars_len && feature.start < feature.end) { CFRange feature_range = CFRangeMake (feature.start, - MIN (feature.end, chars_len) - feature.start); + hb_min (feature.end, chars_len) - feature.start); if (feature.value) CFAttributedStringRemoveAttribute (attr_string, feature_range, kCTKernAttributeName); else @@ -783,7 +824,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level); -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 extern const CFStringRef kCTTypesetterOptionForcedEmbeddingLevel; #endif CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault, @@ -977,7 +1018,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, #define SCRATCH_RESTORE() \ scratch_size = scratch_size_saved; \ - scratch = scratch_saved; + scratch = scratch_saved { /* Setup glyphs */ SCRATCH_SAVE(); @@ -1069,7 +1110,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, if (false) { /* Make sure all runs had the expected direction. */ - bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + HB_UNUSED bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); assert (bool (status_and & kCTRunStatusRightToLeft) == backward); assert (bool (status_or & kCTRunStatusRightToLeft) == backward); } @@ -1116,7 +1157,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, unsigned int cluster = info[count - 1].cluster; for (unsigned int i = count - 1; i > 0; i--) { - cluster = MIN (cluster, info[i - 1].cluster); + cluster = hb_min (cluster, info[i - 1].cluster); info[i - 1].cluster = cluster; } } @@ -1125,7 +1166,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, unsigned int cluster = info[0].cluster; for (unsigned int i = 1; i < count; i++) { - cluster = MIN (cluster, info[i].cluster); + cluster = hb_min (cluster, info[i].cluster); info[i].cluster = cluster; } } @@ -1150,57 +1191,4 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, } -/* - * AAT shaper - */ - -/* - * shaper face data - */ - -struct hb_coretext_aat_face_data_t {}; - -hb_coretext_aat_face_data_t * -_hb_coretext_aat_shaper_face_data_create (hb_face_t *face) -{ - return hb_aat_layout_has_substitution (face) || hb_aat_layout_has_positioning (face) ? - (hb_coretext_aat_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr; -} - -void -_hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_face_data_t *data HB_UNUSED) -{ -} - - -/* - * shaper font data - */ - -struct hb_coretext_aat_font_data_t {}; - -hb_coretext_aat_font_data_t * -_hb_coretext_aat_shaper_font_data_create (hb_font_t *font) -{ - return font->data.coretext ? (hb_coretext_aat_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr; -} - -void -_hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_font_data_t *data HB_UNUSED) -{ -} - - -/* - * shaper - */ - -hb_bool_t -_hb_coretext_aat_shape (hb_shape_plan_t *shape_plan, - hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features) -{ - return _hb_coretext_shape (shape_plan, font, buffer, features, num_features); -} +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-coretext.h b/src/java.desktop/share/native/libharfbuzz/hb-coretext.h index 4b0a6f01b6f..55cac7e294a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-coretext.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-coretext.h @@ -40,8 +40,40 @@ HB_BEGIN_DECLS +/** + * HB_CORETEXT_TAG_MORT: + * + * The #hb_tag_t tag for the `mort` (glyph metamorphosis) table, + * which holds AAT features. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html + * + **/ #define HB_CORETEXT_TAG_MORT HB_TAG('m','o','r','t') + +/** + * HB_CORETEXT_TAG_MORX: + * + * The #hb_tag_t tag for the `morx` (extended glyph metamorphosis) + * table, which holds AAT features. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html + * + **/ #define HB_CORETEXT_TAG_MORX HB_TAG('m','o','r','x') + +/** + * HB_CORETEXT_TAG_KERX: + * + * The #hb_tag_t tag for the `kerx` (extended kerning) table, which + * holds AAT kerning information. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html + * + **/ #define HB_CORETEXT_TAG_KERX HB_TAG('k','e','r','x') diff --git a/src/java.desktop/share/native/libharfbuzz/hb-debug.hh b/src/java.desktop/share/native/libharfbuzz/hb-debug.hh index d5ec94f4e47..db60837748f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-debug.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-debug.hh @@ -29,7 +29,7 @@ #include "hb.hh" #include "hb-atomic.hh" -#include "hb-dsalgs.hh" +#include "hb-algs.hh" #ifndef HB_DEBUG @@ -46,7 +46,6 @@ struct hb_options_t bool unused : 1; /* In-case sign bit is here. */ bool initialized : 1; bool uniscribe_bug_compatible : 1; - bool aat : 1; }; union hb_options_union_t { @@ -63,6 +62,9 @@ extern HB_INTERNAL hb_atomic_int_t _hb_options; static inline hb_options_t hb_options () { +#ifdef HB_NO_GETENV + return hb_options_t (); +#endif /* Make a local copy, so we can access bitfield threadsafely. */ hb_options_union_t u; u.i = _hb_options.get_relaxed (); @@ -158,7 +160,7 @@ _hb_debug_msg_va (const char *what, VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR; fprintf (stderr, "%2u %s" VRBAR "%s", level, - bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars) - 1, (unsigned int) (sizeof (VBAR) - 1) * level), + bars + sizeof (bars) - 1 - hb_min ((unsigned int) sizeof (bars) - 1, (unsigned int) (sizeof (VBAR) - 1) * level), level_dir ? (level_dir > 0 ? DLBAR : ULBAR) : LBAR); } else fprintf (stderr, " " VRBAR LBAR); @@ -246,8 +248,8 @@ struct hb_printer_t { }; template <> -struct hb_printer_t { - const char *print (hb_void_t) { return ""; } +struct hb_printer_t { + const char *print (hb_empty_t) { return ""; } }; @@ -263,7 +265,7 @@ static inline void _hb_warn_no_return (bool returned) } } template <> -/*static*/ inline void _hb_warn_no_return (bool returned HB_UNUSED) +/*static*/ inline void _hb_warn_no_return (bool returned HB_UNUSED) {} template @@ -293,22 +295,23 @@ struct hb_auto_trace_t if (plevel) --*plevel; } - ret_t ret (ret_t v, - const char *func = "", - unsigned int line = 0) + template + T ret (T&& v, + const char *func = "", + unsigned int line = 0) { if (unlikely (returned)) { fprintf (stderr, "OUCH, double calls to return_trace(). This is a bug, please report.\n"); - return v; + return hb_forward (v); } _hb_debug_msg (what, obj, func, true, plevel ? *plevel : 1, -1, "return %s (line %d)", - hb_printer_t().print (v), line); + hb_printer_t().print (v), line); if (plevel) --*plevel; plevel = nullptr; returned = true; - return v; + return hb_forward (v); } private: @@ -327,18 +330,20 @@ struct hb_auto_trace_t<0, ret_t> const char *message, ...) HB_PRINTF_FUNC(6, 7) {} - ret_t ret (ret_t v, - const char *func HB_UNUSED = nullptr, - unsigned int line HB_UNUSED = 0) { return v; } + template + T ret (T&& v, + const char *func HB_UNUSED = nullptr, + unsigned int line HB_UNUSED = 0) { return hb_forward (v); } }; /* For disabled tracing; optimize out everything. * https://github.com/harfbuzz/harfbuzz/pull/605 */ template struct hb_no_trace_t { - ret_t ret (ret_t v, - const char *func HB_UNUSED = "", - unsigned int line HB_UNUSED = 0) { return v; } + template + T ret (T&& v, + const char *func HB_UNUSED = nullptr, + unsigned int line HB_UNUSED = 0) { return hb_forward (v); } }; #define return_trace(RET) return trace.ret (RET, HB_FUNC, __LINE__) @@ -368,10 +373,6 @@ struct hb_no_trace_t { #define HB_DEBUG_FT (HB_DEBUG+0) #endif -#ifndef HB_DEBUG_GET_COVERAGE -#define HB_DEBUG_GET_COVERAGE (HB_DEBUG+0) -#endif - #ifndef HB_DEBUG_OBJECT #define HB_DEBUG_OBJECT (HB_DEBUG+0) #endif @@ -408,7 +409,7 @@ struct hb_no_trace_t { #define TRACE_SANITIZE(this) \ hb_auto_trace_t trace \ (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - " "); + " ") #else #define TRACE_SANITIZE(this) hb_no_trace_t trace #endif @@ -420,7 +421,7 @@ struct hb_no_trace_t { #define TRACE_SERIALIZE(this) \ hb_auto_trace_t trace \ (&c->debug_depth, "SERIALIZE", c, HB_FUNC, \ - " "); + " ") #else #define TRACE_SERIALIZE(this) hb_no_trace_t trace #endif @@ -432,37 +433,24 @@ struct hb_no_trace_t { #define TRACE_SUBSET(this) \ hb_auto_trace_t trace \ (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - " "); + " ") #else #define TRACE_SUBSET(this) hb_no_trace_t trace #endif -#ifndef HB_DEBUG_WOULD_APPLY -#define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0) -#endif -#if HB_DEBUG_WOULD_APPLY -#define TRACE_WOULD_APPLY(this) \ - hb_auto_trace_t trace \ - (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - "%d glyphs", c->len); -#else -#define TRACE_WOULD_APPLY(this) hb_no_trace_t trace -#endif - #ifndef HB_DEBUG_DISPATCH #define HB_DEBUG_DISPATCH ( \ HB_DEBUG_APPLY + \ HB_DEBUG_SANITIZE + \ HB_DEBUG_SERIALIZE + \ - HB_DEBUG_SUBSET + \ - HB_DEBUG_WOULD_APPLY + \ + HB_DEBUG_SUBSET + \ 0) #endif #if HB_DEBUG_DISPATCH #define TRACE_DISPATCH(this, format) \ hb_auto_trace_t trace \ (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - "format %d", (int) format); + "format %d", (int) format) #else #define TRACE_DISPATCH(this, format) hb_no_trace_t trace #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h b/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h index 9409f320781..5dd04050b8e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h @@ -63,7 +63,7 @@ typedef hb_bool_t (*hb_font_get_glyph_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t *glyph, void *user_data); -HB_EXTERN HB_DEPRECATED_FOR(hb_font_funcs_set_nominal_glyph_func or hb_font_funcs_set_variation_glyph_func) void +HB_EXTERN HB_DEPRECATED_FOR(hb_font_funcs_set_nominal_glyph_func and hb_font_funcs_set_variation_glyph_func) void hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_func_t func, void *user_data, hb_destroy_func_t destroy); @@ -165,29 +165,8 @@ hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs, hb_codepoint_t *decomposed); -typedef hb_position_t (*hb_font_get_glyph_kerning_func_t) (hb_font_t *font, void *font_data, - hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, - void *user_data); -typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t; typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_v_kerning_func_t; -/** - * hb_font_funcs_set_glyph_h_kerning_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: - * - * - * - * Since: 0.9.2 - * Deprecated: 2.0.0 - **/ -HB_EXTERN void -hb_font_funcs_set_glyph_h_kerning_func (hb_font_funcs_t *ffuncs, - hb_font_get_glyph_h_kerning_func_t func, - void *user_data, hb_destroy_func_t destroy); - /** * hb_font_funcs_set_glyph_v_kerning_func: * @ffuncs: font functions. @@ -206,19 +185,9 @@ hb_font_funcs_set_glyph_v_kerning_func (hb_font_funcs_t *ffuncs, void *user_data, hb_destroy_func_t destroy); HB_EXTERN hb_position_t -hb_font_get_glyph_h_kerning (hb_font_t *font, - hb_codepoint_t left_glyph, hb_codepoint_t right_glyph); -HB_EXTERN hb_position_t hb_font_get_glyph_v_kerning (hb_font_t *font, hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph); -HB_EXTERN void -hb_font_get_glyph_kerning_for_direction (hb_font_t *font, - hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y); - - #endif HB_END_DECLS diff --git a/src/java.desktop/share/native/libharfbuzz/hb-dispatch.hh b/src/java.desktop/share/native/libharfbuzz/hb-dispatch.hh new file mode 100644 index 00000000000..e946f21c114 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-dispatch.hh @@ -0,0 +1,61 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012,2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_DISPATCH_HH +#define HB_DISPATCH_HH + +#include "hb.hh" + +/* + * Dispatch + */ + +template +struct hb_dispatch_context_t +{ + hb_dispatch_context_t () : debug_depth (0) {} + private: + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const Context* thiz () const { return static_cast (this); } + Context* thiz () { return static_cast< Context *> (this); } + public: + const char *get_name () { return "UNKNOWN"; } + static constexpr unsigned max_debug_depth = MaxDebugDepth; + typedef Return return_t; + template + bool may_dispatch (const T *obj HB_UNUSED, const F *format HB_UNUSED) { return true; } + template + return_t dispatch (const T &obj, Ts&&... ds) + { return obj.dispatch (thiz (), hb_forward (ds)...); } + static return_t no_dispatch_return_value () { return Context::default_return_value (); } + static bool stop_sublookup_iteration (const return_t r HB_UNUSED) { return false; } + unsigned debug_depth; +}; + + +#endif /* HB_DISPATCH_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.cc b/src/java.desktop/share/native/libharfbuzz/hb-draw.cc new file mode 100644 index 00000000000..72444a94c8a --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.cc @@ -0,0 +1,261 @@ +/* + * Copyright © 2019-2020 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.hh" + +#ifndef HB_NO_DRAW +#ifdef HB_EXPERIMENTAL_API + +#include "hb-draw.hh" +#include "hb-ot.h" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-ot-cff2-table.hh" + +/** + * hb_draw_funcs_set_move_to_func: + * @funcs: draw functions object + * @move_to: move-to callback + * + * Sets move-to callback to the draw functions object. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_set_move_to_func (hb_draw_funcs_t *funcs, + hb_draw_move_to_func_t move_to) +{ + if (unlikely (hb_object_is_immutable (funcs))) return; + funcs->move_to = move_to; +} + +/** + * hb_draw_funcs_set_line_to_func: + * @funcs: draw functions object + * @line_to: line-to callback + * + * Sets line-to callback to the draw functions object. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_set_line_to_func (hb_draw_funcs_t *funcs, + hb_draw_line_to_func_t line_to) +{ + if (unlikely (hb_object_is_immutable (funcs))) return; + funcs->line_to = line_to; +} + +/** + * hb_draw_funcs_set_quadratic_to_func: + * @funcs: draw functions object + * @move_to: quadratic-to callback + * + * Sets quadratic-to callback to the draw functions object. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_set_quadratic_to_func (hb_draw_funcs_t *funcs, + hb_draw_quadratic_to_func_t quadratic_to) +{ + if (unlikely (hb_object_is_immutable (funcs))) return; + funcs->quadratic_to = quadratic_to; + funcs->is_quadratic_to_set = true; +} + +/** + * hb_draw_funcs_set_cubic_to_func: + * @funcs: draw functions + * @cubic_to: cubic-to callback + * + * Sets cubic-to callback to the draw functions object. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_set_cubic_to_func (hb_draw_funcs_t *funcs, + hb_draw_cubic_to_func_t cubic_to) +{ + if (unlikely (hb_object_is_immutable (funcs))) return; + funcs->cubic_to = cubic_to; +} + +/** + * hb_draw_funcs_set_close_path_func: + * @funcs: draw functions object + * @close_path: close-path callback + * + * Sets close-path callback to the draw functions object. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_set_close_path_func (hb_draw_funcs_t *funcs, + hb_draw_close_path_func_t close_path) +{ + if (unlikely (hb_object_is_immutable (funcs))) return; + funcs->close_path = close_path; +} + +static void +_move_to_nil (hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, void *user_data HB_UNUSED) {} + +static void +_line_to_nil (hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, void *user_data HB_UNUSED) {} + +static void +_quadratic_to_nil (hb_position_t control_x HB_UNUSED, hb_position_t control_y HB_UNUSED, + hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, + void *user_data HB_UNUSED) {} + +static void +_cubic_to_nil (hb_position_t control1_x HB_UNUSED, hb_position_t control1_y HB_UNUSED, + hb_position_t control2_x HB_UNUSED, hb_position_t control2_y HB_UNUSED, + hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, + void *user_data HB_UNUSED) {} + +static void +_close_path_nil (void *user_data HB_UNUSED) {} + +/** + * hb_draw_funcs_create: + * + * Creates a new draw callbacks object. + * + * Since: EXPERIMENTAL + **/ +hb_draw_funcs_t * +hb_draw_funcs_create () +{ + hb_draw_funcs_t *funcs; + if (unlikely (!(funcs = hb_object_create ()))) + return const_cast (&Null (hb_draw_funcs_t)); + + funcs->move_to = (hb_draw_move_to_func_t) _move_to_nil; + funcs->line_to = (hb_draw_line_to_func_t) _line_to_nil; + funcs->quadratic_to = (hb_draw_quadratic_to_func_t) _quadratic_to_nil; + funcs->is_quadratic_to_set = false; + funcs->cubic_to = (hb_draw_cubic_to_func_t) _cubic_to_nil; + funcs->close_path = (hb_draw_close_path_func_t) _close_path_nil; + return funcs; +} + +/** + * hb_draw_funcs_reference: + * @funcs: draw functions + * + * Add to callbacks object refcount. + * + * Returns: The same object. + * Since: EXPERIMENTAL + **/ +hb_draw_funcs_t * +hb_draw_funcs_reference (hb_draw_funcs_t *funcs) +{ + return hb_object_reference (funcs); +} + +/** + * hb_draw_funcs_destroy: + * @funcs: draw functions + * + * Decreases refcount of callbacks object and deletes the object if it reaches + * to zero. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_destroy (hb_draw_funcs_t *funcs) +{ + if (!hb_object_destroy (funcs)) return; + + free (funcs); +} + +/** + * hb_draw_funcs_make_immutable: + * @funcs: draw functions + * + * Makes funcs object immutable. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_make_immutable (hb_draw_funcs_t *funcs) +{ + if (hb_object_is_immutable (funcs)) + return; + + hb_object_make_immutable (funcs); +} + +/** + * hb_draw_funcs_is_immutable: + * @funcs: draw functions + * + * Checks whether funcs is immutable. + * + * Returns: If is immutable. + * Since: EXPERIMENTAL + **/ +hb_bool_t +hb_draw_funcs_is_immutable (hb_draw_funcs_t *funcs) +{ + return hb_object_is_immutable (funcs); +} + +/** + * hb_font_draw_glyph: + * @font: a font object + * @glyph: a glyph id + * @funcs: draw callbacks object + * @user_data: parameter you like be passed to the callbacks when are called + * + * Draw a glyph. + * + * Returns: Whether the font had the glyph and the operation completed successfully. + * Since: EXPERIMENTAL + **/ +hb_bool_t +hb_font_draw_glyph (hb_font_t *font, hb_codepoint_t glyph, + const hb_draw_funcs_t *funcs, + void *user_data) +{ + if (unlikely (funcs == &Null (hb_draw_funcs_t) || + glyph >= font->face->get_num_glyphs ())) + return false; + + draw_helper_t draw_helper (funcs, user_data); + if (font->face->table.glyf->get_path (font, glyph, draw_helper)) return true; +#ifndef HB_NO_CFF + if (font->face->table.cff1->get_path (font, glyph, draw_helper)) return true; + if (font->face->table.cff2->get_path (font, glyph, draw_helper)) return true; +#endif + + return false; +} + +#endif +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.h b/src/java.desktop/share/native/libharfbuzz/hb-draw.h new file mode 100644 index 00000000000..d5eb6ecc65b --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.h @@ -0,0 +1,98 @@ +/* + * Copyright © 2019-2020 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_DRAW_H +#define HB_DRAW_H + +#include "hb.h" + +HB_BEGIN_DECLS + +#ifdef HB_EXPERIMENTAL_API +typedef void (*hb_draw_move_to_func_t) (hb_position_t to_x, hb_position_t to_y, void *user_data); +typedef void (*hb_draw_line_to_func_t) (hb_position_t to_x, hb_position_t to_y, void *user_data); +typedef void (*hb_draw_quadratic_to_func_t) (hb_position_t control_x, hb_position_t control_y, + hb_position_t to_x, hb_position_t to_y, + void *user_data); +typedef void (*hb_draw_cubic_to_func_t) (hb_position_t control1_x, hb_position_t control1_y, + hb_position_t control2_x, hb_position_t control2_y, + hb_position_t to_x, hb_position_t to_y, + void *user_data); +typedef void (*hb_draw_close_path_func_t) (void *user_data); + +/** + * hb_draw_funcs_t: + * + * Glyph draw callbacks. + * + * _move_to, _line_to and _cubic_to calls are nessecary to be defined but we + * translate _quadratic_to calls to _cubic_to if the callback isn't defined. + * + * Since: EXPERIMENTAL + **/ +typedef struct hb_draw_funcs_t hb_draw_funcs_t; + +HB_EXTERN void +hb_draw_funcs_set_move_to_func (hb_draw_funcs_t *funcs, + hb_draw_move_to_func_t move_to); + +HB_EXTERN void +hb_draw_funcs_set_line_to_func (hb_draw_funcs_t *funcs, + hb_draw_line_to_func_t line_to); + +HB_EXTERN void +hb_draw_funcs_set_quadratic_to_func (hb_draw_funcs_t *funcs, + hb_draw_quadratic_to_func_t quadratic_to); + +HB_EXTERN void +hb_draw_funcs_set_cubic_to_func (hb_draw_funcs_t *funcs, + hb_draw_cubic_to_func_t cubic_to); + +HB_EXTERN void +hb_draw_funcs_set_close_path_func (hb_draw_funcs_t *funcs, + hb_draw_close_path_func_t close_path); + +HB_EXTERN hb_draw_funcs_t * +hb_draw_funcs_create (void); + +HB_EXTERN hb_draw_funcs_t * +hb_draw_funcs_reference (hb_draw_funcs_t *funcs); + +HB_EXTERN void +hb_draw_funcs_destroy (hb_draw_funcs_t *funcs); + +HB_EXTERN void +hb_draw_funcs_make_immutable (hb_draw_funcs_t *funcs); + +HB_EXTERN hb_bool_t +hb_draw_funcs_is_immutable (hb_draw_funcs_t *funcs); +#endif + +HB_END_DECLS + +#endif /* HB_DRAW_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.hh b/src/java.desktop/share/native/libharfbuzz/hb-draw.hh new file mode 100644 index 00000000000..0e5101f9cd2 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.hh @@ -0,0 +1,139 @@ +/* + * Copyright © 2020 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_DRAW_HH +#define HB_DRAW_HH + +#include "hb.hh" + +#ifdef HB_EXPERIMENTAL_API +struct hb_draw_funcs_t +{ + hb_object_header_t header; + + hb_draw_move_to_func_t move_to; + hb_draw_line_to_func_t line_to; + hb_draw_quadratic_to_func_t quadratic_to; + bool is_quadratic_to_set; + hb_draw_cubic_to_func_t cubic_to; + hb_draw_close_path_func_t close_path; +}; + +struct draw_helper_t +{ + draw_helper_t (const hb_draw_funcs_t *funcs_, void *user_data_) + { + funcs = funcs_; + user_data = user_data_; + path_open = false; + path_start_x = current_x = path_start_y = current_y = 0; + } + ~draw_helper_t () { end_path (); } + + void move_to (hb_position_t x, hb_position_t y) + { + if (path_open) end_path (); + current_x = path_start_x = x; + current_y = path_start_y = y; + } + + void line_to (hb_position_t x, hb_position_t y) + { + if (equal_to_current (x, y)) return; + if (!path_open) start_path (); + funcs->line_to (x, y, user_data); + current_x = x; + current_y = y; + } + + void + quadratic_to (hb_position_t control_x, hb_position_t control_y, + hb_position_t to_x, hb_position_t to_y) + { + if (equal_to_current (control_x, control_y) && equal_to_current (to_x, to_y)) + return; + if (!path_open) start_path (); + if (funcs->is_quadratic_to_set) + funcs->quadratic_to (control_x, control_y, to_x, to_y, user_data); + else + funcs->cubic_to (roundf ((current_x + 2.f * control_x) / 3.f), + roundf ((current_y + 2.f * control_y) / 3.f), + roundf ((to_x + 2.f * control_x) / 3.f), + roundf ((to_y + 2.f * control_y) / 3.f), + to_x, to_y, user_data); + current_x = to_x; + current_y = to_y; + } + + void + cubic_to (hb_position_t control1_x, hb_position_t control1_y, + hb_position_t control2_x, hb_position_t control2_y, + hb_position_t to_x, hb_position_t to_y) + { + if (equal_to_current (control1_x, control1_y) && + equal_to_current (control2_x, control2_y) && + equal_to_current (to_x, to_y)) + return; + if (!path_open) start_path (); + funcs->cubic_to (control1_x, control1_y, control2_x, control2_y, to_x, to_y, user_data); + current_x = to_x; + current_y = to_y; + } + + void end_path () + { + if (path_open) + { + if ((path_start_x != current_x) || (path_start_y != current_y)) + funcs->line_to (path_start_x, path_start_y, user_data); + funcs->close_path (user_data); + } + path_open = false; + path_start_x = current_x = path_start_y = current_y = 0; + } + + protected: + bool equal_to_current (hb_position_t x, hb_position_t y) + { return current_x == x && current_y == y; } + + void start_path () + { + if (path_open) end_path (); + path_open = true; + funcs->move_to (path_start_x, path_start_y, user_data); + } + + hb_position_t path_start_x; + hb_position_t path_start_y; + + hb_position_t current_x; + hb_position_t current_y; + + bool path_open; + const hb_draw_funcs_t *funcs; + void *user_data; +}; +#endif + +#endif /* HB_DRAW_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-dsalgs.hh b/src/java.desktop/share/native/libharfbuzz/hb-dsalgs.hh deleted file mode 100644 index f5f28688327..00000000000 --- a/src/java.desktop/share/native/libharfbuzz/hb-dsalgs.hh +++ /dev/null @@ -1,632 +0,0 @@ -/* - * Copyright © 2017 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_DSALGS_HH -#define HB_DSALGS_HH - -#include "hb.hh" -#include "hb-null.hh" - - -/* Void! For when we need a expression-type of void. */ -typedef const struct _hb_void_t *hb_void_t; -#define HB_VOID ((const _hb_void_t *) nullptr) - - -/* - * Bithacks. - */ - -/* Return the number of 1 bits in v. */ -template -static inline HB_CONST_FUNC unsigned int -hb_popcount (T v) -{ -#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) - if (sizeof (T) <= sizeof (unsigned int)) - return __builtin_popcount (v); - - if (sizeof (T) <= sizeof (unsigned long)) - return __builtin_popcountl (v); - - if (sizeof (T) <= sizeof (unsigned long long)) - return __builtin_popcountll (v); -#endif - - if (sizeof (T) <= 4) - { - /* "HACKMEM 169" */ - uint32_t y; - y = (v >> 1) &033333333333; - y = v - y - ((y >>1) & 033333333333); - return (((y + (y >> 3)) & 030707070707) % 077); - } - - if (sizeof (T) == 8) - { - unsigned int shift = 32; - return hb_popcount ((uint32_t) v) + hb_popcount ((uint32_t) (v >> shift)); - } - - if (sizeof (T) == 16) - { - unsigned int shift = 64; - return hb_popcount ((uint64_t) v) + hb_popcount ((uint64_t) (v >> shift)); - } - - assert (0); - return 0; /* Shut up stupid compiler. */ -} - -/* Returns the number of bits needed to store number */ -template -static inline HB_CONST_FUNC unsigned int -hb_bit_storage (T v) -{ - if (unlikely (!v)) return 0; - -#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) - if (sizeof (T) <= sizeof (unsigned int)) - return sizeof (unsigned int) * 8 - __builtin_clz (v); - - if (sizeof (T) <= sizeof (unsigned long)) - return sizeof (unsigned long) * 8 - __builtin_clzl (v); - - if (sizeof (T) <= sizeof (unsigned long long)) - return sizeof (unsigned long long) * 8 - __builtin_clzll (v); -#endif - -#if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__) - if (sizeof (T) <= sizeof (unsigned int)) - { - unsigned long where; - _BitScanReverse (&where, v); - return 1 + where; - } -# if defined(_WIN64) - if (sizeof (T) <= 8) - { - unsigned long where; - _BitScanReverse64 (&where, v); - return 1 + where; - } -# endif -#endif - - if (sizeof (T) <= 4) - { - /* "bithacks" */ - const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000}; - const unsigned int S[] = {1, 2, 4, 8, 16}; - unsigned int r = 0; - for (int i = 4; i >= 0; i--) - if (v & b[i]) - { - v >>= S[i]; - r |= S[i]; - } - return r + 1; - } - if (sizeof (T) <= 8) - { - /* "bithacks" */ - const uint64_t b[] = {0x2ULL, 0xCULL, 0xF0ULL, 0xFF00ULL, 0xFFFF0000ULL, 0xFFFFFFFF00000000ULL}; - const unsigned int S[] = {1, 2, 4, 8, 16, 32}; - unsigned int r = 0; - for (int i = 5; i >= 0; i--) - if (v & b[i]) - { - v >>= S[i]; - r |= S[i]; - } - return r + 1; - } - if (sizeof (T) == 16) - { - unsigned int shift = 64; - return (v >> shift) ? hb_bit_storage ((uint64_t) (v >> shift)) + shift : - hb_bit_storage ((uint64_t) v); - } - - assert (0); - return 0; /* Shut up stupid compiler. */ -} - -/* Returns the number of zero bits in the least significant side of v */ -template -static inline HB_CONST_FUNC unsigned int -hb_ctz (T v) -{ - if (unlikely (!v)) return 0; - -#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) - if (sizeof (T) <= sizeof (unsigned int)) - return __builtin_ctz (v); - - if (sizeof (T) <= sizeof (unsigned long)) - return __builtin_ctzl (v); - - if (sizeof (T) <= sizeof (unsigned long long)) - return __builtin_ctzll (v); -#endif - -#if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__) - if (sizeof (T) <= sizeof (unsigned int)) - { - unsigned long where; - _BitScanForward (&where, v); - return where; - } -# if defined(_WIN64) - if (sizeof (T) <= 8) - { - unsigned long where; - _BitScanForward64 (&where, v); - return where; - } -# endif -#endif - - if (sizeof (T) <= 4) - { - /* "bithacks" */ - unsigned int c = 32; - v &= - (int32_t) v; - if (v) c--; - if (v & 0x0000FFFF) c -= 16; - if (v & 0x00FF00FF) c -= 8; - if (v & 0x0F0F0F0F) c -= 4; - if (v & 0x33333333) c -= 2; - if (v & 0x55555555) c -= 1; - return c; - } - if (sizeof (T) <= 8) - { - /* "bithacks" */ - unsigned int c = 64; - v &= - (int64_t) (v); - if (v) c--; - if (v & 0x00000000FFFFFFFFULL) c -= 32; - if (v & 0x0000FFFF0000FFFFULL) c -= 16; - if (v & 0x00FF00FF00FF00FFULL) c -= 8; - if (v & 0x0F0F0F0F0F0F0F0FULL) c -= 4; - if (v & 0x3333333333333333ULL) c -= 2; - if (v & 0x5555555555555555ULL) c -= 1; - return c; - } - if (sizeof (T) == 16) - { - unsigned int shift = 64; - return (uint64_t) v ? hb_bit_storage ((uint64_t) v) : - hb_bit_storage ((uint64_t) (v >> shift)) + shift; - } - - assert (0); - return 0; /* Shut up stupid compiler. */ -} - - -/* - * Tiny stuff. - */ - -template -static inline T* hb_addressof (T& arg) -{ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-align" - /* https://en.cppreference.com/w/cpp/memory/addressof */ - return reinterpret_cast( - &const_cast( - reinterpret_cast(arg))); -#pragma GCC diagnostic pop -} - -/* ASCII tag/character handling */ -static inline bool ISALPHA (unsigned char c) -{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } -static inline bool ISALNUM (unsigned char c) -{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); } -static inline bool ISSPACE (unsigned char c) -{ return c == ' ' || c =='\f'|| c =='\n'|| c =='\r'|| c =='\t'|| c =='\v'; } -static inline unsigned char TOUPPER (unsigned char c) -{ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; } -static inline unsigned char TOLOWER (unsigned char c) -{ return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; } - -#undef MIN -template -static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; } - -#undef MAX -template -static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; } - -static inline unsigned int DIV_CEIL (const unsigned int a, unsigned int b) -{ return (a + (b - 1)) / b; } - - -#undef ARRAY_LENGTH -template -static inline unsigned int ARRAY_LENGTH (const Type (&)[n]) { return n; } -/* A const version, but does not detect erratically being called on pointers. */ -#define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0]))) - - -static inline int -hb_memcmp (const void *a, const void *b, unsigned int len) -{ - /* It's illegal to pass NULL to memcmp(), even if len is zero. - * So, wrap it. - * https://sourceware.org/bugzilla/show_bug.cgi?id=23878 */ - if (!len) return 0; - return memcmp (a, b, len); -} - -static inline bool -hb_unsigned_mul_overflows (unsigned int count, unsigned int size) -{ - return (size > 0) && (count >= ((unsigned int) -1) / size); -} - -static inline unsigned int -hb_ceil_to_4 (unsigned int v) -{ - return ((v - 1) | 3) + 1; -} - -template struct hb_is_signed; -/* https://github.com/harfbuzz/harfbuzz/issues/1535 */ -template <> struct hb_is_signed { enum { value = true }; }; -template <> struct hb_is_signed { enum { value = true }; }; -template <> struct hb_is_signed { enum { value = true }; }; -template <> struct hb_is_signed { enum { value = true }; }; -template <> struct hb_is_signed { enum { value = false }; }; -template <> struct hb_is_signed { enum { value = false }; }; -template <> struct hb_is_signed { enum { value = false }; }; -template <> struct hb_is_signed { enum { value = false }; }; - -template static inline bool -hb_in_range (T u, T lo, T hi) -{ - /* The sizeof() is here to force template instantiation. - * I'm sure there are better ways to do this but can't think of - * one right now. Declaring a variable won't work as HB_UNUSED - * is unusable on some platforms and unused types are less likely - * to generate a warning than unused variables. */ - static_assert (!hb_is_signed::value, ""); - - /* The casts below are important as if T is smaller than int, - * the subtract results will become a signed int! */ - return (T)(u - lo) <= (T)(hi - lo); -} -template static inline bool -hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2) -{ - return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2); -} -template static inline bool -hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3) -{ - return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3); -} - - -/* - * Sort and search. - */ - -static inline void * -hb_bsearch (const void *key, const void *base, - size_t nmemb, size_t size, - int (*compar)(const void *_key, const void *_item)) -{ - int min = 0, max = (int) nmemb - 1; - while (min <= max) - { - int mid = (min + max) / 2; - const void *p = (const void *) (((const char *) base) + (mid * size)); - int c = compar (key, p); - if (c < 0) - max = mid - 1; - else if (c > 0) - min = mid + 1; - else - return (void *) p; - } - return nullptr; -} - -static inline void * -hb_bsearch_r (const void *key, const void *base, - size_t nmemb, size_t size, - int (*compar)(const void *_key, const void *_item, void *_arg), - void *arg) -{ - int min = 0, max = (int) nmemb - 1; - while (min <= max) - { - int mid = ((unsigned int) min + (unsigned int) max) / 2; - const void *p = (const void *) (((const char *) base) + (mid * size)); - int c = compar (key, p, arg); - if (c < 0) - max = mid - 1; - else if (c > 0) - min = mid + 1; - else - return (void *) p; - } - return nullptr; -} - - -/* From https://github.com/noporpoise/sort_r - * With following modifications: - * - * 10 November 2018: - * https://github.com/noporpoise/sort_r/issues/7 - */ - -/* Isaac Turner 29 April 2014 Public Domain */ - -/* - -hb_sort_r function to be exported. - -Parameters: - base is the array to be sorted - nel is the number of elements in the array - width is the size in bytes of each element of the array - compar is the comparison function - arg is a pointer to be passed to the comparison function - -void hb_sort_r(void *base, size_t nel, size_t width, - int (*compar)(const void *_a, const void *_b, void *_arg), - void *arg); -*/ - - -/* swap a, b iff a>b */ -/* __restrict is same as restrict but better support on old machines */ -static int sort_r_cmpswap(char *__restrict a, char *__restrict b, size_t w, - int (*compar)(const void *_a, const void *_b, - void *_arg), - void *arg) -{ - char tmp, *end = a+w; - if(compar(a, b, arg) > 0) { - for(; a < end; a++, b++) { tmp = *a; *a = *b; *b = tmp; } - return 1; - } - return 0; -} - -/* Note: quicksort is not stable, equivalent values may be swapped */ -static inline void sort_r_simple(void *base, size_t nel, size_t w, - int (*compar)(const void *_a, const void *_b, - void *_arg), - void *arg) -{ - char *b = (char *)base, *end = b + nel*w; - if(nel < 7) { - /* Insertion sort for arbitrarily small inputs */ - char *pi, *pj; - for(pi = b+w; pi < end; pi += w) { - for(pj = pi; pj > b && sort_r_cmpswap(pj-w,pj,w,compar,arg); pj -= w) {} - } - } - else - { - /* nel > 6; Quicksort */ - - /* Use median of first, middle and last items as pivot */ - char *x, *y, *xend, ch; - char *pl, *pm, *pr; - char *last = b+w*(nel-1), *tmp; - char *l[3]; - l[0] = b; - l[1] = b+w*(nel/2); - l[2] = last; - - if(compar(l[0],l[1],arg) > 0) { tmp=l[0]; l[0]=l[1]; l[1]=tmp; } - if(compar(l[1],l[2],arg) > 0) { - tmp=l[1]; l[1]=l[2]; l[2]=tmp; /* swap(l[1],l[2]) */ - if(compar(l[0],l[1],arg) > 0) { tmp=l[0]; l[0]=l[1]; l[1]=tmp; } - } - - /* swap l[id], l[2] to put pivot as last element */ - for(x = l[1], y = last, xend = x+w; x>1); - for(; pl < pm; pl += w) { - if(sort_r_cmpswap(pl, pr, w, compar, arg)) { - pr -= w; /* pivot now at pl */ - break; - } - } - pm = pl+((pr-pl)>>1); - for(; pm < pr; pr -= w) { - if(sort_r_cmpswap(pl, pr, w, compar, arg)) { - pl += w; /* pivot now at pr */ - break; - } - } - } - - sort_r_simple(b, (pl-b)/w, w, compar, arg); - sort_r_simple(pl+w, (end-(pl+w))/w, w, compar, arg); - } -} - -static inline void hb_sort_r(void *base, size_t nel, size_t width, - int (*compar)(const void *_a, const void *_b, void *_arg), - void *arg) -{ - sort_r_simple(base, nel, width, compar, arg); -} - - -template static inline void -hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *), T2 *array2) -{ - for (unsigned int i = 1; i < len; i++) - { - unsigned int j = i; - while (j && compar (&array[j - 1], &array[i]) > 0) - j--; - if (i == j) - continue; - /* Move item i to occupy place for item j, shift what's in between. */ - { - T t = array[i]; - memmove (&array[j + 1], &array[j], (i - j) * sizeof (T)); - array[j] = t; - } - if (array2) - { - T2 t = array2[i]; - memmove (&array2[j + 1], &array2[j], (i - j) * sizeof (T2)); - array2[j] = t; - } - } -} - -template static inline void -hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) -{ - hb_stable_sort (array, len, compar, (int *) nullptr); -} - -static inline hb_bool_t -hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out) -{ - /* Pain because we don't know whether s is nul-terminated. */ - char buf[64]; - len = MIN (ARRAY_LENGTH (buf) - 1, len); - strncpy (buf, s, len); - buf[len] = '\0'; - - char *end; - errno = 0; - unsigned long v = strtoul (buf, &end, base); - if (errno) return false; - if (*end) return false; - *out = v; - return true; -} - - -struct HbOpOr -{ - static constexpr bool passthru_left = true; - static constexpr bool passthru_right = true; - template static void process (T &o, const T &a, const T &b) { o = a | b; } -}; -struct HbOpAnd -{ - static constexpr bool passthru_left = false; - static constexpr bool passthru_right = false; - template static void process (T &o, const T &a, const T &b) { o = a & b; } -}; -struct HbOpMinus -{ - static constexpr bool passthru_left = true; - static constexpr bool passthru_right = false; - template static void process (T &o, const T &a, const T &b) { o = a & ~b; } -}; -struct HbOpXor -{ - static constexpr bool passthru_left = true; - static constexpr bool passthru_right = true; - template static void process (T &o, const T &a, const T &b) { o = a ^ b; } -}; - - -/* Compiler-assisted vectorization. */ - -/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))), - * using vectorized operations if HB_VECTOR_SIZE is set to **bit** numbers (eg 128). - * Define that to 0 to disable. */ -template -struct hb_vector_size_t -{ - elt_t& operator [] (unsigned int i) { return u.v[i]; } - const elt_t& operator [] (unsigned int i) const { return u.v[i]; } - - void clear (unsigned char v = 0) { memset (this, v, sizeof (*this)); } - - template - hb_vector_size_t process (const hb_vector_size_t &o) const - { - hb_vector_size_t r; -#if HB_VECTOR_SIZE - if (HB_VECTOR_SIZE && 0 == (byte_size * 8) % HB_VECTOR_SIZE) - for (unsigned int i = 0; i < ARRAY_LENGTH (u.vec); i++) - Op::process (r.u.vec[i], u.vec[i], o.u.vec[i]); - else -#endif - for (unsigned int i = 0; i < ARRAY_LENGTH (u.v); i++) - Op::process (r.u.v[i], u.v[i], o.u.v[i]); - return r; - } - hb_vector_size_t operator | (const hb_vector_size_t &o) const - { return process (o); } - hb_vector_size_t operator & (const hb_vector_size_t &o) const - { return process (o); } - hb_vector_size_t operator ^ (const hb_vector_size_t &o) const - { return process (o); } - hb_vector_size_t operator ~ () const - { - hb_vector_size_t r; -#if HB_VECTOR_SIZE && 0 - if (HB_VECTOR_SIZE && 0 == (byte_size * 8) % HB_VECTOR_SIZE) - for (unsigned int i = 0; i < ARRAY_LENGTH (u.vec); i++) - r.u.vec[i] = ~u.vec[i]; - else -#endif - for (unsigned int i = 0; i < ARRAY_LENGTH (u.v); i++) - r.u.v[i] = ~u.v[i]; - return r; - } - - private: - static_assert (byte_size / sizeof (elt_t) * sizeof (elt_t) == byte_size, ""); - union { - elt_t v[byte_size / sizeof (elt_t)]; -#if HB_VECTOR_SIZE - hb_vector_size_impl_t vec[byte_size / sizeof (hb_vector_size_impl_t)]; -#endif - } u; -}; - - -#endif /* HB_DSALGS_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.cc b/src/java.desktop/share/native/libharfbuzz/hb-face.cc index e3dc46922f5..6d96dcc3848 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-face.cc @@ -200,10 +200,15 @@ hb_face_create (hb_blob_t *blob, if (unlikely (!blob)) blob = hb_blob_get_empty (); - hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (hb_sanitize_context_t ().sanitize_blob (hb_blob_reference (blob)), index); + blob = hb_sanitize_context_t ().sanitize_blob (hb_blob_reference (blob)); + + hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (blob, index); if (unlikely (!closure)) + { + hb_blob_destroy (blob); return hb_face_get_empty (); + } face = hb_face_create_for_tables (_hb_face_for_data_reference_table, closure, @@ -226,7 +231,7 @@ hb_face_create (hb_blob_t *blob, hb_face_t * hb_face_get_empty () { - return const_cast (&Null(hb_face_t)); + return const_cast (&Null (hb_face_t)); } @@ -367,6 +372,9 @@ hb_blob_t * hb_face_reference_table (const hb_face_t *face, hb_tag_t tag) { + if (unlikely (tag == HB_TAG_NONE)) + return hb_blob_get_empty (); + return face->reference_table (tag); } @@ -531,6 +539,7 @@ hb_face_get_table_tags (const hb_face_t *face, */ +#ifndef HB_NO_FACE_COLLECT_UNICODES /** * hb_face_collect_unicodes: * @face: font face. @@ -542,9 +551,8 @@ void hb_face_collect_unicodes (hb_face_t *face, hb_set_t *out) { - face->table.cmap->collect_unicodes (out); + face->table.cmap->collect_unicodes (out, face->get_num_glyphs ()); } - /** * hb_face_collect_variation_selectors: * @face: font face. @@ -560,7 +568,6 @@ hb_face_collect_variation_selectors (hb_face_t *face, { face->table.cmap->collect_variation_selectors (out); } - /** * hb_face_collect_variation_unicodes: * @face: font face. @@ -577,7 +584,7 @@ hb_face_collect_variation_unicodes (hb_face_t *face, { face->table.cmap->collect_variation_unicodes (variation_selector, out); } - +#endif /* @@ -714,7 +721,10 @@ hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) return false; hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data; + hb_face_builder_data_t::table_entry_t *entry = data->tables.push (); + if (data->tables.in_error()) + return false; entry->tag = tag; entry->blob = hb_blob_reference (blob); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.hh b/src/java.desktop/share/native/libharfbuzz/hb-face.hh index b2730eb0a42..010eaba9e6f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-face.hh @@ -94,7 +94,7 @@ struct hb_face_t unsigned int get_num_glyphs () const { unsigned int ret = num_glyphs.get_relaxed (); - if (unlikely (ret == (unsigned int) -1)) + if (unlikely (ret == UINT_MAX)) return load_num_glyphs (); return ret; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-fallback-shape.cc b/src/java.desktop/share/native/libharfbuzz/hb-fallback-shape.cc index df30871ecde..d1d26b6a9f2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-fallback-shape.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-fallback-shape.cc @@ -26,6 +26,7 @@ #include "hb-shaper-impl.hh" +#ifndef HB_NO_FALLBACK_SHAPE /* * shaper face data @@ -120,3 +121,5 @@ _hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED, return true; } + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.cc b/src/java.desktop/share/native/libharfbuzz/hb-font.cc index 855c9983328..42bf3e948d2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.cc @@ -33,6 +33,9 @@ #include "hb-ot.h" +#include "hb-ot-var-avar-table.hh" +#include "hb-ot-var-fvar-table.hh" + /** * SECTION:hb-font @@ -355,6 +358,7 @@ hb_font_get_glyph_h_kerning_default (hb_font_t *font, return font->parent_scale_x_distance (font->parent->get_glyph_h_kerning (left_glyph, right_glyph)); } +#ifndef HB_DISABLE_DEPRECATED static hb_position_t hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED, void *font_data HB_UNUSED, @@ -373,6 +377,7 @@ hb_font_get_glyph_v_kerning_default (hb_font_t *font, { return font->parent_scale_y_distance (font->parent->get_glyph_v_kerning (top_glyph, bottom_glyph)); } +#endif static hb_bool_t hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED, @@ -672,7 +677,8 @@ hb_font_funcs_set_##name##_func (hb_font_funcs_t *ffuncs, \ void *user_data, \ hb_destroy_func_t destroy) \ { \ - if (hb_object_is_immutable (ffuncs)) { \ + if (hb_object_is_immutable (ffuncs)) \ + { \ if (destroy) \ destroy (user_data); \ return; \ @@ -789,6 +795,29 @@ hb_font_get_nominal_glyph (hb_font_t *font, return font->get_nominal_glyph (unicode, glyph); } +/** + * hb_font_get_nominal_glyphs: + * @font: a font. + * + * + * + * Return value: + * + * Since: 2.6.3 + **/ +unsigned int +hb_font_get_nominal_glyphs (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride) +{ + return font->get_nominal_glyphs (count, + first_unicode, unicode_stride, + first_glyph, glyph_stride); +} + /** * hb_font_get_variation_glyph: * @font: a font. @@ -936,7 +965,6 @@ hb_font_get_glyph_v_origin (hb_font_t *font, * Return value: * * Since: 0.9.2 - * Deprecated: 2.0.0 **/ hb_position_t hb_font_get_glyph_h_kerning (hb_font_t *font, @@ -945,6 +973,7 @@ hb_font_get_glyph_h_kerning (hb_font_t *font, return font->get_glyph_h_kerning (left_glyph, right_glyph); } +#ifndef HB_DISABLE_DEPRECATED /** * hb_font_get_glyph_v_kerning: * @font: a font. @@ -964,6 +993,7 @@ hb_font_get_glyph_v_kerning (hb_font_t *font, { return font->get_glyph_v_kerning (top_glyph, bottom_glyph); } +#endif /** * hb_font_get_glyph_extents: @@ -1185,7 +1215,6 @@ hb_font_subtract_glyph_origin_for_direction (hb_font_t *font, * * * Since: 0.9.2 - * Deprecated: 2.0.0 **/ void hb_font_get_glyph_kerning_for_direction (hb_font_t *font, @@ -1298,6 +1327,8 @@ DEFINE_NULL_INSTANCE (hb_font_t) = 1000, /* x_scale */ 1000, /* y_scale */ + 1<<16, /* x_mult */ + 1<<16, /* y_mult */ 0, /* x_ppem */ 0, /* y_ppem */ @@ -1305,6 +1336,7 @@ DEFINE_NULL_INSTANCE (hb_font_t) = 0, /* num_coords */ nullptr, /* coords */ + nullptr, /* design_coords */ const_cast (&_hb_Null_hb_font_funcs_t), @@ -1328,6 +1360,7 @@ _hb_font_create (hb_face_t *face) font->klass = hb_font_funcs_get_empty (); font->data.init0 (font); font->x_scale = font->y_scale = hb_face_get_upem (face); + font->x_mult = font->y_mult = 1 << 16; return font; } @@ -1347,12 +1380,28 @@ hb_font_create (hb_face_t *face) { hb_font_t *font = _hb_font_create (face); +#ifndef HB_NO_OT_FONT /* Install our in-house, very lightweight, funcs. */ hb_ot_font_set_funcs (font); +#endif return font; } +static void +_hb_font_adopt_var_coords (hb_font_t *font, + int *coords, /* 2.14 normalized */ + float *design_coords, + unsigned int coords_length) +{ + free (font->coords); + free (font->design_coords); + + font->coords = coords; + font->design_coords = design_coords; + font->num_coords = coords_length; +} + /** * hb_font_create_sub_font: * @parent: parent font. @@ -1378,21 +1427,27 @@ hb_font_create_sub_font (hb_font_t *parent) font->x_scale = parent->x_scale; font->y_scale = parent->y_scale; + font->mults_changed (); font->x_ppem = parent->x_ppem; font->y_ppem = parent->y_ppem; font->ptem = parent->ptem; - font->num_coords = parent->num_coords; - if (!font->num_coords) - font->coords = nullptr; - else + unsigned int num_coords = parent->num_coords; + if (num_coords) { - unsigned int size = parent->num_coords * sizeof (parent->coords[0]); - font->coords = (int *) malloc (size); - if (unlikely (!font->coords)) - font->num_coords = 0; + int *coords = (int *) calloc (num_coords, sizeof (parent->coords[0])); + float *design_coords = (float *) calloc (num_coords, sizeof (parent->design_coords[0])); + if (likely (coords && design_coords)) + { + memcpy (coords, parent->coords, num_coords * sizeof (parent->coords[0])); + memcpy (design_coords, parent->design_coords, num_coords * sizeof (parent->design_coords[0])); + _hb_font_adopt_var_coords (font, coords, design_coords, num_coords); + } else - memcpy (font->coords, parent->coords, size); + { + free (coords); + free (design_coords); + } } return font; @@ -1410,7 +1465,7 @@ hb_font_create_sub_font (hb_font_t *parent) hb_font_t * hb_font_get_empty () { - return const_cast (&Null(hb_font_t)); + return const_cast (&Null (hb_font_t)); } /** @@ -1452,6 +1507,7 @@ hb_font_destroy (hb_font_t *font) hb_font_funcs_destroy (font->klass); free (font->coords); + free (font->design_coords); free (font); } @@ -1597,7 +1653,9 @@ hb_font_set_face (hb_font_t *font, hb_face_t *old = font->face; + hb_face_make_immutable (face); font->face = hb_face_reference (face); + font->mults_changed (); hb_face_destroy (old); } @@ -1707,6 +1765,7 @@ hb_font_set_scale (hb_font_t *font, font->x_scale = x_scale; font->y_scale = y_scale; + font->mults_changed (); } /** @@ -1805,21 +1864,11 @@ hb_font_get_ptem (hb_font_t *font) return font->ptem; } +#ifndef HB_NO_VAR /* * Variations */ -static void -_hb_font_adopt_var_coords_normalized (hb_font_t *font, - int *coords, /* 2.14 normalized */ - unsigned int coords_length) -{ - free (font->coords); - - font->coords = coords; - font->num_coords = coords_length; -} - /** * hb_font_set_variations: * @@ -1842,13 +1891,30 @@ hb_font_set_variations (hb_font_t *font, unsigned int coords_length = hb_ot_var_get_axis_count (font->face); int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr; - if (unlikely (coords_length && !normalized)) + float *design_coords = coords_length ? (float *) calloc (coords_length, sizeof (float)) : nullptr; + + if (unlikely (coords_length && !(normalized && design_coords))) + { + free (normalized); + free (design_coords); return; + } - hb_ot_var_normalize_variations (font->face, - variations, variations_length, - normalized, coords_length); - _hb_font_adopt_var_coords_normalized (font, normalized, coords_length); + const OT::fvar &fvar = *font->face->table.fvar; + for (unsigned int i = 0; i < variations_length; i++) + { + hb_ot_var_axis_info_t info; + if (hb_ot_var_find_axis_info (font->face, variations[i].tag, &info) && + info.axis_index < coords_length) + { + float v = variations[i].value; + design_coords[info.axis_index] = v; + normalized[info.axis_index] = fvar.normalize_axis_value (info.axis_index, v); + } + } + font->face->table.avar->map_coords (normalized, coords_length); + + _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); } /** @@ -1865,11 +1931,47 @@ hb_font_set_var_coords_design (hb_font_t *font, return; int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr; - if (unlikely (coords_length && !normalized)) + float *design_coords = coords_length ? (float *) calloc (coords_length, sizeof (float)) : nullptr; + + if (unlikely (coords_length && !(normalized && design_coords))) + { + free (normalized); + free (design_coords); return; + } + + if (coords_length) + memcpy (design_coords, coords, coords_length * sizeof (font->design_coords[0])); hb_ot_var_normalize_coords (font->face, coords_length, coords, normalized); - _hb_font_adopt_var_coords_normalized (font, normalized, coords_length); + _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); +} + +/** + * hb_font_set_var_named_instance: + * @font: a font. + * @instance_index: named instance index. + * + * Sets design coords of a font from a named instance index. + * + * Since: 2.6.0 + */ +void +hb_font_set_var_named_instance (hb_font_t *font, + unsigned instance_index) +{ + if (hb_object_is_immutable (font)) + return; + + unsigned int coords_length = hb_ot_var_named_instance_get_design_coords (font->face, instance_index, nullptr, nullptr); + + float *coords = coords_length ? (float *) calloc (coords_length, sizeof (float)) : nullptr; + if (unlikely (coords_length && !coords)) + return; + + hb_ot_var_named_instance_get_design_coords (font->face, instance_index, &coords_length, coords); + hb_font_set_var_coords_design (font, coords, coords_length); + free (coords); } /** @@ -1886,13 +1988,30 @@ hb_font_set_var_coords_normalized (hb_font_t *font, return; int *copy = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : nullptr; - if (unlikely (coords_length && !copy)) + int *unmapped = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : nullptr; + float *design_coords = coords_length ? (float *) calloc (coords_length, sizeof (design_coords[0])) : nullptr; + + if (unlikely (coords_length && !(copy && unmapped && design_coords))) + { + free (copy); + free (unmapped); + free (design_coords); return; + } if (coords_length) + { memcpy (copy, coords, coords_length * sizeof (coords[0])); + memcpy (unmapped, coords, coords_length * sizeof (coords[0])); + } + + /* Best effort design coords simulation */ + font->face->table.avar->unmap_coords (unmapped, coords_length); + for (unsigned int i = 0; i < coords_length; ++i) + design_coords[i] = font->face->table.fvar->unnormalize_axis_value (i, unmapped[i]); + free (unmapped); - _hb_font_adopt_var_coords_normalized (font, copy, coords_length); + _hb_font_adopt_var_coords (font, copy, design_coords, coords_length); } /** @@ -1913,7 +2032,28 @@ hb_font_get_var_coords_normalized (hb_font_t *font, return font->coords; } +#ifdef HB_EXPERIMENTAL_API +/** + * hb_font_get_var_coords_design: + * + * Return value is valid as long as variation coordinates of the font + * are not modified. + * + * Since: EXPERIMENTAL + */ +const float * +hb_font_get_var_coords_design (hb_font_t *font, + unsigned int *length) +{ + if (length) + *length = font->num_coords; + + return font->design_coords; +} +#endif +#endif +#ifndef HB_DISABLE_DEPRECATED /* * Deprecated get_glyph_func(): */ @@ -2015,6 +2155,13 @@ hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_func_t func, void *user_data, hb_destroy_func_t destroy) { + if (hb_object_is_immutable (ffuncs)) + { + if (destroy) + destroy (user_data); + return; + } + hb_font_get_glyph_trampoline_t *trampoline; trampoline = trampoline_create (func, user_data, destroy); @@ -2036,3 +2183,4 @@ hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, trampoline, trampoline_destroy); } +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.h b/src/java.desktop/share/native/libharfbuzz/hb-font.h index 85893f97c63..217bf33f440 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.h @@ -33,6 +33,7 @@ #include "hb-common.h" #include "hb-face.h" +#include "hb-draw.h" HB_BEGIN_DECLS @@ -157,6 +158,11 @@ typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *fon typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_h_origin_func_t; typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t; +typedef hb_position_t (*hb_font_get_glyph_kerning_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + void *user_data); +typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t; + typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, @@ -356,6 +362,22 @@ hb_font_funcs_set_glyph_v_origin_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_v_origin_func_t func, void *user_data, hb_destroy_func_t destroy); +/** + * hb_font_funcs_set_glyph_h_kerning_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_kerning_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_kerning_func_t func, + void *user_data, hb_destroy_func_t destroy); + /** * hb_font_funcs_set_glyph_extents_func: * @ffuncs: font functions. @@ -438,6 +460,14 @@ hb_font_get_variation_glyph (hb_font_t *font, hb_codepoint_t unicode, hb_codepoint_t variation_selector, hb_codepoint_t *glyph); +HB_EXTERN unsigned int +hb_font_get_nominal_glyphs (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride); + HB_EXTERN hb_position_t hb_font_get_glyph_h_advance (hb_font_t *font, hb_codepoint_t glyph); @@ -469,6 +499,10 @@ hb_font_get_glyph_v_origin (hb_font_t *font, hb_codepoint_t glyph, hb_position_t *x, hb_position_t *y); +HB_EXTERN hb_position_t +hb_font_get_glyph_h_kerning (hb_font_t *font, + hb_codepoint_t left_glyph, hb_codepoint_t right_glyph); + HB_EXTERN hb_bool_t hb_font_get_glyph_extents (hb_font_t *font, hb_codepoint_t glyph, @@ -531,6 +565,12 @@ hb_font_subtract_glyph_origin_for_direction (hb_font_t *font, hb_direction_t direction, hb_position_t *x, hb_position_t *y); +HB_EXTERN void +hb_font_get_glyph_kerning_for_direction (hb_font_t *font, + hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); + HB_EXTERN hb_bool_t hb_font_get_glyph_extents_for_origin (hb_font_t *font, hb_codepoint_t glyph, @@ -665,6 +705,12 @@ hb_font_set_var_coords_design (hb_font_t *font, const float *coords, unsigned int coords_length); +#ifdef HB_EXPERIMENTAL_API +HB_EXTERN const float * +hb_font_get_var_coords_design (hb_font_t *font, + unsigned int *length); +#endif + HB_EXTERN void hb_font_set_var_coords_normalized (hb_font_t *font, const int *coords, /* 2.14 normalized */ @@ -674,6 +720,16 @@ HB_EXTERN const int * hb_font_get_var_coords_normalized (hb_font_t *font, unsigned int *length); +HB_EXTERN void +hb_font_set_var_named_instance (hb_font_t *font, + unsigned instance_index); + +#ifdef HB_EXPERIMENTAL_API +HB_EXTERN hb_bool_t +hb_font_draw_glyph (hb_font_t *font, hb_codepoint_t glyph, + const hb_draw_funcs_t *funcs, void *user_data); +#endif + HB_END_DECLS #endif /* HB_FONT_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.hh b/src/java.desktop/share/native/libharfbuzz/hb-font.hh index dd33d2f7d7c..2fa9cea7388 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.hh @@ -52,7 +52,7 @@ HB_FONT_FUNC_IMPLEMENT (glyph_h_origin) \ HB_FONT_FUNC_IMPLEMENT (glyph_v_origin) \ HB_FONT_FUNC_IMPLEMENT (glyph_h_kerning) \ - HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning) \ + HB_IF_NOT_DEPRECATED (HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning)) \ HB_FONT_FUNC_IMPLEMENT (glyph_extents) \ HB_FONT_FUNC_IMPLEMENT (glyph_contour_point) \ HB_FONT_FUNC_IMPLEMENT (glyph_name) \ @@ -107,8 +107,10 @@ struct hb_font_t hb_font_t *parent; hb_face_t *face; - int x_scale; - int y_scale; + int32_t x_scale; + int32_t y_scale; + int64_t x_mult; + int64_t y_mult; unsigned int x_ppem; unsigned int y_ppem; @@ -118,6 +120,7 @@ struct hb_font_t /* Font variation coordinates. */ unsigned int num_coords; int *coords; + float *design_coords; hb_font_funcs_t *klass; void *user_data; @@ -127,16 +130,16 @@ struct hb_font_t /* Convert from font-space to user-space */ - int dir_scale (hb_direction_t direction) - { return HB_DIRECTION_IS_VERTICAL(direction) ? y_scale : x_scale; } - hb_position_t em_scale_x (int16_t v) { return em_scale (v, x_scale); } - hb_position_t em_scale_y (int16_t v) { return em_scale (v, y_scale); } - hb_position_t em_scalef_x (float v) { return em_scalef (v, this->x_scale); } - hb_position_t em_scalef_y (float v) { return em_scalef (v, this->y_scale); } + int64_t dir_mult (hb_direction_t direction) + { return HB_DIRECTION_IS_VERTICAL(direction) ? y_mult : x_mult; } + hb_position_t em_scale_x (int16_t v) { return em_mult (v, x_mult); } + hb_position_t em_scale_y (int16_t v) { return em_mult (v, y_mult); } + hb_position_t em_scalef_x (float v) { return em_scalef (v, x_scale); } + hb_position_t em_scalef_y (float v) { return em_scalef (v, y_scale); } float em_fscale_x (int16_t v) { return em_fscale (v, x_scale); } float em_fscale_y (int16_t v) { return em_fscale (v, y_scale); } hb_position_t em_scale_dir (int16_t v, hb_direction_t direction) - { return em_scale (v, dir_scale (direction)); } + { return em_mult (v, dir_mult (direction)); } /* Convert from parent-font user-space to our user-space */ hb_position_t parent_scale_x_distance (hb_position_t v) @@ -214,7 +217,7 @@ struct hb_font_t } hb_bool_t get_nominal_glyph (hb_codepoint_t unicode, - hb_codepoint_t *glyph) + hb_codepoint_t *glyph) { *glyph = 0; return klass->get.f.nominal_glyph (this, user_data, @@ -284,7 +287,7 @@ struct hb_font_t } hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) + hb_position_t *x, hb_position_t *y) { *x = *y = 0; return klass->get.f.glyph_h_origin (this, user_data, @@ -304,21 +307,29 @@ struct hb_font_t hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph, hb_codepoint_t right_glyph) { +#ifdef HB_DISABLE_DEPRECATED + return 0; +#else return klass->get.f.glyph_h_kerning (this, user_data, left_glyph, right_glyph, klass->user_data.glyph_h_kerning); +#endif } hb_position_t get_glyph_v_kerning (hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph) { +#ifdef HB_DISABLE_DEPRECATED + return 0; +#else return klass->get.f.glyph_v_kerning (this, user_data, top_glyph, bottom_glyph, klass->user_data.glyph_v_kerning); +#endif } hb_bool_t get_glyph_extents (hb_codepoint_t glyph, - hb_glyph_extents_t *extents) + hb_glyph_extents_t *extents) { memset (extents, 0, sizeof (*extents)); return klass->get.f.glyph_extents (this, user_data, @@ -328,7 +339,7 @@ struct hb_font_t } hb_bool_t get_glyph_contour_point (hb_codepoint_t glyph, unsigned int point_index, - hb_position_t *x, hb_position_t *y) + hb_position_t *x, hb_position_t *y) { *x = *y = 0; return klass->get.f.glyph_contour_point (this, user_data, @@ -599,15 +610,19 @@ struct hb_font_t return false; } - hb_position_t em_scale (int16_t v, int scale) + void mults_changed () + { + signed upem = face->get_upem (); + x_mult = ((int64_t) x_scale << 16) / upem; + y_mult = ((int64_t) y_scale << 16) / upem; + } + + hb_position_t em_mult (int16_t v, int64_t mult) { - int upem = face->get_upem (); - int64_t scaled = v * (int64_t) scale; - scaled += scaled >= 0 ? upem/2 : -upem/2; /* Round. */ - return (hb_position_t) (scaled / upem); + return (hb_position_t) ((v * mult) >> 16); } hb_position_t em_scalef (float v, int scale) - { return (hb_position_t) round (v * scale / face->get_upem ()); } + { return (hb_position_t) roundf (v * scale / face->get_upem ()); } float em_fscale (int16_t v, int scale) { return (float) v * scale / face->get_upem (); } }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ft.cc b/src/java.desktop/share/native/libharfbuzz/hb-ft.cc index d73c4aaac73..6b7b9e08771 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ft.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ft.cc @@ -29,6 +29,8 @@ #include "hb.hh" +#ifdef HAVE_FREETYPE + #include "hb-ft.h" #include "hb-font.hh" @@ -46,8 +48,13 @@ * @short_description: FreeType integration * @include: hb-ft.h * - * Functions for using HarfBuzz with the FreeType library to provide face and + * Functions for using HarfBuzz with the FreeType library. + * + * HarfBuzz supports using FreeType to provide face and * font data. + * + * Note that FreeType is not thread-safe, therefore these + * functions are not thread-safe either. **/ @@ -85,9 +92,7 @@ static hb_ft_font_t * _hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref) { hb_ft_font_t *ft_font = (hb_ft_font_t *) calloc (1, sizeof (hb_ft_font_t)); - - if (unlikely (!ft_font)) - return nullptr; + if (unlikely (!ft_font)) return nullptr; ft_font->lock.init (); ft_font->ft_face = ft_face; @@ -96,7 +101,7 @@ _hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref) ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING; - ft_font->cached_x_scale.set (0); + ft_font->cached_x_scale.set_relaxed (0); ft_font->advance_cache.init (); return ft_font; @@ -125,10 +130,13 @@ _hb_ft_font_destroy (void *data) /** * hb_ft_font_set_load_flags: - * @font: - * @load_flags: + * @font: #hb_font_t to work upon + * @load_flags: The FreeType load flags to set * + * Sets the FT_Load_Glyph load flags for the specified #hb_font_t. * + * For more information, see + * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx * * Since: 1.0.5 **/ @@ -138,7 +146,7 @@ hb_ft_font_set_load_flags (hb_font_t *font, int load_flags) if (hb_object_is_immutable (font)) return; - if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) return; hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; @@ -148,17 +156,21 @@ hb_ft_font_set_load_flags (hb_font_t *font, int load_flags) /** * hb_ft_font_get_load_flags: - * @font: + * @font: #hb_font_t to work upon * + * Fetches the FT_Load_Glyph load flags of the specified #hb_font_t. * + * For more information, see + * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx + * + * Return value: FT_Load_Glyph flags found * - * Return value: * Since: 1.0.5 **/ int hb_ft_font_get_load_flags (hb_font_t *font) { - if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) return 0; const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; @@ -166,17 +178,69 @@ hb_ft_font_get_load_flags (hb_font_t *font) return ft_font->load_flags; } +/** + * hb_ft_get_face: + * @font: #hb_font_t to work upon + * + * Fetches the FT_Face associated with the specified #hb_font_t + * font object. + * + * Return value: the FT_Face found + * + * Since: 0.9.2 + **/ FT_Face hb_ft_font_get_face (hb_font_t *font) { - if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return nullptr; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + return ft_font->ft_face; +} + +/** + * hb_ft_font_lock_face: + * @font: + * + * + * + * Return value: + * Since: 2.6.5 + **/ +FT_Face +hb_ft_font_lock_face (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) return nullptr; const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + ft_font->lock.lock (); + return ft_font->ft_face; } +/** + * hb_ft_font_unlock_face: + * @font: + * + * + * + * Return value: + * Since: 2.6.5 + **/ +void +hb_ft_font_unlock_face (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + ft_font->lock.unlock (); +} static hb_bool_t @@ -346,6 +410,25 @@ hb_ft_get_glyph_v_origin (hb_font_t *font, return true; } +#ifndef HB_NO_OT_SHAPE_FALLBACK +static hb_position_t +hb_ft_get_glyph_h_kerning (hb_font_t *font, + void *font_data, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Vector kerningv; + + FT_Kerning_Mode mode = font->x_ppem ? FT_KERNING_DEFAULT : FT_KERNING_UNFITTED; + if (FT_Get_Kerning (ft_font->ft_face, left_glyph, right_glyph, mode, &kerningv)) + return 0; + + return kerningv.x; +} +#endif + static hb_bool_t hb_ft_get_glyph_extents (hb_font_t *font, void *font_data, @@ -439,7 +522,7 @@ hb_ft_get_glyph_from_name (hb_font_t *font HB_UNUSED, else { /* Make a nul-terminated version. */ char buf[128]; - len = MIN (len, (int) sizeof (buf) - 1); + len = hb_min (len, (int) sizeof (buf) - 1); strncpy (buf, name, len); buf[len] = '\0'; *glyph = FT_Get_Name_Index (ft_face, buf); @@ -497,6 +580,10 @@ static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_tcharmap && ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL; + hb_ft_font_t *ft_font = _hb_ft_font_create (ft_face, symbol, unref); + if (unlikely (!ft_font)) return; + hb_font_set_funcs (font, _hb_ft_get_font_funcs (), - _hb_ft_font_create (ft_face, symbol, unref), + ft_font, _hb_ft_font_destroy); } static hb_blob_t * -reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +_hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) { FT_Face ft_face = (FT_Face) user_data; FT_Byte *buffer; @@ -570,12 +660,22 @@ reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) /** * hb_ft_face_create: - * @ft_face: (destroy destroy) (scope notified): - * @destroy: + * @ft_face: (destroy destroy) (scope notified): FT_Face to work upon + * @destroy: A callback to call when the face object is not needed anymore + * + * Creates an #hb_face_t face object from the specified FT_Face. * + * This variant of the function does not provide any life-cycle management. * + * Most client programs should use hb_ft_face_create_referenced() + * (or, perhaps, hb_ft_face_create_cached()) instead. + * + * If you know you have valid reasons not to use hb_ft_face_create_referenced(), + * then it is the client program's responsibility to destroy @ft_face + * after the #hb_face_t face object has been destroyed. + * + * Return value: (transfer full): the new #hb_face_t face object * - * Return value: (transfer full): * Since: 0.9.2 **/ hb_face_t * @@ -594,7 +694,7 @@ hb_ft_face_create (FT_Face ft_face, face = hb_face_create (blob, ft_face->face_index); hb_blob_destroy (blob); } else { - face = hb_face_create_for_tables (reference_table, ft_face, destroy); + face = hb_face_create_for_tables (_hb_ft_reference_table, ft_face, destroy); } hb_face_set_index (face, ft_face->face_index); @@ -605,11 +705,20 @@ hb_ft_face_create (FT_Face ft_face, /** * hb_ft_face_create_referenced: - * @ft_face: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_face_t face object from the specified FT_Face. * + * This is the preferred variant of the hb_ft_face_create* + * function family, because it calls FT_Reference_Face() on @ft_face, + * ensuring that @ft_face remains alive as long as the resulting + * #hb_face_t face object remains alive. Also calls FT_Done_Face() + * when the #hb_face_t face object is destroyed. * + * Use this version unless you know you have good reasons not to. + * + * Return value: (transfer full): the new #hb_face_t face object * - * Return value: (transfer full): * Since: 0.9.38 **/ hb_face_t * @@ -627,11 +736,21 @@ hb_ft_face_finalize (FT_Face ft_face) /** * hb_ft_face_create_cached: - * @ft_face: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_face_t face object from the specified FT_Face. + * + * This variant of the function caches the newly created #hb_face_t + * face object, using the @generic pointer of @ft_face. Subsequent function + * calls that are passed the same @ft_face parameter will have the same + * #hb_face_t returned to them, and that #hb_face_t will be correctly + * reference counted. * + * However, client programs are still responsible for destroying + * @ft_face after the last #hb_face_t face object has been destroyed. * + * Return value: (transfer full): the new #hb_face_t face object * - * Return value: (transfer full): * Since: 0.9.2 **/ hb_face_t * @@ -649,15 +768,34 @@ hb_ft_face_create_cached (FT_Face ft_face) return hb_face_reference ((hb_face_t *) ft_face->generic.data); } - /** * hb_ft_font_create: - * @ft_face: (destroy destroy) (scope notified): - * @destroy: + * @ft_face: (destroy destroy) (scope notified): FT_Face to work upon + * @destroy: (optional): A callback to call when the font object is not needed anymore + * + * Creates an #hb_font_t font object from the specified FT_Face. + * + * Note: You must set the face size on @ft_face before calling + * hb_ft_font_create() on it. Otherwise, HarfBuzz will not pick up + * the face size. * + * This variant of the function does not provide any life-cycle management. * + * Most client programs should use hb_ft_font_create_referenced() + * instead. + * + * If you know you have valid reasons not to use hb_ft_font_create_referenced(), + * then it is the client program's responsibility to destroy @ft_face + * after the #hb_font_t font object has been destroyed. + * + * HarfBuzz will use the @destroy callback on the #hb_font_t font object + * if it is supplied when you use this function. However, even if @destroy + * is provided, it is the client program's responsibility to destroy @ft_face, + * and it is the client program's responsibility to ensure that @ft_face is + * destroyed only after the #hb_font_t font object has been destroyed. + * + * Return value: (transfer full): the new #hb_font_t font object * - * Return value: (transfer full): * Since: 0.9.2 **/ hb_font_t * @@ -675,6 +813,16 @@ hb_ft_font_create (FT_Face ft_face, return font; } +/** + * hb_ft_font_has_changed: + * @font: #hb_font_t to work upon + * + * Refreshes the state of @font when the underlying FT_Face has changed. + * This function should be called after changing the size or + * variation-axis settings on the FT_Face. + * + * Since: 1.0.5 + **/ void hb_ft_font_changed (hb_font_t *font) { @@ -682,6 +830,7 @@ hb_ft_font_changed (hb_font_t *font) return; hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + FT_Face ft_face = ft_font->ft_face; hb_font_set_scale (font, @@ -693,7 +842,7 @@ hb_ft_font_changed (hb_font_t *font) ft_face->size->metrics.y_ppem); #endif -#ifdef HAVE_FT_GET_VAR_BLEND_COORDINATES +#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR) FT_MM_Var *mm_var = nullptr; if (!FT_Get_MM_Var (ft_face, &mm_var)) { @@ -730,11 +879,23 @@ hb_ft_font_changed (hb_font_t *font) /** * hb_ft_font_create_referenced: - * @ft_face: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_font_t font object from the specified FT_Face. + * + * Note: You must set the face size on @ft_face before calling + * hb_ft_font_create_references() on it. Otherwise, HarfBuzz will not pick up + * the face size. + * + * This is the preferred variant of the hb_ft_font_create* + * function family, because it calls FT_Reference_Face() on @ft_face, + * ensuring that @ft_face remains alive as long as the resulting + * #hb_font_t font object remains alive. * + * Use this version unless you know you have good reasons not to. * + * Return value: (transfer full): the new #hb_font_t font object * - * Return value: (transfer full): * Since: 0.9.38 **/ hb_font_t * @@ -748,7 +909,7 @@ hb_ft_font_create_referenced (FT_Face ft_face) static void free_static_ft_library (); #endif -static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t, hb_ft_library_lazy_loader_t> { static FT_Library create () @@ -793,6 +954,28 @@ _release_blob (FT_Face ft_face) hb_blob_destroy ((hb_blob_t *) ft_face->generic.data); } +/** + * hb_ft_font_set_funcs: + * @font: #hb_font_t to work upon + * + * Configures the font-functions structure of the specified + * #hb_font_t font object to use FreeType font functions. + * + * In particular, you can use this function to configure an + * existing #hb_face_t face object for use with FreeType font + * functions even if that #hb_face_t face object was initially + * created with hb_face_create(), and therefore was not + * initially configured to use FreeType font functions. + * + * An #hb_face_t face object created with hb_ft_face_create() + * is preconfigured for FreeType font functions and does not + * require this function to be used. + * + * Note: Internally, this function creates an FT_Face. +* + * + * Since: 1.0.5 + **/ void hb_ft_font_set_funcs (hb_font_t *font) { @@ -815,8 +998,8 @@ hb_ft_font_set_funcs (hb_font_t *font) return; } - if (FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE)) - FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL); + if (FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL)) + FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE); FT_Set_Char_Size (ft_face, abs (font->x_scale), abs (font->y_scale), @@ -832,7 +1015,7 @@ hb_ft_font_set_funcs (hb_font_t *font) FT_Set_Transform (ft_face, &matrix, nullptr); } -#ifdef HAVE_FT_SET_VAR_BLEND_COORDINATES +#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR) unsigned int num_coords; const int *coords = hb_font_get_var_coords_normalized (font, &num_coords); if (num_coords) @@ -841,7 +1024,7 @@ hb_ft_font_set_funcs (hb_font_t *font) if (ft_coords) { for (unsigned int i = 0; i < num_coords; i++) - ft_coords[i] = coords[i] << 2; + ft_coords[i] = coords[i] * 4; FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords); free (ft_coords); } @@ -854,3 +1037,6 @@ hb_ft_font_set_funcs (hb_font_t *font) _hb_ft_font_set_funcs (font, ft_face, true); hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING); } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ft.h b/src/java.desktop/share/native/libharfbuzz/hb-ft.h index dda30d38157..4962eaf38d4 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ft.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ft.h @@ -110,6 +110,12 @@ hb_ft_font_create_referenced (FT_Face ft_face); HB_EXTERN FT_Face hb_ft_font_get_face (hb_font_t *font); +HB_EXTERN FT_Face +hb_ft_font_lock_face (hb_font_t *font); + +HB_EXTERN void +hb_ft_font_unlock_face (hb_font_t *font); + HB_EXTERN void hb_ft_font_set_load_flags (hb_font_t *font, int load_flags); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-iter.hh b/src/java.desktop/share/native/libharfbuzz/hb-iter.hh index c4ab26dc063..99a441af44f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-iter.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-iter.hh @@ -1,5 +1,6 @@ /* * Copyright © 2018 Google, Inc. + * Copyright © 2019 Facebook, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -22,13 +23,15 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Google Author(s): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod */ #ifndef HB_ITER_HH #define HB_ITER_HH #include "hb.hh" -#include "hb-null.hh" +#include "hb-algs.hh" +#include "hb-meta.hh" /* Unified iterator object. @@ -39,16 +42,32 @@ * copied by value. If the collection / object being iterated on * is writable, then the iterator returns lvalues, otherwise it * returns rvalues. + * + * TODO Document more. + * + * If iterator implementation implements operator!=, then can be + * used in range-based for loop. That comes free if the iterator + * is random-access. Otherwise, the range-based for loop incurs + * one traversal to find end(), which can be avoided if written + * as a while-style for loop, or if iterator implements a faster + * __end__() method. + * TODO When opting in for C++17, address this by changing return + * type of .end()? + */ + +/* + * Base classes for iterators. */ /* Base class for all iterators. */ -template +template struct hb_iter_t { - typedef Iter iter_t; - typedef iter_t const_iter_t; typedef Item item_t; - static constexpr unsigned item_size = hb_static_size (Item); + constexpr unsigned get_item_size () const { return hb_static_size (Item); } + static constexpr bool is_iterator = true; + static constexpr bool is_random_access_iterator = false; + static constexpr bool is_sorted_iterator = false; private: /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ @@ -56,53 +75,119 @@ struct hb_iter_t iter_t* thiz () { return static_cast< iter_t *> (this); } public: - /* Operators. */ - operator iter_t () { return iter(); } - explicit_operator bool () const { return more (); } - item_t& operator * () const { return item (); } - item_t& operator [] (signed i) const { return item_at ((unsigned) i); } - iter_t& operator += (unsigned count) { forward (count); return *thiz(); } - iter_t& operator ++ () { next (); return *thiz(); } - iter_t& operator -= (unsigned count) { rewind (count); return *thiz(); } - iter_t& operator -- () { prev (); return *thiz(); } - iter_t operator + (unsigned count) { iter_t c (*thiz()); c += count; return c; } - iter_t operator ++ (int) { iter_t c (*thiz()); ++*thiz(); return c; } - iter_t operator - (unsigned count) { iter_t c (*thiz()); c -= count; return c; } - iter_t operator -- (int) { iter_t c (*thiz()); --*thiz(); return c; } + /* TODO: + * Port operators below to use hb_enable_if to sniff which method implements + * an operator and use it, and remove hb_iter_fallback_mixin_t completely. */ - /* Methods. */ + /* Operators. */ iter_t iter () const { return *thiz(); } - const_iter_t const_iter () const { return iter (); } - item_t& item () const { return thiz()->__item__ (); } - item_t& item_at (unsigned i) const { return thiz()->__item_at__ (i); } - bool more () const { return thiz()->__more__ (); } + iter_t operator + () const { return *thiz(); } + iter_t begin () const { return *thiz(); } + iter_t end () const { return thiz()->__end__ (); } + explicit operator bool () const { return thiz()->__more__ (); } unsigned len () const { return thiz()->__len__ (); } - void next () { thiz()->__next__ (); } - void forward (unsigned n) { thiz()->__forward__ (n); } - void prev () { thiz()->__prev__ (); } - void rewind (unsigned n) { thiz()->__rewind__ (n); } - bool random_access () const { return thiz()->__random_access__ (); } + /* The following can only be enabled if item_t is reference type. Otherwise + * it will be returning pointer to temporary rvalue. + * TODO Use a wrapper return type to fix for non-reference type. */ + template + hb_remove_reference* operator -> () const { return hb_addressof (**thiz()); } + item_t operator * () const { return thiz()->__item__ (); } + item_t operator * () { return thiz()->__item__ (); } + item_t operator [] (unsigned i) const { return thiz()->__item_at__ (i); } + item_t operator [] (unsigned i) { return thiz()->__item_at__ (i); } + iter_t& operator += (unsigned count) & { thiz()->__forward__ (count); return *thiz(); } + iter_t operator += (unsigned count) && { thiz()->__forward__ (count); return *thiz(); } + iter_t& operator ++ () & { thiz()->__next__ (); return *thiz(); } + iter_t operator ++ () && { thiz()->__next__ (); return *thiz(); } + iter_t& operator -= (unsigned count) & { thiz()->__rewind__ (count); return *thiz(); } + iter_t operator -= (unsigned count) && { thiz()->__rewind__ (count); return *thiz(); } + iter_t& operator -- () & { thiz()->__prev__ (); return *thiz(); } + iter_t operator -- () && { thiz()->__prev__ (); return *thiz(); } + iter_t operator + (unsigned count) const { auto c = thiz()->iter (); c += count; return c; } + friend iter_t operator + (unsigned count, const iter_t &it) { return it + count; } + iter_t operator ++ (int) { iter_t c (*thiz()); ++*thiz(); return c; } + iter_t operator - (unsigned count) const { auto c = thiz()->iter (); c -= count; return c; } + iter_t operator -- (int) { iter_t c (*thiz()); --*thiz(); return c; } + template + iter_t& operator >> (T &v) & { v = **thiz(); ++*thiz(); return *thiz(); } + template + iter_t operator >> (T &v) && { v = **thiz(); ++*thiz(); return *thiz(); } + template + iter_t& operator << (const T v) & { **thiz() = v; ++*thiz(); return *thiz(); } + template + iter_t operator << (const T v) && { **thiz() = v; ++*thiz(); return *thiz(); } protected: - hb_iter_t () {} - hb_iter_t (const hb_iter_t &o HB_UNUSED) {} - void operator = (const hb_iter_t &o HB_UNUSED) {} + hb_iter_t () = default; + hb_iter_t (const hb_iter_t &o HB_UNUSED) = default; + hb_iter_t (hb_iter_t &&o HB_UNUSED) = default; + hb_iter_t& operator = (const hb_iter_t &o HB_UNUSED) = default; + hb_iter_t& operator = (hb_iter_t &&o HB_UNUSED) = default; }; -/* Base class for sorted iterators. Does not enforce anything. - * Just for class taxonomy and requirements. */ -template -struct hb_sorted_iter_t : hb_iter_t +#define HB_ITER_USING(Name) \ + using item_t = typename Name::item_t; \ + using Name::begin; \ + using Name::end; \ + using Name::get_item_size; \ + using Name::is_iterator; \ + using Name::iter; \ + using Name::operator bool; \ + using Name::len; \ + using Name::operator ->; \ + using Name::operator *; \ + using Name::operator []; \ + using Name::operator +=; \ + using Name::operator ++; \ + using Name::operator -=; \ + using Name::operator --; \ + using Name::operator +; \ + using Name::operator -; \ + using Name::operator >>; \ + using Name::operator <<; \ + static_assert (true, "") + +/* Returns iterator / item type of a type. */ +template +using hb_iter_type = decltype (hb_deref (hb_declval (Iterable)).iter ()); +template +using hb_item_type = decltype (*hb_deref (hb_declval (Iterable)).iter ()); + + +template struct hb_array_t; +template struct hb_sorted_array_t; + +struct { - protected: - hb_sorted_iter_t () {} - hb_sorted_iter_t (const hb_sorted_iter_t &o) : hb_iter_t (o) {} - void operator = (const hb_sorted_iter_t &o HB_UNUSED) {} -}; + template hb_iter_type + operator () (T&& c) const + { return hb_deref (hb_forward (c)).iter (); } + + /* Specialization for C arrays. */ + + template inline hb_array_t + operator () (Type *array, unsigned int length) const + { return hb_array_t (array, length); } + + template hb_array_t + operator () (Type (&array)[length]) const + { return hb_array_t (array, length); } + +} +HB_FUNCOBJ (hb_iter); +struct +{ + template unsigned + operator () (T&& c) const + { return c.len (); } + +} +HB_FUNCOBJ (hb_len); /* Mixin to fill in what the subclass doesn't provide. */ -template -struct hb_iter_mixin_t +template +struct hb_iter_fallback_mixin_t { private: /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ @@ -111,42 +196,743 @@ struct hb_iter_mixin_t public: /* Access: Implement __item__(), or __item_at__() if random-access. */ - item_t& __item__ () const { return thiz()->item_at (0); } - item_t& __item_at__ (unsigned i) const { return *(thiz() + i); } + item_t __item__ () const { return (*thiz())[0]; } + item_t __item_at__ (unsigned i) const { return *(*thiz() + i); } /* Termination: Implement __more__(), or __len__() if random-access. */ - bool __more__ () const { return thiz()->__len__ (); } + bool __more__ () const { return bool (thiz()->len ()); } unsigned __len__ () const - { iter_t c (*thiz()); unsigned l = 0; while (c) { c++; l++; }; return l; } + { iter_t c (*thiz()); unsigned l = 0; while (c) { c++; l++; } return l; } /* Advancing: Implement __next__(), or __forward__() if random-access. */ - void __next__ () { thiz()->forward (1); } - void __forward__ (unsigned n) { while (n--) thiz()->next (); } + void __next__ () { *thiz() += 1; } + void __forward__ (unsigned n) { while (*thiz() && n--) ++*thiz(); } /* Rewinding: Implement __prev__() or __rewind__() if bidirectional. */ - void __prev__ () { thiz()->rewind (1); } - void __rewind__ (unsigned n) { while (n--) thiz()->prev (); } + void __prev__ () { *thiz() -= 1; } + void __rewind__ (unsigned n) { while (*thiz() && n--) --*thiz(); } + + /* Range-based for: Implement __end__() if can be done faster, + * and operator!=. */ + iter_t __end__ () const + { + if (thiz()->is_random_access_iterator) + return *thiz() + thiz()->len (); + /* Above expression loops twice. Following loops once. */ + auto it = *thiz(); + while (it) ++it; + return it; + } + + protected: + hb_iter_fallback_mixin_t () = default; + hb_iter_fallback_mixin_t (const hb_iter_fallback_mixin_t &o HB_UNUSED) = default; + hb_iter_fallback_mixin_t (hb_iter_fallback_mixin_t &&o HB_UNUSED) = default; + hb_iter_fallback_mixin_t& operator = (const hb_iter_fallback_mixin_t &o HB_UNUSED) = default; + hb_iter_fallback_mixin_t& operator = (hb_iter_fallback_mixin_t &&o HB_UNUSED) = default; +}; + +template +struct hb_iter_with_fallback_t : + hb_iter_t, + hb_iter_fallback_mixin_t +{ + protected: + hb_iter_with_fallback_t () = default; + hb_iter_with_fallback_t (const hb_iter_with_fallback_t &o HB_UNUSED) = default; + hb_iter_with_fallback_t (hb_iter_with_fallback_t &&o HB_UNUSED) = default; + hb_iter_with_fallback_t& operator = (const hb_iter_with_fallback_t &o HB_UNUSED) = default; + hb_iter_with_fallback_t& operator = (hb_iter_with_fallback_t &&o HB_UNUSED) = default; +}; + +/* + * Meta-programming predicates. + */ + +/* hb_is_iterator() / hb_is_iterator_of() */ + +template +struct hb_is_iterator_of +{ + template + static hb_true_type impl (hb_priority<2>, hb_iter_t> *); + static hb_false_type impl (hb_priority<0>, const void *); + + public: + static constexpr bool value = decltype (impl (hb_prioritize, hb_declval (Iter*)))::value; +}; +#define hb_is_iterator_of(Iter, Item) hb_is_iterator_of::value +#define hb_is_iterator(Iter) hb_is_iterator_of (Iter, typename Iter::item_t) + +/* hb_is_iterable() */ + +template +struct hb_is_iterable +{ + private: + + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (U).iter (), hb_true_type ()); + + template + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_iterable(Iterable) hb_is_iterable::value + +/* hb_is_source_of() / hb_is_sink_of() */ + +template +struct hb_is_source_of +{ + private: + template >))> + static hb_true_type impl (hb_priority<2>); + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (Iter2) >> hb_declval (Item &), hb_true_type ()); + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_source_of(Iter, Item) hb_is_source_of::value + +template +struct hb_is_sink_of +{ + private: + template ))> + static hb_true_type impl (hb_priority<2>); + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (Iter2) << hb_declval (Item), hb_true_type ()); + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_sink_of(Iter, Item) hb_is_sink_of::value + +/* This is commonly used, so define: */ +#define hb_is_sorted_source_of(Iter, Item) \ + (hb_is_source_of(Iter, Item) && Iter::is_sorted_iterator) + + +/* Range-based 'for' for iterables. */ + +template +static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ()) + +template +static inline auto end (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).end ()) + +/* begin()/end() are NOT looked up non-ADL. So each namespace must declare them. + * Do it for namespace OT. */ +namespace OT { + +template +static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ()) + +template +static inline auto end (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).end ()) + +} + + +/* + * Adaptors, combiners, etc. + */ + +template +static inline auto +operator | (Lhs&& lhs, Rhs&& rhs) HB_AUTO_RETURN (hb_forward (rhs) (hb_forward (lhs))) + +/* hb_map(), hb_filter(), hb_reduce() */ + +enum class hb_function_sortedness_t { + NOT_SORTED, + RETAINS_SORTING, + SORTED, +}; + +template +struct hb_map_iter_t : + hb_iter_t, + decltype (hb_get (hb_declval (Proj), *hb_declval (Iter)))> +{ + hb_map_iter_t (const Iter& it, Proj f_) : it (it), f (f_) {} + + typedef decltype (hb_get (hb_declval (Proj), *hb_declval (Iter))) __item_t__; + static constexpr bool is_random_access_iterator = Iter::is_random_access_iterator; + static constexpr bool is_sorted_iterator = + Sorted == hb_function_sortedness_t::SORTED ? true : + Sorted == hb_function_sortedness_t::RETAINS_SORTING ? Iter::is_sorted_iterator : + false; + __item_t__ __item__ () const { return hb_get (f.get (), *it); } + __item_t__ __item_at__ (unsigned i) const { return hb_get (f.get (), it[i]); } + bool __more__ () const { return bool (it); } + unsigned __len__ () const { return it.len (); } + void __next__ () { ++it; } + void __forward__ (unsigned n) { it += n; } + void __prev__ () { --it; } + void __rewind__ (unsigned n) { it -= n; } + hb_map_iter_t __end__ () const { return hb_map_iter_t (it.end (), f); } + bool operator != (const hb_map_iter_t& o) const + { return it != o.it; } + + private: + Iter it; + hb_reference_wrapper f; +}; + +template +struct hb_map_iter_factory_t +{ + hb_map_iter_factory_t (Proj f) : f (f) {} + + template + hb_map_iter_t + operator () (Iter it) + { return hb_map_iter_t (it, f); } + + private: + Proj f; +}; +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map); +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map_retains_sorting); +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map_sorted); + +template +struct hb_filter_iter_t : + hb_iter_with_fallback_t, + typename Iter::item_t> +{ + hb_filter_iter_t (const Iter& it_, Pred p_, Proj f_) : it (it_), p (p_), f (f_) + { while (it && !hb_has (p.get (), hb_get (f.get (), *it))) ++it; } + + typedef typename Iter::item_t __item_t__; + static constexpr bool is_sorted_iterator = Iter::is_sorted_iterator; + __item_t__ __item__ () const { return *it; } + bool __more__ () const { return bool (it); } + void __next__ () { do ++it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); } + void __prev__ () { do --it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); } + hb_filter_iter_t __end__ () const { return hb_filter_iter_t (it.end (), p, f); } + bool operator != (const hb_filter_iter_t& o) const + { return it != o.it; } + + private: + Iter it; + hb_reference_wrapper p; + hb_reference_wrapper f; +}; +template +struct hb_filter_iter_factory_t +{ + hb_filter_iter_factory_t (Pred p, Proj f) : p (p), f (f) {} + + template + hb_filter_iter_t + operator () (Iter it) + { return hb_filter_iter_t (it, p, f); } + + private: + Pred p; + Proj f; +}; +struct +{ + template + hb_filter_iter_factory_t + operator () (Pred&& p = hb_identity, Proj&& f = hb_identity) const + { return hb_filter_iter_factory_t (p, f); } +} +HB_FUNCOBJ (hb_filter); + +template +struct hb_reduce_t +{ + hb_reduce_t (Redu r, InitT init_value) : r (r), init_value (init_value) {} + + template > + AccuT + operator () (Iter it) + { + AccuT value = init_value; + for (; it; ++it) + value = r (value, *it); + return value; + } + + private: + Redu r; + InitT init_value; +}; +struct +{ + template + hb_reduce_t + operator () (Redu&& r, InitT init_value) const + { return hb_reduce_t (r, init_value); } +} +HB_FUNCOBJ (hb_reduce); + + +/* hb_zip() */ - /* Random access: Return true if item_at(), len(), forward() are fast. */ - bool __random_access__ () const { return false; } +template +struct hb_zip_iter_t : + hb_iter_t, + hb_pair_t> +{ + hb_zip_iter_t () {} + hb_zip_iter_t (const A& a, const B& b) : a (a), b (b) {} + + typedef hb_pair_t __item_t__; + static constexpr bool is_random_access_iterator = + A::is_random_access_iterator && + B::is_random_access_iterator; + /* Note. The following categorization is only valid if A is strictly sorted, + * ie. does NOT have duplicates. Previously I tried to categorize sortedness + * more granularly, see commits: + * + * 513762849a683914fc266a17ddf38f133cccf072 + * 4d3cf2adb669c345cc43832d11689271995e160a + * + * However, that was not enough, since hb_sorted_array_t, hb_sorted_vector_t, + * SortedArrayOf, etc all needed to be updated to add more variants. At that + * point I saw it not worth the effort, and instead we now deem all sorted + * collections as essentially strictly-sorted for the purposes of zip. + * + * The above assumption is not as bad as it sounds. Our "sorted" comes with + * no guarantees. It's just a contract, put in place to help you remember, + * and think about, whether an iterator you receive is expected to be + * sorted or not. As such, it's not perfect by definition, and should not + * be treated so. The inaccuracy here just errs in the direction of being + * more permissive, so your code compiles instead of erring on the side of + * marking your zipped iterator unsorted in which case your code won't + * compile. + * + * This semantical limitation does NOT affect logic in any other place I + * know of as of this writing. + */ + static constexpr bool is_sorted_iterator = A::is_sorted_iterator; + + __item_t__ __item__ () const { return __item_t__ (*a, *b); } + __item_t__ __item_at__ (unsigned i) const { return __item_t__ (a[i], b[i]); } + bool __more__ () const { return bool (a) && bool (b); } + unsigned __len__ () const { return hb_min (a.len (), b.len ()); } + void __next__ () { ++a; ++b; } + void __forward__ (unsigned n) { a += n; b += n; } + void __prev__ () { --a; --b; } + void __rewind__ (unsigned n) { a -= n; b -= n; } + hb_zip_iter_t __end__ () const { return hb_zip_iter_t (a.end (), b.end ()); } + /* Note, we should stop if ANY of the iters reaches end. As such two compare + * unequal if both items are unequal, NOT if either is unequal. */ + bool operator != (const hb_zip_iter_t& o) const + { return a != o.a && b != o.b; } + + private: + A a; + B b; +}; +struct +{ HB_PARTIALIZE(2); + template + hb_zip_iter_t, hb_iter_type> + operator () (A&& a, B&& b) const + { return hb_zip_iter_t, hb_iter_type> (hb_iter (a), hb_iter (b)); } +} +HB_FUNCOBJ (hb_zip); + +/* hb_apply() */ + +template +struct hb_apply_t +{ + hb_apply_t (Appl a) : a (a) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + (void) hb_invoke (a, *it); + } + + private: + Appl a; }; +struct +{ + template hb_apply_t + operator () (Appl&& a) const + { return hb_apply_t (a); } + template hb_apply_t + operator () (Appl *a) const + { return hb_apply_t (*a); } +} +HB_FUNCOBJ (hb_apply); + +/* hb_range()/hb_iota()/hb_repeat() */ + +template +struct hb_range_iter_t : + hb_iter_t, T> +{ + hb_range_iter_t (T start, T end_, S step) : v (start), end_ (end_for (start, end_, step)), step (step) {} + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return hb_ridentity (v); } + __item_t__ __item_at__ (unsigned j) const { return v + j * step; } + bool __more__ () const { return v != end_; } + unsigned __len__ () const { return !step ? UINT_MAX : (end_ - v) / step; } + void __next__ () { v += step; } + void __forward__ (unsigned n) { v += n * step; } + void __prev__ () { v -= step; } + void __rewind__ (unsigned n) { v -= n * step; } + hb_range_iter_t __end__ () const { return hb_range_iter_t (end_, end_, step); } + bool operator != (const hb_range_iter_t& o) const + { return v != o.v; } + + private: + static inline T end_for (T start, T end_, S step) + { + if (!step) + return end_; + auto res = (end_ - start) % step; + if (!res) + return end_; + end_ += step - res; + return end_; + } + + private: + T v; + T end_; + S step; +}; +struct +{ + template hb_range_iter_t + operator () (T end = (unsigned) -1) const + { return hb_range_iter_t (0, end, 1u); } + + template hb_range_iter_t + operator () (T start, T end, S step = 1u) const + { return hb_range_iter_t (start, end, step); } +} +HB_FUNCOBJ (hb_range); + +template +struct hb_iota_iter_t : + hb_iter_with_fallback_t, T> +{ + hb_iota_iter_t (T start, S step) : v (start), step (step) {} + + private: + + template + auto + inc (hb_type_identity s, hb_priority<1>) + -> hb_void_t (s), hb_declval ()))> + { v = hb_invoke (hb_forward (s), v); } + + void + inc (S s, hb_priority<0>) + { v += s; } + + public: + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return hb_ridentity (v); } + bool __more__ () const { return true; } + unsigned __len__ () const { return UINT_MAX; } + void __next__ () { inc (step, hb_prioritize); } + void __prev__ () { v -= step; } + hb_iota_iter_t __end__ () const { return *this; } + bool operator != (const hb_iota_iter_t& o) const { return true; } + + private: + T v; + S step; +}; +struct +{ + template hb_iota_iter_t + operator () (T start = 0u, S step = 1u) const + { return hb_iota_iter_t (start, step); } +} +HB_FUNCOBJ (hb_iota); -/* Functions operating on iterators or iteratables. */ +template +struct hb_repeat_iter_t : + hb_iter_t, T> +{ + hb_repeat_iter_t (T value) : v (value) {} + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return v; } + __item_t__ __item_at__ (unsigned j) const { return v; } + bool __more__ () const { return true; } + unsigned __len__ () const { return UINT_MAX; } + void __next__ () {} + void __forward__ (unsigned) {} + void __prev__ () {} + void __rewind__ (unsigned) {} + hb_repeat_iter_t __end__ () const { return *this; } + bool operator != (const hb_repeat_iter_t& o) const { return true; } + + private: + T v; +}; +struct +{ + template hb_repeat_iter_t + operator () (T value) const + { return hb_repeat_iter_t (value); } +} +HB_FUNCOBJ (hb_repeat); + +/* hb_enumerate()/hb_take() */ + +struct +{ + template + auto operator () (Iterable&& it, Index start = 0u) const HB_AUTO_RETURN + ( hb_zip (hb_iota (start), it) ) +} +HB_FUNCOBJ (hb_enumerate); + +struct +{ HB_PARTIALIZE(2); + template + auto operator () (Iterable&& it, unsigned count) const HB_AUTO_RETURN + ( hb_zip (hb_range (count), it) | hb_map (hb_second) ) + + /* Specialization arrays. */ + + template inline hb_array_t + operator () (hb_array_t array, unsigned count) const + { return array.sub_array (0, count); } + + template inline hb_sorted_array_t + operator () (hb_sorted_array_t array, unsigned count) const + { return array.sub_array (0, count); } +} +HB_FUNCOBJ (hb_take); + +struct +{ HB_PARTIALIZE(2); + template + auto operator () (Iter it, unsigned count) const HB_AUTO_RETURN + ( + + hb_iota (it, hb_add (count)) + | hb_map (hb_take (count)) + | hb_take ((hb_len (it) + count - 1) / count) + ) +} +HB_FUNCOBJ (hb_chop); + +/* hb_sink() */ + +template +struct hb_sink_t +{ + hb_sink_t (Sink s) : s (s) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + s << *it; + } + + private: + Sink s; +}; +struct +{ + template hb_sink_t + operator () (Sink&& s) const + { return hb_sink_t (s); } + + template hb_sink_t + operator () (Sink *s) const + { return hb_sink_t (*s); } +} +HB_FUNCOBJ (hb_sink); + +/* hb-drain: hb_sink to void / blackhole / /dev/null. */ + +struct +{ + template + void operator () (Iter it) const + { + for (; it; ++it) + (void) *it; + } +} +HB_FUNCOBJ (hb_drain); + +/* hb_unzip(): unzip and sink to two sinks. */ + +template +struct hb_unzip_t +{ + hb_unzip_t (Sink1 s1, Sink2 s2) : s1 (s1), s2 (s2) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + { + const auto &v = *it; + s1 << v.first; + s2 << v.second; + } + } + + private: + Sink1 s1; + Sink2 s2; +}; +struct +{ + template hb_unzip_t + operator () (Sink1&& s1, Sink2&& s2) const + { return hb_unzip_t (s1, s2); } + + template hb_unzip_t + operator () (Sink1 *s1, Sink2 *s2) const + { return hb_unzip_t (*s1, *s2); } +} +HB_FUNCOBJ (hb_unzip); + + +/* hb-all, hb-any, hb-none. */ + +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (!hb_match (hb_forward (p), hb_get (hb_forward (f), *it))) + return false; + return true; + } +} +HB_FUNCOBJ (hb_all); +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (hb_match (hb_forward (p), hb_get (hb_forward (f), *it))) + return true; + return false; + } +} +HB_FUNCOBJ (hb_any); +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (hb_match (hb_forward (p), hb_get (hb_forward (f), *it))) + return false; + return true; + } +} +HB_FUNCOBJ (hb_none); + +/* + * Algorithms operating on iterators. + */ -template inline void -hb_fill (const C& c, const V &v) +template +inline void +hb_fill (C& c, const V &v) { - for (typename C::iter_t i (c); i; i++) - hb_assign (*i, v); + for (auto i = hb_iter (c); i; i++) + *i = v; } -template inline bool -hb_copy (hb_iter_t &id, hb_iter_t &is) +template +inline void +hb_copy (S&& is, D&& id) { - for (; id && is; ++id, ++is) - *id = *is; - return !is; + hb_iter (is) | hb_sink (id); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-kern.hh b/src/java.desktop/share/native/libharfbuzz/hb-kern.hh index 43d70d7f221..42e54936410 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-kern.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-kern.hh @@ -52,8 +52,7 @@ struct hb_kern_machine_t OT::hb_ot_apply_context_t c (1, font, buffer); c.set_lookup_mask (kern_mask); c.set_lookup_props (OT::LookupFlag::IgnoreMarks); - OT::hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c.iter_input; - skippy_iter.init (&c); + auto &skippy_iter = c.iter_input; bool horizontal = HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction); unsigned int count = buffer->len; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh b/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh index 2ae288494f4..0c820e7429f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh @@ -32,30 +32,15 @@ #include "hb.hh" #include "hb-blob.hh" -#include "hb-array.hh" -#include "hb-vector.hh" +#include "hb-dispatch.hh" +#include "hb-sanitize.hh" +#include "hb-serialize.hh" /* * Casts */ -/* Cast to struct T, reference to reference */ -template -static inline const Type& CastR(const TObject &X) -{ return reinterpret_cast (X); } -template -static inline Type& CastR(TObject &X) -{ return reinterpret_cast (X); } - -/* Cast to struct T, pointer to pointer */ -template -static inline const Type* CastP(const TObject *X) -{ return reinterpret_cast (X); } -template -static inline Type* CastP(TObject *X) -{ return reinterpret_cast (X); } - /* StructAtOffset(P,Ofs) returns the struct T& that is placed at memory * location pointed to by P plus Ofs bytes. */ template @@ -69,7 +54,7 @@ static inline const Type& StructAtOffsetUnaligned(const void *P, unsigned int of { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" - return * reinterpret_cast ((char *) P + offset); + return * reinterpret_cast ((const char *) P + offset); #pragma GCC diagnostic pop } template @@ -134,7 +119,7 @@ static inline Type& StructAfter(TObject &X) #define DEFINE_SIZE_ARRAY(size, array) \ DEFINE_COMPILES_ASSERTION ((void) (array)[0].static_size) \ - DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + VAR * sizeof ((array)[0])) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + (HB_VAR_ARRAY+0) * sizeof ((array)[0])) \ static constexpr unsigned null_size = (size); \ static constexpr unsigned min_size = (size) @@ -143,615 +128,6 @@ static inline Type& StructAfter(TObject &X) DEFINE_SIZE_ARRAY(size, array) -/* - * Dispatch - */ - -template -struct hb_dispatch_context_t -{ - static constexpr unsigned max_debug_depth = MaxDebugDepth; - typedef Return return_t; - template - bool may_dispatch (const T *obj HB_UNUSED, const F *format HB_UNUSED) { return true; } - static return_t no_dispatch_return_value () { return Context::default_return_value (); } - static bool stop_sublookup_iteration (const return_t r HB_UNUSED) { return false; } -}; - - -/* - * Sanitize - * - * - * === Introduction === - * - * The sanitize machinery is at the core of our zero-cost font loading. We - * mmap() font file into memory and create a blob out of it. Font subtables - * are returned as a readonly sub-blob of the main font blob. These table - * blobs are then sanitized before use, to ensure invalid memory access does - * not happen. The toplevel sanitize API use is like, eg. to load the 'head' - * table: - * - * hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table (face); - * - * The blob then can be converted to a head table struct with: - * - * const head *head_table = head_blob->as (); - * - * What the reference_table does is, to call hb_face_reference_table() to load - * the table blob, sanitize it and return either the sanitized blob, or empty - * blob if sanitization failed. The blob->as() function returns the null - * object of its template type argument if the blob is empty. Otherwise, it - * just casts the blob contents to the desired type. - * - * Sanitizing a blob of data with a type T works as follows (with minor - * simplification): - * - * - Cast blob content to T*, call sanitize() method of it, - * - If sanitize succeeded, return blob. - * - Otherwise, if blob is not writable, try making it writable, - * or copy if cannot be made writable in-place, - * - Call sanitize() again. Return blob if sanitize succeeded. - * - Return empty blob otherwise. - * - * - * === The sanitize() contract === - * - * The sanitize() method of each object type shall return true if it's safe to - * call other methods of the object, and false otherwise. - * - * Note that what sanitize() checks for might align with what the specification - * describes as valid table data, but does not have to be. In particular, we - * do NOT want to be pedantic and concern ourselves with validity checks that - * are irrelevant to our use of the table. On the contrary, we want to be - * lenient with error handling and accept invalid data to the extent that it - * does not impose extra burden on us. - * - * Based on the sanitize contract, one can see that what we check for depends - * on how we use the data in other table methods. Ie. if other table methods - * assume that offsets do NOT point out of the table data block, then that's - * something sanitize() must check for (GSUB/GPOS/GDEF/etc work this way). On - * the other hand, if other methods do such checks themselves, then sanitize() - * does not have to bother with them (glyf/local work this way). The choice - * depends on the table structure and sanitize() performance. For example, to - * check glyf/loca offsets in sanitize() would cost O(num-glyphs). We try hard - * to avoid such costs during font loading. By postponing such checks to the - * actual glyph loading, we reduce the sanitize cost to O(1) and total runtime - * cost to O(used-glyphs). As such, this is preferred. - * - * The same argument can be made re GSUB/GPOS/GDEF, but there, the table - * structure is so complicated that by checking all offsets at sanitize() time, - * we make the code much simpler in other methods, as offsets and referenced - * objects do not need to be validated at each use site. - */ - -/* This limits sanitizing time on really broken fonts. */ -#ifndef HB_SANITIZE_MAX_EDITS -#define HB_SANITIZE_MAX_EDITS 32 -#endif -#ifndef HB_SANITIZE_MAX_OPS_FACTOR -#define HB_SANITIZE_MAX_OPS_FACTOR 8 -#endif -#ifndef HB_SANITIZE_MAX_OPS_MIN -#define HB_SANITIZE_MAX_OPS_MIN 16384 -#endif -#ifndef HB_SANITIZE_MAX_OPS_MAX -#define HB_SANITIZE_MAX_OPS_MAX 0x3FFFFFFF -#endif - -struct hb_sanitize_context_t : - hb_dispatch_context_t -{ - hb_sanitize_context_t () : - debug_depth (0), - start (nullptr), end (nullptr), - max_ops (0), - writable (false), edit_count (0), - blob (nullptr), - num_glyphs (65536), - num_glyphs_set (false) {} - - const char *get_name () { return "SANITIZE"; } - template - bool may_dispatch (const T *obj HB_UNUSED, const F *format) - { return format->sanitize (this); } - template - return_t dispatch (const T &obj) { return obj.sanitize (this); } - static return_t default_return_value () { return true; } - static return_t no_dispatch_return_value () { return false; } - bool stop_sublookup_iteration (const return_t r) const { return !r; } - - void init (hb_blob_t *b) - { - this->blob = hb_blob_reference (b); - this->writable = false; - } - - void set_num_glyphs (unsigned int num_glyphs_) - { - num_glyphs = num_glyphs_; - num_glyphs_set = true; - } - unsigned int get_num_glyphs () { return num_glyphs; } - - void set_max_ops (int max_ops_) { max_ops = max_ops_; } - - template - void set_object (const T *obj) - { - reset_object (); - - if (!obj) return; - - const char *obj_start = (const char *) obj; - if (unlikely (obj_start < this->start || this->end <= obj_start)) - this->start = this->end = nullptr; - else - { - this->start = obj_start; - this->end = obj_start + MIN (this->end - obj_start, obj->get_size ()); - } - } - - void reset_object () - { - this->start = this->blob->data; - this->end = this->start + this->blob->length; - assert (this->start <= this->end); /* Must not overflow. */ - } - - void start_processing () - { - reset_object (); - this->max_ops = MAX ((unsigned int) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR, - (unsigned) HB_SANITIZE_MAX_OPS_MIN); - this->edit_count = 0; - this->debug_depth = 0; - - DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1, - "start [%p..%p] (%lu bytes)", - this->start, this->end, - (unsigned long) (this->end - this->start)); - } - - void end_processing () - { - DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1, - "end [%p..%p] %u edit requests", - this->start, this->end, this->edit_count); - - hb_blob_destroy (this->blob); - this->blob = nullptr; - this->start = this->end = nullptr; - } - - bool check_range (const void *base, - unsigned int len) const - { - const char *p = (const char *) base; - bool ok = this->start <= p && - p <= this->end && - (unsigned int) (this->end - p) >= len && - this->max_ops-- > 0; - - DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, - "check_range [%p..%p] (%d bytes) in [%p..%p] -> %s", - p, p + len, len, - this->start, this->end, - ok ? "OK" : "OUT-OF-RANGE"); - - return likely (ok); - } - - template - bool check_range (const T *base, - unsigned int a, - unsigned int b) const - { - return !hb_unsigned_mul_overflows (a, b) && - this->check_range (base, a * b); - } - - template - bool check_range (const T *base, - unsigned int a, - unsigned int b, - unsigned int c) const - { - return !hb_unsigned_mul_overflows (a, b) && - this->check_range (base, a * b, c); - } - - template - bool check_array (const T *base, unsigned int len) const - { - return this->check_range (base, len, hb_static_size (T)); - } - - template - bool check_array (const T *base, - unsigned int a, - unsigned int b) const - { - return this->check_range (base, a, b, hb_static_size (T)); - } - - template - bool check_struct (const Type *obj) const - { return likely (this->check_range (obj, obj->min_size)); } - - bool may_edit (const void *base, unsigned int len) - { - if (this->edit_count >= HB_SANITIZE_MAX_EDITS) - return false; - - const char *p = (const char *) base; - this->edit_count++; - - DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, - "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s", - this->edit_count, - p, p + len, len, - this->start, this->end, - this->writable ? "GRANTED" : "DENIED"); - - return this->writable; - } - - template - bool try_set (const Type *obj, const ValueType &v) - { - if (this->may_edit (obj, hb_static_size (Type))) - { - hb_assign (* const_cast (obj), v); - return true; - } - return false; - } - - template - hb_blob_t *sanitize_blob (hb_blob_t *blob) - { - bool sane; - - init (blob); - - retry: - DEBUG_MSG_FUNC (SANITIZE, start, "start"); - - start_processing (); - - if (unlikely (!start)) - { - end_processing (); - return blob; - } - - Type *t = CastP (const_cast (start)); - - sane = t->sanitize (this); - if (sane) - { - if (edit_count) - { - DEBUG_MSG_FUNC (SANITIZE, start, "passed first round with %d edits; going for second round", edit_count); - - /* sanitize again to ensure no toe-stepping */ - edit_count = 0; - sane = t->sanitize (this); - if (edit_count) { - DEBUG_MSG_FUNC (SANITIZE, start, "requested %d edits in second round; FAILLING", edit_count); - sane = false; - } - } - } - else - { - if (edit_count && !writable) { - start = hb_blob_get_data_writable (blob, nullptr); - end = start + blob->length; - - if (start) - { - writable = true; - /* ok, we made it writable by relocating. try again */ - DEBUG_MSG_FUNC (SANITIZE, start, "retry"); - goto retry; - } - } - } - - end_processing (); - - DEBUG_MSG_FUNC (SANITIZE, start, sane ? "PASSED" : "FAILED"); - if (sane) - { - hb_blob_make_immutable (blob); - return blob; - } - else - { - hb_blob_destroy (blob); - return hb_blob_get_empty (); - } - } - - template - hb_blob_t *reference_table (const hb_face_t *face, hb_tag_t tableTag = Type::tableTag) - { - if (!num_glyphs_set) - set_num_glyphs (hb_face_get_glyph_count (face)); - return sanitize_blob (hb_face_reference_table (face, tableTag)); - } - - mutable unsigned int debug_depth; - const char *start, *end; - mutable int max_ops; - private: - bool writable; - unsigned int edit_count; - hb_blob_t *blob; - unsigned int num_glyphs; - bool num_glyphs_set; -}; - -struct hb_sanitize_with_object_t -{ - template - hb_sanitize_with_object_t (hb_sanitize_context_t *c, - const T& obj) : c (c) - { c->set_object (obj); } - ~hb_sanitize_with_object_t () - { c->reset_object (); } - - private: - hb_sanitize_context_t *c; -}; - - -/* - * Serialize - */ - -struct hb_serialize_context_t -{ - hb_serialize_context_t (void *start_, unsigned int size) - { - this->start = (char *) start_; - this->end = this->start + size; - reset (); - } - - bool in_error () const { return !this->successful; } - - void reset () - { - this->successful = true; - this->head = this->start; - this->debug_depth = 0; - } - - bool propagate_error (bool e) - { return this->successful = this->successful && e; } - template bool propagate_error (const T &obj) - { return this->successful = this->successful && !obj.in_error (); } - template bool propagate_error (const T *obj) - { return this->successful = this->successful && !obj->in_error (); } - template bool propagate_error (T1 &o1, T2 &o2) - { return propagate_error (o1) && propagate_error (o2); } - template bool propagate_error (T1 *o1, T2 *o2) - { return propagate_error (o1) && propagate_error (o2); } - template - bool propagate_error (T1 &o1, T2 &o2, T3 &o3) - { return propagate_error (o1) && propagate_error (o2, o3); } - template - bool propagate_error (T1 *o1, T2 *o2, T3 *o3) - { return propagate_error (o1) && propagate_error (o2, o3); } - - /* To be called around main operation. */ - template - Type *start_serialize () - { - DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1, - "start [%p..%p] (%lu bytes)", - this->start, this->end, - (unsigned long) (this->end - this->start)); - - return start_embed (); - } - void end_serialize () - { - DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1, - "end [%p..%p] serialized %d bytes; %s", - this->start, this->end, - (int) (this->head - this->start), - this->successful ? "successful" : "UNSUCCESSFUL"); - } - - unsigned int length () const { return this->head - this->start; } - - void align (unsigned int alignment) - { - unsigned int l = length () % alignment; - if (l) - allocate_size (alignment - l); - } - - template - Type *start_embed (const Type *_ HB_UNUSED = nullptr) const - { - Type *ret = reinterpret_cast (this->head); - return ret; - } - - template - Type *allocate_size (unsigned int size) - { - if (unlikely (!this->successful || this->end - this->head < ptrdiff_t (size))) { - this->successful = false; - return nullptr; - } - memset (this->head, 0, size); - char *ret = this->head; - this->head += size; - return reinterpret_cast (ret); - } - - template - Type *allocate_min () - { - return this->allocate_size (Type::min_size); - } - - template - Type *embed (const Type &obj) - { - unsigned int size = obj.get_size (); - Type *ret = this->allocate_size (size); - if (unlikely (!ret)) return nullptr; - memcpy (ret, &obj, size); - return ret; - } - template - hb_serialize_context_t &operator << (const Type &obj) { embed (obj); return *this; } - - template - Type *extend_size (Type &obj, unsigned int size) - { - assert (this->start <= (char *) &obj); - assert ((char *) &obj <= this->head); - assert ((char *) &obj + size >= this->head); - if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return nullptr; - return reinterpret_cast (&obj); - } - - template - Type *extend_min (Type &obj) { return extend_size (obj, obj.min_size); } - - template - Type *extend (Type &obj) { return extend_size (obj, obj.get_size ()); } - - /* Output routines. */ - template - Type *copy () const - { - assert (this->successful); - unsigned int len = this->head - this->start; - void *p = malloc (len); - if (p) - memcpy (p, this->start, len); - return reinterpret_cast (p); - } - hb_bytes_t copy_bytes () const - { - assert (this->successful); - unsigned int len = this->head - this->start; - void *p = malloc (len); - if (p) - memcpy (p, this->start, len); - else - return hb_bytes_t (); - return hb_bytes_t ((char *) p, len); - } - hb_blob_t *copy_blob () const - { - assert (this->successful); - return hb_blob_create (this->start, - this->head - this->start, - HB_MEMORY_MODE_DUPLICATE, - nullptr, nullptr); - } - - public: - unsigned int debug_depth; - char *start, *end, *head; - bool successful; -}; - - - -/* - * Big-endian integers. - */ - -template struct BEInt; - -template -struct BEInt -{ - public: - void set (Type V) { v = V; } - operator Type () const { return v; } - private: uint8_t v; -}; -template -struct BEInt -{ - public: - void set (Type V) - { - v[0] = (V >> 8) & 0xFF; - v[1] = (V ) & 0xFF; - } - operator Type () const - { -#if ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \ - defined(__BYTE_ORDER) && \ - (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN) - /* Spoon-feed the compiler a big-endian integer with alignment 1. - * https://github.com/harfbuzz/harfbuzz/pull/1398 */ - struct __attribute__((packed)) packed_uint16_t { uint16_t v; }; -#if __BYTE_ORDER == __LITTLE_ENDIAN - return __builtin_bswap16 (((packed_uint16_t *) this)->v); -#else /* __BYTE_ORDER == __BIG_ENDIAN */ - return ((packed_uint16_t *) this)->v; -#endif -#endif - return (v[0] << 8) - + (v[1] ); - } - private: uint8_t v[2]; -}; -template -struct BEInt -{ - public: - void set (Type V) - { - v[0] = (V >> 16) & 0xFF; - v[1] = (V >> 8) & 0xFF; - v[2] = (V ) & 0xFF; - } - operator Type () const - { - return (v[0] << 16) - + (v[1] << 8) - + (v[2] ); - } - private: uint8_t v[3]; -}; -template -struct BEInt -{ - public: - typedef Type type; - void set (Type V) - { - v[0] = (V >> 24) & 0xFF; - v[1] = (V >> 16) & 0xFF; - v[2] = (V >> 8) & 0xFF; - v[3] = (V ) & 0xFF; - } - operator Type () const - { - return (v[0] << 24) - + (v[1] << 16) - + (v[2] << 8) - + (v[3] ); - } - private: uint8_t v[4]; -}; - /* * Lazy loaders. @@ -814,7 +190,7 @@ struct hb_lazy_loader_t : hb_data_wrapper_t const Returned * operator -> () const { return get (); } const Returned & operator * () const { return *get (); } - explicit_operator bool () const + explicit operator bool () const { return get_stored () != Funcs::get_null (); } template operator const C * () const { return get (); } @@ -858,7 +234,7 @@ struct hb_lazy_loader_t : hb_data_wrapper_t static Returned* convert (Stored *p) { return p; } /* By default null/init/fini the object. */ - static const Stored* get_null () { return &Null(Stored); } + static const Stored* get_null () { return &Null (Stored); } static Stored *create (Data *data) { Stored *p = (Stored *) calloc (1, sizeof (Stored)); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-map.cc b/src/java.desktop/share/native/libharfbuzz/hb-map.cc index 3fccfa0fa01..114efcb3d75 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-map.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-map.cc @@ -69,7 +69,7 @@ hb_map_create () hb_map_t * hb_map_get_empty () { - return const_cast (&Null(hb_map_t)); + return const_cast (&Null (hb_map_t)); } /** diff --git a/src/java.desktop/share/native/libharfbuzz/hb-map.hh b/src/java.desktop/share/native/libharfbuzz/hb-map.hh index c04a8dff966..a5c997c32e8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-map.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-map.hh @@ -30,31 +30,36 @@ #include "hb.hh" -template -inline uint32_t Hash (const T &v) -{ - /* Knuth's multiplicative method: */ - return (uint32_t) v * 2654435761u; -} - - /* - * hb_map_t + * hb_hashmap_t */ -struct hb_map_t +template +struct hb_hashmap_t { - HB_NO_COPY_ASSIGN (hb_map_t); - hb_map_t () { init (); } - ~hb_map_t () { fini (); } + HB_DELETE_COPY_ASSIGN (hb_hashmap_t); + hb_hashmap_t () { init (); } + ~hb_hashmap_t () { fini (); } + + static_assert (hb_is_integral (K) || hb_is_pointer (K), ""); + static_assert (hb_is_integral (V) || hb_is_pointer (V), ""); struct item_t { - hb_codepoint_t key; - hb_codepoint_t value; - - bool is_unused () const { return key == INVALID; } - bool is_tombstone () const { return key != INVALID && value == INVALID; } + K key; + V value; + uint32_t hash; + + void clear () { key = kINVALID; value = vINVALID; hash = 0; } + + bool operator == (const K &o) { return hb_deref (key) == hb_deref (o); } + bool operator == (const item_t &o) { return *this == o.key; } + bool is_unused () const { return key == kINVALID; } + bool is_tombstone () const { return key != kINVALID && value == vINVALID; } + bool is_real () const { return key != kINVALID && value != vINVALID; } + hb_pair_t get_pair() const { return hb_pair_t (key, value); } }; hb_object_header_t header; @@ -82,14 +87,22 @@ struct hb_map_t { free (items); items = nullptr; + population = occupancy = 0; } void fini () { - population = occupancy = 0; hb_object_fini (this); fini_shallow (); } + void reset () + { + if (unlikely (hb_object_is_immutable (this))) + return; + successful = true; + clear (); + } + bool in_error () const { return !successful; } bool resize () @@ -104,7 +117,8 @@ struct hb_map_t successful = false; return false; } - memset (new_items, 0xFF, (size_t) new_size * sizeof (item_t)); + for (auto &_ : hb_iter (new_items, new_size)) + _.clear (); unsigned int old_size = mask + 1; item_t *old_items = items; @@ -118,22 +132,96 @@ struct hb_map_t /* Insert back old items. */ if (old_items) for (unsigned int i = 0; i < old_size; i++) - if (old_items[i].key != INVALID && old_items[i].value != INVALID) - set (old_items[i].key, old_items[i].value); + if (old_items[i].is_real ()) + set_with_hash (old_items[i].key, + old_items[i].hash, + old_items[i].value); free (old_items); return true; } - void set (hb_codepoint_t key, hb_codepoint_t value) + void set (K key, V value) + { + set_with_hash (key, hb_hash (key), value); + } + + V get (K key) const + { + if (unlikely (!items)) return vINVALID; + unsigned int i = bucket_for (key); + return items[i].is_real () && items[i] == key ? items[i].value : vINVALID; + } + + void del (K key) { set (key, vINVALID); } + + /* Has interface. */ + static constexpr V SENTINEL = vINVALID; + typedef V value_t; + value_t operator [] (K k) const { return get (k); } + bool has (K k, V *vp = nullptr) const + { + V v = (*this)[k]; + if (vp) *vp = v; + return v != SENTINEL; + } + /* Projection. */ + V operator () (K k) const { return get (k); } + + void clear () + { + if (unlikely (hb_object_is_immutable (this))) + return; + if (items) + for (auto &_ : hb_iter (items, mask + 1)) + _.clear (); + + population = occupancy = 0; + } + + bool is_empty () const { return population == 0; } + + unsigned int get_population () const { return population; } + + /* + * Iterator + */ + auto iter () const HB_AUTO_RETURN + ( + + hb_array (items, mask ? mask + 1 : 0) + | hb_filter (&item_t::is_real) + | hb_map (&item_t::get_pair) + ) + auto keys () const HB_AUTO_RETURN + ( + + hb_array (items, mask ? mask + 1 : 0) + | hb_filter (&item_t::is_real) + | hb_map (&item_t::key) + | hb_map (hb_ridentity) + ) + auto values () const HB_AUTO_RETURN + ( + + hb_array (items, mask ? mask + 1 : 0) + | hb_filter (&item_t::is_real) + | hb_map (&item_t::value) + | hb_map (hb_ridentity) + ) + + /* Sink interface. */ + hb_hashmap_t& operator << (const hb_pair_t& v) + { set (v.first, v.second); return *this; } + + protected: + + void set_with_hash (K key, uint32_t hash, V value) { if (unlikely (!successful)) return; - if (unlikely (key == INVALID)) return; + if (unlikely (key == kINVALID)) return; if ((occupancy + occupancy / 2) >= mask && !resize ()) return; - unsigned int i = bucket_for (key); + unsigned int i = bucket_for_hash (key, hash); - if (value == INVALID && items[i].key != key) + if (value == vINVALID && items[i].key != key) return; /* Trying to delete non-existent key. */ if (!items[i].is_unused ()) @@ -145,55 +233,32 @@ struct hb_map_t items[i].key = key; items[i].value = value; + items[i].hash = hash; occupancy++; if (!items[i].is_tombstone ()) population++; - - } - hb_codepoint_t get (hb_codepoint_t key) const - { - if (unlikely (!items)) return INVALID; - unsigned int i = bucket_for (key); - return items[i].key == key ? items[i].value : INVALID; } - void del (hb_codepoint_t key) { set (key, INVALID); } - - bool has (hb_codepoint_t key) const - { return get (key) != INVALID; } - - hb_codepoint_t operator [] (unsigned int key) const - { return get (key); } - - static constexpr hb_codepoint_t INVALID = HB_MAP_VALUE_INVALID; - - void clear () + unsigned int bucket_for (K key) const { - memset (items, 0xFF, ((size_t) mask + 1) * sizeof (item_t)); - population = occupancy = 0; + return bucket_for_hash (key, hb_hash (key)); } - bool is_empty () const { return population == 0; } - - unsigned int get_population () const { return population; } - - protected: - - unsigned int bucket_for (hb_codepoint_t key) const + unsigned int bucket_for_hash (K key, uint32_t hash) const { - unsigned int i = Hash (key) % prime; + unsigned int i = hash % prime; unsigned int step = 0; - unsigned int tombstone = INVALID; + unsigned int tombstone = (unsigned) -1; while (!items[i].is_unused ()) { - if (items[i].key == key) + if (items[i].hash == hash && items[i] == key) return i; - if (tombstone == INVALID && items[i].is_tombstone ()) + if (tombstone == (unsigned) -1 && items[i].is_tombstone ()) tombstone = i; i = (i + ++step) & mask; } - return tombstone == INVALID ? i : tombstone; + return tombstone == (unsigned) -1 ? i : tombstone; } static unsigned int prime_for (unsigned int shift) @@ -248,5 +313,14 @@ struct hb_map_t } }; +/* + * hb_map_t + */ + +struct hb_map_t : hb_hashmap_t {}; + #endif /* HB_MAP_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-meta.hh b/src/java.desktop/share/native/libharfbuzz/hb-meta.hh new file mode 100644 index 00000000000..ea64416f0d2 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-meta.hh @@ -0,0 +1,410 @@ +/* + * Copyright © 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_META_HH +#define HB_META_HH + +#include "hb.hh" + + +/* + * C++ template meta-programming & fundamentals used with them. + */ + +/* Void! For when we need a expression-type of void. */ +struct hb_empty_t {}; + +/* https://en.cppreference.com/w/cpp/types/void_t */ +template struct _hb_void_t { typedef void type; }; +template using hb_void_t = typename _hb_void_t::type; + +template struct _hb_head_t { typedef Head type; }; +template using hb_head_t = typename _hb_head_t::type; + +template struct hb_integral_constant { static constexpr T value = v; }; +template using hb_bool_constant = hb_integral_constant; +using hb_true_type = hb_bool_constant; +using hb_false_type = hb_bool_constant; + + +/* Basic type SFINAE. */ + +template struct hb_enable_if {}; +template struct hb_enable_if { typedef T type; }; +#define hb_enable_if(Cond) typename hb_enable_if<(Cond)>::type* = nullptr +/* Concepts/Requires alias: */ +#define hb_requires(Cond) hb_enable_if((Cond)) + +template struct hb_is_same : hb_false_type {}; +template struct hb_is_same : hb_true_type {}; +#define hb_is_same(T, T2) hb_is_same::value + +/* Function overloading SFINAE and priority. */ + +#define HB_RETURN(Ret, E) -> hb_head_t { return (E); } +#define HB_AUTO_RETURN(E) -> decltype ((E)) { return (E); } +#define HB_VOID_RETURN(E) -> hb_void_t { (E); } + +template struct hb_priority : hb_priority {}; +template <> struct hb_priority<0> {}; +#define hb_prioritize hb_priority<16> () + +#define HB_FUNCOBJ(x) static_const x HB_UNUSED + + +template struct hb_type_identity_t { typedef T type; }; +template using hb_type_identity = typename hb_type_identity_t::type; + +struct +{ + template constexpr T* + operator () (T& arg) const + { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + /* https://en.cppreference.com/w/cpp/memory/addressof */ + return reinterpret_cast ( + &const_cast ( + reinterpret_cast (arg))); +#pragma GCC diagnostic pop + } +} +HB_FUNCOBJ (hb_addressof); + +template static inline T hb_declval (); +#define hb_declval(T) (hb_declval ()) + +template struct hb_match_const : hb_type_identity_t, hb_bool_constant{}; +template struct hb_match_const : hb_type_identity_t, hb_bool_constant {}; +template using hb_remove_const = typename hb_match_const::type; +template using hb_add_const = const T; +#define hb_is_const(T) hb_match_const::value +template struct hb_match_reference : hb_type_identity_t, hb_bool_constant{}; +template struct hb_match_reference : hb_type_identity_t, hb_bool_constant {}; +template struct hb_match_reference : hb_type_identity_t, hb_bool_constant {}; +template using hb_remove_reference = typename hb_match_reference::type; +template auto _hb_try_add_lvalue_reference (hb_priority<1>) -> hb_type_identity; +template auto _hb_try_add_lvalue_reference (hb_priority<0>) -> hb_type_identity; +template using hb_add_lvalue_reference = decltype (_hb_try_add_lvalue_reference (hb_prioritize)); +template auto _hb_try_add_rvalue_reference (hb_priority<1>) -> hb_type_identity; +template auto _hb_try_add_rvalue_reference (hb_priority<0>) -> hb_type_identity; +template using hb_add_rvalue_reference = decltype (_hb_try_add_rvalue_reference (hb_prioritize)); +#define hb_is_reference(T) hb_match_reference::value +template struct hb_match_pointer : hb_type_identity_t, hb_bool_constant{}; +template struct hb_match_pointer : hb_type_identity_t, hb_bool_constant {}; +template using hb_remove_pointer = typename hb_match_pointer::type; +template auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity*>; +template auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity; +template using hb_add_pointer = decltype (_hb_try_add_pointer (hb_prioritize)); +#define hb_is_pointer(T) hb_match_pointer::value + + +/* TODO Add feature-parity to std::decay. */ +template using hb_decay = hb_remove_const>; + + +template +struct _hb_conditional { typedef T type; }; +template +struct _hb_conditional { typedef F type; }; +template +using hb_conditional = typename _hb_conditional::type; + + +template +struct hb_is_convertible +{ + private: + static constexpr bool from_void = hb_is_same (void, hb_decay); + static constexpr bool to_void = hb_is_same (void, hb_decay ); + static constexpr bool either_void = from_void || to_void; + static constexpr bool both_void = from_void && to_void; + + static hb_true_type impl2 (hb_conditional); + + template + static auto impl (hb_priority<1>) -> decltype (impl2 (hb_declval (T))); + template + static hb_false_type impl (hb_priority<0>); + public: + static constexpr bool value = both_void || + (!either_void && + decltype (impl> (hb_prioritize))::value); +}; +#define hb_is_convertible(From,To) hb_is_convertible::value + +template +using hb_is_base_of = hb_is_convertible *, hb_decay *>; +#define hb_is_base_of(Base,Derived) hb_is_base_of::value + +template +using hb_is_cr_convertible = hb_bool_constant< + hb_is_same (hb_decay, hb_decay) && + (!hb_is_const (From) || hb_is_const (To)) && + (!hb_is_reference (To) || hb_is_const (To) || hb_is_reference (To)) +>; +#define hb_is_cr_convertible(From,To) hb_is_cr_convertible::value + +/* std::move and std::forward */ + +template +static constexpr hb_remove_reference&& hb_move (T&& t) { return (hb_remove_reference&&) (t); } + +template +static constexpr T&& hb_forward (hb_remove_reference& t) { return (T&&) t; } +template +static constexpr T&& hb_forward (hb_remove_reference&& t) { return (T&&) t; } + +struct +{ + template constexpr auto + operator () (T&& v) const HB_AUTO_RETURN (hb_forward (v)) + + template constexpr auto + operator () (T *v) const HB_AUTO_RETURN (*v) +} +HB_FUNCOBJ (hb_deref); + +struct +{ + template constexpr auto + operator () (T&& v) const HB_AUTO_RETURN (hb_forward (v)) + + template constexpr auto + operator () (T& v) const HB_AUTO_RETURN (hb_addressof (v)) +} +HB_FUNCOBJ (hb_ref); + +template +struct hb_reference_wrapper +{ + hb_reference_wrapper (T v) : v (v) {} + bool operator == (const hb_reference_wrapper& o) const { return v == o.v; } + bool operator != (const hb_reference_wrapper& o) const { return v != o.v; } + operator T () const { return v; } + T get () const { return v; } + T v; +}; +template +struct hb_reference_wrapper +{ + hb_reference_wrapper (T& v) : v (hb_addressof (v)) {} + bool operator == (const hb_reference_wrapper& o) const { return v == o.v; } + bool operator != (const hb_reference_wrapper& o) const { return v != o.v; } + operator T& () const { return *v; } + T& get () const { return *v; } + T* v; +}; + + +template +using hb_is_integral = hb_bool_constant< + hb_is_same (hb_decay, char) || + hb_is_same (hb_decay, signed char) || + hb_is_same (hb_decay, unsigned char) || + hb_is_same (hb_decay, signed int) || + hb_is_same (hb_decay, unsigned int) || + hb_is_same (hb_decay, signed short) || + hb_is_same (hb_decay, unsigned short) || + hb_is_same (hb_decay, signed long) || + hb_is_same (hb_decay, unsigned long) || + hb_is_same (hb_decay, signed long long) || + hb_is_same (hb_decay, unsigned long long) || + false +>; +#define hb_is_integral(T) hb_is_integral::value +template +using hb_is_floating_point = hb_bool_constant< + hb_is_same (hb_decay, float) || + hb_is_same (hb_decay, double) || + hb_is_same (hb_decay, long double) || + false +>; +#define hb_is_floating_point(T) hb_is_floating_point::value +template +using hb_is_arithmetic = hb_bool_constant< + hb_is_integral (T) || + hb_is_floating_point (T) || + false +>; +#define hb_is_arithmetic(T) hb_is_arithmetic::value + + +template +using hb_is_signed = hb_conditional, + hb_false_type>; +#define hb_is_signed(T) hb_is_signed::value +template +using hb_is_unsigned = hb_conditional, + hb_false_type>; +#define hb_is_unsigned(T) hb_is_unsigned::value + +template struct hb_int_min; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +#define hb_int_min(T) hb_int_min::value +template struct hb_int_max; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +#define hb_int_max(T) hb_int_max::value + + + +template +struct _hb_is_destructible : hb_false_type {}; +template +struct _hb_is_destructible> : hb_true_type {}; +template +using hb_is_destructible = _hb_is_destructible; +#define hb_is_destructible(T) hb_is_destructible::value + +template +struct _hb_is_constructible : hb_false_type {}; +template +struct _hb_is_constructible, Ts...> : hb_true_type {}; +template +using hb_is_constructible = _hb_is_constructible; +#define hb_is_constructible(...) hb_is_constructible<__VA_ARGS__>::value + +template +using hb_is_default_constructible = hb_is_constructible; +#define hb_is_default_constructible(T) hb_is_default_constructible::value + +template +using hb_is_copy_constructible = hb_is_constructible>>; +#define hb_is_copy_constructible(T) hb_is_copy_constructible::value + +template +using hb_is_move_constructible = hb_is_constructible>>; +#define hb_is_move_constructible(T) hb_is_move_constructible::value + +template +struct _hb_is_assignable : hb_false_type {}; +template +struct _hb_is_assignable> : hb_true_type {}; +template +using hb_is_assignable = _hb_is_assignable; +#define hb_is_assignable(T,U) hb_is_assignable::value + +template +using hb_is_copy_assignable = hb_is_assignable, + hb_add_lvalue_reference>>; +#define hb_is_copy_assignable(T) hb_is_copy_assignable::value + +template +using hb_is_move_assignable = hb_is_assignable, + hb_add_rvalue_reference>; +#define hb_is_move_assignable(T) hb_is_move_assignable::value + +/* Trivial versions. */ + +template union hb_trivial { T value; }; + +template +using hb_is_trivially_destructible= hb_is_destructible>; +#define hb_is_trivially_destructible(T) hb_is_trivially_destructible::value + +/* Don't know how to do the following. */ +//template +//using hb_is_trivially_constructible= hb_is_constructible, hb_trivial...>; +//#define hb_is_trivially_constructible(...) hb_is_trivially_constructible<__VA_ARGS__>::value + +template +using hb_is_trivially_default_constructible= hb_is_default_constructible>; +#define hb_is_trivially_default_constructible(T) hb_is_trivially_default_constructible::value + +template +using hb_is_trivially_copy_constructible= hb_is_copy_constructible>; +#define hb_is_trivially_copy_constructible(T) hb_is_trivially_copy_constructible::value + +template +using hb_is_trivially_move_constructible= hb_is_move_constructible>; +#define hb_is_trivially_move_constructible(T) hb_is_trivially_move_constructible::value + +/* Don't know how to do the following. */ +//template +//using hb_is_trivially_assignable= hb_is_assignable, hb_trivial>; +//#define hb_is_trivially_assignable(T,U) hb_is_trivially_assignable::value + +template +using hb_is_trivially_copy_assignable= hb_is_copy_assignable>; +#define hb_is_trivially_copy_assignable(T) hb_is_trivially_copy_assignable::value + +template +using hb_is_trivially_move_assignable= hb_is_move_assignable>; +#define hb_is_trivially_move_assignable(T) hb_is_trivially_move_assignable::value + +template +using hb_is_trivially_copyable= hb_bool_constant< + hb_is_trivially_destructible (T) && + (!hb_is_move_assignable (T) || hb_is_trivially_move_assignable (T)) && + (!hb_is_move_constructible (T) || hb_is_trivially_move_constructible (T)) && + (!hb_is_copy_assignable (T) || hb_is_trivially_copy_assignable (T)) && + (!hb_is_copy_constructible (T) || hb_is_trivially_copy_constructible (T)) && + true +>; +#define hb_is_trivially_copyable(T) hb_is_trivially_copyable::value + +template +using hb_is_trivial= hb_bool_constant< + hb_is_trivially_copyable (T) && + hb_is_trivially_default_constructible (T) +>; +#define hb_is_trivial(T) hb_is_trivial::value + +/* hb_unwrap_type (T) + * If T has no T::type, returns T. Otherwise calls itself on T::type recursively. + */ + +template +struct _hb_unwrap_type : hb_type_identity_t {}; +template +struct _hb_unwrap_type> : _hb_unwrap_type {}; +template +using hb_unwrap_type = _hb_unwrap_type; +#define hb_unwrap_type(T) typename hb_unwrap_type::type + +#endif /* HB_META_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh b/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh index 1582b40cb04..f2d2962b713 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh @@ -48,12 +48,22 @@ /* Defined externally, i.e. in config.h; must have typedef'ed hb_mutex_impl_t as well. */ +#elif !defined(HB_NO_MT) && (defined(HAVE_PTHREAD) || defined(__APPLE__)) + +#include +typedef pthread_mutex_t hb_mutex_impl_t; +#define HB_MUTEX_IMPL_INIT PTHREAD_MUTEX_INITIALIZER +#define hb_mutex_impl_init(M) pthread_mutex_init (M, nullptr) +#define hb_mutex_impl_lock(M) pthread_mutex_lock (M) +#define hb_mutex_impl_unlock(M) pthread_mutex_unlock (M) +#define hb_mutex_impl_finish(M) pthread_mutex_destroy (M) + + #elif !defined(HB_NO_MT) && defined(_WIN32) -#include typedef CRITICAL_SECTION hb_mutex_impl_t; #define HB_MUTEX_IMPL_INIT {0} -#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) #define hb_mutex_impl_init(M) InitializeCriticalSectionEx (M, 0, 0) #else #define hb_mutex_impl_init(M) InitializeCriticalSection (M) @@ -63,17 +73,6 @@ typedef CRITICAL_SECTION hb_mutex_impl_t; #define hb_mutex_impl_finish(M) DeleteCriticalSection (M) -#elif !defined(HB_NO_MT) && (defined(HAVE_PTHREAD) || defined(__APPLE__)) - -#include -typedef pthread_mutex_t hb_mutex_impl_t; -#define HB_MUTEX_IMPL_INIT PTHREAD_MUTEX_INITIALIZER -#define hb_mutex_impl_init(M) pthread_mutex_init (M, nullptr) -#define hb_mutex_impl_lock(M) pthread_mutex_lock (M) -#define hb_mutex_impl_unlock(M) pthread_mutex_unlock (M) -#define hb_mutex_impl_finish(M) pthread_mutex_destroy (M) - - #elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) #if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD) @@ -92,25 +91,7 @@ typedef volatile int hb_mutex_impl_t; #define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END -#elif !defined(HB_NO_MT) - -#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD) -# include -# define HB_SCHED_YIELD() sched_yield () -#else -# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END -#endif - -#define HB_MUTEX_INT_NIL 1 /* Warn that fallback implementation is in use. */ -typedef volatile int hb_mutex_impl_t; -#define HB_MUTEX_IMPL_INIT 0 -#define hb_mutex_impl_init(M) *(M) = 0 -#define hb_mutex_impl_lock(M) HB_STMT_START { while (*(M)) HB_SCHED_YIELD (); (*(M))++; } HB_STMT_END -#define hb_mutex_impl_unlock(M) (*(M))--; -#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END - - -#else /* HB_NO_MT */ +#elif defined(HB_NO_MT) typedef int hb_mutex_impl_t; #define HB_MUTEX_IMPL_INIT 0 @@ -120,6 +101,11 @@ typedef int hb_mutex_impl_t; #define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END +#else + +#error "Could not find any system to define mutex macros." +#error "Check hb-mutex.hh for possible resolutions." + #endif @@ -127,8 +113,6 @@ typedef int hb_mutex_impl_t; struct hb_mutex_t { - /* TODO Add tracing. */ - hb_mutex_impl_t m; void init () { hb_mutex_impl_init (&m); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-null.hh b/src/java.desktop/share/native/libharfbuzz/hb-null.hh index 8a0e2d7dd05..d2bc3322e53 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-null.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-null.hh @@ -28,6 +28,7 @@ #define HB_NULL_HH #include "hb.hh" +#include "hb-meta.hh" /* @@ -36,7 +37,7 @@ /* Global nul-content Null pool. Enlarge as necessary. */ -#define HB_NULL_POOL_SIZE 9880 +#define HB_NULL_POOL_SIZE 384 /* Use SFINAE to sniff whether T has min_size; in which case return T::null_size, * otherwise return sizeof(T). */ @@ -45,18 +46,13 @@ * https://stackoverflow.com/questions/7776448/sfinae-tried-with-bool-gives-compiler-error-template-argument-tvalue-invol */ -template struct _hb_bool_type {}; - -template -struct _hb_null_size -{ enum { value = sizeof (T) }; }; +template +struct _hb_null_size : hb_integral_constant {}; template -struct _hb_null_size > -{ enum { value = T::null_size }; }; +struct _hb_null_size> : hb_integral_constant {}; template -struct hb_null_size -{ enum { value = _hb_null_size >::value }; }; +using hb_null_size = _hb_null_size; #define hb_null_size(T) hb_null_size::value /* These doesn't belong here, but since is copy/paste from above, put it here. */ @@ -64,56 +60,36 @@ struct hb_null_size /* hb_static_size (T) * Returns T::static_size if T::min_size is defined, or sizeof (T) otherwise. */ -template -struct _hb_static_size -{ enum { value = sizeof (T) }; }; +template +struct _hb_static_size : hb_integral_constant {}; template -struct _hb_static_size > -{ enum { value = T::static_size }; }; - +struct _hb_static_size> : hb_integral_constant {}; template -struct hb_static_size -{ enum { value = _hb_static_size >::value }; }; +using hb_static_size = _hb_static_size; #define hb_static_size(T) hb_static_size::value -/* hb_assign (obj, value) - * Calls obj.set (value) if obj.min_size is defined and value has different type - * from obj, or obj = v otherwise. */ - -template -struct _hb_assign -{ static inline void value (T &o, const V v) { o = v; } }; -template -struct _hb_assign > -{ static inline void value (T &o, const V v) { o.set (v); } }; -template -struct _hb_assign > -{ static inline void value (T &o, const T v) { o = v; } }; - -template -static inline void hb_assign (T &o, const V v) -{ _hb_assign >::value (o, v); } - - /* * Null() */ extern HB_INTERNAL -hb_vector_size_impl_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)]; +uint64_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)]; /* Generic nul-content Null objects. */ template -static inline Type const & Null () { - static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); - return *reinterpret_cast (_hb_NullPool); -} +struct Null { + static Type const & get_null () + { + static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); + return *reinterpret_cast (_hb_NullPool); + } +}; template struct NullHelper { - typedef typename hb_remove_const (typename hb_remove_reference (QType)) Type; - static const Type & get_null () { return Null (); } + typedef hb_remove_const> Type; + static const Type & get_null () { return Null::get_null (); } }; #define Null(Type) NullHelper::get_null () @@ -122,11 +98,13 @@ struct NullHelper } /* Close namespace. */ \ extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::null_size]; \ template <> \ - /*static*/ inline const Namespace::Type& Null () { \ - return *reinterpret_cast (_hb_Null_##Namespace##_##Type); \ - } \ + struct Null { \ + static Namespace::Type const & get_null () { \ + return *reinterpret_cast (_hb_Null_##Namespace##_##Type); \ + } \ + }; \ namespace Namespace { \ - static_assert (true, "Just so we take semicolon after.") + static_assert (true, "") /* Require semicolon after. */ #define DEFINE_NULL_NAMESPACE_BYTES(Namespace, Type) \ const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::null_size] @@ -134,10 +112,12 @@ struct NullHelper #define DECLARE_NULL_INSTANCE(Type) \ extern HB_INTERNAL const Type _hb_Null_##Type; \ template <> \ - /*static*/ inline const Type& Null () { \ - return _hb_Null_##Type; \ - } \ -static_assert (true, "Just so we take semicolon after.") + struct Null { \ + static Type const & get_null () { \ + return _hb_Null_##Type; \ + } \ + }; \ + static_assert (true, "") /* Require semicolon after. */ #define DEFINE_NULL_INSTANCE(Type) \ const Type _hb_Null_##Type @@ -148,31 +128,31 @@ static_assert (true, "Just so we take semicolon after.") * causing bad memory access. So, races there are not actually introducing incorrectness * in the code. Has ~12kb binary size overhead to have it, also clang build fails with it. */ extern HB_INTERNAL -/*thread_local*/ hb_vector_size_impl_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)]; +/*thread_local*/ uint64_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)]; /* CRAP pool: Common Region for Access Protection. */ template static inline Type& Crap () { static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); Type *obj = reinterpret_cast (_hb_CrapPool); - memcpy (obj, &Null(Type), sizeof (*obj)); + memcpy (obj, &Null (Type), sizeof (*obj)); return *obj; } template struct CrapHelper { - typedef typename hb_remove_const (typename hb_remove_reference (QType)) Type; + typedef hb_remove_const> Type; static Type & get_crap () { return Crap (); } }; #define Crap(Type) CrapHelper::get_crap () template struct CrapOrNullHelper { - static Type & get () { return Crap(Type); } + static Type & get () { return Crap (Type); } }; template struct CrapOrNullHelper { - static const Type & get () { return Null(Type); } + static const Type & get () { return Null (Type); } }; #define CrapOrNull(Type) CrapOrNullHelper::get () @@ -184,7 +164,7 @@ struct CrapOrNullHelper { template struct hb_nonnull_ptr_t { - typedef typename hb_remove_pointer (P) T; + typedef hb_remove_pointer

    T; hb_nonnull_ptr_t (T *v_ = nullptr) : v (v_) {} T * operator = (T *v_) { return v = v_; } @@ -194,7 +174,7 @@ struct hb_nonnull_ptr_t /* Only auto-cast to const types. */ template operator const C * () const { return get (); } operator const char * () const { return (const char *) get (); } - T * get () const { return v ? v : const_cast (&Null(T)); } + T * get () const { return v ? v : const_cast (&Null (T)); } T * get_raw () const { return v; } T *v; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh b/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh new file mode 100644 index 00000000000..9d2867e4835 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh @@ -0,0 +1,237 @@ + +#line 1 "hb-number-parser.rl" +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + +#ifndef HB_NUMBER_PARSER_HH +#define HB_NUMBER_PARSER_HH + +#include "hb.hh" + + +#line 35 "hb-number-parser.hh" +static const unsigned char _double_parser_trans_keys[] = { + 0u, 0u, 43u, 57u, 46u, 57u, 48u, 57u, 43u, 57u, 48u, 57u, 48u, 101u, 48u, 57u, + 46u, 101u, 0 +}; + +static const char _double_parser_key_spans[] = { + 0, 15, 12, 10, 15, 10, 54, 10, + 56 +}; + +static const unsigned char _double_parser_index_offsets[] = { + 0, 0, 16, 29, 40, 56, 67, 122, + 133 +}; + +static const char _double_parser_indicies[] = { + 0, 1, 2, 3, 1, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 1, 3, 1, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 1, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 1, 6, 1, 7, 1, 1, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 1, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 1, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9, 1, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 1, 3, 1, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9, 1, 0 +}; + +static const char _double_parser_trans_targs[] = { + 2, 0, 2, 3, 8, 6, 5, 5, + 7, 4 +}; + +static const char _double_parser_trans_actions[] = { + 0, 0, 1, 0, 2, 3, 0, 4, + 5, 0 +}; + +static const int double_parser_start = 1; +static const int double_parser_first_final = 6; +static const int double_parser_error = 0; + +static const int double_parser_en_main = 1; + + +#line 68 "hb-number-parser.rl" + + +/* Works only for n < 512 */ +static inline double +_pow10 (unsigned exponent) +{ + static const double _powers_of_10[] = + { + 1.0e+256, + 1.0e+128, + 1.0e+64, + 1.0e+32, + 1.0e+16, + 1.0e+8, + 10000., + 100., + 10. + }; + unsigned mask = 1 << (ARRAY_LENGTH (_powers_of_10) - 1); + double result = 1; + for (const double *power = _powers_of_10; mask; ++power, mask >>= 1) + if (exponent & mask) result *= *power; + return result; +} + +/* a variant of strtod that also gets end of buffer in its second argument */ +static inline double +strtod_rl (const char *p, const char **end_ptr /* IN/OUT */) +{ + double value = 0; + double frac = 0; + double frac_count = 0; + unsigned exp = 0; + bool neg = false, exp_neg = false, exp_overflow = false; + const unsigned long long MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 2^52-1 */ + const unsigned MAX_EXP = 0x7FFu; /* 2^11-1 */ + + const char *pe = *end_ptr; + while (p < pe && ISSPACE (*p)) + p++; + + int cs; + +#line 139 "hb-number-parser.hh" + { + cs = double_parser_start; + } + +#line 144 "hb-number-parser.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _double_parser_trans_keys + (cs<<1); + _inds = _double_parser_indicies + _double_parser_index_offsets[cs]; + + _slen = _double_parser_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _double_parser_trans_targs[_trans]; + + if ( _double_parser_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _double_parser_trans_actions[_trans] ) { + case 1: +#line 37 "hb-number-parser.rl" + { neg = true; } + break; + case 4: +#line 38 "hb-number-parser.rl" + { exp_neg = true; } + break; + case 2: +#line 40 "hb-number-parser.rl" + { + value = value * 10. + ((*p) - '0'); +} + break; + case 3: +#line 43 "hb-number-parser.rl" + { + if (likely (frac <= MAX_FRACT / 10)) + { + frac = frac * 10. + ((*p) - '0'); + ++frac_count; + } +} + break; + case 5: +#line 50 "hb-number-parser.rl" + { + if (likely (exp * 10 + ((*p) - '0') <= MAX_EXP)) + exp = exp * 10 + ((*p) - '0'); + else + exp_overflow = true; +} + break; +#line 202 "hb-number-parser.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + _out: {} + } + +#line 113 "hb-number-parser.rl" + + + *end_ptr = p; + + if (frac_count) value += frac / _pow10 (frac_count); + if (neg) value *= -1.; + + if (unlikely (exp_overflow)) + { + if (value == 0) return value; + if (exp_neg) return neg ? -DBL_MIN : DBL_MIN; + else return neg ? -DBL_MAX : DBL_MAX; + } + + if (exp) + { + if (exp_neg) value /= _pow10 (exp); + else value *= _pow10 (exp); + } + + return value; +} + +#endif /* HB_NUMBER_PARSER_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-number.cc b/src/java.desktop/share/native/libharfbuzz/hb-number.cc new file mode 100644 index 00000000000..f4ce693d8ed --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-number.cc @@ -0,0 +1,80 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + +#include "hb.hh" +#include "hb-machinery.hh" +#include "hb-number.hh" +#include "hb-number-parser.hh" + +template +static bool +_parse_number (const char **pp, const char *end, T *pv, + bool whole_buffer, Func f) +{ + char buf[32]; + unsigned len = hb_min (ARRAY_LENGTH (buf) - 1, (unsigned) (end - *pp)); + strncpy (buf, *pp, len); + buf[len] = '\0'; + + char *p = buf; + char *pend = p; + + errno = 0; + *pv = f (p, &pend); + if (unlikely (errno || p == pend || + /* Check if consumed whole buffer if is requested */ + (whole_buffer && pend - p != end - *pp))) + return false; + + *pp += pend - p; + return true; +} + +bool +hb_parse_int (const char **pp, const char *end, int *pv, bool whole_buffer) +{ + return _parse_number (pp, end, pv, whole_buffer, + [] (const char *p, char **end) + { return strtol (p, end, 10); }); +} + +bool +hb_parse_uint (const char **pp, const char *end, unsigned *pv, + bool whole_buffer, int base) +{ + return _parse_number (pp, end, pv, whole_buffer, + [base] (const char *p, char **end) + { return strtoul (p, end, base); }); +} + +bool +hb_parse_double (const char **pp, const char *end, double *pv, bool whole_buffer) +{ + const char *pend = end; + *pv = strtod_rl (*pp, &pend); + if (unlikely (*pp == pend)) return false; + *pp = pend; + return !whole_buffer || end == pend; +} diff --git a/src/java.desktop/share/native/libharfbuzz/hb-number.hh b/src/java.desktop/share/native/libharfbuzz/hb-number.hh new file mode 100644 index 00000000000..47d902cf3e7 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-number.hh @@ -0,0 +1,41 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + +#ifndef HB_NUMBER_HH +#define HB_NUMBER_HH + +HB_INTERNAL bool +hb_parse_int (const char **pp, const char *end, int *pv, + bool whole_buffer = false); + +HB_INTERNAL bool +hb_parse_uint (const char **pp, const char *end, unsigned int *pv, + bool whole_buffer = false, int base = 10); + +HB_INTERNAL bool +hb_parse_double (const char **pp, const char *end, double *pv, + bool whole_buffer = false); + +#endif /* HB_NUMBER_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-object.hh b/src/java.desktop/share/native/libharfbuzz/hb-object.hh index 1d7b6bf4f0b..f01508ed40b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-object.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-object.hh @@ -168,8 +168,8 @@ struct hb_user_data_array_t void *data; hb_destroy_func_t destroy; - bool operator == (hb_user_data_key_t *other_key) const { return key == other_key; } - bool operator == (hb_user_data_item_t &other) const { return key == other.key; } + bool operator == (const hb_user_data_key_t *other_key) const { return key == other_key; } + bool operator == (const hb_user_data_item_t &other) const { return key == other.key; } void fini () { if (destroy) destroy (data); } }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh b/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh index 72b203041d6..95a8f75ff20 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh @@ -56,7 +56,7 @@ typedef struct TableRecord { int cmp (Tag t) const { return -t.cmp (tag); } - static int cmp (const void *pa, const void *pb) + HB_INTERNAL static int cmp (const void *pa, const void *pb) { const TableRecord *a = (const TableRecord *) pa; const TableRecord *b = (const TableRecord *) pb; @@ -86,27 +86,22 @@ typedef struct OffsetTable const TableRecord& get_table (unsigned int i) const { return tables[i]; } unsigned int get_table_tags (unsigned int start_offset, - unsigned int *table_count, /* IN/OUT */ - hb_tag_t *table_tags /* OUT */) const + unsigned int *table_count, /* IN/OUT */ + hb_tag_t *table_tags /* OUT */) const { if (table_count) { - if (start_offset >= tables.len) - *table_count = 0; - else - *table_count = MIN (*table_count, tables.len - start_offset); - - const TableRecord *sub_tables = tables.arrayZ + start_offset; - unsigned int count = *table_count; - for (unsigned int i = 0; i < count; i++) - table_tags[i] = sub_tables[i].tag; + + tables.sub_array (start_offset, table_count) + | hb_map (&TableRecord::tag) + | hb_sink (hb_array (table_tags, *table_count)) + ; } return tables.len; } bool find_table_index (hb_tag_t tag, unsigned int *table_index) const { Tag t; - t.set (tag); + t = tag; return tables.bfind (t, table_index, HB_BFIND_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); } const TableRecord& get_table_by_tag (hb_tag_t tag) const @@ -127,7 +122,7 @@ typedef struct OffsetTable /* Alloc 12 for the OTHeader. */ if (unlikely (!c->extend_min (*this))) return_trace (false); /* Write sfntVersion (bytes 0..3). */ - sfnt_version.set (sfnt_tag); + sfnt_version = sfnt_tag; /* Take space for numTables, searchRange, entrySelector, RangeShift * and the TableRecords themselves. */ if (unlikely (!tables.serialize (c, items.length))) return_trace (false); @@ -140,15 +135,16 @@ typedef struct OffsetTable { TableRecord &rec = tables.arrayZ[i]; hb_blob_t *blob = items[i].blob; - rec.tag.set (items[i].tag); - rec.length.set (hb_blob_get_length (blob)); + rec.tag = items[i].tag; + rec.length = blob->length; rec.offset.serialize (c, this); /* Allocate room for the table and copy it. */ char *start = (char *) c->allocate_size (rec.length); - if (unlikely (!start)) {return false;} + if (unlikely (!start)) return false; - memcpy (start, hb_blob_get_data (blob, nullptr), rec.length); + if (likely (rec.length)) + memcpy (start, blob->data, rec.length); /* 4-byte alignment. */ c->align (4); @@ -159,7 +155,7 @@ typedef struct OffsetTable { head *h = (head *) start; checksum_adjustment = &h->checkSumAdjustment; - checksum_adjustment->set (0); + *checksum_adjustment = 0; } rec.checkSum.set_for_data (start, end - start); @@ -177,10 +173,10 @@ typedef struct OffsetTable for (unsigned int i = 0; i < items.length; i++) { TableRecord &rec = tables.arrayZ[i]; - checksum.set (checksum + rec.checkSum); + checksum = checksum + rec.checkSum; } - checksum_adjustment->set (0xB1B0AFBAu - checksum); + *checksum_adjustment = 0xB1B0AFBAu - checksum; } return_trace (true); @@ -222,7 +218,7 @@ struct TTCHeaderVersion1 Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ FixedVersion<>version; /* Version of the TTC Header (1.0), * 0x00010000u */ - LArrayOf > + LArrayOf> table; /* Array of offsets to the OffsetTable for each font * from the beginning of the file */ public: @@ -248,7 +244,7 @@ struct TTCHeader switch (u.header.version.major) { case 2: /* version 2 is compatible with version 1 */ case 1: return u.version1.get_face (i); - default:return Null(OpenTypeFontFace); + default:return Null (OpenTypeFontFace); } } @@ -283,10 +279,10 @@ struct TTCHeader struct ResourceRecord { const OpenTypeFontFace & get_face (const void *data_base) const - { return CastR ((data_base+offset).arrayZ); } + { return * reinterpret_cast ((data_base+offset).arrayZ); } bool sanitize (hb_sanitize_context_t *c, - const void *data_base) const + const void *data_base) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && @@ -334,7 +330,7 @@ struct ResourceTypeRecord protected: Tag tag; /* Resource type. */ HBUINT16 resCountM1; /* Number of resources minus 1. */ - NNOffsetTo > + NNOffsetTo> resourcesZ; /* Offset from beginning of resource type list * to reference item list for this type. */ public: @@ -390,7 +386,7 @@ struct ResourceMap HBUINT32 reserved1; /* Reserved for handle to next resource map */ HBUINT16 resreved2; /* Reserved for file reference number */ HBUINT16 attrs; /* Resource fork attribute */ - NNOffsetTo > + NNOffsetTo> typeList; /* Offset from beginning of map to * resource type list */ Offset16 nameList; /* Offset from beginning of map to @@ -422,7 +418,7 @@ struct ResourceForkHeader } protected: - LNNOffsetTo > + LNNOffsetTo> data; /* Offset from beginning of resource fork * to resource data */ LNNOffsetTo @@ -477,7 +473,7 @@ struct OpenTypeFontFile case TrueTypeTag: return u.fontFace; case TTCTag: return u.ttcHeader.get_face (i); case DFontTag: return u.rfHeader.get_face (i, base_offset); - default: return Null(OpenTypeFontFace); + default: return Null (OpenTypeFontFace); } } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh b/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh index 596099305c1..624194651d3 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh @@ -52,22 +52,34 @@ namespace OT { * Int types */ -template struct hb_signedness_int; -template <> struct hb_signedness_int { typedef unsigned int value; }; -template <> struct hb_signedness_int { typedef signed int value; }; - /* Integer types in big-endian order and no alignment requirement */ template struct IntType { typedef Type type; - typedef typename hb_signedness_int::value>::value wide_type; + typedef hb_conditional wide_type; - void set (wide_type i) { v.set (i); } + IntType& operator = (wide_type i) { v = i; return *this; } operator wide_type () const { return v; } - bool operator == (const IntType &o) const { return (Type) v == (Type) o.v; } - bool operator != (const IntType &o) const { return !(*this == o); } - static int cmp (const IntType *a, const IntType *b) { return b->cmp (*a); } + bool operator == (const IntType &o) const { return (Type) v == (Type) o.v; } + bool operator != (const IntType &o) const { return !(*this == o); } + + IntType& operator += (unsigned count) { *this = *this + count; return *this; } + IntType& operator -= (unsigned count) { *this = *this - count; return *this; } + IntType& operator ++ () { *this += 1; return *this; } + IntType& operator -- () { *this -= 1; return *this; } + IntType operator ++ (int) { IntType c (*this); ++*this; return c; } + IntType operator -- (int) { IntType c (*this); --*this; return c; } + + HB_INTERNAL static int cmp (const IntType *a, const IntType *b) + { return b->cmp (*a); } + HB_INTERNAL static int cmp (const void *a, const void *b) + { + IntType *pa = (IntType *) a; + IntType *pb = (IntType *) b; + + return pb->cmp (*pa); + } template int cmp (Type2 a) const { @@ -110,19 +122,21 @@ typedef HBUINT16 UFWORD; /* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */ struct F2DOT14 : HBINT16 { + F2DOT14& operator = (uint16_t i ) { HBINT16::operator= (i); return *this; } // 16384 means 1<<14 float to_float () const { return ((int32_t) v) / 16384.f; } - void set_float (float f) { v.set (round (f * 16384.f)); } + void set_float (float f) { v = roundf (f * 16384.f); } public: DEFINE_SIZE_STATIC (2); }; /* 32-bit signed fixed-point number (16.16). */ -struct Fixed : HBINT32 +struct HBFixed : HBINT32 { + HBFixed& operator = (uint32_t i) { HBINT32::operator= (i); return *this; } // 65536 means 1<<16 float to_float () const { return ((int32_t) v) / 65536.f; } - void set_float (float f) { v.set (round (f * 65536.f)); } + void set_float (float f) { v = roundf (f * 65536.f); } public: DEFINE_SIZE_STATIC (4); }; @@ -147,6 +161,7 @@ struct LONGDATETIME * system, feature, or baseline */ struct Tag : HBUINT32 { + Tag& operator = (hb_tag_t i) { HBUINT32::operator= (i); return *this; } /* What the char* converters return is NOT nul-terminated. Print using "%.4s" */ operator const char* () const { return reinterpret_cast (&this->v); } operator char* () { return reinterpret_cast (&this->v); } @@ -155,11 +170,15 @@ struct Tag : HBUINT32 }; /* Glyph index number, same as uint16 (length = 16 bits) */ -typedef HBUINT16 GlyphID; +struct HBGlyphID : HBUINT16 +{ + HBGlyphID& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } +}; /* Script/language-system/feature index */ struct Index : HBUINT16 { static constexpr unsigned NOT_FOUND_INDEX = 0xFFFFu; + Index& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } }; DECLARE_NULL_NAMESPACE_BYTES (OT, Index); @@ -169,6 +188,8 @@ typedef Index NameID; template struct Offset : Type { + Offset& operator = (typename Type::type i) { Type::operator= (i); return *this; } + typedef Type type; bool is_null () const { return has_null && 0 == *this; } @@ -176,7 +197,7 @@ struct Offset : Type void *serialize (hb_serialize_context_t *c, const void *base) { void *t = c->start_embed (); - this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */ + c->check_assign (*this, (unsigned) ((char *) t - (char *) base)); return t; } @@ -191,6 +212,8 @@ typedef Offset Offset32; /* CheckSum */ struct CheckSum : HBUINT32 { + CheckSum& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; } + /* This is reference implementation from the spec. */ static uint32_t CalcTableChecksum (const HBUINT32 *Table, uint32_t Length) { @@ -205,7 +228,7 @@ struct CheckSum : HBUINT32 /* Note: data should be 4byte aligned and have 4byte padding at the end. */ void set_for_data (const void *data, unsigned int length) - { set (CalcTableChecksum ((const HBUINT32 *) data, length)); } + { *this = CalcTableChecksum ((const HBUINT32 *) data, length); } public: DEFINE_SIZE_STATIC (4); @@ -248,13 +271,18 @@ struct _hb_has_null template struct _hb_has_null { - static const Type *get_null () { return &Null(Type); } - static Type *get_crap () { return &Crap(Type); } + static const Type *get_null () { return &Null (Type); } + static Type *get_crap () { return &Crap (Type); } }; template struct OffsetTo : Offset { + HB_DELETE_COPY_ASSIGN (OffsetTo); + OffsetTo () = default; + + OffsetTo& operator = (typename OffsetType::type i) { OffsetType::operator= (i); return *this; } + const Type& operator () (const void *base) const { if (unlikely (this->is_null ())) return *_hb_has_null::get_null (); @@ -266,24 +294,73 @@ struct OffsetTo : Offset return StructAtOffset (base, *this); } + template + friend const Type& operator + (const Base &base, const OffsetTo &offset) { return offset ((const void *) base); } + template + friend const Type& operator + (const OffsetTo &offset, const Base &base) { return offset ((const void *) base); } + template + friend Type& operator + (Base &&base, OffsetTo &offset) { return offset ((void *) base); } + template + friend Type& operator + (OffsetTo &offset, Base &&base) { return offset ((void *) base); } + Type& serialize (hb_serialize_context_t *c, const void *base) { return * (Type *) Offset::serialize (c, base); } - template - void serialize_subset (hb_subset_context_t *c, const T &src, const void *base) + template + bool serialize_subset (hb_subset_context_t *c, const OffsetTo& src, + const void *src_base, Ts&&... ds) { - if (&src == &Null (T)) - { - this->set (0); - return; - } - serialize (c->serializer, base); - if (!src.subset (c)) - this->set (0); + *this = 0; + if (src.is_null ()) + return false; + + auto *s = c->serializer; + + s->push (); + + bool ret = c->dispatch (src_base+src, hb_forward (ds)...); + + if (ret || !has_null) + s->add_link (*this, s->pop_pack ()); + else + s->pop_discard (); + + return ret; + } + + /* TODO: Somehow merge this with previous function into a serialize_dispatch(). */ + /* Workaround clang bug: https://bugs.llvm.org/show_bug.cgi?id=23029 + * Can't compile: whence = hb_serialize_context_t::Head followed by Ts&&... + */ + template + bool serialize_copy (hb_serialize_context_t *c, const OffsetTo& src, + const void *src_base, unsigned dst_bias, + hb_serialize_context_t::whence_t whence, + Ts&&... ds) + { + *this = 0; + if (src.is_null ()) + return false; + + c->push (); + + bool ret = c->copy (src_base+src, hb_forward (ds)...); + + c->add_link (*this, c->pop_pack (), whence, dst_bias); + + return ret; } + bool serialize_copy (hb_serialize_context_t *c, const OffsetTo& src, + const void *src_base, unsigned dst_bias = 0) + { return serialize_copy (c, src, src_base, dst_bias, hb_serialize_context_t::Head); } + bool sanitize_shallow (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); @@ -293,39 +370,13 @@ struct OffsetTo : Offset return_trace (true); } - bool sanitize (hb_sanitize_context_t *c, const void *base) const - { - TRACE_SANITIZE (this); - return_trace (sanitize_shallow (c, base) && - (this->is_null () || - StructAtOffset (base, *this).sanitize (c) || - neuter (c))); - } - template - bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1) const - { - TRACE_SANITIZE (this); - return_trace (sanitize_shallow (c, base) && - (this->is_null () || - StructAtOffset (base, *this).sanitize (c, d1) || - neuter (c))); - } - template - bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1, T2 d2) const - { - TRACE_SANITIZE (this); - return_trace (sanitize_shallow (c, base) && - (this->is_null () || - StructAtOffset (base, *this).sanitize (c, d1, d2) || - neuter (c))); - } - template - bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1, T2 d2, T3 d3) const + template + bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const { TRACE_SANITIZE (this); return_trace (sanitize_shallow (c, base) && (this->is_null () || - StructAtOffset (base, *this).sanitize (c, d1, d2, d3) || + c->dispatch (StructAtOffset (base, *this), hb_forward (ds)...) || neuter (c))); } @@ -338,14 +389,12 @@ struct OffsetTo : Offset DEFINE_SIZE_STATIC (sizeof (OffsetType)); }; /* Partial specializations. */ -template struct LOffsetTo : OffsetTo {}; -template struct NNOffsetTo : OffsetTo {}; -template struct LNNOffsetTo : OffsetTo {}; - -template -static inline const Type& operator + (const Base &base, const OffsetTo &offset) { return offset (base); } -template -static inline Type& operator + (Base &base, OffsetTo &offset) { return offset (base); } +template +using LOffsetTo = OffsetTo; +template +using NNOffsetTo = OffsetTo; +template +using LNNOffsetTo = LOffsetTo; /* @@ -358,7 +407,7 @@ struct UnsizedArrayOf typedef Type item_t; static constexpr unsigned item_size = hb_static_size (Type); - HB_NO_CREATE_COPY_ASSIGN_TEMPLATE (UnsizedArrayOf, Type); + HB_DELETE_CREATE_COPY_ASSIGN (UnsizedArrayOf); const Type& operator [] (int i_) const { @@ -384,7 +433,7 @@ struct UnsizedArrayOf { return hb_array (arrayZ, len); } hb_array_t as_array (unsigned int len) const { return hb_array (arrayZ, len); } - operator hb_array_t () { return as_array (); } + operator hb_array_t< Type> () { return as_array (); } operator hb_array_t () const { return as_array (); } template @@ -393,42 +442,49 @@ struct UnsizedArrayOf template const Type &lsearch (unsigned int len, const T &x, const Type ¬_found = Null (Type)) const { return *as_array (len).lsearch (x, ¬_found); } + template + bool lfind (unsigned int len, const T &x, unsigned *pos = nullptr) const + { return as_array (len).lfind (x, pos); } void qsort (unsigned int len, unsigned int start = 0, unsigned int end = (unsigned int) -1) { as_array (len).qsort (start, end); } - bool sanitize (hb_sanitize_context_t *c, unsigned int count) const + bool serialize (hb_serialize_context_t *c, unsigned int items_len) { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c, count))) return_trace (false); - - /* Note: for structs that do not reference other structs, - * we do not need to call their sanitize() as we already did - * a bound check on the aggregate array size. We just include - * a small unreachable expression to make sure the structs - * pointed to do have a simple sanitize(), ie. they do not - * reference other structs via offsets. - */ - (void) (false && arrayZ[0].sanitize (c)); - + TRACE_SERIALIZE (this); + if (unlikely (!c->extend (*this, items_len))) return_trace (false); return_trace (true); } - bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base) const + template + bool serialize (hb_serialize_context_t *c, Iterator items) { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c, count))) return_trace (false); - for (unsigned int i = 0; i < count; i++) - if (unlikely (!arrayZ[i].sanitize (c, base))) - return_trace (false); + TRACE_SERIALIZE (this); + unsigned count = items.len (); + if (unlikely (!serialize (c, count))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; return_trace (true); } - template - bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base, T user_data) const + + UnsizedArrayOf* copy (hb_serialize_context_t *c, unsigned count) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!as_array (count).copy (c))) return_trace (nullptr); + return_trace (out); + } + + template + bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const { TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c, count))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable (Type)) return_trace (true); for (unsigned int i = 0; i < count; i++) - if (unlikely (!arrayZ[i].sanitize (c, base, user_data))) + if (unlikely (!c->dispatch (arrayZ[i], hb_forward (ds)...))) return_trace (false); return_trace (true); } @@ -440,14 +496,14 @@ struct UnsizedArrayOf } public: - Type arrayZ[VAR]; + Type arrayZ[HB_VAR_ARRAY]; public: DEFINE_SIZE_UNBOUNDED (0); }; /* Unsized array of offset's */ template -struct UnsizedOffsetArrayOf : UnsizedArrayOf > {}; +using UnsizedOffsetArrayOf = UnsizedArrayOf>; /* Unsized array of offsets relative to the beginning of the array itself. */ template @@ -468,17 +524,12 @@ struct UnsizedOffsetListOf : UnsizedOffsetArrayOf return this+*p; } - - bool sanitize (hb_sanitize_context_t *c, unsigned int count) const - { - TRACE_SANITIZE (this); - return_trace ((UnsizedOffsetArrayOf::sanitize (c, count, this))); - } - template - bool sanitize (hb_sanitize_context_t *c, unsigned int count, T user_data) const + template + bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const { TRACE_SANITIZE (this); - return_trace ((UnsizedOffsetArrayOf::sanitize (c, count, this, user_data))); + return_trace ((UnsizedOffsetArrayOf + ::sanitize (c, count, this, hb_forward (ds)...))); } }; @@ -501,8 +552,8 @@ struct SortedUnsizedArrayOf : UnsizedArrayOf { return *as_array (len).bsearch (x, ¬_found); } template bool bfind (unsigned int len, const T &x, unsigned int *i = nullptr, - hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE, - unsigned int to_store = (unsigned int) -1) const + hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const { return as_array (len).bfind (x, i, not_found, to_store); } }; @@ -514,7 +565,7 @@ struct ArrayOf typedef Type item_t; static constexpr unsigned item_size = hb_static_size (Type); - HB_NO_CREATE_COPY_ASSIGN_TEMPLATE2 (ArrayOf, Type, LenType); + HB_DELETE_CREATE_COPY_ASSIGN (ArrayOf); const Type& operator [] (int i_) const { @@ -532,74 +583,83 @@ struct ArrayOf unsigned int get_size () const { return len.static_size + len * Type::static_size; } - hb_array_t as_array () - { return hb_array (arrayZ, len); } - hb_array_t as_array () const - { return hb_array (arrayZ, len); } - operator hb_array_t (void) { return as_array (); } - operator hb_array_t (void) const { return as_array (); } + explicit operator bool () const { return len; } + + void pop () { len--; } + + hb_array_t< Type> as_array () { return hb_array (arrayZ, len); } + hb_array_t as_array () const { return hb_array (arrayZ, len); } + + /* Iterator. */ + typedef hb_array_t iter_t; + typedef hb_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } hb_array_t sub_array (unsigned int start_offset, unsigned int count) const - { return as_array ().sub_array (start_offset, count);} + { return as_array ().sub_array (start_offset, count); } hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const - { return as_array ().sub_array (start_offset, count);} + { return as_array ().sub_array (start_offset, count); } hb_array_t sub_array (unsigned int start_offset, unsigned int count) - { return as_array ().sub_array (start_offset, count);} + { return as_array ().sub_array (start_offset, count); } hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) - { return as_array ().sub_array (start_offset, count);} + { return as_array ().sub_array (start_offset, count); } - bool serialize (hb_serialize_context_t *c, unsigned int items_len) + hb_success_t serialize (hb_serialize_context_t *c, unsigned items_len) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - len.set (items_len); /* TODO(serialize) Overflow? */ + c->check_assign (len, items_len); if (unlikely (!c->extend (*this))) return_trace (false); return_trace (true); } - template - bool serialize (hb_serialize_context_t *c, hb_array_t items) + template + hb_success_t serialize (hb_serialize_context_t *c, Iterator items) { TRACE_SERIALIZE (this); - if (unlikely (!serialize (c, items.length))) return_trace (false); - for (unsigned int i = 0; i < items.length; i++) - hb_assign (arrayZ[i], items[i]); + unsigned count = items.len (); + if (unlikely (!serialize (c, count))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; return_trace (true); } - bool sanitize (hb_sanitize_context_t *c) const + Type* serialize_append (hb_serialize_context_t *c) { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c))) return_trace (false); - - /* Note: for structs that do not reference other structs, - * we do not need to call their sanitize() as we already did - * a bound check on the aggregate array size. We just include - * a small unreachable expression to make sure the structs - * pointed to do have a simple sanitize(), ie. they do not - * reference other structs via offsets. - */ - (void) (false && arrayZ[0].sanitize (c)); - - return_trace (true); + TRACE_SERIALIZE (this); + len++; + if (unlikely (!len || !c->extend (*this))) + { + len--; + return_trace (nullptr); + } + return_trace (&arrayZ[len - 1]); } - bool sanitize (hb_sanitize_context_t *c, const void *base) const + + ArrayOf* copy (hb_serialize_context_t *c) const { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c))) return_trace (false); - unsigned int count = len; - for (unsigned int i = 0; i < count; i++) - if (unlikely (!arrayZ[i].sanitize (c, base))) - return_trace (false); - return_trace (true); + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!c->extend_min (out))) return_trace (nullptr); + c->check_assign (out->len, len); + if (unlikely (!as_array ().copy (c))) return_trace (nullptr); + return_trace (out); } - template - bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable (Type)) return_trace (true); unsigned int count = len; for (unsigned int i = 0; i < count; i++) - if (unlikely (!arrayZ[i].sanitize (c, base, user_data))) + if (unlikely (!c->dispatch (arrayZ[i], hb_forward (ds)...))) return_trace (false); return_trace (true); } @@ -610,6 +670,9 @@ struct ArrayOf template const Type &lsearch (const T &x, const Type ¬_found = Null (Type)) const { return *as_array ().lsearch (x, ¬_found); } + template + bool lfind (const T &x, unsigned *pos = nullptr) const + { return as_array ().lfind (x, pos); } void qsort (unsigned int start = 0, unsigned int end = (unsigned int) -1) { as_array ().qsort (start, end); } @@ -622,20 +685,21 @@ struct ArrayOf public: LenType len; - Type arrayZ[VAR]; + Type arrayZ[HB_VAR_ARRAY]; public: DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); }; -template struct LArrayOf : ArrayOf {}; -typedef ArrayOf PString; +template +using LArrayOf = ArrayOf; +using PString = ArrayOf; /* Array of Offset's */ template -struct OffsetArrayOf : ArrayOf > {}; +using OffsetArrayOf = ArrayOf>; template -struct LOffsetArrayOf : ArrayOf > {}; +using LOffsetArrayOf = ArrayOf>; template -struct LOffsetLArrayOf : ArrayOf, HBUINT32> {}; +using LOffsetLArrayOf = ArrayOf, HBUINT32>; /* Array of offsets relative to the beginning of the array itself. */ template @@ -661,20 +725,15 @@ struct OffsetListOf : OffsetArrayOf if (unlikely (!out)) return_trace (false); unsigned int count = this->len; for (unsigned int i = 0; i < count; i++) - out->arrayZ[i].serialize_subset (c, (*this)[i], out); + out->arrayZ[i].serialize_subset (c, this->arrayZ[i], this, out); return_trace (true); } - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (OffsetArrayOf::sanitize (c, this)); - } - template - bool sanitize (hb_sanitize_context_t *c, T user_data) const + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { TRACE_SANITIZE (this); - return_trace (OffsetArrayOf::sanitize (c, this, user_data)); + return_trace (OffsetArrayOf::sanitize (c, this, hb_forward (ds)...)); } }; @@ -684,7 +743,7 @@ struct HeadlessArrayOf { static constexpr unsigned item_size = Type::static_size; - HB_NO_CREATE_COPY_ASSIGN_TEMPLATE2 (HeadlessArrayOf, Type, LenType); + HB_DELETE_CREATE_COPY_ASSIGN (HeadlessArrayOf); const Type& operator [] (int i_) const { @@ -699,34 +758,53 @@ struct HeadlessArrayOf return arrayZ[i-1]; } unsigned int get_size () const - { return lenP1.static_size + (lenP1 ? lenP1 - 1 : 0) * Type::static_size; } + { return lenP1.static_size + get_length () * Type::static_size; } - bool serialize (hb_serialize_context_t *c, - hb_array_t items) + unsigned get_length () const { return lenP1 ? lenP1 - 1 : 0; } + + hb_array_t< Type> as_array () { return hb_array (arrayZ, get_length ()); } + hb_array_t as_array () const { return hb_array (arrayZ, get_length ()); } + + /* Iterator. */ + typedef hb_array_t iter_t; + typedef hb_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } + + bool serialize (hb_serialize_context_t *c, unsigned int items_len) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - lenP1.set (items.length + 1); /* TODO(serialize) Overflow? */ + c->check_assign (lenP1, items_len + 1); if (unlikely (!c->extend (*this))) return_trace (false); - for (unsigned int i = 0; i < items.length; i++) - arrayZ[i] = items[i]; + return_trace (true); + } + template + bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + unsigned count = items.len (); + if (unlikely (!serialize (c, count))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; return_trace (true); } - bool sanitize (hb_sanitize_context_t *c) const + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return_trace (false); - - /* Note: for structs that do not reference other structs, - * we do not need to call their sanitize() as we already did - * a bound check on the aggregate array size. We just include - * a small unreachable expression to make sure the structs - * pointed to do have a simple sanitize(), ie. they do not - * reference other structs via offsets. - */ - (void) (false && arrayZ[0].sanitize (c)); - + if (!sizeof... (Ts) && hb_is_trivially_copyable (Type)) return_trace (true); + unsigned int count = get_length (); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], hb_forward (ds)...))) + return_trace (false); return_trace (true); } @@ -740,7 +818,7 @@ struct HeadlessArrayOf public: LenType lenP1; - Type arrayZ[VAR]; + Type arrayZ[HB_VAR_ARRAY]; public: DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); }; @@ -749,7 +827,7 @@ struct HeadlessArrayOf template struct ArrayOfM1 { - HB_NO_CREATE_COPY_ASSIGN_TEMPLATE2 (ArrayOfM1, Type, LenType); + HB_DELETE_CREATE_COPY_ASSIGN (ArrayOfM1); const Type& operator [] (int i_) const { @@ -766,14 +844,14 @@ struct ArrayOfM1 unsigned int get_size () const { return lenM1.static_size + (lenM1 + 1) * Type::static_size; } - template - bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return_trace (false); unsigned int count = lenM1 + 1; for (unsigned int i = 0; i < count; i++) - if (unlikely (!arrayZ[i].sanitize (c, base, user_data))) + if (unlikely (!c->dispatch (arrayZ[i], hb_forward (ds)...))) return_trace (false); return_trace (true); } @@ -788,7 +866,7 @@ struct ArrayOfM1 public: LenType lenM1; - Type arrayZ[VAR]; + Type arrayZ[HB_VAR_ARRAY]; public: DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); }; @@ -797,21 +875,40 @@ struct ArrayOfM1 template struct SortedArrayOf : ArrayOf { - hb_sorted_array_t as_array () - { return hb_sorted_array (this->arrayZ, this->len); } - hb_sorted_array_t as_array () const - { return hb_sorted_array (this->arrayZ, this->len); } - operator hb_sorted_array_t () { return as_array (); } - operator hb_sorted_array_t () const { return as_array (); } + hb_sorted_array_t< Type> as_array () { return hb_sorted_array (this->arrayZ, this->len); } + hb_sorted_array_t as_array () const { return hb_sorted_array (this->arrayZ, this->len); } + + /* Iterator. */ + typedef hb_sorted_array_t iter_t; + typedef hb_sorted_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } + + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int count) const + { return as_array ().sub_array (start_offset, count); } + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const + { return as_array ().sub_array (start_offset, count); } + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int count) + { return as_array ().sub_array (start_offset, count); } + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) + { return as_array ().sub_array (start_offset, count); } - hb_array_t sub_array (unsigned int start_offset, unsigned int count) const - { return as_array ().sub_array (start_offset, count);} - hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const - { return as_array ().sub_array (start_offset, count);} - hb_array_t sub_array (unsigned int start_offset, unsigned int count) - { return as_array ().sub_array (start_offset, count);} - hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) - { return as_array ().sub_array (start_offset, count);} + bool serialize (hb_serialize_context_t *c, unsigned int items_len) + { + TRACE_SERIALIZE (this); + bool ret = ArrayOf::serialize (c, items_len); + return_trace (ret); + } + template + bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + bool ret = ArrayOf::serialize (c, items); + return_trace (ret); + } template Type &bsearch (const T &x, Type ¬_found = Crap (Type)) @@ -821,8 +918,8 @@ struct SortedArrayOf : ArrayOf { return *as_array ().bsearch (x, ¬_found); } template bool bfind (const T &x, unsigned int *i = nullptr, - hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE, - unsigned int to_store = (unsigned int) -1) const + hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const { return as_array ().bfind (x, i, not_found, to_store); } }; @@ -841,15 +938,16 @@ struct BinSearchHeader return_trace (c->check_struct (this)); } - void set (unsigned int v) + BinSearchHeader& operator = (unsigned int v) { - len.set (v); + len = v; assert (len == v); - entrySelector.set (MAX (1u, hb_bit_storage (v)) - 1); - searchRange.set (16 * (1u << entrySelector)); - rangeShift.set (v * 16 > searchRange - ? 16 * v - searchRange - : 0); + entrySelector = hb_max (1u, hb_bit_storage (v)) - 1; + searchRange = 16 * (1u << entrySelector); + rangeShift = v * 16 > searchRange + ? 16 * v - searchRange + : 0; + return *this; } protected: @@ -863,7 +961,7 @@ struct BinSearchHeader }; template -struct BinSearchArrayOf : SortedArrayOf > {}; +using BinSearchArrayOf = SortedArrayOf>; struct VarSizedBinSearchHeader @@ -893,7 +991,7 @@ struct VarSizedBinSearchArrayOf { static constexpr unsigned item_size = Type::static_size; - HB_NO_CREATE_COPY_ASSIGN_TEMPLATE (VarSizedBinSearchArrayOf, Type); + HB_DELETE_CREATE_COPY_ASSIGN (VarSizedBinSearchArrayOf); bool last_is_terminator () const { @@ -928,40 +1026,15 @@ struct VarSizedBinSearchArrayOf unsigned int get_size () const { return header.static_size + header.nUnits * header.unitSize; } - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c))) return_trace (false); - - /* Note: for structs that do not reference other structs, - * we do not need to call their sanitize() as we already did - * a bound check on the aggregate array size. We just include - * a small unreachable expression to make sure the structs - * pointed to do have a simple sanitize(), ie. they do not - * reference other structs via offsets. - */ - (void) (false && StructAtOffset (&bytesZ, 0).sanitize (c)); - - return_trace (true); - } - bool sanitize (hb_sanitize_context_t *c, const void *base) const + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable (Type)) return_trace (true); unsigned int count = get_length (); for (unsigned int i = 0; i < count; i++) - if (unlikely (!(*this)[i].sanitize (c, base))) - return_trace (false); - return_trace (true); - } - template - bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const - { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c))) return_trace (false); - unsigned int count = get_length (); - for (unsigned int i = 0; i < count; i++) - if (unlikely (!(*this)[i].sanitize (c, base, user_data))) + if (unlikely (!(*this)[i].sanitize (c, hb_forward (ds)...))) return_trace (false); return_trace (true); } @@ -969,18 +1042,15 @@ struct VarSizedBinSearchArrayOf template const Type *bsearch (const T &key) const { - unsigned int size = header.unitSize; - int min = 0, max = (int) get_length () - 1; - while (min <= max) - { - int mid = ((unsigned int) min + (unsigned int) max) / 2; - const Type *p = (const Type *) (((const char *) &bytesZ) + (mid * size)); - int c = p->cmp (key); - if (c < 0) max = mid - 1; - else if (c > 0) min = mid + 1; - else return p; - } - return nullptr; + unsigned pos; + return hb_bsearch_impl (&pos, + key, + (const void *) bytesZ, + get_length (), + header.unitSize, + _hb_cmp_method) + ? (const Type *) (((const char *) &bytesZ) + (pos * header.unitSize)) + : nullptr; } private: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh index d278e03d930..28073724efc 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh @@ -27,6 +27,7 @@ #define HB_OT_CFF_COMMON_HH #include "hb-open-type.hh" +#include "hb-bimap.hh" #include "hb-ot-layout-common.hh" #include "hb-cff-interp-dict-common.hh" #include "hb-subset-plan.hh" @@ -37,16 +38,19 @@ using namespace OT; #define CFF_UNDEF_CODE 0xFFFFFFFF +using objidx_t = hb_serialize_context_t::objidx_t; +using whence_t = hb_serialize_context_t::whence_t; + /* utility macro */ template -static inline const Type& StructAtOffsetOrNull(const void *P, unsigned int offset) -{ return offset? (* reinterpret_cast ((const char *) P + offset)): Null(Type); } +static inline const Type& StructAtOffsetOrNull (const void *P, unsigned int offset) +{ return offset ? StructAtOffset (P, offset) : Null (Type); } -inline unsigned int calcOffSize(unsigned int dataSize) +inline unsigned int calcOffSize (unsigned int dataSize) { unsigned int size = 1; unsigned int offset = dataSize + 1; - while ((offset & ~0xFF) != 0) + while (offset & ~0xFF) { size++; offset >>= 8; @@ -57,8 +61,8 @@ inline unsigned int calcOffSize(unsigned int dataSize) struct code_pair_t { - hb_codepoint_t code; - hb_codepoint_t glyph; + hb_codepoint_t code; + hb_codepoint_t glyph; }; typedef hb_vector_t str_buff_t; @@ -82,27 +86,20 @@ struct str_buff_vec_t : hb_vector_t template struct CFFIndex { - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (likely ((count.sanitize (c) && count == 0) || /* empty INDEX */ - (c->check_struct (this) && offSize >= 1 && offSize <= 4 && - c->check_array (offsets, offSize, count + 1) && - c->check_array ((const HBUINT8*)data_base (), 1, max_offset () - 1)))); - } - static unsigned int calculate_offset_array_size (unsigned int offSize, unsigned int count) { return offSize * (count + 1); } unsigned int offset_array_size () const { return calculate_offset_array_size (offSize, count); } - static unsigned int calculate_serialized_size (unsigned int offSize, unsigned int count, unsigned int dataSize) + CFFIndex *copy (hb_serialize_context_t *c) const { - if (count == 0) - return COUNT::static_size; - else - return min_size + calculate_offset_array_size (offSize, count) + dataSize; + TRACE_SERIALIZE (this); + unsigned int size = get_size (); + CFFIndex *out = c->allocate_size (size); + if (likely (out)) + memcpy (out, this, size); + return_trace (out); } bool serialize (hb_serialize_context_t *c, const CFFIndex &src) @@ -110,7 +107,7 @@ struct CFFIndex TRACE_SERIALIZE (this); unsigned int size = src.get_size (); CFFIndex *dest = c->allocate_size (size); - if (unlikely (dest == nullptr)) return_trace (false); + if (unlikely (!dest)) return_trace (false); memcpy (dest, &src, size); return_trace (true); } @@ -123,16 +120,16 @@ struct CFFIndex if (byteArray.length == 0) { COUNT *dest = c->allocate_min (); - if (unlikely (dest == nullptr)) return_trace (false); - dest->set (0); + if (unlikely (!dest)) return_trace (false); + *dest = 0; } else { /* serialize CFFIndex header */ if (unlikely (!c->extend_min (*this))) return_trace (false); - this->count.set (byteArray.length); - this->offSize.set (offSize_); - if (!unlikely (c->allocate_size (offSize_ * (byteArray.length + 1)))) + this->count = byteArray.length; + this->offSize = offSize_; + if (unlikely (!c->allocate_size (offSize_ * (byteArray.length + 1)))) return_trace (false); /* serialize indices */ @@ -149,9 +146,8 @@ struct CFFIndex for (unsigned int i = 0; i < byteArray.length; i++) { const byte_str_t &bs = byteArray[i]; - unsigned char *dest = c->allocate_size (bs.length); - if (unlikely (dest == nullptr)) - return_trace (false); + unsigned char *dest = c->allocate_size (bs.length); + if (unlikely (!dest)) return_trace (false); memcpy (dest, &bs[0], bs.length); } } @@ -166,14 +162,77 @@ struct CFFIndex byteArray.init (); byteArray.resize (buffArray.length); for (unsigned int i = 0; i < byteArray.length; i++) - { - byteArray[i] = byte_str_t (buffArray[i].arrayZ (), buffArray[i].length); - } + byteArray[i] = byte_str_t (buffArray[i].arrayZ, buffArray[i].length); bool result = this->serialize (c, offSize_, byteArray); byteArray.fini (); return result; } + template + bool serialize (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + if (it.len () == 0) + { + COUNT *dest = c->allocate_min (); + if (unlikely (!dest)) return_trace (false); + *dest = 0; + } + else + { + serialize_header(c, + it | hb_map ([] (const byte_str_t &_) { return _.length; })); + for (const byte_str_t &_ : +it) + _.copy (c); + } + return_trace (true); + } + + bool serialize (hb_serialize_context_t *c, + const byte_str_array_t &byteArray) + { return serialize (c, + hb_iter (byteArray)); } + + bool serialize (hb_serialize_context_t *c, + const str_buff_vec_t &buffArray) + { + auto it = + + hb_iter (buffArray) + | hb_map ([] (const str_buff_t &_) { return byte_str_t (_.arrayZ, _.length); }) + ; + return serialize (c, it); + } + + template + bool serialize_header (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + + unsigned total = + it | hb_reduce (hb_add, 0); + unsigned off_size = calcOffSize (total); + + /* serialize CFFIndex header */ + if (unlikely (!c->extend_min (*this))) return_trace (false); + this->count = it.len (); + this->offSize = off_size; + if (unlikely (!c->allocate_size (off_size * (it.len () + 1)))) + return_trace (false); + + /* serialize indices */ + unsigned int offset = 1; + unsigned int i = 0; + for (unsigned _ : +it) + { + CFFIndex::set_offset_at (i++, offset); + offset += _; + } + CFFIndex::set_offset_at (i, offset); + + return_trace (true); + } + void set_offset_at (unsigned int index, unsigned int offset) { HBUINT8 *p = offsets + offSize * index + offSize; @@ -181,7 +240,7 @@ struct CFFIndex for (; size; size--) { --p; - p->set (offset & 0xFF); + *p = offset & 0xFF; offset >>= 8; } } @@ -199,37 +258,38 @@ struct CFFIndex unsigned int length_at (unsigned int index) const { - if (likely ((offset_at (index + 1) >= offset_at (index)) && - (offset_at (index + 1) <= offset_at (count)))) - return offset_at (index + 1) - offset_at (index); - else - return 0; + if (unlikely ((offset_at (index + 1) < offset_at (index)) || + (offset_at (index + 1) > offset_at (count)))) + return 0; + return offset_at (index + 1) - offset_at (index); } const unsigned char *data_base () const - { return (const unsigned char *)this + min_size + offset_array_size (); } + { return (const unsigned char *) this + min_size + offset_array_size (); } unsigned int data_size () const { return HBINT8::static_size; } byte_str_t operator [] (unsigned int index) const { - if (likely (index < count)) - return byte_str_t (data_base () + offset_at (index) - 1, length_at (index)); - else - return Null(byte_str_t); + if (unlikely (index >= count)) return Null (byte_str_t); + return byte_str_t (data_base () + offset_at (index) - 1, length_at (index)); } unsigned int get_size () const { - if (this != &Null(CFFIndex)) - { - if (count > 0) - return min_size + offset_array_size () + (offset_at (count) - 1); - else - return count.static_size; /* empty CFFIndex contains count only */ - } - else - return 0; + if (this == &Null (CFFIndex)) return 0; + if (count > 0) + return min_size + offset_array_size () + (offset_at (count) - 1); + return count.static_size; /* empty CFFIndex contains count only */ + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely ((c->check_struct (this) && count == 0) || /* empty INDEX */ + (c->check_struct (this) && offSize >= 1 && offSize <= 4 && + c->check_array (offsets, offSize, count + 1) && + c->check_array ((const HBUINT8*) data_base (), 1, max_offset () - 1)))); } protected: @@ -245,10 +305,11 @@ struct CFFIndex } public: - COUNT count; /* Number of object data. Note there are (count+1) offsets */ - HBUINT8 offSize; /* The byte size of each offset in the offsets array. */ - HBUINT8 offsets[VAR]; /* The array of (count + 1) offsets into objects array (1-base). */ - /* HBUINT8 data[VAR]; Object data */ + COUNT count; /* Number of object data. Note there are (count+1) offsets */ + HBUINT8 offSize; /* The byte size of each offset in the offsets array. */ + HBUINT8 offsets[HB_VAR_ARRAY]; + /* The array of (count + 1) offsets into objects array (1-base). */ + /* HBUINT8 data[HB_VAR_ARRAY]; Object data */ public: DEFINE_SIZE_ARRAY (COUNT::static_size + HBUINT8::static_size, offsets); }; @@ -260,7 +321,7 @@ struct CFFIndexOf : CFFIndex { if (likely (index < CFFIndex::count)) return byte_str_t (CFFIndex::data_base () + CFFIndex::offset_at (index) - 1, CFFIndex::length_at (index)); - return Null(byte_str_t); + return Null (byte_str_t); } template @@ -275,9 +336,9 @@ struct CFFIndexOf : CFFIndex TRACE_SERIALIZE (this); /* serialize CFFIndex header */ if (unlikely (!c->extend_min (*this))) return_trace (false); - this->count.set (dataArrayLen); - this->offSize.set (offSize_); - if (!unlikely (c->allocate_size (offSize_ * (dataArrayLen + 1)))) + this->count = dataArrayLen; + this->offSize = offSize_; + if (unlikely (!c->allocate_size (offSize_ * (dataArrayLen + 1)))) return_trace (false); /* serialize indices */ @@ -293,112 +354,74 @@ struct CFFIndexOf : CFFIndex /* serialize data */ for (unsigned int i = 0; i < dataArrayLen; i++) { - TYPE *dest = c->start_embed (); - if (unlikely (dest == nullptr || - !dest->serialize (c, dataArray[i], param1, param2))) + TYPE *dest = c->start_embed (); + if (unlikely (!dest || !dest->serialize (c, dataArray[i], param1, param2))) return_trace (false); } return_trace (true); } - - /* in parallel to above */ - template - static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */, - const DATA *dataArray, - unsigned int dataArrayLen, - hb_vector_t &dataSizeArray, /* OUT */ - const PARAM ¶m) - { - /* determine offset size */ - unsigned int totalDataSize = 0; - for (unsigned int i = 0; i < dataArrayLen; i++) - { - unsigned int dataSize = TYPE::calculate_serialized_size (dataArray[i], param); - dataSizeArray[i] = dataSize; - totalDataSize += dataSize; - } - offSize_ = calcOffSize (totalDataSize); - - return CFFIndex::calculate_serialized_size (offSize_, dataArrayLen, totalDataSize); - } }; /* Top Dict, Font Dict, Private Dict */ struct Dict : UnsizedByteStr { - template + template bool serialize (hb_serialize_context_t *c, const DICTVAL &dictval, OP_SERIALIZER& opszr, - PARAM& param) + Ts&&... ds) { TRACE_SERIALIZE (this); for (unsigned int i = 0; i < dictval.get_count (); i++) - { - if (unlikely (!opszr.serialize (c, dictval[i], param))) + if (unlikely (!opszr.serialize (c, dictval[i], hb_forward (ds)...))) return_trace (false); - } - return_trace (true); - } - /* in parallel to above */ - template - static unsigned int calculate_serialized_size (const DICTVAL &dictval, - OP_SERIALIZER& opszr, - PARAM& param) - { - unsigned int size = 0; - for (unsigned int i = 0; i < dictval.get_count (); i++) - size += opszr.calculate_serialized_size (dictval[i], param); - return size; - } - - template - static unsigned int calculate_serialized_size (const DICTVAL &dictval, - OP_SERIALIZER& opszr) - { - unsigned int size = 0; - for (unsigned int i = 0; i < dictval.get_count (); i++) - size += opszr.calculate_serialized_size (dictval[i]); - return size; + return_trace (true); } - template - static bool serialize_int_op (hb_serialize_context_t *c, op_code_t op, int value, op_code_t intOp) + template + static bool serialize_int_op (hb_serialize_context_t *c, op_code_t op, V value, op_code_t intOp) { // XXX: not sure why but LLVM fails to compile the following 'unlikely' macro invocation - if (/*unlikely*/ (!serialize_int (c, intOp, value))) + if (/*unlikely*/ (!serialize_int (c, intOp, value))) return false; TRACE_SERIALIZE (this); /* serialize the opcode */ HBUINT8 *p = c->allocate_size (OpCode_Size (op)); - if (unlikely (p == nullptr)) return_trace (false); + if (unlikely (!p)) return_trace (false); if (Is_OpCode_ESC (op)) { - p->set (OpCode_escape); + *p = OpCode_escape; op = Unmake_OpCode_ESC (op); p++; } - p->set (op); + *p = op; return_trace (true); } - static bool serialize_uint4_op (hb_serialize_context_t *c, op_code_t op, int value) - { return serialize_int_op (c, op, value, OpCode_longintdict); } + template + static bool serialize_int4_op (hb_serialize_context_t *c, op_code_t op, V value) + { return serialize_int_op (c, op, value, OpCode_longintdict); } - static bool serialize_uint2_op (hb_serialize_context_t *c, op_code_t op, int value) - { return serialize_int_op (c, op, value, OpCode_shortint); } + template + static bool serialize_int2_op (hb_serialize_context_t *c, op_code_t op, V value) + { return serialize_int_op (c, op, value, OpCode_shortint); } - static bool serialize_offset4_op (hb_serialize_context_t *c, op_code_t op, int value) + template + static bool serialize_link_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence) { - return serialize_uint4_op (c, op, value); + T &ofs = *(T *) (c->head + OpCode_Size (int_op)); + if (unlikely (!serialize_int_op (c, op, 0, int_op))) return false; + c->add_link (ofs, link, whence); + return true; } - static bool serialize_offset2_op (hb_serialize_context_t *c, op_code_t op, int value) - { - return serialize_uint2_op (c, op, value); - } + static bool serialize_link4_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence = whence_t::Head) + { return serialize_link_op (c, op, link, whence); } + + static bool serialize_link2_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence = whence_t::Head) + { return serialize_link_op (c, op, link, whence); } }; struct TopDict : Dict {}; @@ -407,155 +430,39 @@ struct PrivateDict : Dict {}; struct table_info_t { - void init () { offSize = offset = size = 0; } + void init () { offset = size = 0; link = 0; } unsigned int offset; unsigned int size; - unsigned int offSize; -}; - -/* used to remap font index or SID from fullset to subset. - * set to CFF_UNDEF_CODE if excluded from subset */ -struct remap_t : hb_vector_t -{ - void init () { SUPER::init (); } - - void fini () { SUPER::fini (); } - - bool reset (unsigned int size) - { - if (unlikely (!SUPER::resize (size))) - return false; - for (unsigned int i = 0; i < length; i++) - (*this)[i] = CFF_UNDEF_CODE; - count = 0; - return true; - } - - bool identity (unsigned int size) - { - if (unlikely (!SUPER::resize (size))) - return false; - unsigned int i; - for (i = 0; i < length; i++) - (*this)[i] = i; - count = i; - return true; - } - - bool excludes (hb_codepoint_t id) const - { return (id < length) && ((*this)[id] == CFF_UNDEF_CODE); } - - bool includes (hb_codepoint_t id) const - { return !excludes (id); } - - unsigned int add (unsigned int i) - { - if ((*this)[i] == CFF_UNDEF_CODE) - (*this)[i] = count++; - return (*this)[i]; - } - - hb_codepoint_t get_count () const { return count; } - - protected: - hb_codepoint_t count; - - private: - typedef hb_vector_t SUPER; + objidx_t link; }; template struct FDArray : CFFIndexOf { - /* used by CFF1 */ - template + template bool serialize (hb_serialize_context_t *c, - unsigned int offSize_, - const hb_vector_t &fontDicts, + Iterator it, OP_SERIALIZER& opszr) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); - this->count.set (fontDicts.length); - this->offSize.set (offSize_); - if (!unlikely (c->allocate_size (offSize_ * (fontDicts.length + 1)))) - return_trace (false); - - /* serialize font dict offsets */ - unsigned int offset = 1; - unsigned int fid = 0; - for (; fid < fontDicts.length; fid++) - { - CFFIndexOf::set_offset_at (fid, offset); - offset += FontDict::calculate_serialized_size (fontDicts[fid], opszr); - } - CFFIndexOf::set_offset_at (fid, offset); - /* serialize font dicts */ - for (unsigned int i = 0; i < fontDicts.length; i++) + /* serialize INDEX data */ + hb_vector_t sizes; + c->push (); + + it + | hb_map ([&] (const hb_pair_t &_) { FontDict *dict = c->start_embed (); - if (unlikely (!dict->serialize (c, fontDicts[i], opszr, fontDicts[i]))) - return_trace (false); - } - return_trace (true); - } - - /* used by CFF2 */ - template - bool serialize (hb_serialize_context_t *c, - unsigned int offSize_, - const hb_vector_t &fontDicts, - unsigned int fdCount, - const remap_t &fdmap, - OP_SERIALIZER& opszr, - const hb_vector_t &privateInfos) - { - TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); - this->count.set (fdCount); - this->offSize.set (offSize_); - if (!unlikely (c->allocate_size (offSize_ * (fdCount + 1)))) - return_trace (false); - - /* serialize font dict offsets */ - unsigned int offset = 1; - unsigned int fid = 0; - for (unsigned i = 0; i < fontDicts.length; i++) - if (fdmap.includes (i)) - { - CFFIndexOf::set_offset_at (fid++, offset); - offset += FontDict::calculate_serialized_size (fontDicts[i], opszr); - } - CFFIndexOf::set_offset_at (fid, offset); + dict->serialize (c, _.first, opszr, _.second); + return c->head - (const char*)dict; + }) + | hb_sink (sizes) + ; + c->pop_pack (false); - /* serialize font dicts */ - for (unsigned int i = 0; i < fontDicts.length; i++) - if (fdmap.includes (i)) - { - FontDict *dict = c->start_embed (); - if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privateInfos[fdmap[i]]))) - return_trace (false); - } - return_trace (true); - } - - /* in parallel to above */ - template - static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */, - const hb_vector_t &fontDicts, - unsigned int fdCount, - const remap_t &fdmap, - OP_SERIALIZER& opszr) - { - unsigned int dictsSize = 0; - for (unsigned int i = 0; i < fontDicts.len; i++) - if (fdmap.includes (i)) - dictsSize += FontDict::calculate_serialized_size (fontDicts[i], opszr); - - offSize_ = calcOffSize (dictsSize); - return CFFIndex::calculate_serialized_size (offSize_, fdCount, dictsSize); + /* serialize INDEX header */ + return_trace (CFFIndex::serialize_header (c, hb_iter (sizes))); } }; @@ -574,21 +481,20 @@ struct FDSelect0 { } hb_codepoint_t get_fd (hb_codepoint_t glyph) const - { - return (hb_codepoint_t)fds[glyph]; - } + { return (hb_codepoint_t) fds[glyph]; } unsigned int get_size (unsigned int num_glyphs) const { return HBUINT8::static_size * num_glyphs; } - HBUINT8 fds[VAR]; + HBUINT8 fds[HB_VAR_ARRAY]; - DEFINE_SIZE_MIN (1); + DEFINE_SIZE_MIN (0); }; template -struct FDSelect3_4_Range { - bool sanitize (hb_sanitize_context_t *c, const void */*nullptr*/, unsigned int fdcount) const +struct FDSelect3_4_Range +{ + bool sanitize (hb_sanitize_context_t *c, const void * /*nullptr*/, unsigned int fdcount) const { TRACE_SANITIZE (this); return_trace (first < c->get_num_glyphs () && (fd < fdcount)); @@ -596,12 +502,13 @@ struct FDSelect3_4_Range { GID_TYPE first; FD_TYPE fd; - + public: DEFINE_SIZE_STATIC (GID_TYPE::static_size + FD_TYPE::static_size); }; template -struct FDSelect3_4 { +struct FDSelect3_4 +{ unsigned int get_size () const { return GID_TYPE::static_size * 2 + ranges.get_size (); } @@ -613,10 +520,8 @@ struct FDSelect3_4 { return_trace (false); for (unsigned int i = 1; i < nRanges (); i++) - { if (unlikely (ranges[i - 1].first >= ranges[i].first)) - return_trace (false); - } + return_trace (false); if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ()))) return_trace (false); @@ -631,13 +536,13 @@ struct FDSelect3_4 { if (glyph < ranges[i].first) break; - return (hb_codepoint_t)ranges[i - 1].fd; + return (hb_codepoint_t) ranges[i - 1].fd; } - GID_TYPE &nRanges () { return ranges.len; } - GID_TYPE nRanges () const { return ranges.len; } - GID_TYPE &sentinel () { return StructAfter (ranges[nRanges () - 1]); } - const GID_TYPE &sentinel () const { return StructAfter (ranges[nRanges () - 1]); } + GID_TYPE &nRanges () { return ranges.len; } + GID_TYPE nRanges () const { return ranges.len; } + GID_TYPE &sentinel () { return StructAfter (ranges[nRanges () - 1]); } + const GID_TYPE &sentinel () const { return StructAfter (ranges[nRanges () - 1]); } ArrayOf, GID_TYPE> ranges; /* GID_TYPE sentinel */ @@ -648,56 +553,60 @@ struct FDSelect3_4 { typedef FDSelect3_4 FDSelect3; typedef FDSelect3_4_Range FDSelect3_Range; -struct FDSelect { - bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const - { - TRACE_SANITIZE (this); - - return_trace (likely (c->check_struct (this) && (format == 0 || format == 3) && - (format == 0)? - u.format0.sanitize (c, fdcount): - u.format3.sanitize (c, fdcount))); - } - +struct FDSelect +{ bool serialize (hb_serialize_context_t *c, const FDSelect &src, unsigned int num_glyphs) { TRACE_SERIALIZE (this); unsigned int size = src.get_size (num_glyphs); FDSelect *dest = c->allocate_size (size); - if (unlikely (dest == nullptr)) return_trace (false); + if (unlikely (!dest)) return_trace (false); memcpy (dest, &src, size); return_trace (true); } - unsigned int calculate_serialized_size (unsigned int num_glyphs) const - { return get_size (num_glyphs); } - unsigned int get_size (unsigned int num_glyphs) const { - unsigned int size = format.static_size; - if (format == 0) - size += u.format0.get_size (num_glyphs); - else - size += u.format3.get_size (); - return size; + switch (format) + { + case 0: return format.static_size + u.format0.get_size (num_glyphs); + case 3: return format.static_size + u.format3.get_size (); + default:return 0; + } } hb_codepoint_t get_fd (hb_codepoint_t glyph) const { - if (this == &Null(FDSelect)) - return 0; - if (format == 0) - return u.format0.get_fd (glyph); - else - return u.format3.get_fd (glyph); + if (this == &Null (FDSelect)) return 0; + + switch (format) + { + case 0: return u.format0.get_fd (glyph); + case 3: return u.format3.get_fd (glyph); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, fdcount)); + case 3: return_trace (u.format3.sanitize (c, fdcount)); + default:return_trace (false); + } } HBUINT8 format; union { - FDSelect0 format0; - FDSelect3 format3; + FDSelect0 format0; + FDSelect3 format3; } u; - + public: DEFINE_SIZE_MIN (1); }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-std-str.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-std-str.hh new file mode 100644 index 00000000000..65d56ae18b5 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-std-str.hh @@ -0,0 +1,425 @@ +/* + * Copyright © 2019 Adobe, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_CFF1_STD_STR_HH +#if 0 /* Make checks happy. */ +#define HB_OT_CFF1_STD_STR_HH +#include "hb.hh" +#endif + +_S(".notdef") +_S("space") +_S("exclam") +_S("quotedbl") +_S("numbersign") +_S("dollar") +_S("percent") +_S("ampersand") +_S("quoteright") +_S("parenleft") +_S("parenright") +_S("asterisk") +_S("plus") +_S("comma") +_S("hyphen") +_S("period") +_S("slash") +_S("zero") +_S("one") +_S("two") +_S("three") +_S("four") +_S("five") +_S("six") +_S("seven") +_S("eight") +_S("nine") +_S("colon") +_S("semicolon") +_S("less") +_S("equal") +_S("greater") +_S("question") +_S("at") +_S("A") +_S("B") +_S("C") +_S("D") +_S("E") +_S("F") +_S("G") +_S("H") +_S("I") +_S("J") +_S("K") +_S("L") +_S("M") +_S("N") +_S("O") +_S("P") +_S("Q") +_S("R") +_S("S") +_S("T") +_S("U") +_S("V") +_S("W") +_S("X") +_S("Y") +_S("Z") +_S("bracketleft") +_S("backslash") +_S("bracketright") +_S("asciicircum") +_S("underscore") +_S("quoteleft") +_S("a") +_S("b") +_S("c") +_S("d") +_S("e") +_S("f") +_S("g") +_S("h") +_S("i") +_S("j") +_S("k") +_S("l") +_S("m") +_S("n") +_S("o") +_S("p") +_S("q") +_S("r") +_S("s") +_S("t") +_S("u") +_S("v") +_S("w") +_S("x") +_S("y") +_S("z") +_S("braceleft") +_S("bar") +_S("braceright") +_S("asciitilde") +_S("exclamdown") +_S("cent") +_S("sterling") +_S("fraction") +_S("yen") +_S("florin") +_S("section") +_S("currency") +_S("quotesingle") +_S("quotedblleft") +_S("guillemotleft") +_S("guilsinglleft") +_S("guilsinglright") +_S("fi") +_S("fl") +_S("endash") +_S("dagger") +_S("daggerdbl") +_S("periodcentered") +_S("paragraph") +_S("bullet") +_S("quotesinglbase") +_S("quotedblbase") +_S("quotedblright") +_S("guillemotright") +_S("ellipsis") +_S("perthousand") +_S("questiondown") +_S("grave") +_S("acute") +_S("circumflex") +_S("tilde") +_S("macron") +_S("breve") +_S("dotaccent") +_S("dieresis") +_S("ring") +_S("cedilla") +_S("hungarumlaut") +_S("ogonek") +_S("caron") +_S("emdash") +_S("AE") +_S("ordfeminine") +_S("Lslash") +_S("Oslash") +_S("OE") +_S("ordmasculine") +_S("ae") +_S("dotlessi") +_S("lslash") +_S("oslash") +_S("oe") +_S("germandbls") +_S("onesuperior") +_S("logicalnot") +_S("mu") +_S("trademark") +_S("Eth") +_S("onehalf") +_S("plusminus") +_S("Thorn") +_S("onequarter") +_S("divide") +_S("brokenbar") +_S("degree") +_S("thorn") +_S("threequarters") +_S("twosuperior") +_S("registered") +_S("minus") +_S("eth") +_S("multiply") +_S("threesuperior") +_S("copyright") +_S("Aacute") +_S("Acircumflex") +_S("Adieresis") +_S("Agrave") +_S("Aring") +_S("Atilde") +_S("Ccedilla") +_S("Eacute") +_S("Ecircumflex") +_S("Edieresis") +_S("Egrave") +_S("Iacute") +_S("Icircumflex") +_S("Idieresis") +_S("Igrave") +_S("Ntilde") +_S("Oacute") +_S("Ocircumflex") +_S("Odieresis") +_S("Ograve") +_S("Otilde") +_S("Scaron") +_S("Uacute") +_S("Ucircumflex") +_S("Udieresis") +_S("Ugrave") +_S("Yacute") +_S("Ydieresis") +_S("Zcaron") +_S("aacute") +_S("acircumflex") +_S("adieresis") +_S("agrave") +_S("aring") +_S("atilde") +_S("ccedilla") +_S("eacute") +_S("ecircumflex") +_S("edieresis") +_S("egrave") +_S("iacute") +_S("icircumflex") +_S("idieresis") +_S("igrave") +_S("ntilde") +_S("oacute") +_S("ocircumflex") +_S("odieresis") +_S("ograve") +_S("otilde") +_S("scaron") +_S("uacute") +_S("ucircumflex") +_S("udieresis") +_S("ugrave") +_S("yacute") +_S("ydieresis") +_S("zcaron") +_S("exclamsmall") +_S("Hungarumlautsmall") +_S("dollaroldstyle") +_S("dollarsuperior") +_S("ampersandsmall") +_S("Acutesmall") +_S("parenleftsuperior") +_S("parenrightsuperior") +_S("twodotenleader") +_S("onedotenleader") +_S("zerooldstyle") +_S("oneoldstyle") +_S("twooldstyle") +_S("threeoldstyle") +_S("fouroldstyle") +_S("fiveoldstyle") +_S("sixoldstyle") +_S("sevenoldstyle") +_S("eightoldstyle") +_S("nineoldstyle") +_S("commasuperior") +_S("threequartersemdash") +_S("periodsuperior") +_S("questionsmall") +_S("asuperior") +_S("bsuperior") +_S("centsuperior") +_S("dsuperior") +_S("esuperior") +_S("isuperior") +_S("lsuperior") +_S("msuperior") +_S("nsuperior") +_S("osuperior") +_S("rsuperior") +_S("ssuperior") +_S("tsuperior") +_S("ff") +_S("ffi") +_S("ffl") +_S("parenleftinferior") +_S("parenrightinferior") +_S("Circumflexsmall") +_S("hyphensuperior") +_S("Gravesmall") +_S("Asmall") +_S("Bsmall") +_S("Csmall") +_S("Dsmall") +_S("Esmall") +_S("Fsmall") +_S("Gsmall") +_S("Hsmall") +_S("Ismall") +_S("Jsmall") +_S("Ksmall") +_S("Lsmall") +_S("Msmall") +_S("Nsmall") +_S("Osmall") +_S("Psmall") +_S("Qsmall") +_S("Rsmall") +_S("Ssmall") +_S("Tsmall") +_S("Usmall") +_S("Vsmall") +_S("Wsmall") +_S("Xsmall") +_S("Ysmall") +_S("Zsmall") +_S("colonmonetary") +_S("onefitted") +_S("rupiah") +_S("Tildesmall") +_S("exclamdownsmall") +_S("centoldstyle") +_S("Lslashsmall") +_S("Scaronsmall") +_S("Zcaronsmall") +_S("Dieresissmall") +_S("Brevesmall") +_S("Caronsmall") +_S("Dotaccentsmall") +_S("Macronsmall") +_S("figuredash") +_S("hypheninferior") +_S("Ogoneksmall") +_S("Ringsmall") +_S("Cedillasmall") +_S("questiondownsmall") +_S("oneeighth") +_S("threeeighths") +_S("fiveeighths") +_S("seveneighths") +_S("onethird") +_S("twothirds") +_S("zerosuperior") +_S("foursuperior") +_S("fivesuperior") +_S("sixsuperior") +_S("sevensuperior") +_S("eightsuperior") +_S("ninesuperior") +_S("zeroinferior") +_S("oneinferior") +_S("twoinferior") +_S("threeinferior") +_S("fourinferior") +_S("fiveinferior") +_S("sixinferior") +_S("seveninferior") +_S("eightinferior") +_S("nineinferior") +_S("centinferior") +_S("dollarinferior") +_S("periodinferior") +_S("commainferior") +_S("Agravesmall") +_S("Aacutesmall") +_S("Acircumflexsmall") +_S("Atildesmall") +_S("Adieresissmall") +_S("Aringsmall") +_S("AEsmall") +_S("Ccedillasmall") +_S("Egravesmall") +_S("Eacutesmall") +_S("Ecircumflexsmall") +_S("Edieresissmall") +_S("Igravesmall") +_S("Iacutesmall") +_S("Icircumflexsmall") +_S("Idieresissmall") +_S("Ethsmall") +_S("Ntildesmall") +_S("Ogravesmall") +_S("Oacutesmall") +_S("Ocircumflexsmall") +_S("Otildesmall") +_S("Odieresissmall") +_S("OEsmall") +_S("Oslashsmall") +_S("Ugravesmall") +_S("Uacutesmall") +_S("Ucircumflexsmall") +_S("Udieresissmall") +_S("Yacutesmall") +_S("Thornsmall") +_S("Ydieresissmall") +_S("001.000") +_S("001.001") +_S("001.002") +_S("001.003") +_S("Black") +_S("Bold") +_S("Book") +_S("Light") +_S("Medium") +_S("Regular") +_S("Roman") +_S("Semibold") + +#endif /* HB_OT_CFF1_STD_STR_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc index a5ee1f31f6f..24287364ba2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc @@ -24,11 +24,29 @@ * Adobe Author(s): Michiharu Ariza */ +#include "hb.hh" + +#ifndef HB_NO_CFF + +#include "hb-draw.hh" +#include "hb-algs.hh" #include "hb-ot-cff1-table.hh" #include "hb-cff1-interp-cs.hh" using namespace CFF; +struct sid_to_gid_t +{ + uint16_t sid; + uint8_t gid; + + int cmp (uint16_t a) const + { + if (a == sid) return 0; + return (a < sid) ? -1 : 1; + } +}; + /* SID to code */ static const uint8_t standard_encoding_to_code [] = { @@ -100,6 +118,80 @@ static const uint16_t expert_subset_charset_to_sid [] = 340, 341, 342, 343, 344, 345, 346 }; +/* SID to glyph ID */ +static const sid_to_gid_t expert_charset_sid_to_gid [] = +{ + { 1, 1 }, { 13, 12 }, { 14, 13 }, { 15, 14 }, + { 27, 26 }, { 28, 27 }, { 99, 15 }, { 109, 46 }, + { 110, 47 }, { 150, 111 }, { 155, 101 }, { 158, 100 }, + { 163, 102 }, { 164, 112 }, { 169, 113 }, { 229, 2 }, + { 230, 3 }, { 231, 4 }, { 232, 5 }, { 233, 6 }, + { 234, 7 }, { 235, 8 }, { 236, 9 }, { 237, 10 }, + { 238, 11 }, { 239, 16 }, { 240, 17 }, { 241, 18 }, + { 242, 19 }, { 243, 20 }, { 244, 21 }, { 245, 22 }, + { 246, 23 }, { 247, 24 }, { 248, 25 }, { 249, 28 }, + { 250, 29 }, { 251, 30 }, { 252, 31 }, { 253, 32 }, + { 254, 33 }, { 255, 34 }, { 256, 35 }, { 257, 36 }, + { 258, 37 }, { 259, 38 }, { 260, 39 }, { 261, 40 }, + { 262, 41 }, { 263, 42 }, { 264, 43 }, { 265, 44 }, + { 266, 45 }, { 267, 48 }, { 268, 49 }, { 269, 50 }, + { 270, 51 }, { 271, 52 }, { 272, 53 }, { 273, 54 }, + { 274, 55 }, { 275, 56 }, { 276, 57 }, { 277, 58 }, + { 278, 59 }, { 279, 60 }, { 280, 61 }, { 281, 62 }, + { 282, 63 }, { 283, 64 }, { 284, 65 }, { 285, 66 }, + { 286, 67 }, { 287, 68 }, { 288, 69 }, { 289, 70 }, + { 290, 71 }, { 291, 72 }, { 292, 73 }, { 293, 74 }, + { 294, 75 }, { 295, 76 }, { 296, 77 }, { 297, 78 }, + { 298, 79 }, { 299, 80 }, { 300, 81 }, { 301, 82 }, + { 302, 83 }, { 303, 84 }, { 304, 85 }, { 305, 86 }, + { 306, 87 }, { 307, 88 }, { 308, 89 }, { 309, 90 }, + { 310, 91 }, { 311, 92 }, { 312, 93 }, { 313, 94 }, + { 314, 95 }, { 315, 96 }, { 316, 97 }, { 317, 98 }, + { 318, 99 }, { 319, 103 }, { 320, 104 }, { 321, 105 }, + { 322, 106 }, { 323, 107 }, { 324, 108 }, { 325, 109 }, + { 326, 110 }, { 327, 114 }, { 328, 115 }, { 329, 116 }, + { 330, 117 }, { 331, 118 }, { 332, 119 }, { 333, 120 }, + { 334, 121 }, { 335, 122 }, { 336, 123 }, { 337, 124 }, + { 338, 125 }, { 339, 126 }, { 340, 127 }, { 341, 128 }, + { 342, 129 }, { 343, 130 }, { 344, 131 }, { 345, 132 }, + { 346, 133 }, { 347, 134 }, { 348, 135 }, { 349, 136 }, + { 350, 137 }, { 351, 138 }, { 352, 139 }, { 353, 140 }, + { 354, 141 }, { 355, 142 }, { 356, 143 }, { 357, 144 }, + { 358, 145 }, { 359, 146 }, { 360, 147 }, { 361, 148 }, + { 362, 149 }, { 363, 150 }, { 364, 151 }, { 365, 152 }, + { 366, 153 }, { 367, 154 }, { 368, 155 }, { 369, 156 }, + { 370, 157 }, { 371, 158 }, { 372, 159 }, { 373, 160 }, + { 374, 161 }, { 375, 162 }, { 376, 163 }, { 377, 164 }, + { 378, 165 } +}; + +/* SID to glyph ID */ +static const sid_to_gid_t expert_subset_charset_sid_to_gid [] = +{ + { 1, 1 }, { 13, 8 }, { 14, 9 }, { 15, 10 }, + { 27, 22 }, { 28, 23 }, { 99, 11 }, { 109, 41 }, + { 110, 42 }, { 150, 64 }, { 155, 55 }, { 158, 54 }, + { 163, 56 }, { 164, 65 }, { 169, 66 }, { 231, 2 }, + { 232, 3 }, { 235, 4 }, { 236, 5 }, { 237, 6 }, + { 238, 7 }, { 239, 12 }, { 240, 13 }, { 241, 14 }, + { 242, 15 }, { 243, 16 }, { 244, 17 }, { 245, 18 }, + { 246, 19 }, { 247, 20 }, { 248, 21 }, { 249, 24 }, + { 250, 25 }, { 251, 26 }, { 253, 27 }, { 254, 28 }, + { 255, 29 }, { 256, 30 }, { 257, 31 }, { 258, 32 }, + { 259, 33 }, { 260, 34 }, { 261, 35 }, { 262, 36 }, + { 263, 37 }, { 264, 38 }, { 265, 39 }, { 266, 40 }, + { 267, 43 }, { 268, 44 }, { 269, 45 }, { 270, 46 }, + { 272, 47 }, { 300, 48 }, { 301, 49 }, { 302, 50 }, + { 305, 51 }, { 314, 52 }, { 315, 53 }, { 320, 57 }, + { 321, 58 }, { 322, 59 }, { 323, 60 }, { 324, 61 }, + { 325, 62 }, { 326, 63 }, { 327, 67 }, { 328, 68 }, + { 329, 69 }, { 330, 70 }, { 331, 71 }, { 332, 72 }, + { 333, 73 }, { 334, 74 }, { 335, 75 }, { 336, 76 }, + { 337, 77 }, { 338, 78 }, { 339, 79 }, { 340, 80 }, + { 341, 81 }, { 342, 82 }, { 343, 83 }, { 344, 84 }, + { 345, 85 }, { 346, 86 } +}; + /* code to SID */ static const uint8_t standard_encoding_to_sid [] = { @@ -153,6 +245,18 @@ hb_codepoint_t OT::cff1::lookup_expert_subset_charset_for_sid (hb_codepoint_t gl return 0; } +hb_codepoint_t OT::cff1::lookup_expert_charset_for_glyph (hb_codepoint_t sid) +{ + const auto *pair = hb_sorted_array (expert_charset_sid_to_gid).bsearch (sid); + return pair ? pair->gid : 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_subset_charset_for_glyph (hb_codepoint_t sid) +{ + const auto *pair = hb_sorted_array (expert_subset_charset_sid_to_gid).bsearch (sid); + return pair ? pair->gid : 0; +} + hb_codepoint_t OT::cff1::lookup_standard_encoding_for_sid (hb_codepoint_t code) { if (code < ARRAY_LENGTH (standard_encoding_to_sid)) @@ -165,8 +269,8 @@ struct bounds_t { void init () { - min.set_int (0x7FFFFFFF, 0x7FFFFFFF); - max.set_int (-0x80000000, -0x80000000); + min.set_int (INT_MAX, INT_MAX); + max.set_int (INT_MIN, INT_MIN); } void update (const point_t &pt) @@ -199,14 +303,13 @@ struct bounds_t } } - bool empty () const - { return (min.x >= max.x) || (min.y >= max.y); } + bool empty () const { return (min.x >= max.x) || (min.y >= max.y); } point_t min; point_t max; }; -struct extents_param_t +struct cff1_extents_param_t { void init (const OT::cff1::accelerator_t *_cff) { @@ -215,25 +318,25 @@ struct extents_param_t bounds.init (); } - void start_path () { path_open = true; } - void end_path () { path_open = false; } + void start_path () { path_open = true; } + void end_path () { path_open = false; } bool is_path_open () const { return path_open; } - bool path_open; - bounds_t bounds; + bool path_open; + bounds_t bounds; const OT::cff1::accelerator_t *cff; }; -struct cff1_path_procs_extents_t : path_procs_t +struct cff1_path_procs_extents_t : path_procs_t { - static void moveto (cff1_cs_interp_env_t &env, extents_param_t& param, const point_t &pt) + static void moveto (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt) { param.end_path (); env.moveto (pt); } - static void line (cff1_cs_interp_env_t &env, extents_param_t& param, const point_t &pt1) + static void line (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt1) { if (!param.is_path_open ()) { @@ -244,7 +347,7 @@ struct cff1_path_procs_extents_t : path_procs_t +struct cff1_cs_opset_extents_t : cff1_cs_opset_t { - static void process_seac (cff1_cs_interp_env_t &env, extents_param_t& param) + static void process_seac (cff1_cs_interp_env_t &env, cff1_extents_param_t& param) { unsigned int n = env.argStack.get_count (); point_t delta; @@ -292,20 +395,25 @@ bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, boun if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false; unsigned int fd = cff->fdSelect->get_fd (glyph); - cff1_cs_interpreter_t interp; + cff1_cs_interpreter_t interp; const byte_str_t str = (*cff->charStrings)[glyph]; interp.env.init (str, *cff, fd); interp.env.set_in_seac (in_seac); - extents_param_t param; + cff1_extents_param_t param; param.init (cff); if (unlikely (!interp.interpret (param))) return false; bounds = param.bounds; return true; } -bool OT::cff1::accelerator_t::get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const +bool OT::cff1::accelerator_t::get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const { - bounds_t bounds; +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + bounds_t bounds; if (!_get_bounds (this, glyph, bounds)) return false; @@ -317,8 +425,8 @@ bool OT::cff1::accelerator_t::get_extents (hb_codepoint_t glyph, hb_glyph_extent } else { - extents->x_bearing = (int32_t)bounds.min.x.floor (); - extents->width = (int32_t)bounds.max.x.ceil () - extents->x_bearing; + extents->x_bearing = font->em_scalef_x (bounds.min.x.to_real ()); + extents->width = font->em_scalef_x (bounds.max.x.to_real () - bounds.min.x.to_real ()); } if (bounds.min.y >= bounds.max.y) { @@ -327,13 +435,137 @@ bool OT::cff1::accelerator_t::get_extents (hb_codepoint_t glyph, hb_glyph_extent } else { - extents->y_bearing = (int32_t)bounds.max.y.ceil (); - extents->height = (int32_t)bounds.min.y.floor () - extents->y_bearing; + extents->y_bearing = font->em_scalef_y (bounds.max.y.to_real ()); + extents->height = font->em_scalef_y (bounds.min.y.to_real () - bounds.max.y.to_real ()); } return true; } +#ifdef HB_EXPERIMENTAL_API +struct cff1_path_param_t +{ + cff1_path_param_t (const OT::cff1::accelerator_t *cff_, hb_font_t *font_, + draw_helper_t &draw_helper_, point_t *delta_) + { + draw_helper = &draw_helper_; + cff = cff_; + font = font_; + delta = delta_; + } + + void move_to (const point_t &p) + { + point_t point = p; + if (delta) point.move (*delta); + draw_helper->move_to (font->em_scalef_x (point.x.to_real ()), font->em_scalef_y (point.y.to_real ())); + } + + void line_to (const point_t &p) + { + point_t point = p; + if (delta) point.move (*delta); + draw_helper->line_to (font->em_scalef_x (point.x.to_real ()), font->em_scalef_y (point.y.to_real ())); + } + + void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3) + { + point_t point1 = p1, point2 = p2, point3 = p3; + if (delta) + { + point1.move (*delta); + point2.move (*delta); + point3.move (*delta); + } + draw_helper->cubic_to (font->em_scalef_x (point1.x.to_real ()), font->em_scalef_y (point1.y.to_real ()), + font->em_scalef_x (point2.x.to_real ()), font->em_scalef_y (point2.y.to_real ()), + font->em_scalef_x (point3.x.to_real ()), font->em_scalef_y (point3.y.to_real ())); + } + + void end_path () { draw_helper->end_path (); } + + hb_font_t *font; + draw_helper_t *draw_helper; + point_t *delta; + + const OT::cff1::accelerator_t *cff; +}; + +struct cff1_path_procs_path_t : path_procs_t +{ + static void moveto (cff1_cs_interp_env_t &env, cff1_path_param_t& param, const point_t &pt) + { + param.move_to (pt); + env.moveto (pt); + } + + static void line (cff1_cs_interp_env_t &env, cff1_path_param_t ¶m, const point_t &pt1) + { + param.line_to (pt1); + env.moveto (pt1); + } + + static void curve (cff1_cs_interp_env_t &env, cff1_path_param_t ¶m, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + param.cubic_to (pt1, pt2, pt3); + env.moveto (pt3); + } +}; + +static bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph, + draw_helper_t &draw_helper, bool in_seac = false, point_t *delta = nullptr); + +struct cff1_cs_opset_path_t : cff1_cs_opset_t +{ + static void process_seac (cff1_cs_interp_env_t &env, cff1_path_param_t& param) + { + /* End previous path */ + param.end_path (); + + unsigned int n = env.argStack.get_count (); + point_t delta; + delta.x = env.argStack[n-4]; + delta.y = env.argStack[n-3]; + hb_codepoint_t base = param.cff->std_code_to_glyph (env.argStack[n-2].to_int ()); + hb_codepoint_t accent = param.cff->std_code_to_glyph (env.argStack[n-1].to_int ()); + + if (unlikely (!(!env.in_seac && base && accent + && _get_path (param.cff, param.font, base, *param.draw_helper, true) + && _get_path (param.cff, param.font, accent, *param.draw_helper, true, &delta)))) + env.set_error (); + } +}; + +bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph, + draw_helper_t &draw_helper, bool in_seac, point_t *delta) +{ + if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false; + + unsigned int fd = cff->fdSelect->get_fd (glyph); + cff1_cs_interpreter_t interp; + const byte_str_t str = (*cff->charStrings)[glyph]; + interp.env.init (str, *cff, fd); + interp.env.set_in_seac (in_seac); + cff1_path_param_t param (cff, font, draw_helper, delta); + if (unlikely (!interp.interpret (param))) return false; + + /* Let's end the path specially since it is called inside seac also */ + param.end_path (); + + return true; +} + +bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + return _get_path (this, font, glyph, draw_helper); +} +#endif + struct get_seac_param_t { void init (const OT::cff1::accelerator_t *_cff) @@ -383,3 +615,6 @@ bool OT::cff1::accelerator_t::get_seac_components (hb_codepoint_t glyph, hb_code } return false; } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh index 73258e79007..6768e4ea107 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh @@ -27,15 +27,21 @@ #ifndef HB_OT_CFF1_TABLE_HH #define HB_OT_CFF1_TABLE_HH -#include "hb-ot-head-table.hh" #include "hb-ot-cff-common.hh" #include "hb-subset-cff1.hh" +#include "hb-draw.hh" + +#define HB_STRING_ARRAY_NAME cff1_std_strings +#define HB_STRING_ARRAY_LIST "hb-ot-cff1-std-str.hh" +#include "hb-string-array.hh" +#undef HB_STRING_ARRAY_LIST +#undef HB_STRING_ARRAY_NAME namespace CFF { /* * CFF -- Compact Font Format (CFF) - * http://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5176.CFF.pdf + * https://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5176.CFF.pdf */ #define HB_OT_TAG_cff1 HB_TAG('C','F','F',' ') @@ -49,7 +55,6 @@ template struct CFF1IndexOf : CFFIndexOf {}; typedef CFFIndex CFF1Index; typedef CFF1Index CFF1CharStrings; -typedef FDArray CFF1FDArray; typedef Subrs CFF1Subrs; struct CFF1FDSelect : FDSelect {}; @@ -59,14 +64,14 @@ struct Encoding0 { bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && codes[nCodes - 1].sanitize (c)); + return_trace (codes.sanitize (c)); } hb_codepoint_t get_code (hb_codepoint_t glyph) const { assert (glyph > 0); glyph--; - if (glyph < nCodes) + if (glyph < nCodes ()) { return (hb_codepoint_t)codes[glyph]; } @@ -74,13 +79,12 @@ struct Encoding0 { return CFF_UNDEF_CODE; } - unsigned int get_size () const - { return HBUINT8::static_size * (nCodes + 1); } + HBUINT8 &nCodes () { return codes.len; } + HBUINT8 nCodes () const { return codes.len; } - HBUINT8 nCodes; - HBUINT8 codes[VAR]; + ArrayOf codes; - DEFINE_SIZE_ARRAY(1, codes); + DEFINE_SIZE_ARRAY_SIZED (1, codes); }; struct Encoding1_Range { @@ -97,34 +101,34 @@ struct Encoding1_Range { }; struct Encoding1 { - unsigned int get_size () const - { return HBUINT8::static_size + Encoding1_Range::static_size * nRanges; } - bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && ((nRanges == 0) || (ranges[nRanges - 1]).sanitize (c))); + return_trace (ranges.sanitize (c)); } hb_codepoint_t get_code (hb_codepoint_t glyph) const { assert (glyph > 0); glyph--; - for (unsigned int i = 0; i < nRanges; i++) + for (unsigned int i = 0; i < nRanges (); i++) { if (glyph <= ranges[i].nLeft) { - return (hb_codepoint_t)ranges[i].first + glyph; + hb_codepoint_t code = (hb_codepoint_t) ranges[i].first + glyph; + return (likely (code < 0x100) ? code: CFF_UNDEF_CODE); } glyph -= (ranges[i].nLeft + 1); } return CFF_UNDEF_CODE; } - HBUINT8 nRanges; - Encoding1_Range ranges[VAR]; + HBUINT8 &nRanges () { return ranges.len; } + HBUINT8 nRanges () const { return ranges.len; } + + ArrayOf ranges; - DEFINE_SIZE_ARRAY (1, ranges); + DEFINE_SIZE_ARRAY_SIZED (1, ranges); }; struct SuppEncoding { @@ -144,47 +148,33 @@ struct CFF1SuppEncData { bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && ((nSups == 0) || (supps[nSups - 1]).sanitize (c))); + return_trace (supps.sanitize (c)); } void get_codes (hb_codepoint_t sid, hb_vector_t &codes) const { - for (unsigned int i = 0; i < nSups; i++) + for (unsigned int i = 0; i < nSups (); i++) if (sid == supps[i].glyph) codes.push (supps[i].code); } - unsigned int get_size () const - { return HBUINT8::static_size + SuppEncoding::static_size * nSups; } + HBUINT8 &nSups () { return supps.len; } + HBUINT8 nSups () const { return supps.len; } - HBUINT8 nSups; - SuppEncoding supps[VAR]; + ArrayOf supps; - DEFINE_SIZE_ARRAY (1, supps); + DEFINE_SIZE_ARRAY_SIZED (1, supps); }; -struct Encoding { - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - - if (unlikely (!c->check_struct (this))) - return_trace (false); - unsigned int fmt = format & 0x7F; - if (unlikely (fmt > 1)) - return_trace (false); - if (unlikely (!((fmt == 0)? u.format0.sanitize (c): u.format1.sanitize (c)))) - return_trace (false); - return_trace (((format & 0x80) == 0) || suppEncData ().sanitize (c)); - } - +struct Encoding +{ /* serialize a fullset Encoding */ bool serialize (hb_serialize_context_t *c, const Encoding &src) { TRACE_SERIALIZE (this); unsigned int size = src.get_size (); Encoding *dest = c->allocate_size (size); - if (unlikely (dest == nullptr)) return_trace (false); + if (unlikely (!dest)) return_trace (false); memcpy (dest, &src, size); return_trace (true); } @@ -198,72 +188,66 @@ struct Encoding { { TRACE_SERIALIZE (this); Encoding *dest = c->extend_min (*this); - if (unlikely (dest == nullptr)) return_trace (false); - dest->format.set (format | ((supp_codes.length > 0)? 0x80: 0)); - if (format == 0) + if (unlikely (!dest)) return_trace (false); + dest->format = format | ((supp_codes.length > 0) ? 0x80 : 0); + switch (format) { + case 0: { Encoding0 *fmt0 = c->allocate_size (Encoding0::min_size + HBUINT8::static_size * enc_count); - if (unlikely (fmt0 == nullptr)) return_trace (false); - fmt0->nCodes.set (enc_count); + if (unlikely (!fmt0)) return_trace (false); + fmt0->nCodes () = enc_count; unsigned int glyph = 0; for (unsigned int i = 0; i < code_ranges.length; i++) { hb_codepoint_t code = code_ranges[i].code; for (int left = (int)code_ranges[i].glyph; left >= 0; left--) - fmt0->codes[glyph++].set (code++); + fmt0->codes[glyph++] = code++; if (unlikely (!((glyph <= 0x100) && (code <= 0x100)))) return_trace (false); } } - else + break; + + case 1: { Encoding1 *fmt1 = c->allocate_size (Encoding1::min_size + Encoding1_Range::static_size * code_ranges.length); - if (unlikely (fmt1 == nullptr)) return_trace (false); - fmt1->nRanges.set (code_ranges.length); + if (unlikely (!fmt1)) return_trace (false); + fmt1->nRanges () = code_ranges.length; for (unsigned int i = 0; i < code_ranges.length; i++) { if (unlikely (!((code_ranges[i].code <= 0xFF) && (code_ranges[i].glyph <= 0xFF)))) return_trace (false); - fmt1->ranges[i].first.set (code_ranges[i].code); - fmt1->ranges[i].nLeft.set (code_ranges[i].glyph); + fmt1->ranges[i].first = code_ranges[i].code; + fmt1->ranges[i].nLeft = code_ranges[i].glyph; } } - if (supp_codes.length > 0) + break; + + } + + if (supp_codes.length) { CFF1SuppEncData *suppData = c->allocate_size (CFF1SuppEncData::min_size + SuppEncoding::static_size * supp_codes.length); - if (unlikely (suppData == nullptr)) return_trace (false); - suppData->nSups.set (supp_codes.length); + if (unlikely (!suppData)) return_trace (false); + suppData->nSups () = supp_codes.length; for (unsigned int i = 0; i < supp_codes.length; i++) { - suppData->supps[i].code.set (supp_codes[i].code); - suppData->supps[i].glyph.set (supp_codes[i].glyph); /* actually SID */ + suppData->supps[i].code = supp_codes[i].code; + suppData->supps[i].glyph = supp_codes[i].glyph; /* actually SID */ } } - return_trace (true); - } - /* parallel to above: calculate the size of a subset Encoding */ - static unsigned int calculate_serialized_size (uint8_t format, - unsigned int enc_count, - unsigned int supp_count) - { - unsigned int size = min_size; - if (format == 0) - size += Encoding0::min_size + HBUINT8::static_size * enc_count; - else - size += Encoding1::min_size + Encoding1_Range::static_size * enc_count; - if (supp_count > 0) - size += CFF1SuppEncData::min_size + SuppEncoding::static_size * supp_count; - return size; + return_trace (true); } unsigned int get_size () const { unsigned int size = min_size; - if (table_format () == 0) - size += u.format0.get_size (); - else - size += u.format1.get_size (); + switch (table_format ()) + { + case 0: size += u.format0.get_size (); break; + case 1: size += u.format1.get_size (); break; + } if (has_supplement ()) size += suppEncData ().get_size (); return size; @@ -271,14 +255,16 @@ struct Encoding { hb_codepoint_t get_code (hb_codepoint_t glyph) const { - if (table_format () == 0) - return u.format0.get_code (glyph); - else - return u.format1.get_code (glyph); + switch (table_format ()) + { + case 0: return u.format0.get_code (glyph); + case 1: return u.format1.get_code (glyph); + default:return 0; + } } - uint8_t table_format () const { return (format & 0x7F); } - bool has_supplement () const { return (format & 0x80) != 0; } + uint8_t table_format () const { return format & 0x7F; } + bool has_supplement () const { return format & 0x80; } void get_supplement_codes (hb_codepoint_t sid, hb_vector_t &codes) const { @@ -287,21 +273,37 @@ struct Encoding { suppEncData().get_codes (sid, codes); } + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (table_format ()) + { + case 0: if (unlikely (!u.format0.sanitize (c))) { return_trace (false); } break; + case 1: if (unlikely (!u.format1.sanitize (c))) { return_trace (false); } break; + default:return_trace (false); + } + return_trace (likely (!has_supplement () || suppEncData ().sanitize (c))); + } + protected: const CFF1SuppEncData &suppEncData () const { - if ((format & 0x7F) == 0) - return StructAfter (u.format0.codes[u.format0.nCodes-1]); - else - return StructAfter (u.format1.ranges[u.format1.nRanges-1]); + switch (table_format ()) + { + case 0: return StructAfter (u.format0.codes[u.format0.nCodes ()-1]); + case 1: return StructAfter (u.format1.ranges[u.format1.nRanges ()-1]); + default:return Null (CFF1SuppEncData); + } } public: HBUINT8 format; - union { - Encoding0 format0; - Encoding1 format1; + Encoding0 format0; + Encoding1 format1; } u; /* CFF1SuppEncData suppEncData; */ @@ -343,7 +345,7 @@ struct Charset0 { return HBUINT16::static_size * (num_glyphs - 1); } - HBUINT16 sids[VAR]; + HBUINT16 sids[HB_VAR_ARRAY]; DEFINE_SIZE_ARRAY(0, sids); }; @@ -425,7 +427,7 @@ struct Charset1_2 { return size; } - Charset_Range ranges[VAR]; + Charset_Range ranges[HB_VAR_ARRAY]; DEFINE_SIZE_ARRAY (0, ranges); }; @@ -435,30 +437,15 @@ typedef Charset1_2 Charset2; typedef Charset_Range Charset1_Range; typedef Charset_Range Charset2_Range; -struct Charset { - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - - if (unlikely (!c->check_struct (this))) - return_trace (false); - if (format == 0) - return_trace (u.format0.sanitize (c, c->get_num_glyphs ())); - else if (format == 1) - return_trace (u.format1.sanitize (c, c->get_num_glyphs ())); - else if (likely (format == 2)) - return_trace (u.format2.sanitize (c, c->get_num_glyphs ())); - else - return_trace (false); - } - +struct Charset +{ /* serialize a fullset Charset */ bool serialize (hb_serialize_context_t *c, const Charset &src, unsigned int num_glyphs) { TRACE_SERIALIZE (this); unsigned int size = src.get_size (num_glyphs); Charset *dest = c->allocate_size (size); - if (unlikely (dest == nullptr)) return_trace (false); + if (unlikely (!dest)) return_trace (false); memcpy (dest, &src, size); return_trace (true); } @@ -471,93 +458,103 @@ struct Charset { { TRACE_SERIALIZE (this); Charset *dest = c->extend_min (*this); - if (unlikely (dest == nullptr)) return_trace (false); - dest->format.set (format); - if (format == 0) + if (unlikely (!dest)) return_trace (false); + dest->format = format; + switch (format) + { + case 0: { Charset0 *fmt0 = c->allocate_size (Charset0::min_size + HBUINT16::static_size * (num_glyphs - 1)); - if (unlikely (fmt0 == nullptr)) return_trace (false); + if (unlikely (!fmt0)) return_trace (false); unsigned int glyph = 0; for (unsigned int i = 0; i < sid_ranges.length; i++) { hb_codepoint_t sid = sid_ranges[i].code; for (int left = (int)sid_ranges[i].glyph; left >= 0; left--) - fmt0->sids[glyph++].set (sid++); + fmt0->sids[glyph++] = sid++; } } - else if (format == 1) + break; + + case 1: { Charset1 *fmt1 = c->allocate_size (Charset1::min_size + Charset1_Range::static_size * sid_ranges.length); - if (unlikely (fmt1 == nullptr)) return_trace (false); + if (unlikely (!fmt1)) return_trace (false); for (unsigned int i = 0; i < sid_ranges.length; i++) { if (unlikely (!(sid_ranges[i].glyph <= 0xFF))) return_trace (false); - fmt1->ranges[i].first.set (sid_ranges[i].code); - fmt1->ranges[i].nLeft.set (sid_ranges[i].glyph); + fmt1->ranges[i].first = sid_ranges[i].code; + fmt1->ranges[i].nLeft = sid_ranges[i].glyph; } } - else /* format 2 */ + break; + + case 2: { Charset2 *fmt2 = c->allocate_size (Charset2::min_size + Charset2_Range::static_size * sid_ranges.length); - if (unlikely (fmt2 == nullptr)) return_trace (false); + if (unlikely (!fmt2)) return_trace (false); for (unsigned int i = 0; i < sid_ranges.length; i++) { if (unlikely (!(sid_ranges[i].glyph <= 0xFFFF))) return_trace (false); - fmt2->ranges[i].first.set (sid_ranges[i].code); - fmt2->ranges[i].nLeft.set (sid_ranges[i].glyph); + fmt2->ranges[i].first = sid_ranges[i].code; + fmt2->ranges[i].nLeft = sid_ranges[i].glyph; } + } + break; + } return_trace (true); } - /* parallel to above: calculate the size of a subset Charset */ - static unsigned int calculate_serialized_size ( - uint8_t format, - unsigned int count) + unsigned int get_size (unsigned int num_glyphs) const { - unsigned int size = min_size; - if (format == 0) - size += Charset0::min_size + HBUINT16::static_size * (count - 1); - else if (format == 1) - size += Charset1::min_size + Charset1_Range::static_size * count; - else - size += Charset2::min_size + Charset2_Range::static_size * count; - - return size; + switch (format) + { + case 0: return min_size + u.format0.get_size (num_glyphs); + case 1: return min_size + u.format1.get_size (num_glyphs); + case 2: return min_size + u.format2.get_size (num_glyphs); + default:return 0; + } } - unsigned int get_size (unsigned int num_glyphs) const + hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned int num_glyphs) const { - unsigned int size = min_size; - if (format == 0) - size += u.format0.get_size (num_glyphs); - else if (format == 1) - size += u.format1.get_size (num_glyphs); - else - size += u.format2.get_size (num_glyphs); - return size; + if (unlikely (glyph >= num_glyphs)) return 0; + switch (format) + { + case 0: return u.format0.get_sid (glyph); + case 1: return u.format1.get_sid (glyph); + case 2: return u.format2.get_sid (glyph); + default:return 0; + } } - hb_codepoint_t get_sid (hb_codepoint_t glyph) const + hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const { - if (format == 0) - return u.format0.get_sid (glyph); - else if (format == 1) - return u.format1.get_sid (glyph); - else - return u.format2.get_sid (glyph); + switch (format) + { + case 0: return u.format0.get_glyph (sid, num_glyphs); + case 1: return u.format1.get_glyph (sid, num_glyphs); + case 2: return u.format2.get_glyph (sid, num_glyphs); + default:return 0; + } } - hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const + bool sanitize (hb_sanitize_context_t *c) const { - if (format == 0) - return u.format0.get_glyph (sid, num_glyphs); - else if (format == 1) - return u.format1.get_glyph (sid, num_glyphs); - else - return u.format2.get_glyph (sid, num_glyphs); + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, c->get_num_glyphs ())); + case 1: return_trace (u.format1.sanitize (c, c->get_num_glyphs ())); + case 2: return_trace (u.format2.sanitize (c, c->get_num_glyphs ())); + default:return_trace (false); + } } HBUINT8 format; @@ -573,48 +570,32 @@ struct Charset { struct CFF1StringIndex : CFF1Index { bool serialize (hb_serialize_context_t *c, const CFF1StringIndex &strings, - unsigned int offSize_, const remap_t &sidmap) + const hb_inc_bimap_t &sidmap) { TRACE_SERIALIZE (this); - if (unlikely ((strings.count == 0) || (sidmap.get_count () == 0))) + if (unlikely ((strings.count == 0) || (sidmap.get_population () == 0))) { - if (!unlikely (c->extend_min (this->count))) + if (unlikely (!c->extend_min (this->count))) return_trace (false); - count.set (0); + count = 0; return_trace (true); } byte_str_array_t bytesArray; bytesArray.init (); - if (!bytesArray.resize (sidmap.get_count ())) + if (!bytesArray.resize (sidmap.get_population ())) return_trace (false); for (unsigned int i = 0; i < strings.count; i++) { hb_codepoint_t j = sidmap[i]; - if (j != CFF_UNDEF_CODE) + if (j != HB_MAP_VALUE_INVALID) bytesArray[j] = strings[i]; } - bool result = CFF1Index::serialize (c, offSize_, bytesArray); + bool result = CFF1Index::serialize (c, bytesArray); bytesArray.fini (); return_trace (result); } - - /* in parallel to above */ - unsigned int calculate_serialized_size (unsigned int &offSize /*OUT*/, const remap_t &sidmap) const - { - offSize = 0; - if ((count == 0) || (sidmap.get_count () == 0)) - return count.static_size; - - unsigned int dataSize = 0; - for (unsigned int i = 0; i < count; i++) - if (sidmap[i] != CFF_UNDEF_CODE) - dataSize += length_at (i); - - offSize = calcOffSize(dataSize); - return CFF1Index::calculate_serialized_size (offSize, sidmap.get_count (), dataSize); - } }; struct cff1_top_dict_interp_env_t : num_interp_env_t @@ -717,7 +698,7 @@ struct cff1_top_dict_values_t : top_dict_values_t unsigned int EncodingOffset; unsigned int CharsetOffset; unsigned int FDSelectOffset; - table_info_t privateDictInfo; + table_info_t privateDictInfo; }; struct cff1_top_dict_opset_t : top_dict_opset_t @@ -859,21 +840,10 @@ struct cff1_private_dict_values_base_t : dict_values_t { dict_values_t::init (); subrsOffset = 0; - localSubrs = &Null(CFF1Subrs); + localSubrs = &Null (CFF1Subrs); } void fini () { dict_values_t::fini (); } - unsigned int calculate_serialized_size () const - { - unsigned int size = 0; - for (unsigned int i = 0; i < dict_values_t::get_count; i++) - if (dict_values_t::get_value (i).op == OpCode_Subrs) - size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs); - else - size += dict_values_t::get_value (i).str.length; - return size; - } - unsigned int subrsOffset; const CFF1Subrs *localSubrs; }; @@ -976,6 +946,37 @@ typedef dict_interpreter_t cff1 typedef CFF1Index CFF1NameIndex; typedef CFF1IndexOf CFF1TopDictIndex; +struct cff1_font_dict_values_mod_t +{ + cff1_font_dict_values_mod_t() { init (); } + + void init () { init ( &Null (cff1_font_dict_values_t), CFF_UNDEF_SID ); } + + void init (const cff1_font_dict_values_t *base_, + unsigned int fontName_) + { + base = base_; + fontName = fontName_; + privateDictInfo.init (); + } + + unsigned get_count () const { return base->get_count (); } + + const op_str_t &operator [] (unsigned int i) const { return (*base)[i]; } + + const cff1_font_dict_values_t *base; + table_info_t privateDictInfo; + unsigned int fontName; +}; + +struct CFF1FDArray : FDArray +{ + /* FDArray::serialize() requires this partial specialization to compile */ + template + bool serialize (hb_serialize_context_t *c, ITER it, OP_SERIALIZER& opszr) + { return FDArray::serialize (c, it, opszr); } +}; + } /* namespace CFF */ namespace OT { @@ -1010,7 +1011,7 @@ struct cff1 const OT::cff1 *cff = this->blob->template as (); - if (cff == &Null(OT::cff1)) + if (cff == &Null (OT::cff1)) { fini (); return; } nameIndex = &cff->nameIndex (cff); @@ -1031,7 +1032,7 @@ struct cff1 } if (is_predef_charset ()) - charset = &Null(Charset); + charset = &Null (Charset); else { charset = &StructAtOffsetOrNull (cff, topDict.CharsetOffset); @@ -1043,16 +1044,30 @@ struct cff1 { fdArray = &StructAtOffsetOrNull (cff, topDict.FDArrayOffset); fdSelect = &StructAtOffsetOrNull (cff, topDict.FDSelectOffset); - if (unlikely ((fdArray == &Null(CFF1FDArray)) || !fdArray->sanitize (&sc) || - (fdSelect == &Null(CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count))) + if (unlikely ((fdArray == &Null (CFF1FDArray)) || !fdArray->sanitize (&sc) || + (fdSelect == &Null (CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count))) { fini (); return; } fdCount = fdArray->count; } else { - fdArray = &Null(CFF1FDArray); - fdSelect = &Null(CFF1FDSelect); + fdArray = &Null (CFF1FDArray); + fdSelect = &Null (CFF1FDSelect); + } + + encoding = &Null (Encoding); + if (is_CID ()) + { + if (unlikely (charset == &Null (Charset))) { fini (); return; } + } + else + { + if (!is_predef_encoding ()) + { + encoding = &StructAtOffsetOrNull (cff, topDict.EncodingOffset); + if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) { fini (); return; } + } } stringIndex = &StructAtOffset (topDictIndex, topDictIndex->get_size ()); @@ -1065,14 +1080,15 @@ struct cff1 charStrings = &StructAtOffsetOrNull (cff, topDict.charStringsOffset); - if ((charStrings == &Null(CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc))) + if ((charStrings == &Null (CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc))) { fini (); return; } num_glyphs = charStrings->count; if (num_glyphs != sc.get_num_glyphs ()) { fini (); return; } - privateDicts.resize (fdCount); + if (unlikely (!privateDicts.resize (fdCount))) + { fini (); return; } for (unsigned int i = 0; i < fdCount; i++) privateDicts[i].init (); @@ -1083,14 +1099,14 @@ struct cff1 { byte_str_t fontDictStr = (*fdArray)[i]; if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; } - cff1_font_dict_values_t *font; + cff1_font_dict_values_t *font; cff1_font_dict_interpreter_t font_interp; font_interp.env.init (fontDictStr); font = fontDicts.push (); - if (unlikely (font == &Crap(cff1_font_dict_values_t))) { fini (); return; } + if (unlikely (font == &Crap (cff1_font_dict_values_t))) { fini (); return; } font->init (); if (unlikely (!font_interp.interpret (*font))) { fini (); return; } - PRIVDICTVAL *priv = &privateDicts[i]; + PRIVDICTVAL *priv = &privateDicts[i]; const byte_str_t privDictStr (StructAtOffset (cff, font->privateDictInfo.offset), font->privateDictInfo.size); if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } dict_interpreter_t priv_interp; @@ -1099,15 +1115,15 @@ struct cff1 if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset); - if (priv->localSubrs != &Null(CFF1Subrs) && + if (priv->localSubrs != &Null (CFF1Subrs) && unlikely (!priv->localSubrs->sanitize (&sc))) { fini (); return; } } } else /* non-CID */ { - cff1_top_dict_values_t *font = &topDict; - PRIVDICTVAL *priv = &privateDicts[0]; + cff1_top_dict_values_t *font = &topDict; + PRIVDICTVAL *priv = &privateDicts[0]; const byte_str_t privDictStr (StructAtOffset (cff, font->privateDictInfo.offset), font->privateDictInfo.size); if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } @@ -1117,7 +1133,7 @@ struct cff1 if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset); - if (priv->localSubrs != &Null(CFF1Subrs) && + if (priv->localSubrs != &Null (CFF1Subrs) && unlikely (!priv->localSubrs->sanitize (&sc))) { fini (); return; } } @@ -1133,8 +1149,8 @@ struct cff1 blob = nullptr; } - bool is_valid () const { return blob != nullptr; } - bool is_CID () const { return topDict.is_CID (); } + bool is_valid () const { return blob; } + bool is_CID () const { return topDict.is_CID (); } bool is_predef_charset () const { return topDict.CharsetOffset <= ExpertSubsetCharset; } @@ -1144,144 +1160,232 @@ struct cff1 if (unlikely (sid == CFF_UNDEF_SID)) return 0; - if (charset != &Null(Charset)) + if (charset != &Null (Charset)) return charset->get_glyph (sid, num_glyphs); else if ((topDict.CharsetOffset == ISOAdobeCharset) && (code <= 228 /*zcaron*/)) return sid; return 0; } - protected: - hb_blob_t *blob; - hb_sanitize_context_t sc; - - public: - const Charset *charset; - const CFF1NameIndex *nameIndex; - const CFF1TopDictIndex *topDictIndex; - const CFF1StringIndex *stringIndex; - const CFF1Subrs *globalSubrs; - const CFF1CharStrings *charStrings; - const CFF1FDArray *fdArray; - const CFF1FDSelect *fdSelect; - unsigned int fdCount; - - cff1_top_dict_values_t topDict; - hb_vector_t fontDicts; - hb_vector_t privateDicts; - - unsigned int num_glyphs; - }; - - struct accelerator_t : accelerator_templ_t - { - HB_INTERNAL bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; - HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const; - }; + bool is_predef_encoding () const { return topDict.EncodingOffset <= ExpertEncoding; } - struct accelerator_subset_t : accelerator_templ_t - { - void init (hb_face_t *face) + hb_codepoint_t glyph_to_code (hb_codepoint_t glyph) const { - SUPER::init (face); - if (blob == nullptr) return; - - const OT::cff1 *cff = this->blob->as (); - encoding = &Null(Encoding); - if (is_CID ()) - { - if (unlikely (charset == &Null(Charset))) { fini (); return; } - } + if (encoding != &Null (Encoding)) + return encoding->get_code (glyph); else { - if (!is_predef_encoding ()) + hb_codepoint_t sid = glyph_to_sid (glyph); + if (sid == 0) return 0; + hb_codepoint_t code = 0; + switch (topDict.EncodingOffset) { - encoding = &StructAtOffsetOrNull (cff, topDict.EncodingOffset); - if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) { fini (); return; } + case StandardEncoding: + code = lookup_standard_encoding_for_code (sid); + break; + case ExpertEncoding: + code = lookup_expert_encoding_for_code (sid); + break; + default: + break; } + return code; } } - bool is_predef_encoding () const { return topDict.EncodingOffset <= ExpertEncoding; } - - hb_codepoint_t glyph_to_code (hb_codepoint_t glyph) const + hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph) const { - if (encoding != &Null(Encoding)) - return encoding->get_code (glyph); + if (charset != &Null (Charset)) + return charset->get_sid (glyph, num_glyphs); else { - hb_codepoint_t sid = glyph_to_sid (glyph); - if (sid == 0) return 0; - hb_codepoint_t code = 0; - switch (topDict.EncodingOffset) + hb_codepoint_t sid = 0; + switch (topDict.CharsetOffset) { - case StandardEncoding: - code = lookup_standard_encoding_for_code (sid); + case ISOAdobeCharset: + if (glyph <= 228 /*zcaron*/) sid = glyph; + break; + case ExpertCharset: + sid = lookup_expert_charset_for_sid (glyph); break; - case ExpertEncoding: - code = lookup_expert_encoding_for_code (sid); + case ExpertSubsetCharset: + sid = lookup_expert_subset_charset_for_sid (glyph); break; default: break; } - return code; + return sid; } } - hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph) const + hb_codepoint_t sid_to_glyph (hb_codepoint_t sid) const { - if (charset != &Null(Charset)) - return charset->get_sid (glyph); + if (charset != &Null (Charset)) + return charset->get_glyph (sid, num_glyphs); else { - hb_codepoint_t sid = 0; + hb_codepoint_t glyph = 0; switch (topDict.CharsetOffset) { - case ISOAdobeCharset: - if (glyph <= 228 /*zcaron*/) sid = glyph; + case ISOAdobeCharset: + if (sid <= 228 /*zcaron*/) glyph = sid; break; - case ExpertCharset: - sid = lookup_expert_charset_for_sid (glyph); + case ExpertCharset: + glyph = lookup_expert_charset_for_glyph (sid); break; - case ExpertSubsetCharset: - sid = lookup_expert_subset_charset_for_sid (glyph); + case ExpertSubsetCharset: + glyph = lookup_expert_subset_charset_for_glyph (sid); break; default: break; } - return sid; + return glyph; } } - const Encoding *encoding; + protected: + hb_blob_t *blob; + hb_sanitize_context_t sc; - private: - typedef accelerator_templ_t SUPER; + public: + const Encoding *encoding; + const Charset *charset; + const CFF1NameIndex *nameIndex; + const CFF1TopDictIndex *topDictIndex; + const CFF1StringIndex *stringIndex; + const CFF1Subrs *globalSubrs; + const CFF1CharStrings *charStrings; + const CFF1FDArray *fdArray; + const CFF1FDSelect *fdSelect; + unsigned int fdCount; + + cff1_top_dict_values_t topDict; + hb_vector_t + fontDicts; + hb_vector_t privateDicts; + + unsigned int num_glyphs; }; - bool subset (hb_subset_plan_t *plan) const + struct accelerator_t : accelerator_templ_t { - hb_blob_t *cff_prime = nullptr; - - bool success = true; - if (hb_subset_cff1 (plan, &cff_prime)) { - success = success && plan->add_table (HB_OT_TAG_cff1, cff_prime); - hb_blob_t *head_blob = hb_sanitize_context_t().reference_table (plan->source); - success = success && head_blob && plan->add_table (HB_OT_TAG_head, head_blob); - hb_blob_destroy (head_blob); - } else { - success = false; + void init (hb_face_t *face) + { + SUPER::init (face); + + if (!is_valid ()) return; + if (is_CID ()) return; + + /* fill glyph_names */ + for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) + { + hb_codepoint_t sid = glyph_to_sid (gid); + gname_t gname; + gname.sid = sid; + if (sid < cff1_std_strings_length) + gname.name = cff1_std_strings (sid); + else + { + byte_str_t ustr = (*stringIndex)[sid - cff1_std_strings_length]; + gname.name = hb_bytes_t ((const char*)ustr.arrayZ, ustr.length); + } + if (unlikely (!gname.name.arrayZ)) { fini (); return; } + glyph_names.push (gname); + } + glyph_names.qsort (); } - hb_blob_destroy (cff_prime); - return success; - } + void fini () + { + glyph_names.fini (); + + SUPER::fini (); + } + + bool get_glyph_name (hb_codepoint_t glyph, + char *buf, unsigned int buf_len) const + { + if (!buf) return true; + if (unlikely (!is_valid ())) return false; + if (is_CID()) return false; + hb_codepoint_t sid = glyph_to_sid (glyph); + const char *str; + size_t str_len; + if (sid < cff1_std_strings_length) + { + hb_bytes_t byte_str = cff1_std_strings (sid); + str = byte_str.arrayZ; + str_len = byte_str.length; + } + else + { + byte_str_t ubyte_str = (*stringIndex)[sid - cff1_std_strings_length]; + str = (const char *)ubyte_str.arrayZ; + str_len = ubyte_str.length; + } + if (!str_len) return false; + unsigned int len = hb_min (buf_len - 1, str_len); + strncpy (buf, (const char*)str, len); + buf[len] = '\0'; + return true; + } + + bool get_glyph_from_name (const char *name, int len, + hb_codepoint_t *glyph) const + { + if (len < 0) len = strlen (name); + if (unlikely (!len)) return false; + + gname_t key = { hb_bytes_t (name, len), 0 }; + const gname_t *gname = glyph_names.bsearch (key); + if (!gname) return false; + hb_codepoint_t gid = sid_to_glyph (gname->sid); + if (!gid && gname->sid) return false; + *glyph = gid; + return true; + } + + HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; + HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const; +#ifdef HB_EXPERIMENTAL_API + HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const; +#endif + + private: + struct gname_t + { + hb_bytes_t name; + uint16_t sid; + + static int cmp (const void *a_, const void *b_) + { + const gname_t *a = (const gname_t *)a_; + const gname_t *b = (const gname_t *)b_; + int minlen = hb_min (a->name.length, b->name.length); + int ret = strncmp (a->name.arrayZ, b->name.arrayZ, minlen); + if (ret) return ret; + return a->name.length - b->name.length; + } + + int cmp (const gname_t &a) const { return cmp (&a, this); } + }; + + hb_sorted_vector_t glyph_names; + + typedef accelerator_templ_t SUPER; + }; + + struct accelerator_subset_t : accelerator_templ_t {}; + + bool subset (hb_subset_context_t *c) const { return hb_subset_cff1 (c); } protected: HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_code (hb_codepoint_t sid); HB_INTERNAL static hb_codepoint_t lookup_expert_encoding_for_code (hb_codepoint_t sid); HB_INTERNAL static hb_codepoint_t lookup_expert_charset_for_sid (hb_codepoint_t glyph); HB_INTERNAL static hb_codepoint_t lookup_expert_subset_charset_for_sid (hb_codepoint_t glyph); + HB_INTERNAL static hb_codepoint_t lookup_expert_charset_for_glyph (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_expert_subset_charset_for_glyph (hb_codepoint_t sid); HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_sid (hb_codepoint_t code); public: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc index 45eb8bd2761..56f331ed447 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc @@ -24,24 +24,29 @@ * Adobe Author(s): Michiharu Ariza */ +#include "hb.hh" + +#ifndef HB_NO_OT_FONT_CFF + #include "hb-ot-cff2-table.hh" #include "hb-cff2-interp-cs.hh" +#include "hb-draw.hh" using namespace CFF; -struct extents_param_t +struct cff2_extents_param_t { void init () { path_open = false; - min_x.set_int (0x7FFFFFFF); - min_y.set_int (0x7FFFFFFF); - max_x.set_int (-0x80000000); - max_y.set_int (-0x80000000); + min_x.set_int (INT_MAX); + min_y.set_int (INT_MAX); + max_x.set_int (INT_MIN); + max_y.set_int (INT_MIN); } - void start_path () { path_open = true; } - void end_path () { path_open = false; } + void start_path () { path_open = true; } + void end_path () { path_open = false; } bool is_path_open () const { return path_open; } void update_bounds (const point_t &pt) @@ -59,15 +64,15 @@ struct extents_param_t number_t max_y; }; -struct cff2_path_procs_extents_t : path_procs_t +struct cff2_path_procs_extents_t : path_procs_t { - static void moveto (cff2_cs_interp_env_t &env, extents_param_t& param, const point_t &pt) + static void moveto (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt) { param.end_path (); env.moveto (pt); } - static void line (cff2_cs_interp_env_t &env, extents_param_t& param, const point_t &pt1) + static void line (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1) { if (!param.is_path_open ()) { @@ -78,7 +83,7 @@ struct cff2_path_procs_extents_t : path_procs_t {}; +struct cff2_cs_opset_extents_t : cff2_cs_opset_t {}; bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const { +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; - unsigned int num_coords; - const int *coords = hb_font_get_var_coords_normalized (font, &num_coords); unsigned int fd = fdSelect->get_fd (glyph); - cff2_cs_interpreter_t interp; + cff2_cs_interpreter_t interp; const byte_str_t str = (*charStrings)[glyph]; - interp.env.init (str, *this, fd, coords, num_coords); - extents_param_t param; + interp.env.init (str, *this, fd, font->coords, font->num_coords); + cff2_extents_param_t param; param.init (); if (unlikely (!interp.interpret (param))) return false; @@ -118,8 +126,8 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, } else { - extents->x_bearing = (int32_t)param.min_x.floor (); - extents->width = (int32_t)param.max_x.ceil () - extents->x_bearing; + extents->x_bearing = font->em_scalef_x (param.min_x.to_real ()); + extents->width = font->em_scalef_x (param.max_x.to_real () - param.min_x.to_real ()); } if (param.min_y >= param.max_y) { @@ -128,9 +136,80 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, } else { - extents->y_bearing = (int32_t)param.max_y.ceil (); - extents->height = (int32_t)param.min_y.floor () - extents->y_bearing; + extents->y_bearing = font->em_scalef_y (param.max_y.to_real ()); + extents->height = font->em_scalef_y (param.min_y.to_real () - param.max_y.to_real ()); + } + + return true; +} + +#ifdef HB_EXPERIMENTAL_API +struct cff2_path_param_t +{ + cff2_path_param_t (hb_font_t *font_, draw_helper_t &draw_helper_) + { + draw_helper = &draw_helper_; + font = font_; + } + + void move_to (const point_t &p) + { draw_helper->move_to (font->em_scalef_x (p.x.to_real ()), font->em_scalef_y (p.y.to_real ())); } + + void line_to (const point_t &p) + { draw_helper->line_to (font->em_scalef_x (p.x.to_real ()), font->em_scalef_y (p.y.to_real ())); } + + void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3) + { + draw_helper->cubic_to (font->em_scalef_x (p1.x.to_real ()), font->em_scalef_y (p1.y.to_real ()), + font->em_scalef_x (p2.x.to_real ()), font->em_scalef_y (p2.y.to_real ()), + font->em_scalef_x (p3.x.to_real ()), font->em_scalef_y (p3.y.to_real ())); + } + + protected: + draw_helper_t *draw_helper; + hb_font_t *font; +}; + +struct cff2_path_procs_path_t : path_procs_t +{ + static void moveto (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt) + { + param.move_to (pt); + env.moveto (pt); + } + + static void line (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1) + { + param.line_to (pt1); + env.moveto (pt1); } + static void curve (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + param.cubic_to (pt1, pt2, pt3); + env.moveto (pt3); + } +}; + +struct cff2_cs_opset_path_t : cff2_cs_opset_t {}; + +bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; + + unsigned int fd = fdSelect->get_fd (glyph); + cff2_cs_interpreter_t interp; + const byte_str_t str = (*charStrings)[glyph]; + interp.env.init (str, *this, fd, font->coords, font->num_coords); + cff2_path_param_t param (font, draw_helper); + if (unlikely (!interp.interpret (param))) return false; return true; } +#endif + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh index 8111d503844..90c0b5b9cf6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh @@ -27,9 +27,9 @@ #ifndef HB_OT_CFF2_TABLE_HH #define HB_OT_CFF2_TABLE_HH -#include "hb-ot-head-table.hh" #include "hb-ot-cff-common.hh" #include "hb-subset-cff2.hh" +#include "hb-draw.hh" namespace CFF { @@ -43,7 +43,6 @@ typedef CFFIndex CFF2Index; template struct CFF2IndexOf : CFFIndexOf {}; typedef CFF2Index CFF2CharStrings; -typedef FDArray CFF2FDArray; typedef Subrs CFF2Subrs; typedef FDSelect3_4 FDSelect4; @@ -51,62 +50,63 @@ typedef FDSelect3_4_Range FDSelect4_Range; struct CFF2FDSelect { - bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const - { - TRACE_SANITIZE (this); - - return_trace (likely (c->check_struct (this) && (format == 0 || format == 3 || format == 4) && - (format == 0)? - u.format0.sanitize (c, fdcount): - ((format == 3)? - u.format3.sanitize (c, fdcount): - u.format4.sanitize (c, fdcount)))); - } - bool serialize (hb_serialize_context_t *c, const CFF2FDSelect &src, unsigned int num_glyphs) { TRACE_SERIALIZE (this); unsigned int size = src.get_size (num_glyphs); CFF2FDSelect *dest = c->allocate_size (size); - if (unlikely (dest == nullptr)) return_trace (false); + if (unlikely (!dest)) return_trace (false); memcpy (dest, &src, size); return_trace (true); } - unsigned int calculate_serialized_size (unsigned int num_glyphs) const - { return get_size (num_glyphs); } - unsigned int get_size (unsigned int num_glyphs) const { - unsigned int size = format.static_size; - if (format == 0) - size += u.format0.get_size (num_glyphs); - else if (format == 3) - size += u.format3.get_size (); - else - size += u.format4.get_size (); - return size; + switch (format) + { + case 0: return format.static_size + u.format0.get_size (num_glyphs); + case 3: return format.static_size + u.format3.get_size (); + case 4: return format.static_size + u.format4.get_size (); + default:return 0; + } } hb_codepoint_t get_fd (hb_codepoint_t glyph) const { - if (this == &Null(CFF2FDSelect)) + if (this == &Null (CFF2FDSelect)) return 0; - if (format == 0) - return u.format0.get_fd (glyph); - else if (format == 3) - return u.format3.get_fd (glyph); - else - return u.format4.get_fd (glyph); + + switch (format) + { + case 0: return u.format0.get_fd (glyph); + case 3: return u.format3.get_fd (glyph); + case 4: return u.format4.get_fd (glyph); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, fdcount)); + case 3: return_trace (u.format3.sanitize (c, fdcount)); + case 4: return_trace (u.format4.sanitize (c, fdcount)); + default:return_trace (false); + } } HBUINT8 format; union { - FDSelect0 format0; - FDSelect3 format3; - FDSelect4 format4; + FDSelect0 format0; + FDSelect3 format3; + FDSelect4 format4; } u; - + public: DEFINE_SIZE_MIN (2); }; @@ -123,7 +123,7 @@ struct CFF2VariationStore TRACE_SERIALIZE (this); unsigned int size_ = varStore->get_size (); CFF2VariationStore *dest = c->allocate_size (size_); - if (unlikely (dest == nullptr)) return_trace (false); + if (unlikely (!dest)) return_trace (false); memcpy (dest, varStore, size_); return_trace (true); } @@ -146,26 +146,6 @@ struct cff2_top_dict_values_t : top_dict_values_t<> } void fini () { top_dict_values_t<>::fini (); } - unsigned int calculate_serialized_size () const - { - unsigned int size = 0; - for (unsigned int i = 0; i < get_count (); i++) - { - op_code_t op = get_value (i).op; - switch (op) - { - case OpCode_vstore: - case OpCode_FDSelect: - size += OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op); - break; - default: - size += top_dict_values_t<>::calculate_serialized_op_size (get_value (i)); - break; - } - } - return size; - } - unsigned int vstoreOffset; unsigned int FDSelectOffset; }; @@ -252,22 +232,11 @@ struct cff2_private_dict_values_base_t : dict_values_t { dict_values_t::init (); subrsOffset = 0; - localSubrs = &Null(CFF2Subrs); + localSubrs = &Null (CFF2Subrs); ivs = 0; } void fini () { dict_values_t::fini (); } - unsigned int calculate_serialized_size () const - { - unsigned int size = 0; - for (unsigned int i = 0; i < dict_values_t::get_count; i++) - if (dict_values_t::get_value (i).op == OpCode_Subrs) - size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs); - else - size += dict_values_t::get_value (i).str.length; - return size; - } - unsigned int subrsOffset; const CFF2Subrs *localSubrs; unsigned int ivs; @@ -400,6 +369,14 @@ struct cff2_private_dict_opset_subset_t : dict_opset_t typedef dict_interpreter_t cff2_top_dict_interpreter_t; typedef dict_interpreter_t cff2_font_dict_interpreter_t; +struct CFF2FDArray : FDArray +{ + /* FDArray::serialize does not compile without this partial specialization */ + template + bool serialize (hb_serialize_context_t *c, ITER it, OP_SERIALIZER& opszr) + { return FDArray::serialize (c, it, opszr); } +}; + } /* namespace CFF */ namespace OT { @@ -434,7 +411,7 @@ struct cff2 const OT::cff2 *cff2 = this->blob->template as (); - if (cff2 == &Null(OT::cff2)) + if (cff2 == &Null (OT::cff2)) { fini (); return; } { /* parse top dict */ @@ -452,11 +429,11 @@ struct cff2 fdArray = &StructAtOffsetOrNull (cff2, topDict.FDArrayOffset); fdSelect = &StructAtOffsetOrNull (cff2, topDict.FDSelectOffset); - if (((varStore != &Null(CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) || - (charStrings == &Null(CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) || - (globalSubrs == &Null(CFF2Subrs)) || unlikely (!globalSubrs->sanitize (&sc)) || - (fdArray == &Null(CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) || - (((fdSelect != &Null(CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count))))) + if (((varStore != &Null (CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) || + (charStrings == &Null (CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) || + (globalSubrs == &Null (CFF2Subrs)) || unlikely (!globalSubrs->sanitize (&sc)) || + (fdArray == &Null (CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) || + (((fdSelect != &Null (CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count))))) { fini (); return; } num_glyphs = charStrings->count; @@ -464,7 +441,8 @@ struct cff2 { fini (); return; } fdCount = fdArray->count; - privateDicts.resize (fdCount); + if (!privateDicts.resize (fdCount)) + { fini (); return; } /* parse font dicts and gather private dicts */ for (unsigned int i = 0; i < fdCount; i++) @@ -475,7 +453,7 @@ struct cff2 cff2_font_dict_interpreter_t font_interp; font_interp.env.init (fontDictStr); font = fontDicts.push (); - if (unlikely (font == &Crap(cff2_font_dict_values_t))) { fini (); return; } + if (unlikely (font == &Crap (cff2_font_dict_values_t))) { fini (); return; } font->init (); if (unlikely (!font_interp.interpret (*font))) { fini (); return; } @@ -487,7 +465,7 @@ struct cff2 if (unlikely (!priv_interp.interpret (privateDicts[i]))) { fini (); return; } privateDicts[i].localSubrs = &StructAtOffsetOrNull (&privDictStr[0], privateDicts[i].subrsOffset); - if (privateDicts[i].localSubrs != &Null(CFF2Subrs) && + if (privateDicts[i].localSubrs != &Null (CFF2Subrs) && unlikely (!privateDicts[i].localSubrs->sanitize (&sc))) { fini (); return; } } @@ -503,7 +481,7 @@ struct cff2 blob = nullptr; } - bool is_valid () const { return blob != nullptr; } + bool is_valid () const { return blob; } protected: hb_blob_t *blob; @@ -529,27 +507,14 @@ struct cff2 HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; +#ifdef HB_EXPERIMENTAL_API + HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const; +#endif }; typedef accelerator_templ_t accelerator_subset_t; - bool subset (hb_subset_plan_t *plan) const - { - hb_blob_t *cff2_prime = nullptr; - - bool success = true; - if (hb_subset_cff2 (plan, &cff2_prime)) { - success = success && plan->add_table (HB_OT_TAG_cff2, cff2_prime); - hb_blob_t *head_blob = hb_sanitize_context_t().reference_table (plan->source); - success = success && head_blob && plan->add_table (HB_OT_TAG_head, head_blob); - hb_blob_destroy (head_blob); - } else { - success = false; - } - hb_blob_destroy (cff2_prime); - - return success; - } + bool subset (hb_subset_context_t *c) const { return hb_subset_cff2 (c); } public: FixedVersion version; /* Version of CFF2 table. set to 0x0200u */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh index 9e2ada67c4a..7160b168a7a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh @@ -56,6 +56,18 @@ struct CmapSubtableFormat0 out->add (i); } + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + for (unsigned i = 0; i < 256; i++) + if (glyphIdArray[i]) + { + hb_codepoint_t glyph = glyphIdArray[i]; + unicodes->add (i); + mapping->set (i, glyph); + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -74,154 +86,203 @@ struct CmapSubtableFormat0 struct CmapSubtableFormat4 { - struct segment_plan - { - HBUINT16 start_code; - HBUINT16 end_code; - bool use_delta; - }; - bool serialize (hb_serialize_context_t *c, - const hb_subset_plan_t *plan, - const hb_vector_t &segments) + template + HBUINT16* serialize_endcode_array (hb_serialize_context_t *c, + Iterator it) { - TRACE_SERIALIZE (this); - - if (unlikely (!c->extend_min (*this))) return_trace (false); + HBUINT16 *endCode = c->start_embed (); + hb_codepoint_t prev_endcp = 0xFFFF; - this->format.set (4); - this->length.set (get_sub_table_size (segments)); - - this->segCountX2.set (segments.length * 2); - this->entrySelector.set (MAX (1u, hb_bit_storage (segments.length)) - 1); - this->searchRange.set (2 * (1u << this->entrySelector)); - this->rangeShift.set (segments.length * 2 > this->searchRange - ? 2 * segments.length - this->searchRange - : 0); - - HBUINT16 *end_count = c->allocate_size (HBUINT16::static_size * segments.length); - c->allocate_size (HBUINT16::static_size); // 2 bytes of padding. - HBUINT16 *start_count = c->allocate_size (HBUINT16::static_size * segments.length); - HBINT16 *id_delta = c->allocate_size (HBUINT16::static_size * segments.length); - HBUINT16 *id_range_offset = c->allocate_size (HBUINT16::static_size * segments.length); - - if (id_range_offset == nullptr) - return_trace (false); + for (const hb_item_type _ : +it) + { + if (prev_endcp != 0xFFFF && prev_endcp + 1u != _.first) + { + HBUINT16 end_code; + end_code = prev_endcp; + c->copy (end_code); + } + prev_endcp = _.first; + } - for (unsigned int i = 0; i < segments.length; i++) { - end_count[i].set (segments[i].end_code); - start_count[i].set (segments[i].start_code); - if (segments[i].use_delta) + // last endCode + HBUINT16 endcode; + endcode = prev_endcp; + if (unlikely (!c->copy (endcode))) return nullptr; + // There must be a final entry with end_code == 0xFFFF. + if (prev_endcp != 0xFFFF) { - hb_codepoint_t cp = segments[i].start_code; - hb_codepoint_t start_gid = 0; - if (unlikely (!plan->new_gid_for_codepoint (cp, &start_gid) && cp != 0xFFFF)) - return_trace (false); - id_delta[i].set (start_gid - segments[i].start_code); - } else { - id_delta[i].set (0); - unsigned int num_codepoints = segments[i].end_code - segments[i].start_code + 1; - HBUINT16 *glyph_id_array = c->allocate_size (HBUINT16::static_size * num_codepoints); - if (glyph_id_array == nullptr) - return_trace (false); - // From the cmap spec: - // - // id_range_offset[i]/2 - // + (cp - segments[i].start_code) - // + (id_range_offset + i) - // = - // glyph_id_array + (cp - segments[i].start_code) - // - // So, solve for id_range_offset[i]: - // - // id_range_offset[i] - // = - // 2 * (glyph_id_array - id_range_offset - i) - id_range_offset[i].set (2 * ( - glyph_id_array - id_range_offset - i)); - for (unsigned int j = 0; j < num_codepoints; j++) - { - hb_codepoint_t cp = segments[i].start_code + j; - hb_codepoint_t new_gid; - if (unlikely (!plan->new_gid_for_codepoint (cp, &new_gid))) - return_trace (false); - glyph_id_array[j].set (new_gid); - } + HBUINT16 finalcode; + finalcode = 0xFFFF; + if (unlikely (!c->copy (finalcode))) return nullptr; } } - return_trace (true); + return endCode; } - static size_t get_sub_table_size (const hb_vector_t &segments) + template + HBUINT16* serialize_startcode_array (hb_serialize_context_t *c, + Iterator it) { - size_t segment_size = 0; - for (unsigned int i = 0; i < segments.length; i++) + HBUINT16 *startCode = c->start_embed (); + hb_codepoint_t prev_cp = 0xFFFF; + + for (const hb_item_type _ : +it) { - // Parallel array entries - segment_size += - 2 // end count - + 2 // start count - + 2 // delta - + 2; // range offset - - if (!segments[i].use_delta) - // Add bytes for the glyph index array entries for this segment. - segment_size += (segments[i].end_code - segments[i].start_code + 1) * 2; + if (prev_cp == 0xFFFF || prev_cp + 1u != _.first) + { + HBUINT16 start_code; + start_code = _.first; + c->copy (start_code); + } + + prev_cp = _.first; } - return min_size - + 2 // Padding - + segment_size; + // There must be a final entry with end_code == 0xFFFF. + if (it.len () == 0 || prev_cp != 0xFFFF) + { + HBUINT16 finalcode; + finalcode = 0xFFFF; + if (unlikely (!c->copy (finalcode))) return nullptr; + } + + return startCode; } - static bool create_sub_table_plan (const hb_subset_plan_t *plan, - hb_vector_t *segments) + template + HBINT16* serialize_idDelta_array (hb_serialize_context_t *c, + Iterator it, + HBUINT16 *endCode, + HBUINT16 *startCode, + unsigned segcount) { - segment_plan *segment = nullptr; - hb_codepoint_t last_gid = 0; + unsigned i = 0; + hb_codepoint_t last_gid = 0, start_gid = 0, last_cp = 0xFFFF; + bool use_delta = true; - hb_codepoint_t cp = HB_SET_VALUE_INVALID; - while (plan->unicodes->next (&cp)) { - hb_codepoint_t new_gid; - if (unlikely (!plan->new_gid_for_codepoint (cp, &new_gid))) + HBINT16 *idDelta = c->start_embed (); + if ((char *)idDelta - (char *)startCode != (int) segcount * (int) HBINT16::static_size) + return nullptr; + + for (const hb_item_type _ : +it) + { + if (_.first == startCode[i]) { - DEBUG_MSG(SUBSET, nullptr, "Unable to find new gid for %04x", cp); - return false; + use_delta = true; + start_gid = _.second; } + else if (_.second != last_gid + 1) use_delta = false; - /* Stop adding to cmap if we are now outside of unicode BMP. */ - if (cp > 0xFFFF) break; - - if (!segment || - cp != segment->end_code + 1u) + if (_.first == endCode[i]) { - segment = segments->push (); - segment->start_code.set (cp); - segment->end_code.set (cp); - segment->use_delta = true; - } else { - segment->end_code.set (cp); - if (last_gid + 1u != new_gid) - // gid's are not consecutive in this segment so delta - // cannot be used. - segment->use_delta = false; + HBINT16 delta; + if (use_delta) delta = (int)start_gid - (int)startCode[i]; + else delta = 0; + c->copy (delta); + + i++; } - last_gid = new_gid; + last_gid = _.second; + last_cp = _.first; } - // There must be a final entry with end_code == 0xFFFF. Check if we need to add one. - if (segment == nullptr || segment->end_code != 0xFFFF) + if (it.len () == 0 || last_cp != 0xFFFF) { - segment = segments->push (); - segment->start_code.set (0xFFFF); - segment->end_code.set (0xFFFF); - segment->use_delta = true; + HBINT16 delta; + delta = 1; + if (unlikely (!c->copy (delta))) return nullptr; } - return true; + return idDelta; + } + + template + HBUINT16* serialize_rangeoffset_glyid (hb_serialize_context_t *c, + Iterator it, + HBUINT16 *endCode, + HBUINT16 *startCode, + HBINT16 *idDelta, + unsigned segcount) + { + HBUINT16 *idRangeOffset = c->allocate_size (HBUINT16::static_size * segcount); + if (unlikely (!c->check_success (idRangeOffset))) return nullptr; + if (unlikely ((char *)idRangeOffset - (char *)idDelta != (int) segcount * (int) HBINT16::static_size)) return nullptr; + + + hb_range (segcount) + | hb_filter ([&] (const unsigned _) { return idDelta[_] == 0; }) + | hb_apply ([&] (const unsigned i) + { + idRangeOffset[i] = 2 * (c->start_embed () - idRangeOffset - i); + + + it + | hb_filter ([&] (const hb_item_type _) { return _.first >= startCode[i] && _.first <= endCode[i]; }) + | hb_apply ([&] (const hb_item_type _) + { + HBUINT16 glyID; + glyID = _.second; + c->copy (glyID); + }) + ; + + + }) + ; + + return idRangeOffset; + } + + template + void serialize (hb_serialize_context_t *c, + Iterator it) + { + auto format4_iter = + + it + | hb_filter ([&] (const hb_pair_t _) + { return _.first <= 0xFFFF; }) + ; + + if (format4_iter.len () == 0) return; + + unsigned table_initpos = c->length (); + if (unlikely (!c->extend_min (*this))) return; + this->format = 4; + + //serialize endCode[] + HBUINT16 *endCode = serialize_endcode_array (c, format4_iter); + if (unlikely (!endCode)) return; + + unsigned segcount = (c->length () - min_size) / HBUINT16::static_size; + + // 2 bytes of padding. + if (unlikely (!c->allocate_size (HBUINT16::static_size))) return; // 2 bytes of padding. + + // serialize startCode[] + HBUINT16 *startCode = serialize_startcode_array (c, format4_iter); + if (unlikely (!startCode)) return; + + //serialize idDelta[] + HBINT16 *idDelta = serialize_idDelta_array (c, format4_iter, endCode, startCode, segcount); + if (unlikely (!idDelta)) return; + + HBUINT16 *idRangeOffset = serialize_rangeoffset_glyid (c, format4_iter, endCode, startCode, idDelta, segcount); + if (unlikely (!c->check_success (idRangeOffset))) return; + + if (unlikely (!c->check_assign(this->length, c->length () - table_initpos))) return; + this->segCountX2 = segcount * 2; + this->entrySelector = hb_max (1u, hb_bit_storage (segcount)) - 1; + this->searchRange = 2 * (1u << this->entrySelector); + this->rangeShift = segcount * 2 > this->searchRange + ? 2 * segcount - this->searchRange + : 0; } struct accelerator_t @@ -244,27 +305,28 @@ struct CmapSubtableFormat4 bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { - /* Custom two-array bsearch. */ - int min = 0, max = (int) this->segCount - 1; - const HBUINT16 *startCount = this->startCount; - const HBUINT16 *endCount = this->endCount; - unsigned int i; - while (min <= max) + struct CustomRange { - int mid = ((unsigned int) min + (unsigned int) max) / 2; - if (codepoint < startCount[mid]) - max = mid - 1; - else if (codepoint > endCount[mid]) - min = mid + 1; - else + int cmp (hb_codepoint_t k, + unsigned distance) const { - i = mid; - goto found; + if (k > last) return +1; + if (k < (&last)[distance]) return -1; + return 0; } - } - return false; + HBUINT16 last; + }; + + const HBUINT16 *found = hb_bsearch (codepoint, + this->endCount, + this->segCount, + 2, + _hb_cmp_method, + this->segCount + 1); + if (!found) + return false; + unsigned int i = found - endCount; - found: hb_codepoint_t gid; unsigned int rangeOffset = this->idRangeOffset[i]; if (rangeOffset == 0) @@ -286,10 +348,10 @@ struct CmapSubtableFormat4 *glyph = gid; return true; } - static bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph) - { - return ((const accelerator_t *) obj)->get_glyph (codepoint, glyph); - } + + HB_INTERNAL static bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph) + { return ((const accelerator_t *) obj)->get_glyph (codepoint, glyph); } + void collect_unicodes (hb_set_t *out) const { unsigned int count = this->segCount; @@ -297,14 +359,22 @@ struct CmapSubtableFormat4 count--; /* Skip sentinel segment. */ for (unsigned int i = 0; i < count; i++) { + hb_codepoint_t start = this->startCount[i]; + hb_codepoint_t end = this->endCount[i]; unsigned int rangeOffset = this->idRangeOffset[i]; if (rangeOffset == 0) - out->add_range (this->startCount[i], this->endCount[i]); + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu; + if (unlikely (!gid)) + continue; + out->add (codepoint); + } + } else { - for (hb_codepoint_t codepoint = this->startCount[i]; - codepoint <= this->endCount[i]; - codepoint++) + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) { unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; if (unlikely (index >= this->glyphIdArrayLength)) @@ -318,6 +388,45 @@ struct CmapSubtableFormat4 } } + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + unsigned count = this->segCount; + if (count && this->startCount[count - 1] == 0xFFFFu) + count--; /* Skip sentinel segment. */ + for (unsigned i = 0; i < count; i++) + { + hb_codepoint_t start = this->startCount[i]; + hb_codepoint_t end = this->endCount[i]; + unsigned rangeOffset = this->idRangeOffset[i]; + if (rangeOffset == 0) + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu; + if (unlikely (!gid)) + continue; + unicodes->add (codepoint); + mapping->set (codepoint, gid); + } + } + else + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + unsigned index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; + if (unlikely (index >= this->glyphIdArrayLength)) + break; + hb_codepoint_t gid = this->glyphIdArray[index]; + if (unlikely (!gid)) + continue; + unicodes->add (codepoint); + mapping->set (codepoint, gid); + } + } + } + } + const HBUINT16 *endCount; const HBUINT16 *startCount; const HBUINT16 *idDelta; @@ -338,6 +447,13 @@ struct CmapSubtableFormat4 accel.collect_unicodes (out); } + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + accelerator_t accel (this); + accel.collect_mapping (unicodes, mapping); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -349,9 +465,9 @@ struct CmapSubtableFormat4 /* Some broken fonts have too long of a "length" value. * If that is the case, just change the value to truncate * the subtable at the end of the blob. */ - uint16_t new_length = (uint16_t) MIN ((uintptr_t) 65535, - (uintptr_t) (c->end - - (char *) this)); + uint16_t new_length = (uint16_t) hb_min ((uintptr_t) 65535, + (uintptr_t) (c->end - + (char *) this)); if (!c->try_set (&length, new_length)) return_trace (false); } @@ -440,6 +556,21 @@ struct CmapSubtableTrimmed out->add (start + i); } + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + hb_codepoint_t start_cp = startCharCode; + unsigned count = glyphIdArray.len; + for (unsigned i = 0; i < count; i++) + if (glyphIdArray[i]) + { + hb_codepoint_t unicode = start_cp + i; + hb_codepoint_t glyphid = glyphIdArray[i]; + unicodes->add (unicode); + mapping->set (unicode, glyphid); + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -451,7 +582,7 @@ struct CmapSubtableTrimmed UINT length; /* Byte length of this subtable. */ UINT language; /* Ignore. */ UINT startCharCode; /* First character code covered. */ - ArrayOf + ArrayOf glyphIdArray; /* Array of glyph index values for character * codes in the range. */ public: @@ -475,12 +606,56 @@ struct CmapSubtableLongSegmented return true; } - void collect_unicodes (hb_set_t *out) const + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const + { + for (unsigned int i = 0; i < this->groups.len; i++) + { + hb_codepoint_t start = this->groups[i].startCharCode; + hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode, + (hb_codepoint_t) HB_UNICODE_MAX); + hb_codepoint_t gid = this->groups[i].glyphID; + if (!gid) + { + /* Intention is: if (hb_is_same (T, CmapSubtableFormat13)) continue; */ + if (! T::group_get_glyph (this->groups[i], end)) continue; + start++; + gid++; + } + if (unlikely ((unsigned int) gid >= num_glyphs)) continue; + if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs)) + end = start + (hb_codepoint_t) num_glyphs - gid; + + out->add_range (start, end); + } + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping, /* OUT */ + unsigned num_glyphs) const { - for (unsigned int i = 0; i < this->groups.len; i++) { - out->add_range (this->groups[i].startCharCode, - MIN ((hb_codepoint_t) this->groups[i].endCharCode, - (hb_codepoint_t) HB_UNICODE_MAX)); + for (unsigned i = 0; i < this->groups.len; i++) + { + hb_codepoint_t start = this->groups[i].startCharCode; + hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode, + (hb_codepoint_t) HB_UNICODE_MAX); + hb_codepoint_t gid = this->groups[i].glyphID; + if (!gid) + { + /* Intention is: if (hb_is_same (T, CmapSubtableFormat13)) continue; */ + if (! T::group_get_glyph (this->groups[i], end)) continue; + start++; + gid++; + } + if (unlikely ((unsigned int) gid >= num_glyphs)) continue; + if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs)) + end = start + (hb_codepoint_t) num_glyphs - gid; + + for (unsigned cp = start; cp <= end; cp++) + { + unicodes->add (cp); + mapping->set (cp, gid); + gid++; + } } } @@ -490,15 +665,6 @@ struct CmapSubtableLongSegmented return_trace (c->check_struct (this) && groups.sanitize (c)); } - bool serialize (hb_serialize_context_t *c, - const hb_vector_t &group_data) - { - TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); - if (unlikely (!groups.serialize (c, group_data.as_array ()))) return_trace (false); - return true; - } - protected: HBUINT16 format; /* Subtable format; set to 12. */ HBUINT16 reserved; /* Reserved; set to 0. */ @@ -518,63 +684,66 @@ struct CmapSubtableFormat12 : CmapSubtableLongSegmented group.glyphID + (u - group.startCharCode) : 0; } - bool serialize (hb_serialize_context_t *c, - const hb_vector_t &groups) + template + void serialize (hb_serialize_context_t *c, + Iterator it) { - if (unlikely (!c->extend_min (*this))) return false; - - this->format.set (12); - this->reserved.set (0); - this->length.set (get_sub_table_size (groups)); + if (it.len () == 0) return; + unsigned table_initpos = c->length (); + if (unlikely (!c->extend_min (*this))) return; - return CmapSubtableLongSegmented::serialize (c, groups); - } + hb_codepoint_t startCharCode = 0xFFFF, endCharCode = 0xFFFF; + hb_codepoint_t glyphID = 0; - static size_t get_sub_table_size (const hb_vector_t &groups) - { - return 16 + 12 * groups.length; - } - - static bool create_sub_table_plan (const hb_subset_plan_t *plan, - hb_vector_t *groups) - { - CmapSubtableLongGroup *group = nullptr; - - hb_codepoint_t cp = HB_SET_VALUE_INVALID; - while (plan->unicodes->next (&cp)) { - hb_codepoint_t new_gid; - if (unlikely (!plan->new_gid_for_codepoint (cp, &new_gid))) + for (const hb_item_type _ : +it) + { + if (startCharCode == 0xFFFF) { - DEBUG_MSG(SUBSET, nullptr, "Unable to find new gid for %04x", cp); - return false; + startCharCode = _.first; + endCharCode = _.first; + glyphID = _.second; } - - if (!group || !_is_gid_consecutive (group, cp, new_gid)) + else if (!_is_gid_consecutive (endCharCode, startCharCode, glyphID, _.first, _.second)) { - group = groups->push (); - group->startCharCode.set (cp); - group->endCharCode.set (cp); - group->glyphID.set (new_gid); + CmapSubtableLongGroup grouprecord; + grouprecord.startCharCode = startCharCode; + grouprecord.endCharCode = endCharCode; + grouprecord.glyphID = glyphID; + c->copy (grouprecord); + + startCharCode = _.first; + endCharCode = _.first; + glyphID = _.second; } - else group->endCharCode.set (cp); + else + endCharCode = _.first; } - DEBUG_MSG(SUBSET, nullptr, "cmap"); - for (unsigned int i = 0; i < groups->length; i++) { - CmapSubtableLongGroup& group = (*groups)[i]; - DEBUG_MSG(SUBSET, nullptr, " %d: U+%04X-U+%04X, gid %d-%d", i, (uint32_t) group.startCharCode, (uint32_t) group.endCharCode, (uint32_t) group.glyphID, (uint32_t) group.glyphID + ((uint32_t) group.endCharCode - (uint32_t) group.startCharCode)); - } + CmapSubtableLongGroup record; + record.startCharCode = startCharCode; + record.endCharCode = endCharCode; + record.glyphID = glyphID; + c->copy (record); - return true; + this->format = 12; + this->reserved = 0; + this->length = c->length () - table_initpos; + this->groups.len = (this->length - min_size)/CmapSubtableLongGroup::static_size; } - private: - static bool _is_gid_consecutive (CmapSubtableLongGroup *group, + static size_t get_sub_table_size (const hb_sorted_vector_t &groups_data) + { return 16 + 12 * groups_data.length; } + + private: + static bool _is_gid_consecutive (hb_codepoint_t endCharCode, + hb_codepoint_t startCharCode, + hb_codepoint_t glyphID, hb_codepoint_t cp, hb_codepoint_t new_gid) { - return (cp - 1 == group->endCharCode) && - new_gid == group->glyphID + (cp - group->startCharCode); + return (cp - 1 == endCharCode) && + new_gid == glyphID + (cp - startCharCode); } }; @@ -623,12 +792,69 @@ struct DefaultUVS : SortedArrayOf for (unsigned int i = 0; i < count; i++) { hb_codepoint_t first = arrayZ[i].startUnicodeValue; - hb_codepoint_t last = MIN ((hb_codepoint_t) (first + arrayZ[i].additionalCount), - (hb_codepoint_t) HB_UNICODE_MAX); + hb_codepoint_t last = hb_min ((hb_codepoint_t) (first + arrayZ[i].additionalCount), + (hb_codepoint_t) HB_UNICODE_MAX); out->add_range (first, last); } } + DefaultUVS* copy (hb_serialize_context_t *c, + const hb_set_t *unicodes) const + { + DefaultUVS *out = c->start_embed (); + if (unlikely (!out)) return nullptr; + auto snap = c->snapshot (); + + HBUINT32 len; + len = 0; + if (unlikely (!c->copy (len))) return nullptr; + unsigned init_len = c->length (); + + hb_codepoint_t lastCode = HB_MAP_VALUE_INVALID; + int count = -1; + + for (const UnicodeValueRange& _ : as_array ()) + { + for (const unsigned addcnt : hb_range ((unsigned) _.additionalCount + 1)) + { + unsigned curEntry = (unsigned) _.startUnicodeValue + addcnt; + if (!unicodes->has (curEntry)) continue; + count += 1; + if (lastCode == HB_MAP_VALUE_INVALID) + lastCode = curEntry; + else if (lastCode + count != curEntry) + { + UnicodeValueRange rec; + rec.startUnicodeValue = lastCode; + rec.additionalCount = count - 1; + c->copy (rec); + + lastCode = curEntry; + count = 0; + } + } + } + + if (lastCode != HB_MAP_VALUE_INVALID) + { + UnicodeValueRange rec; + rec.startUnicodeValue = lastCode; + rec.additionalCount = count; + c->copy (rec); + } + + if (c->length () - init_len == 0) + { + c->revert (snap); + return nullptr; + } + else + { + if (unlikely (!c->check_assign (out->len, (c->length () - init_len) / UnicodeValueRange::static_size))) return nullptr; + return out; + } + } + public: DEFINE_SIZE_ARRAY (4, *this); }; @@ -636,9 +862,7 @@ struct DefaultUVS : SortedArrayOf struct UVSMapping { int cmp (const hb_codepoint_t &codepoint) const - { - return unicodeValue.cmp (codepoint); - } + { return unicodeValue.cmp (codepoint); } bool sanitize (hb_sanitize_context_t *c) const { @@ -647,7 +871,7 @@ struct UVSMapping } HBUINT24 unicodeValue; /* Base Unicode value of the UVS */ - GlyphID glyphID; /* Glyph ID of the UVS */ + HBGlyphID glyphID; /* Glyph ID of the UVS */ public: DEFINE_SIZE_STATIC (5); }; @@ -658,7 +882,63 @@ struct NonDefaultUVS : SortedArrayOf { unsigned int count = len; for (unsigned int i = 0; i < count; i++) - out->add (arrayZ[i].glyphID); + out->add (arrayZ[i].unicodeValue); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + unsigned count = len; + for (unsigned i = 0; i < count; i++) + { + hb_codepoint_t unicode = arrayZ[i].unicodeValue; + hb_codepoint_t glyphid = arrayZ[i].glyphID; + unicodes->add (unicode); + mapping->set (unicode, glyphid); + } + } + + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const + { + + as_array () + | hb_filter (unicodes, &UVSMapping::unicodeValue) + | hb_map (&UVSMapping::glyphID) + | hb_sink (glyphset) + ; + } + + NonDefaultUVS* copy (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map) const + { + NonDefaultUVS *out = c->start_embed (); + if (unlikely (!out)) return nullptr; + + auto it = + + as_array () + | hb_filter ([&] (const UVSMapping& _) + { + return unicodes->has (_.unicodeValue) || glyphs_requested->has (_.glyphID); + }) + ; + + if (!it) return nullptr; + + HBUINT32 len; + len = it.len (); + if (unlikely (!c->copy (len))) return nullptr; + + for (const UVSMapping& _ : it) + { + UVSMapping mapping; + mapping.unicodeValue = _.unicodeValue; + mapping.glyphID = glyph_map->get (_.glyphID); + c->copy (mapping); + } + + return out; } public: @@ -682,17 +962,37 @@ struct VariationSelectorRecord return GLYPH_VARIANT_NOT_FOUND; } + VariationSelectorRecord(const VariationSelectorRecord& other) + { + *this = other; + } + + void operator= (const VariationSelectorRecord& other) + { + varSelector = other.varSelector; + HBUINT32 offset = other.defaultUVS; + defaultUVS = offset; + offset = other.nonDefaultUVS; + nonDefaultUVS = offset; + } + void collect_unicodes (hb_set_t *out, const void *base) const { (base+defaultUVS).collect_unicodes (out); (base+nonDefaultUVS).collect_unicodes (out); } - int cmp (const hb_codepoint_t &variation_selector) const + void collect_mapping (const void *base, + hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const { - return varSelector.cmp (variation_selector); + (base+defaultUVS).collect_unicodes (unicodes); + (base+nonDefaultUVS).collect_mapping (unicodes, mapping); } + int cmp (const hb_codepoint_t &variation_selector) const + { return varSelector.cmp (variation_selector); } + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); @@ -701,6 +1001,45 @@ struct VariationSelectorRecord nonDefaultUVS.sanitize (c, base)); } + hb_pair_t + copy (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map, + const void *base) const + { + auto snap = c->snapshot (); + auto *out = c->embed (*this); + if (unlikely (!out)) return hb_pair (0, 0); + + out->defaultUVS = 0; + out->nonDefaultUVS = 0; + + unsigned non_default_uvs_objidx = 0; + if (nonDefaultUVS != 0) + { + c->push (); + if (c->copy (base+nonDefaultUVS, unicodes, glyphs_requested, glyph_map)) + non_default_uvs_objidx = c->pop_pack (); + else c->pop_discard (); + } + + unsigned default_uvs_objidx = 0; + if (defaultUVS != 0) + { + c->push (); + if (c->copy (base+defaultUVS, unicodes)) + default_uvs_objidx = c->pop_pack (); + else c->pop_discard (); + } + + + if (!default_uvs_objidx && !non_default_uvs_objidx) + c->revert (snap); + + return hb_pair (default_uvs_objidx, non_default_uvs_objidx); + } + HBUINT24 varSelector; /* Variation selector. */ LOffsetTo defaultUVS; /* Offset to Default UVS Table. May be 0. */ @@ -715,9 +1054,7 @@ struct CmapSubtableFormat14 glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint, hb_codepoint_t variation_selector, hb_codepoint_t *glyph) const - { - return record.bsearch (variation_selector).get_glyph (codepoint, glyph, this); - } + { return record.bsearch (variation_selector).get_glyph (codepoint, glyph, this); } void collect_variation_selectors (hb_set_t *out) const { @@ -727,8 +1064,110 @@ struct CmapSubtableFormat14 } void collect_variation_unicodes (hb_codepoint_t variation_selector, hb_set_t *out) const + { record.bsearch (variation_selector).collect_unicodes (out, this); } + + void serialize (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map, + const void *base) + { + auto snap = c->snapshot (); + unsigned table_initpos = c->length (); + const char* init_tail = c->tail; + + if (unlikely (!c->extend_min (*this))) return; + this->format = 14; + + auto src_tbl = reinterpret_cast (base); + + /* + * Some versions of OTS require that offsets are in order. Due to the use + * of push()/pop_pack() serializing the variation records in order results + * in the offsets being in reverse order (first record has the largest + * offset). While this is perfectly valid, it will cause some versions of + * OTS to consider this table bad. + * + * So to prevent this issue we serialize the variation records in reverse + * order, so that the offsets are ordered from small to large. Since + * variation records are supposed to be in increasing order of varSelector + * we then have to reverse the order of the written variation selector + * records after everything is finalized. + */ + hb_vector_t> obj_indices; + for (int i = src_tbl->record.len - 1; i >= 0; i--) + { + hb_pair_t result = src_tbl->record[i].copy (c, unicodes, glyphs_requested, glyph_map, base); + if (result.first || result.second) + obj_indices.push (result); + } + + if (c->length () - table_initpos == CmapSubtableFormat14::min_size) + { + c->revert (snap); + return; + } + + if (unlikely (!c->check_success (!obj_indices.in_error ()))) + return; + + int tail_len = init_tail - c->tail; + c->check_assign (this->length, c->length () - table_initpos + tail_len); + c->check_assign (this->record.len, + (c->length () - table_initpos - CmapSubtableFormat14::min_size) / + VariationSelectorRecord::static_size); + + /* Correct the incorrect write order by reversing the order of the variation + records array. */ + _reverse_variation_records (); + + /* Now that records are in the right order, we can set up the offsets. */ + _add_links_to_variation_records (c, obj_indices); + } + + void _reverse_variation_records () + { + record.as_array ().reverse (); + } + + void _add_links_to_variation_records (hb_serialize_context_t *c, + const hb_vector_t>& obj_indices) + { + for (unsigned i = 0; i < obj_indices.length; i++) + { + /* + * Since the record array has been reversed (see comments in copy()) + * but obj_indices has not been, the indices at obj_indices[i] + * are for the variation record at record[j]. + */ + int j = obj_indices.length - 1 - i; + c->add_link (record[j].defaultUVS, obj_indices[i].first); + c->add_link (record[j].nonDefaultUVS, obj_indices[i].second); + } + } + + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const + { + + hb_iter (record) + | hb_filter (hb_bool, &VariationSelectorRecord::nonDefaultUVS) + | hb_map (&VariationSelectorRecord::nonDefaultUVS) + | hb_map (hb_add (this)) + | hb_apply ([=] (const NonDefaultUVS& _) { _.closure_glyphs (unicodes, glyphset); }) + ; + } + + void collect_unicodes (hb_set_t *out) const + { + for (const VariationSelectorRecord& _ : record) + _.collect_unicodes (out, this); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const { - record.bsearch (variation_selector).collect_unicodes (out, this); + for (const VariationSelectorRecord& _ : record) + _.collect_mapping (this, unicodes, mapping); } bool sanitize (hb_sanitize_context_t *c) const @@ -766,20 +1205,52 @@ struct CmapSubtable default: return false; } } - void collect_unicodes (hb_set_t *out) const + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs = UINT_MAX) const { switch (u.format) { case 0: u.format0 .collect_unicodes (out); return; case 4: u.format4 .collect_unicodes (out); return; case 6: u.format6 .collect_unicodes (out); return; case 10: u.format10.collect_unicodes (out); return; - case 12: u.format12.collect_unicodes (out); return; - case 13: u.format13.collect_unicodes (out); return; + case 12: u.format12.collect_unicodes (out, num_glyphs); return; + case 13: u.format13.collect_unicodes (out, num_glyphs); return; case 14: default: return; } } + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping, /* OUT */ + unsigned num_glyphs = UINT_MAX) const + { + switch (u.format) { + case 0: u.format0 .collect_mapping (unicodes, mapping); return; + case 4: u.format4 .collect_mapping (unicodes, mapping); return; + case 6: u.format6 .collect_mapping (unicodes, mapping); return; + case 10: u.format10.collect_mapping (unicodes, mapping); return; + case 12: u.format12.collect_mapping (unicodes, mapping, num_glyphs); return; + case 13: u.format13.collect_mapping (unicodes, mapping, num_glyphs); return; + case 14: + default: return; + } + } + + template + void serialize (hb_serialize_context_t *c, + Iterator it, + unsigned format, + const hb_subset_plan_t *plan, + const void *base) + { + switch (format) { + case 4: return u.format4.serialize (c, it); + case 12: return u.format12.serialize (c, it); + case 14: return u.format14.serialize (c, plan->unicodes, plan->glyphs_requested, plan->glyph_map, base); + default: return; + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -831,6 +1302,40 @@ struct EncodingRecord subtable.sanitize (c, base)); } + template + EncodingRecord* copy (hb_serialize_context_t *c, + Iterator it, + unsigned format, + const void *base, + const hb_subset_plan_t *plan, + /* INOUT */ unsigned *objidx) const + { + TRACE_SERIALIZE (this); + auto snap = c->snapshot (); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + out->subtable = 0; + + if (*objidx == 0) + { + CmapSubtable *cmapsubtable = c->push (); + unsigned origin_length = c->length (); + cmapsubtable->serialize (c, it, format, plan, &(base+subtable)); + if (c->length () - origin_length > 0) *objidx = c->pop_pack (); + else c->pop_discard (); + } + + if (*objidx == 0) + { + c->revert (snap); + return_trace (nullptr); + } + + c->add_link (out->subtable, *objidx); + return_trace (out); + } + HBUINT16 platformID; /* Platform ID. */ HBUINT16 encodingID; /* Platform-specific encoding ID. */ LOffsetTo @@ -843,124 +1348,128 @@ struct cmap { static constexpr hb_tag_t tableTag = HB_OT_TAG_cmap; - struct subset_plan + template + void serialize (hb_serialize_context_t *c, + Iterator it, + EncodingRecIter encodingrec_iter, + const void *base, + const hb_subset_plan_t *plan) { - size_t final_size () const + if (unlikely (!c->extend_min ((*this)))) return; + this->version = 0; + + unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0; + + for (const EncodingRecord& _ : encodingrec_iter) { - return 4 // header - + 8 * 3 // 3 EncodingRecord - + CmapSubtableFormat4::get_sub_table_size (this->format4_segments) - + CmapSubtableFormat12::get_sub_table_size (this->format12_groups); + unsigned format = (base+_.subtable).u.format; + if (!plan->glyphs_requested->is_empty ()) + { + hb_set_t unicodes_set; + hb_map_t cp_glyphid_map; + (base+_.subtable).collect_mapping (&unicodes_set, &cp_glyphid_map); + + auto table_iter = + + hb_zip (unicodes_set.iter(), unicodes_set.iter() | hb_map(cp_glyphid_map)) + | hb_filter (plan->_glyphset, hb_second) + | hb_filter ([plan] (const hb_pair_t& p) + { + return plan->unicodes->has (p.first) || + plan->glyphs_requested->has (p.second); + }) + | hb_map ([plan] (const hb_pair_t& p_org) + { + return hb_pair_t (p_org.first, plan->glyph_map->get(p_org.second)); + }) + ; + + if (format == 4) c->copy (_, table_iter, 4u, base, plan, &format4objidx); + else if (format == 12) c->copy (_, table_iter, 12u, base, plan, &format12objidx); + else if (format == 14) c->copy (_, table_iter, 14u, base, plan, &format14objidx); + } + /* when --gids option is not used, we iterate input unicodes instead of + * all codepoints in each subtable, which is more efficient */ + else + { + hb_set_t unicodes_set; + (base+_.subtable).collect_unicodes (&unicodes_set); + + if (format == 4) c->copy (_, + it | hb_filter (unicodes_set, hb_first), 4u, base, plan, &format4objidx); + else if (format == 12) c->copy (_, + it | hb_filter (unicodes_set, hb_first), 12u, base, plan, &format12objidx); + else if (format == 14) c->copy (_, it, 14u, base, plan, &format14objidx); + } } - hb_vector_t format4_segments; - hb_vector_t format12_groups; - }; + c->check_assign(this->encodingRecord.len, (c->length () - cmap::min_size)/EncodingRecord::static_size); + } - bool _create_plan (const hb_subset_plan_t *plan, - subset_plan *cmap_plan) const + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const { - if (unlikely (!CmapSubtableFormat4::create_sub_table_plan (plan, &cmap_plan->format4_segments))) - return false; - - return CmapSubtableFormat12::create_sub_table_plan (plan, &cmap_plan->format12_groups); + + hb_iter (encodingRecord) + | hb_map (&EncodingRecord::subtable) + | hb_map (hb_add (this)) + | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == 14; }) + | hb_apply ([=] (const CmapSubtable& _) { _.u.format14.closure_glyphs (unicodes, glyphset); }) + ; } - bool _subset (const hb_subset_plan_t *plan, - const subset_plan &cmap_subset_plan, - size_t dest_sz, - void *dest) const + bool subset (hb_subset_context_t *c) const { - hb_serialize_context_t c (dest, dest_sz); - - cmap *table = c.start_serialize (); - if (unlikely (!c.extend_min (*table))) - { - return false; - } + TRACE_SUBSET (this); - table->version.set (0); + cmap *cmap_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->check_success (cmap_prime))) return_trace (false); - if (unlikely (!table->encodingRecord.serialize (&c, /* numTables */ 3))) - return false; - - // TODO(grieger): Convert the below to a for loop + auto encodingrec_iter = + + hb_iter (encodingRecord) + | hb_filter ([&] (const EncodingRecord& _) + { + if ((_.platformID == 0 && _.encodingID == 3) || + (_.platformID == 0 && _.encodingID == 4) || + (_.platformID == 3 && _.encodingID == 1) || + (_.platformID == 3 && _.encodingID == 10) || + (this + _.subtable).u.format == 14) + return true; - // Format 4, Plat 0 Encoding Record - EncodingRecord &format4_plat0_rec = table->encodingRecord[0]; - format4_plat0_rec.platformID.set (0); // Unicode - format4_plat0_rec.encodingID.set (3); + return false; + }) + ; - // Format 4, Plat 3 Encoding Record - EncodingRecord &format4_plat3_rec = table->encodingRecord[1]; - format4_plat3_rec.platformID.set (3); // Windows - format4_plat3_rec.encodingID.set (1); // Unicode BMP + if (unlikely (!encodingrec_iter.len ())) return_trace (false); - // Format 12 Encoding Record - EncodingRecord &format12_rec = table->encodingRecord[2]; - format12_rec.platformID.set (3); // Windows - format12_rec.encodingID.set (10); // Unicode UCS-4 + const EncodingRecord *unicode_bmp= nullptr, *unicode_ucs4 = nullptr, *ms_bmp = nullptr, *ms_ucs4 = nullptr; + bool has_format12 = false; - // Write out format 4 sub table + for (const EncodingRecord& _ : encodingrec_iter) { - CmapSubtable &subtable = format4_plat0_rec.subtable.serialize (&c, table); - format4_plat3_rec.subtable.set (format4_plat0_rec.subtable); - subtable.u.format.set (4); - - CmapSubtableFormat4 &format4 = subtable.u.format4; - if (unlikely (!format4.serialize (&c, plan, cmap_subset_plan.format4_segments))) - return false; + unsigned format = (this + _.subtable).u.format; + if (format == 12) has_format12 = true; + + const EncodingRecord *table = hb_addressof (_); + if (_.platformID == 0 && _.encodingID == 3) unicode_bmp = table; + else if (_.platformID == 0 && _.encodingID == 4) unicode_ucs4 = table; + else if (_.platformID == 3 && _.encodingID == 1) ms_bmp = table; + else if (_.platformID == 3 && _.encodingID == 10) ms_ucs4 = table; } - // Write out format 12 sub table. - { - CmapSubtable &subtable = format12_rec.subtable.serialize (&c, table); - subtable.u.format.set (12); - - CmapSubtableFormat12 &format12 = subtable.u.format12; - if (unlikely (!format12.serialize (&c, cmap_subset_plan.format12_groups))) - return false; - } - - c.end_serialize (); - - return true; - } - - bool subset (hb_subset_plan_t *plan) const - { - subset_plan cmap_subset_plan; - - if (unlikely (!_create_plan (plan, &cmap_subset_plan))) - { - DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cmap subsetting plan."); - return false; - } - - // We now know how big our blob needs to be - size_t dest_sz = cmap_subset_plan.final_size (); - void *dest = malloc (dest_sz); - if (unlikely (!dest)) { - DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %lu for cmap subset output", (unsigned long) dest_sz); - return false; - } - - if (unlikely (!_subset (plan, cmap_subset_plan, dest_sz, dest))) - { - DEBUG_MSG(SUBSET, nullptr, "Failed to perform subsetting of cmap."); - free (dest); - return false; - } - - // all done, write the blob into dest - hb_blob_t *cmap_prime = hb_blob_create ((const char *) dest, - dest_sz, - HB_MEMORY_MODE_READONLY, - dest, - free); - bool result = plan->add_table (HB_OT_TAG_cmap, cmap_prime); - hb_blob_destroy (cmap_prime); - return result; + if (unlikely (!has_format12 && !unicode_bmp && !ms_bmp)) return_trace (false); + if (unlikely (has_format12 && (!unicode_ucs4 && !ms_ucs4))) return_trace (false); + + auto it = + + hb_iter (c->plan->unicodes) + | hb_map ([&] (hb_codepoint_t _) + { + hb_codepoint_t new_gid = HB_MAP_VALUE_INVALID; + c->plan->new_gid_for_codepoint (_, &new_gid); + return hb_pair_t (_, new_gid); + }) + | hb_filter ([&] (const hb_pair_t _) + { return (_.second != HB_MAP_VALUE_INVALID); }) + ; + cmap_prime->serialize (c->serializer, it, encodingrec_iter, this, c->plan); + return_trace (true); } const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const @@ -969,6 +1478,15 @@ struct cmap const CmapSubtable *subtable; + /* Symbol subtable. + * Prefer symbol if available. + * https://github.com/harfbuzz/harfbuzz/issues/1918 */ + if ((subtable = this->find_subtable (3, 0))) + { + if (symbol) *symbol = true; + return subtable; + } + /* 32-bit subtables. */ if ((subtable = this->find_subtable (3, 10))) return subtable; if ((subtable = this->find_subtable (0, 6))) return subtable; @@ -981,13 +1499,6 @@ struct cmap if ((subtable = this->find_subtable (0, 1))) return subtable; if ((subtable = this->find_subtable (0, 0))) return subtable; - /* Symbol subtable. */ - if ((subtable = this->find_subtable (3, 0))) - { - if (symbol) *symbol = true; - return subtable; - } - /* Meh. */ return &Null (CmapSubtable); } @@ -1008,9 +1519,9 @@ struct cmap this->get_glyph_data = subtable; if (unlikely (symbol)) - { this->get_glyph_funcZ = get_glyph_from_symbol; - } else { + else + { switch (subtable->u.format) { /* Accelerate format 4 and format 12. */ default: @@ -1020,20 +1531,20 @@ struct cmap this->get_glyph_funcZ = get_glyph_from; break; case 4: - { - this->format4_accel.init (&subtable->u.format4); - this->get_glyph_data = &this->format4_accel; - this->get_glyph_funcZ = this->format4_accel.get_glyph_func; - } + { + this->format4_accel.init (&subtable->u.format4); + this->get_glyph_data = &this->format4_accel; + this->get_glyph_funcZ = this->format4_accel.get_glyph_func; break; } + } } } void fini () { this->table.destroy (); } bool get_nominal_glyph (hb_codepoint_t unicode, - hb_codepoint_t *glyph) const + hb_codepoint_t *glyph) const { if (unlikely (!this->get_glyph_funcZ)) return false; return this->get_glyph_funcZ (this->get_glyph_data, unicode, glyph); @@ -1076,19 +1587,16 @@ struct cmap return get_nominal_glyph (unicode, glyph); } - void collect_unicodes (hb_set_t *out) const - { - subtable->collect_unicodes (out); - } + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const + { subtable->collect_unicodes (out, num_glyphs); } + void collect_mapping (hb_set_t *unicodes, hb_map_t *mapping, + unsigned num_glyphs = UINT_MAX) const + { subtable->collect_mapping (unicodes, mapping, num_glyphs); } void collect_variation_selectors (hb_set_t *out) const - { - subtable_uvs->collect_variation_selectors (out); - } + { subtable_uvs->collect_variation_selectors (out); } void collect_variation_unicodes (hb_codepoint_t variation_selector, hb_set_t *out) const - { - subtable_uvs->collect_variation_unicodes (variation_selector, out); - } + { subtable_uvs->collect_variation_unicodes (variation_selector, out); } protected: typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj, @@ -1096,18 +1604,18 @@ struct cmap hb_codepoint_t *glyph); template - static bool get_glyph_from (const void *obj, - hb_codepoint_t codepoint, - hb_codepoint_t *glyph) + HB_INTERNAL static bool get_glyph_from (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) { const Type *typed_obj = (const Type *) obj; return typed_obj->get_glyph (codepoint, glyph); } template - static bool get_glyph_from_symbol (const void *obj, - hb_codepoint_t codepoint, - hb_codepoint_t *glyph) + HB_INTERNAL static bool get_glyph_from_symbol (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) { const Type *typed_obj = (const Type *) obj; if (likely (typed_obj->get_glyph (codepoint, glyph))) @@ -1135,6 +1643,7 @@ struct cmap CmapSubtableFormat4::accelerator_t format4_accel; + public: hb_blob_ptr_t table; }; @@ -1144,8 +1653,8 @@ struct cmap unsigned int encoding_id) const { EncodingRecord key; - key.platformID.set (platform_id); - key.encodingID.set (encoding_id); + key.platformID = platform_id; + key.encodingID = encoding_id; const EncodingRecord &result = encodingRecord.bsearch (key); if (!result.subtable) @@ -1154,6 +1663,28 @@ struct cmap return &(this+result.subtable); } + const EncodingRecord *find_encodingrec (unsigned int platform_id, + unsigned int encoding_id) const + { + EncodingRecord key; + key.platformID = platform_id; + key.encodingID = encoding_id; + + return encodingRecord.as_array ().bsearch (key); + } + + bool find_subtable (unsigned format) const + { + auto it = + + hb_iter (encodingRecord) + | hb_map (&EncodingRecord::subtable) + | hb_map (hb_add (this)) + | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == format; }) + ; + + return it.len (); + } + public: bool sanitize (hb_sanitize_context_t *c) const @@ -1165,9 +1696,9 @@ struct cmap } protected: - HBUINT16 version; /* Table version number (0). */ + HBUINT16 version; /* Table version number (0). */ SortedArrayOf - encodingRecord; /* Encoding tables. */ + encodingRecord; /* Encoding tables. */ public: DEFINE_SIZE_ARRAY (4, encodingRecord); }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cbdt-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cbdt-table.hh index 36ec2be984f..3e619bd4035 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cbdt-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cbdt-table.hh @@ -21,7 +21,7 @@ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * - * Google Author(s): Seigo Nonaka + * Google Author(s): Seigo Nonaka, Calder Kitagawa */ #ifndef HB_OT_COLOR_CBDT_TABLE_HH @@ -43,6 +43,35 @@ namespace OT { +struct cblc_bitmap_size_subset_context_t +{ + const char *cbdt; + unsigned int cbdt_length; + hb_vector_t *cbdt_prime; + unsigned int size; /* INOUT + * Input: old size of IndexSubtable + * Output: new size of IndexSubtable + */ + unsigned int num_tables; /* INOUT + * Input: old number of subtables. + * Output: new number of subtables. + */ + hb_codepoint_t start_glyph; /* OUT */ + hb_codepoint_t end_glyph; /* OUT */ +}; + +static inline bool +_copy_data_to_cbdt (hb_vector_t *cbdt_prime, + const void *data, + unsigned length) +{ + unsigned int new_len = cbdt_prime->length + length; + if (unlikely (!cbdt_prime->alloc (new_len))) return false; + memcpy (cbdt_prime->arrayZ + cbdt_prime->length, data, length); + cbdt_prime->length = new_len; + return true; +} + struct SmallGlyphMetrics { bool sanitize (hb_sanitize_context_t *c) const @@ -51,12 +80,12 @@ struct SmallGlyphMetrics return_trace (c->check_struct (this)); } - void get_extents (hb_glyph_extents_t *extents) const + void get_extents (hb_font_t *font, hb_glyph_extents_t *extents) const { - extents->x_bearing = bearingX; - extents->y_bearing = bearingY; - extents->width = width; - extents->height = - (hb_position_t) height; + extents->x_bearing = font->em_scale_x (bearingX); + extents->y_bearing = font->em_scale_y (bearingY); + extents->width = font->em_scale_x (width); + extents->height = font->em_scale_y (-static_cast(height)); } HBUINT8 height; @@ -65,7 +94,7 @@ struct SmallGlyphMetrics HBINT8 bearingY; HBUINT8 advance; public: - DEFINE_SIZE_STATIC(5); + DEFINE_SIZE_STATIC (5); }; struct BigGlyphMetrics : SmallGlyphMetrics @@ -74,7 +103,7 @@ struct BigGlyphMetrics : SmallGlyphMetrics HBINT8 vertBearingY; HBUINT8 vertAdvance; public: - DEFINE_SIZE_STATIC(8); + DEFINE_SIZE_STATIC (8); }; struct SBitLineMetrics @@ -98,7 +127,7 @@ struct SBitLineMetrics HBINT8 padding1; HBINT8 padding2; public: - DEFINE_SIZE_STATIC(12); + DEFINE_SIZE_STATIC (12); }; @@ -118,7 +147,7 @@ struct IndexSubtableHeader HBUINT16 imageFormat; HBUINT32 imageDataOffset; public: - DEFINE_SIZE_STATIC(8); + DEFINE_SIZE_STATIC (8); }; template @@ -143,11 +172,23 @@ struct IndexSubtableFormat1Or3 return true; } + bool add_offset (hb_serialize_context_t *c, + unsigned int offset, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + Offset embedded_offset; + embedded_offset = offset; + *size += sizeof (OffsetType); + auto *o = c->embed (embedded_offset); + return_trace ((bool) o); + } + IndexSubtableHeader header; - UnsizedArrayOf > + UnsizedArrayOf> offsetArrayZ; public: - DEFINE_SIZE_ARRAY(8, offsetArrayZ); + DEFINE_SIZE_ARRAY (8, offsetArrayZ); }; struct IndexSubtableFormat1 : IndexSubtableFormat1Or3 {}; @@ -159,35 +200,153 @@ struct IndexSubtable { TRACE_SANITIZE (this); if (!u.header.sanitize (c)) return_trace (false); - switch (u.header.indexFormat) { + switch (u.header.indexFormat) + { case 1: return_trace (u.format1.sanitize (c, glyph_count)); case 3: return_trace (u.format3.sanitize (c, glyph_count)); default:return_trace (true); } } + bool + finish_subtable (hb_serialize_context_t *c, + unsigned int cbdt_prime_len, + unsigned int num_glyphs, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + + unsigned int local_offset = cbdt_prime_len - u.header.imageDataOffset; + switch (u.header.indexFormat) + { + case 1: return_trace (u.format1.add_offset (c, local_offset, size)); + case 3: { + if (!u.format3.add_offset (c, local_offset, size)) + return_trace (false); + if (!(num_glyphs & 0x01)) // Pad to 32-bit alignment if needed. + return_trace (u.format3.add_offset (c, 0, size)); + return_trace (true); + } + // TODO: implement 2, 4, 5. + case 2: case 4: // No-op. + case 5: // Pad to 32-bit aligned. + default: return_trace (false); + } + } + + bool + fill_missing_glyphs (hb_serialize_context_t *c, + unsigned int cbdt_prime_len, + unsigned int num_missing, + unsigned int *size /* OUT (accumulated) */, + unsigned int *num_glyphs /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + + unsigned int local_offset = cbdt_prime_len - u.header.imageDataOffset; + switch (u.header.indexFormat) + { + case 1: { + for (unsigned int i = 0; i < num_missing; i++) + { + if (unlikely (!u.format1.add_offset (c, local_offset, size))) + return_trace (false); + *num_glyphs += 1; + } + return_trace (true); + } + case 3: { + for (unsigned int i = 0; i < num_missing; i++) + { + if (unlikely (!u.format3.add_offset (c, local_offset, size))) + return_trace (false); + *num_glyphs += 1; + } + return_trace (true); + } + // TODO: implement 2, 4, 5. + case 2: // Add empty space in cbdt_prime?. + case 4: case 5: // No-op as sparse is supported. + default: return_trace (false); + } + } + + bool + copy_glyph_at_idx (hb_serialize_context_t *c, unsigned int idx, + const char *cbdt, unsigned int cbdt_length, + hb_vector_t *cbdt_prime /* INOUT */, + IndexSubtable *subtable_prime /* INOUT */, + unsigned int *size /* OUT (accumulated) */) const + { + TRACE_SERIALIZE (this); + + unsigned int offset, length, format; + if (unlikely (!get_image_data (idx, &offset, &length, &format))) return_trace (false); + if (unlikely (offset > cbdt_length || cbdt_length - offset < length)) return_trace (false); + + auto *header_prime = subtable_prime->get_header (); + unsigned int new_local_offset = cbdt_prime->length - (unsigned int) header_prime->imageDataOffset; + if (unlikely (!_copy_data_to_cbdt (cbdt_prime, cbdt + offset, length))) return_trace (false); + + return_trace (subtable_prime->add_offset (c, new_local_offset, size)); + } + + bool + add_offset (hb_serialize_context_t *c, unsigned int local_offset, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + switch (u.header.indexFormat) + { + case 1: return_trace (u.format1.add_offset (c, local_offset, size)); + case 3: return_trace (u.format3.add_offset (c, local_offset, size)); + // TODO: Implement tables 2, 4, 5 + case 2: // Should be a no-op. + case 4: case 5: // Handle sparse cases. + default: return_trace (false); + } + } + bool get_extents (hb_glyph_extents_t *extents HB_UNUSED) const { - switch (u.header.indexFormat) { + switch (u.header.indexFormat) + { case 2: case 5: /* TODO */ case 1: case 3: case 4: /* Variable-metrics formats do not have metrics here. */ default:return (false); } } - bool get_image_data (unsigned int idx, - unsigned int *offset, - unsigned int *length, - unsigned int *format) const + bool + get_image_data (unsigned int idx, unsigned int *offset, + unsigned int *length, unsigned int *format) const { *format = u.header.imageFormat; - switch (u.header.indexFormat) { + switch (u.header.indexFormat) + { case 1: return u.format1.get_image_data (idx, offset, length); case 3: return u.format3.get_image_data (idx, offset, length); default: return false; } } + const IndexSubtableHeader* get_header () const { return &u.header; } + + void populate_header (unsigned index_format, + unsigned image_format, + unsigned int image_data_offset, + unsigned int *size) + { + u.header.indexFormat = index_format; + u.header.imageFormat = image_format; + u.header.imageDataOffset = image_data_offset; + switch (u.header.indexFormat) + { + case 1: *size += IndexSubtableFormat1::min_size; break; + case 3: *size += IndexSubtableFormat3::min_size; break; + } + } + protected: union { IndexSubtableHeader header; @@ -209,12 +368,135 @@ struct IndexSubtableRecord offsetToSubtable.sanitize (c, base, lastGlyphIndex - firstGlyphIndex + 1)); } - bool get_extents (hb_glyph_extents_t *extents, - const void *base) const + const IndexSubtable* get_subtable (const void *base) const + { + return &(base+offsetToSubtable); + } + + bool add_new_subtable (hb_subset_context_t* c, + cblc_bitmap_size_subset_context_t *bitmap_size_context, + IndexSubtableRecord *record, + const hb_vector_t> *lookup, /* IN */ + const void *base, + unsigned int *start /* INOUT */) const + { + TRACE_SERIALIZE (this); + + auto *subtable = c->serializer->start_embed (); + if (unlikely (!subtable)) return_trace (false); + if (unlikely (!c->serializer->extend_min (subtable))) return_trace (false); + + auto *old_subtable = get_subtable (base); + auto *old_header = old_subtable->get_header (); + + subtable->populate_header (old_header->indexFormat, + old_header->imageFormat, + bitmap_size_context->cbdt_prime->length, + &bitmap_size_context->size); + + unsigned int num_glyphs = 0; + bool early_exit = false; + for (unsigned int i = *start; i < lookup->length; i++) + { + hb_codepoint_t new_gid = (*lookup)[i].first; + const IndexSubtableRecord *next_record = (*lookup)[i].second; + const IndexSubtable *next_subtable = next_record->get_subtable (base); + auto *next_header = next_subtable->get_header (); + if (next_header != old_header) + { + *start = i; + early_exit = true; + break; + } + unsigned int num_missing = record->add_glyph_for_subset (new_gid); + if (unlikely (!subtable->fill_missing_glyphs (c->serializer, + bitmap_size_context->cbdt_prime->length, + num_missing, + &bitmap_size_context->size, + &num_glyphs))) + return_trace (false); + + hb_codepoint_t old_gid = 0; + c->plan->old_gid_for_new_gid (new_gid, &old_gid); + if (old_gid < next_record->firstGlyphIndex) + return_trace (false); + + unsigned int old_idx = (unsigned int) old_gid - next_record->firstGlyphIndex; + if (unlikely (!next_subtable->copy_glyph_at_idx (c->serializer, + old_idx, + bitmap_size_context->cbdt, + bitmap_size_context->cbdt_length, + bitmap_size_context->cbdt_prime, + subtable, + &bitmap_size_context->size))) + return_trace (false); + num_glyphs += 1; + } + if (!early_exit) + *start = lookup->length; + if (unlikely (!subtable->finish_subtable (c->serializer, + bitmap_size_context->cbdt_prime->length, + num_glyphs, + &bitmap_size_context->size))) + return_trace (false); + return_trace (true); + } + + bool add_new_record (hb_subset_context_t *c, + cblc_bitmap_size_subset_context_t *bitmap_size_context, + const hb_vector_t> *lookup, /* IN */ + const void *base, + unsigned int *start, /* INOUT */ + hb_vector_t* records /* INOUT */) const + { + TRACE_SERIALIZE (this); + auto snap = c->serializer->snapshot (); + unsigned int old_size = bitmap_size_context->size; + unsigned int old_cbdt_prime_length = bitmap_size_context->cbdt_prime->length; + + // Set to invalid state to indicate filling glyphs is not yet started. + if (unlikely (!records->resize (records->length + 1))) + return_trace (c->serializer->check_success (false)); + + (*records)[records->length - 1].firstGlyphIndex = 1; + (*records)[records->length - 1].lastGlyphIndex = 0; + bitmap_size_context->size += IndexSubtableRecord::min_size; + + c->serializer->push (); + + if (unlikely (!add_new_subtable (c, bitmap_size_context, &((*records)[records->length - 1]), lookup, base, start))) + { + c->serializer->pop_discard (); + c->serializer->revert (snap); + bitmap_size_context->cbdt_prime->shrink (old_cbdt_prime_length); + bitmap_size_context->size = old_size; + records->resize (records->length - 1); + return_trace (false); + } + + bitmap_size_context->num_tables += 1; + return_trace (true); + } + + unsigned int add_glyph_for_subset (hb_codepoint_t gid) { - return (base+offsetToSubtable).get_extents (extents); + if (firstGlyphIndex > lastGlyphIndex) + { + firstGlyphIndex = gid; + lastGlyphIndex = gid; + return 0; + } + // TODO maybe assert? this shouldn't occur. + if (lastGlyphIndex > gid) + return 0; + unsigned int num_missing = (unsigned int) (gid - lastGlyphIndex - 1); + lastGlyphIndex = gid; + return num_missing; } + bool get_extents (hb_glyph_extents_t *extents, const void *base) const + { return (base+offsetToSubtable).get_extents (extents); } + bool get_image_data (unsigned int gid, const void *base, unsigned int *offset, @@ -226,11 +508,11 @@ struct IndexSubtableRecord offset, length, format); } - GlyphID firstGlyphIndex; - GlyphID lastGlyphIndex; + HBGlyphID firstGlyphIndex; + HBGlyphID lastGlyphIndex; LOffsetTo offsetToSubtable; public: - DEFINE_SIZE_STATIC(8); + DEFINE_SIZE_STATIC (8); }; struct IndexSubtableArray @@ -243,6 +525,79 @@ struct IndexSubtableArray return_trace (indexSubtablesZ.sanitize (c, count, this)); } + void + build_lookup (hb_subset_context_t *c, cblc_bitmap_size_subset_context_t *bitmap_size_context, + hb_vector_t> *lookup /* OUT */) const + { + bool start_glyph_is_set = false; + for (hb_codepoint_t new_gid = 0; new_gid < c->plan->num_output_glyphs (); new_gid++) + { + hb_codepoint_t old_gid; + if (unlikely (!c->plan->old_gid_for_new_gid (new_gid, &old_gid))) continue; + + const IndexSubtableRecord* record = find_table (old_gid, bitmap_size_context->num_tables); + if (unlikely (!record)) continue; + + // Don't add gaps to the lookup. The best way to determine if a glyph is a + // gap is that it has no image data. + unsigned int offset, length, format; + if (unlikely (!record->get_image_data (old_gid, this, &offset, &length, &format))) continue; + + lookup->push (hb_pair_t (new_gid, record)); + + if (!start_glyph_is_set) + { + bitmap_size_context->start_glyph = new_gid; + start_glyph_is_set = true; + } + + bitmap_size_context->end_glyph = new_gid; + } + } + + bool + subset (hb_subset_context_t *c, + cblc_bitmap_size_subset_context_t *bitmap_size_context) const + { + TRACE_SUBSET (this); + + auto *dst = c->serializer->start_embed (); + if (unlikely (!dst)) return_trace (false); + + hb_vector_t> lookup; + build_lookup (c, bitmap_size_context, &lookup); + if (unlikely (lookup.in_error ())) + return c->serializer->check_success (false); + + bitmap_size_context->size = 0; + bitmap_size_context->num_tables = 0; + hb_vector_t records; + for (unsigned int start = 0; start < lookup.length;) + { + if (unlikely (!lookup[start].second->add_new_record (c, bitmap_size_context, &lookup, this, &start, &records))) + { + // Discard any leftover pushes to the serializer from successful records. + for (unsigned int i = 0; i < records.length; i++) + c->serializer->pop_discard (); + return_trace (false); + } + } + + /* Workaround to ensure offset ordering is from least to greatest when + * resolving links. */ + hb_vector_t objidxs; + for (unsigned int i = 0; i < records.length; i++) + objidxs.push (c->serializer->pop_pack ()); + for (unsigned int i = 0; i < records.length; i++) + { + IndexSubtableRecord* record = c->serializer->embed (records[i]); + if (unlikely (!record)) return_trace (false); + c->serializer->add_link (record->offsetToSubtable, objidxs[records.length - 1 - i]); + } + return_trace (true); + } + public: const IndexSubtableRecord* find_table (hb_codepoint_t glyph, unsigned int numTables) const { @@ -274,14 +629,48 @@ struct BitmapSizeTable vertical.sanitize (c)); } - const IndexSubtableRecord *find_table (hb_codepoint_t glyph, - const void *base, - const void **out_base) const + const IndexSubtableRecord * + find_table (hb_codepoint_t glyph, const void *base, const void **out_base) const { *out_base = &(base+indexSubtableArrayOffset); return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables); } + bool + subset (hb_subset_context_t *c, const void *base, + const char *cbdt, unsigned int cbdt_length, + hb_vector_t *cbdt_prime /* INOUT */) const + { + TRACE_SUBSET (this); + auto *out_table = c->serializer->embed (this); + if (unlikely (!out_table)) return_trace (false); + + cblc_bitmap_size_subset_context_t bitmap_size_context; + bitmap_size_context.cbdt = cbdt; + bitmap_size_context.cbdt_length = cbdt_length; + bitmap_size_context.cbdt_prime = cbdt_prime; + bitmap_size_context.size = indexTablesSize; + bitmap_size_context.num_tables = numberOfIndexSubtables; + bitmap_size_context.start_glyph = 1; + bitmap_size_context.end_glyph = 0; + + if (!out_table->indexSubtableArrayOffset.serialize_subset (c, + indexSubtableArrayOffset, + base, + &bitmap_size_context)) + return_trace (false); + if (!bitmap_size_context.size || + !bitmap_size_context.num_tables || + bitmap_size_context.start_glyph > bitmap_size_context.end_glyph) + return_trace (false); + + out_table->indexTablesSize = bitmap_size_context.size; + out_table->numberOfIndexSubtables = bitmap_size_context.num_tables; + out_table->startGlyphIndex = bitmap_size_context.start_glyph; + out_table->endGlyphIndex = bitmap_size_context.end_glyph; + return_trace (true); + } + protected: LNNOffsetTo indexSubtableArrayOffset; @@ -290,14 +679,14 @@ struct BitmapSizeTable HBUINT32 colorRef; SBitLineMetrics horizontal; SBitLineMetrics vertical; - GlyphID startGlyphIndex; - GlyphID endGlyphIndex; + HBGlyphID startGlyphIndex; + HBGlyphID endGlyphIndex; HBUINT8 ppemX; HBUINT8 ppemY; HBUINT8 bitDepth; HBINT8 flags; public: - DEFINE_SIZE_STATIC(48); + DEFINE_SIZE_STATIC (48); }; @@ -310,7 +699,7 @@ struct GlyphBitmapDataFormat17 SmallGlyphMetrics glyphMetrics; LArrayOf data; public: - DEFINE_SIZE_ARRAY(9, data); + DEFINE_SIZE_ARRAY (9, data); }; struct GlyphBitmapDataFormat18 @@ -318,14 +707,14 @@ struct GlyphBitmapDataFormat18 BigGlyphMetrics glyphMetrics; LArrayOf data; public: - DEFINE_SIZE_ARRAY(12, data); + DEFINE_SIZE_ARRAY (12, data); }; struct GlyphBitmapDataFormat19 { LArrayOf data; public: - DEFINE_SIZE_ARRAY(4, data); + DEFINE_SIZE_ARRAY (4, data); }; struct CBLC @@ -342,22 +731,60 @@ struct CBLC sizeTables.sanitize (c, this)); } + static bool + sink_cbdt (hb_subset_context_t *c, hb_vector_t* cbdt_prime) + { + hb_blob_t *cbdt_prime_blob = hb_blob_create (cbdt_prime->arrayZ, + cbdt_prime->length, + HB_MEMORY_MODE_WRITABLE, + cbdt_prime->arrayZ, + free); + cbdt_prime->init (); // Leak arrayZ to the blob. + bool ret = c->plan->add_table (HB_OT_TAG_CBDT, cbdt_prime_blob); + hb_blob_destroy (cbdt_prime_blob); + return ret; + } + + bool + subset_size_table (hb_subset_context_t *c, const BitmapSizeTable& table, + const char *cbdt /* IN */, unsigned int cbdt_length, + CBLC *cblc_prime /* INOUT */, hb_vector_t *cbdt_prime /* INOUT */) const + { + TRACE_SUBSET (this); + cblc_prime->sizeTables.len++; + + auto snap = c->serializer->snapshot (); + auto cbdt_prime_len = cbdt_prime->length; + + if (!table.subset (c, this, cbdt, cbdt_length, cbdt_prime)) + { + cblc_prime->sizeTables.len--; + c->serializer->revert (snap); + cbdt_prime->shrink (cbdt_prime_len); + return_trace (false); + } + return_trace (true); + } + + // Implemented in cc file as it depends on definition of CBDT. + HB_INTERNAL bool subset (hb_subset_context_t *c) const; + protected: const BitmapSizeTable &choose_strike (hb_font_t *font) const { unsigned count = sizeTables.len; if (unlikely (!count)) - return Null(BitmapSizeTable); + return Null (BitmapSizeTable); - unsigned int requested_ppem = MAX (font->x_ppem, font->y_ppem); + unsigned int requested_ppem = hb_max (font->x_ppem, font->y_ppem); if (!requested_ppem) requested_ppem = 1<<30; /* Choose largest strike. */ unsigned int best_i = 0; - unsigned int best_ppem = MAX (sizeTables[0].ppemX, sizeTables[0].ppemY); + unsigned int best_ppem = hb_max (sizeTables[0].ppemX, sizeTables[0].ppemY); for (unsigned int i = 1; i < count; i++) { - unsigned int ppem = MAX (sizeTables[i].ppemX, sizeTables[i].ppemY); + unsigned int ppem = hb_max (sizeTables[i].ppemX, sizeTables[i].ppemY); if ((requested_ppem <= ppem && ppem < best_ppem) || (requested_ppem > best_ppem && ppem > best_ppem)) { @@ -373,7 +800,7 @@ struct CBLC FixedVersion<> version; LArrayOf sizeTables; public: - DEFINE_SIZE_ARRAY(8, sizeTables); + DEFINE_SIZE_ARRAY (8, sizeTables); }; struct CBDT @@ -384,8 +811,8 @@ struct CBDT { void init (hb_face_t *face) { - cblc = hb_sanitize_context_t().reference_table (face); - cbdt = hb_sanitize_context_t().reference_table (face); + cblc = hb_sanitize_context_t ().reference_table (face); + cbdt = hb_sanitize_context_t ().reference_table (face); upem = hb_face_get_upem (face); } @@ -396,8 +823,8 @@ struct CBDT this->cbdt.destroy (); } - bool get_extents (hb_font_t *font, hb_codepoint_t glyph, - hb_glyph_extents_t *extents) const + bool + get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const { const void *base; const BitmapSizeTable &strike = this->cblc->choose_strike (font); @@ -412,48 +839,42 @@ struct CBDT if (!subtable_record->get_image_data (glyph, base, &image_offset, &image_length, &image_format)) return false; + unsigned int cbdt_len = cbdt.get_length (); + if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) + return false; + + switch (image_format) { - unsigned int cbdt_len = cbdt.get_length (); - if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) + case 17: { + if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) return false; - - switch (image_format) - { - case 17: { - if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) - return false; - const GlyphBitmapDataFormat17& glyphFormat17 = - StructAtOffset (this->cbdt, image_offset); - glyphFormat17.glyphMetrics.get_extents (extents); - break; - } - case 18: { - if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) - return false; - const GlyphBitmapDataFormat18& glyphFormat18 = - StructAtOffset (this->cbdt, image_offset); - glyphFormat18.glyphMetrics.get_extents (extents); - break; - } - default: - // TODO: Support other image formats. - return false; - } + auto &glyphFormat17 = StructAtOffset (this->cbdt, image_offset); + glyphFormat17.glyphMetrics.get_extents (font, extents); + break; + } + case 18: { + if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) + return false; + auto &glyphFormat18 = StructAtOffset (this->cbdt, image_offset); + glyphFormat18.glyphMetrics.get_extents (font, extents); + break; + } + default: return false; /* TODO: Support other image formats. */ } /* Convert to font units. */ - double x_scale = upem / (double) strike.ppemX; - double y_scale = upem / (double) strike.ppemY; - extents->x_bearing = round (extents->x_bearing * x_scale); - extents->y_bearing = round (extents->y_bearing * y_scale); - extents->width = round (extents->width * x_scale); - extents->height = round (extents->height * y_scale); + float x_scale = upem / (float) strike.ppemX; + float y_scale = upem / (float) strike.ppemY; + extents->x_bearing = roundf (extents->x_bearing * x_scale); + extents->y_bearing = roundf (extents->y_bearing * y_scale); + extents->width = roundf (extents->width * x_scale); + extents->height = roundf (extents->height * y_scale); return true; } - hb_blob_t* reference_png (hb_font_t *font, - hb_codepoint_t glyph) const + hb_blob_t* + reference_png (hb_font_t *font, hb_codepoint_t glyph) const { const void *base; const BitmapSizeTable &strike = this->cblc->choose_strike (font); @@ -465,44 +886,41 @@ struct CBDT if (!subtable_record->get_image_data (glyph, base, &image_offset, &image_length, &image_format)) return hb_blob_get_empty (); + unsigned int cbdt_len = cbdt.get_length (); + if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) + return hb_blob_get_empty (); + + switch (image_format) + { + case 17: { - unsigned int cbdt_len = cbdt.get_length (); - if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) + if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) return hb_blob_get_empty (); - - switch (image_format) - { - case 17: { - if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) - return hb_blob_get_empty (); - const GlyphBitmapDataFormat17& glyphFormat17 = - StructAtOffset (this->cbdt, image_offset); - return hb_blob_create_sub_blob (cbdt.get_blob (), - image_offset + GlyphBitmapDataFormat17::min_size, - glyphFormat17.data.len); - } - case 18: { - if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) - return hb_blob_get_empty (); - const GlyphBitmapDataFormat18& glyphFormat18 = - StructAtOffset (this->cbdt, image_offset); - return hb_blob_create_sub_blob (cbdt.get_blob (), - image_offset + GlyphBitmapDataFormat18::min_size, - glyphFormat18.data.len); - } - case 19: { - if (unlikely (image_length < GlyphBitmapDataFormat19::min_size)) - return hb_blob_get_empty (); - const GlyphBitmapDataFormat19& glyphFormat19 = - StructAtOffset (this->cbdt, image_offset); - return hb_blob_create_sub_blob (cbdt.get_blob (), - image_offset + GlyphBitmapDataFormat19::min_size, - glyphFormat19.data.len); - } - } + auto &glyphFormat17 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat17::min_size, + glyphFormat17.data.len); + } + case 18: + { + if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) + return hb_blob_get_empty (); + auto &glyphFormat18 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat18::min_size, + glyphFormat18.data.len); + } + case 19: + { + if (unlikely (image_length < GlyphBitmapDataFormat19::min_size)) + return hb_blob_get_empty (); + auto &glyphFormat19 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat19::min_size, + glyphFormat19.data.len); + } + default: return hb_blob_get_empty (); /* TODO: Support other image formats. */ } - - return hb_blob_get_empty (); } bool has_data () const { return cbdt.get_length (); } @@ -525,9 +943,41 @@ struct CBDT FixedVersion<> version; UnsizedArrayOf dataZ; public: - DEFINE_SIZE_ARRAY(4, dataZ); + DEFINE_SIZE_ARRAY (4, dataZ); }; +inline bool +CBLC::subset (hb_subset_context_t *c) const +{ + TRACE_SUBSET (this); + + auto *cblc_prime = c->serializer->start_embed (); + + // Use a vector as a secondary buffer as the tables need to be built in parallel. + hb_vector_t cbdt_prime; + + if (unlikely (!cblc_prime)) return_trace (false); + if (unlikely (!c->serializer->extend_min (cblc_prime))) return_trace (false); + cblc_prime->version = version; + + hb_blob_t* cbdt_blob = hb_sanitize_context_t ().reference_table (c->plan->source); + unsigned int cbdt_length; + CBDT* cbdt = (CBDT *) hb_blob_get_data (cbdt_blob, &cbdt_length); + if (unlikely (cbdt_length < CBDT::min_size)) + { + hb_blob_destroy (cbdt_blob); + return_trace (false); + } + _copy_data_to_cbdt (&cbdt_prime, cbdt, CBDT::min_size); + + for (const BitmapSizeTable& table : + sizeTables.iter ()) + subset_size_table (c, table, (const char *) cbdt, cbdt_length, cblc_prime, &cbdt_prime); + + hb_blob_destroy (cbdt_blob); + + return_trace (CBLC::sink_cbdt (c, &cbdt_prime)); +} + struct CBDT_accelerator_t : CBDT::accelerator_t {}; } /* namespace OT */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-colr-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-colr-table.hh index 362b4a14de6..21821d458a5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-colr-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-colr-table.hh @@ -1,5 +1,6 @@ /* * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2020 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -20,6 +21,8 @@ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Calder Kitagawa */ #ifndef HB_OT_COLOR_COLR_TABLE_HH @@ -39,6 +42,8 @@ namespace OT { struct LayerRecord { + operator hb_ot_color_layer_t () const { return {glyphId, colorIdx}; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -46,7 +51,7 @@ struct LayerRecord } public: - GlyphID glyphId; /* Glyph ID of layer glyph */ + HBGlyphID glyphId; /* Glyph ID of layer glyph */ Index colorIdx; /* Index value to use with a * selected color palette. * An index value of 0xFFFF @@ -73,7 +78,7 @@ struct BaseGlyphRecord } public: - GlyphID glyphId; /* Glyph ID of reference glyph */ + HBGlyphID glyphId; /* Glyph ID of reference glyph */ HBUINT16 firstLayerIdx; /* Index (from beginning of * the Layer Records) to the * layer record. There will be @@ -98,22 +103,50 @@ struct COLR { const BaseGlyphRecord &record = (this+baseGlyphsZ).bsearch (numBaseGlyphs, glyph); - hb_array_t all_layers ((this+layersZ).arrayZ, numLayers); + hb_array_t all_layers = (this+layersZ).as_array (numLayers); hb_array_t glyph_layers = all_layers.sub_array (record.firstLayerIdx, record.numLayers); if (count) { - hb_array_t segment_layers = glyph_layers.sub_array (start_offset, *count); - *count = segment_layers.length; - for (unsigned int i = 0; i < segment_layers.length; i++) - { - layers[i].glyph = segment_layers.arrayZ[i].glyphId; - layers[i].color_index = segment_layers.arrayZ[i].colorIdx; - } + + glyph_layers.sub_array (start_offset, count) + | hb_sink (hb_array (layers, *count)) + ; } return glyph_layers.length; } + struct accelerator_t + { + accelerator_t () {} + ~accelerator_t () { fini (); } + + void init (hb_face_t *face) + { colr = hb_sanitize_context_t ().reference_table (face); } + + void fini () { this->colr.destroy (); } + + bool is_valid () { return colr.get_blob ()->length; } + + void closure_glyphs (hb_codepoint_t glyph, + hb_set_t *related_ids /* OUT */) const + { colr->closure_glyphs (glyph, related_ids); } + + private: + hb_blob_ptr_t colr; + }; + + void closure_glyphs (hb_codepoint_t glyph, + hb_set_t *related_ids /* OUT */) const + { + const BaseGlyphRecord *record = get_base_glyph_record (glyph); + if (!record) return; + + auto glyph_layers = (this+layersZ).as_array (numLayers).sub_array (record->firstLayerIdx, + record->numLayers); + if (!glyph_layers.length) return; + related_ids->add_array (&glyph_layers[0].glyphId, glyph_layers.length, LayerRecord::min_size); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -122,12 +155,117 @@ struct COLR (this+layersZ).sanitize (c, numLayers))); } + template + bool serialize (hb_serialize_context_t *c, + unsigned version, + BaseIterator base_it, + LayerIterator layer_it) + { + TRACE_SERIALIZE (this); + if (unlikely (base_it.len () != layer_it.len ())) + return_trace (false); + + if (unlikely (!c->extend_min (this))) return_trace (false); + this->version = version; + numLayers = 0; + numBaseGlyphs = base_it.len (); + baseGlyphsZ = COLR::min_size; + layersZ = COLR::min_size + numBaseGlyphs * BaseGlyphRecord::min_size; + + for (const hb_item_type _ : + base_it.iter ()) + { + auto* record = c->embed (_); + if (unlikely (!record)) return_trace (false); + record->firstLayerIdx = numLayers; + numLayers += record->numLayers; + } + + for (const hb_item_type& _ : + layer_it.iter ()) + _.as_array ().copy (c); + + return_trace (true); + } + + const BaseGlyphRecord* get_base_glyph_record (hb_codepoint_t gid) const + { + if ((unsigned int) gid == 0) // Ignore notdef. + return nullptr; + const BaseGlyphRecord* record = &(this+baseGlyphsZ).bsearch (numBaseGlyphs, (unsigned int) gid); + if ((record && (hb_codepoint_t) record->glyphId != gid)) + record = nullptr; + return record; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map; + + auto base_it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map_retains_sorting ([&](hb_codepoint_t new_gid) + { + hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid); + + const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid); + if (unlikely (!old_record)) + return hb_pair_t (false, Null (BaseGlyphRecord)); + + BaseGlyphRecord new_record; + new_record.glyphId = new_gid; + new_record.numLayers = old_record->numLayers; + return hb_pair_t (true, new_record); + }) + | hb_filter (hb_first) + | hb_map_retains_sorting (hb_second) + ; + + auto layer_it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map (reverse_glyph_map) + | hb_map_retains_sorting ([&](hb_codepoint_t old_gid) + { + const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid); + hb_vector_t out_layers; + + if (unlikely (!old_record || + old_record->firstLayerIdx >= numLayers || + old_record->firstLayerIdx + old_record->numLayers > numLayers)) + return hb_pair_t> (false, out_layers); + + auto layers = (this+layersZ).as_array (numLayers).sub_array (old_record->firstLayerIdx, + old_record->numLayers); + out_layers.resize (layers.length); + for (unsigned int i = 0; i < layers.length; i++) { + out_layers[i] = layers[i]; + hb_codepoint_t new_gid = 0; + if (unlikely (!c->plan->new_gid_for_old_gid (out_layers[i].glyphId, &new_gid))) + return hb_pair_t> (false, out_layers); + out_layers[i].glyphId = new_gid; + } + + return hb_pair_t> (true, out_layers); + }) + | hb_filter (hb_first) + | hb_map_retains_sorting (hb_second) + ; + + if (unlikely (!base_it || !layer_it || base_it.len () != layer_it.len ())) + return_trace (false); + + COLR *colr_prime = c->serializer->start_embed (); + return_trace (colr_prime->serialize (c->serializer, version, base_it, layer_it)); + } + protected: HBUINT16 version; /* Table version number (starts at 0). */ HBUINT16 numBaseGlyphs; /* Number of Base Glyph Records. */ - LNNOffsetTo > + LNNOffsetTo> baseGlyphsZ; /* Offset to Base Glyph records. */ - LNNOffsetTo > + LNNOffsetTo> layersZ; /* Offset to Layer Records. */ HBUINT16 numLayers; /* Number of Layer Records. */ public: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cpal-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cpal-table.hh index f4ef6973417..f5f642d6bfa 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cpal-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cpal-table.hh @@ -87,15 +87,15 @@ struct CPALV1Tail } protected: - LNNOffsetTo > + LNNOffsetTo> paletteFlagsZ; /* Offset from the beginning of CPAL table to * the Palette Type Array. Set to 0 if no array * is provided. */ - LNNOffsetTo > + LNNOffsetTo> paletteLabelsZ; /* Offset from the beginning of CPAL table to * the palette labels array. Set to 0 if no * array is provided. */ - LNNOffsetTo > + LNNOffsetTo> colorLabelsZ; /* Offset from the beginning of CPAL table to * the color labels array. Set to 0 * if no array is provided. */ @@ -115,7 +115,7 @@ struct CPAL { return min_size + numPalettes * sizeof (colorRecordIndicesZ[0]); } unsigned int get_palette_count () const { return numPalettes; } - unsigned int get_color_count () const { return numColors; } + unsigned int get_color_count () const { return numColors; } hb_ot_color_palette_flags_t get_palette_flags (unsigned int palette_index) const { return v1 ().get_palette_flags (this, palette_index, numPalettes); } @@ -142,12 +142,9 @@ struct CPAL numColors); if (color_count) { - hb_array_t segment_colors = palette_colors.sub_array (start_offset, *color_count); - /* Always return numColors colors per palette even if it has out-of-bounds start index. */ - unsigned int count = MIN (MAX (numColors - start_offset, 0), *color_count); - *color_count = count; - for (unsigned int i = 0; i < count; i++) - colors[i] = segment_colors[i]; /* Bound-checked read. */ + + palette_colors.sub_array (start_offset, color_count) + | hb_sink (hb_array (colors, *color_count)) + ; } return numColors; } @@ -155,7 +152,7 @@ struct CPAL private: const CPALV1Tail& v1 () const { - if (version == 0) return Null(CPALV1Tail); + if (version == 0) return Null (CPALV1Tail); return StructAfter (*this); } @@ -176,7 +173,7 @@ struct CPAL HBUINT16 numPalettes; /* Number of palettes in the table. */ HBUINT16 numColorRecords; /* Total number of color records, combined for * all palettes. */ - LNNOffsetTo > + LNNOffsetTo> colorRecordsZ; /* Offset from the beginning of CPAL table to * the first ColorRecord. */ UnsizedArrayOf diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-sbix-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-sbix-table.hh index 5b89796d561..27b935edbbc 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-sbix-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-sbix-table.hh @@ -1,5 +1,6 @@ /* * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2020 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -20,12 +21,15 @@ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Calder Kitagawa */ #ifndef HB_OT_COLOR_SBIX_TABLE_HH #define HB_OT_COLOR_SBIX_TABLE_HH #include "hb-open-type.hh" +#include "hb-ot-layout-common.hh" /* * sbix -- Standard Bitmap Graphics @@ -40,6 +44,20 @@ namespace OT { struct SBIXGlyph { + SBIXGlyph* copy (hb_serialize_context_t *c, unsigned int data_length) const + { + TRACE_SERIALIZE (this); + SBIXGlyph* new_glyph = c->start_embed (); + if (unlikely (!new_glyph)) return_trace (nullptr); + if (unlikely (!c->extend_min (new_glyph))) return_trace (nullptr); + + new_glyph->xOffset = xOffset; + new_glyph->yOffset = yOffset; + new_glyph->graphicType = graphicType; + data.copy (c, data_length); + return_trace (new_glyph); + } + HBINT16 xOffset; /* The horizontal (x-axis) offset from the left * edge of the graphic to the glyph’s origin. * That is, the x-coordinate of the point on the @@ -62,6 +80,9 @@ struct SBIXGlyph struct SBIXStrike { + static unsigned int get_size (unsigned num_glyphs) + { return min_size + num_glyphs * HBUINT32::static_size; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -116,16 +137,59 @@ struct SBIXStrike return hb_blob_create_sub_blob (sbix_blob, glyph_offset, glyph_length); } + bool subset (hb_subset_context_t *c, unsigned int available_len) const + { + TRACE_SUBSET (this); + unsigned int num_output_glyphs = c->plan->num_output_glyphs (); + + auto* out = c->serializer->start_embed (); + if (unlikely (!out)) return_trace (false); + auto snap = c->serializer->snapshot (); + if (unlikely (!c->serializer->extend (*out, num_output_glyphs + 1))) return_trace (false); + out->ppem = ppem; + out->resolution = resolution; + HBUINT32 head; + head = get_size (num_output_glyphs + 1); + + bool has_glyphs = false; + for (unsigned new_gid = 0; new_gid < num_output_glyphs; new_gid++) + { + hb_codepoint_t old_gid; + if (!c->plan->old_gid_for_new_gid (new_gid, &old_gid) || + unlikely (imageOffsetsZ[old_gid].is_null () || + imageOffsetsZ[old_gid + 1].is_null () || + imageOffsetsZ[old_gid + 1] <= imageOffsetsZ[old_gid] || + imageOffsetsZ[old_gid + 1] - imageOffsetsZ[old_gid] <= SBIXGlyph::min_size) || + (unsigned int) imageOffsetsZ[old_gid + 1] > available_len) + { + out->imageOffsetsZ[new_gid] = head; + continue; + } + has_glyphs = true; + unsigned int delta = imageOffsetsZ[old_gid + 1] - imageOffsetsZ[old_gid]; + unsigned int glyph_data_length = delta - SBIXGlyph::min_size; + if (!(this+imageOffsetsZ[old_gid]).copy (c->serializer, glyph_data_length)) + return_trace (false); + out->imageOffsetsZ[new_gid] = head; + head += delta; + } + if (has_glyphs) + out->imageOffsetsZ[num_output_glyphs] = head; + else + c->serializer->revert (snap); + return_trace (has_glyphs); + } + public: HBUINT16 ppem; /* The PPEM size for which this strike was designed. */ HBUINT16 resolution; /* The device pixel density (in PPI) for which this * strike was designed. (E.g., 96 PPI, 192 PPI.) */ protected: - UnsizedArrayOf > + UnsizedArrayOf> imageOffsetsZ; /* Offset from the beginning of the strike data header * to bitmap data for an individual glyph ID. */ public: - DEFINE_SIZE_STATIC (8); + DEFINE_SIZE_ARRAY (4, imageOffsetsZ); }; struct sbix @@ -140,7 +204,7 @@ struct sbix { void init (hb_face_t *face) { - table = hb_sanitize_context_t().reference_table (face); + table = hb_sanitize_context_t ().reference_table (face); num_glyphs = face->get_num_glyphs (); } void fini () { table.destroy (); } @@ -173,9 +237,9 @@ struct sbix { unsigned count = table->strikes.len; if (unlikely (!count)) - return Null(SBIXStrike); + return Null (SBIXStrike); - unsigned int requested_ppem = MAX (font->x_ppem, font->y_ppem); + unsigned int requested_ppem = hb_max (font->x_ppem, font->y_ppem); if (!requested_ppem) requested_ppem = 1<<30; /* Choose largest strike. */ /* TODO Add DPI sensitivity as well? */ @@ -235,18 +299,25 @@ struct sbix const PNGHeader &png = *blob->as(); extents->x_bearing = x_offset; - extents->y_bearing = y_offset; + extents->y_bearing = png.IHDR.height + y_offset; extents->width = png.IHDR.width; - extents->height = png.IHDR.height; + extents->height = -1 * png.IHDR.height; /* Convert to font units. */ if (strike_ppem) { - double scale = font->face->get_upem () / (double) strike_ppem; - extents->x_bearing = round (extents->x_bearing * scale); - extents->y_bearing = round (extents->y_bearing * scale); - extents->width = round (extents->width * scale); - extents->height = round (extents->height * scale); + float scale = font->face->get_upem () / (float) strike_ppem; + extents->x_bearing = font->em_scalef_x (extents->x_bearing * scale); + extents->y_bearing = font->em_scalef_y (extents->y_bearing * scale); + extents->width = font->em_scalef_x (extents->width * scale); + extents->height = font->em_scalef_y (extents->height * scale); + } + else + { + extents->x_bearing = font->em_scale_x (extents->x_bearing); + extents->y_bearing = font->em_scale_y (extents->y_bearing); + extents->width = font->em_scale_x (extents->width); + extents->height = font->em_scale_y (extents->height); } hb_blob_destroy (blob); @@ -268,6 +339,63 @@ struct sbix strikes.sanitize (c, this))); } + bool + add_strike (hb_subset_context_t *c, unsigned i) const + { + if (strikes[i].is_null () || c->source_blob->length < (unsigned) strikes[i]) + return false; + + return (this+strikes[i]).subset (c, c->source_blob->length - (unsigned) strikes[i]); + } + + bool serialize_strike_offsets (hb_subset_context_t *c) const + { + TRACE_SERIALIZE (this); + + auto *out = c->serializer->start_embed> (); + if (unlikely (!out)) return_trace (false); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + hb_vector_t*> new_strikes; + hb_vector_t objidxs; + for (int i = strikes.len - 1; i >= 0; --i) + { + auto* o = out->serialize_append (c->serializer); + if (unlikely (!o)) return_trace (false); + *o = 0; + auto snap = c->serializer->snapshot (); + c->serializer->push (); + bool ret = add_strike (c, i); + if (!ret) + { + c->serializer->pop_discard (); + out->pop (); + c->serializer->revert (snap); + } + else + { + objidxs.push (c->serializer->pop_pack ()); + new_strikes.push (o); + } + } + for (unsigned int i = 0; i < new_strikes.length; ++i) + c->serializer->add_link (*new_strikes[i], objidxs[new_strikes.length - 1 - i]); + + return_trace (true); + } + + bool subset (hb_subset_context_t* c) const + { + TRACE_SUBSET (this); + + sbix *sbix_prime = c->serializer->start_embed (); + if (unlikely (!sbix_prime)) return_trace (false); + if (unlikely (!c->serializer->embed (this->version))) return_trace (false); + if (unlikely (!c->serializer->embed (this->flags))) return_trace (false); + + return_trace (serialize_strike_offsets (c)); + } + protected: HBUINT16 version; /* Table version number — set to 1 */ HBUINT16 flags; /* Bit 0: Set to 1. Bit 1: Draw outlines. diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-svg-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-svg-table.hh index eb0ba22debc..ccf9ed3365c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-svg-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-svg-table.hh @@ -62,7 +62,7 @@ struct SVGDocumentIndexEntry * this index entry. */ HBUINT16 endGlyphID; /* The last glyph ID in the range described by * this index entry. Must be >= startGlyphID. */ - LNNOffsetTo > + LNNOffsetTo> svgDoc; /* Offset from the beginning of the SVG Document Index * to an SVG document. Must be non-zero. */ HBUINT32 svgDocLength; /* Length of the SVG document. @@ -80,7 +80,7 @@ struct SVG struct accelerator_t { void init (hb_face_t *face) - { table = hb_sanitize_context_t().reference_table (face); } + { table = hb_sanitize_context_t ().reference_table (face); } void fini () { table.destroy (); } hb_blob_t *reference_blob_for_glyph (hb_codepoint_t glyph_id) const @@ -107,7 +107,7 @@ struct SVG protected: HBUINT16 version; /* Table version (starting at 0). */ - LOffsetTo > + LOffsetTo> svgDocEntries; /* Offset (relative to the start of the SVG table) to the * SVG Documents Index. Must be non-zero. */ /* Array of SVG Document Index Entries. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc index 84aeb96126e..d37e134c08b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc @@ -25,20 +25,21 @@ * Google Author(s): Sascha Brawer, Behdad Esfahbod */ -#include "hb-open-type.hh" +#include "hb.hh" + +#ifndef HB_NO_COLOR + +#include "hb-ot.h" + #include "hb-ot-color-cbdt-table.hh" #include "hb-ot-color-colr-table.hh" #include "hb-ot-color-cpal-table.hh" #include "hb-ot-color-sbix-table.hh" #include "hb-ot-color-svg-table.hh" -#include "hb-ot-face.hh" -#include "hb-ot.h" #include #include -#include "hb-ot-layout.hh" - /** * SECTION:hb-ot-color @@ -47,6 +48,8 @@ * @include: hb-ot.h * * Functions for fetching color-font information from OpenType font faces. + * + * HarfBuzz supports `COLR`/`CPAL`, `sbix`, `CBDT`, and `SVG` color fonts. **/ @@ -57,9 +60,11 @@ /** * hb_ot_color_has_palettes: - * @face: a font face. + * @face: #hb_face_t to work upon + * + * Tests whether a face includes a `CPAL` color-palette table. * - * Returns: whether CPAL table is available. + * Return value: true if data found, false otherwise * * Since: 2.1.0 */ @@ -71,10 +76,11 @@ hb_ot_color_has_palettes (hb_face_t *face) /** * hb_ot_color_palette_get_count: - * @face: a font face. + * @face: #hb_face_t to work upon * - * Returns: the number of color palettes in @face, or zero if @face has - * no colors. + * Fetches the number of color palettes in a face. + * + * Return value: the number of palettes found * * Since: 2.1.0 */ @@ -86,13 +92,16 @@ hb_ot_color_palette_get_count (hb_face_t *face) /** * hb_ot_color_palette_get_name_id: - * @face: a font face. - * @palette_index: the index of the color palette whose name is being requested. + * @face: #hb_face_t to work upon + * @palette_index: The index of the color palette + * + * Fetches the `name` table Name ID that provides display names for + * a `CPAL` color palette. * - * Retrieves the name id of a color palette. For example, a color font can - * have themed palettes like "Spring", "Summer", "Fall", and "Winter". + * Palette display names can be generic (e.g., "Default") or provide + * specific, themed names (e.g., "Spring", "Summer", "Fall", and "Winter"). * - * Returns: an identifier within @face's `name` table. + * Return value: the Named ID found for the palette. * If the requested palette has no name the result is #HB_OT_NAME_ID_INVALID. * * Since: 2.1.0 @@ -106,10 +115,16 @@ hb_ot_color_palette_get_name_id (hb_face_t *face, /** * hb_ot_color_palette_color_get_name_id: - * @face: a font face. - * @color_index: palette entry index. + * @face: #hb_face_t to work upon + * @color_index: The index of the color * - * Returns: Name ID associated with a palette entry, e.g. eye color + * Fetches the `name` table Name ID that provides display names for + * the specificed color in a face's `CPAL` color palette. + * + * Display names can be generic (e.g., "Background") or specific + * (e.g., "Eye color"). + * + * Return value: the Name ID found for the color. * * Since: 2.1.0 */ @@ -122,10 +137,12 @@ hb_ot_color_palette_color_get_name_id (hb_face_t *face, /** * hb_ot_color_palette_get_flags: - * @face: a font face - * @palette_index: the index of the color palette whose flags are being requested + * @face: #hb_face_t to work upon + * @palette_index: The index of the color palette + * + * Fetches the flags defined for a color palette. * - * Returns: the flags for the requested color palette. + * Return value: the #hb_ot_color_palette_flags_t of the requested color palette * * Since: 2.1.0 */ @@ -138,25 +155,22 @@ hb_ot_color_palette_get_flags (hb_face_t *face, /** * hb_ot_color_palette_get_colors: - * @face: a font face. - * @palette_index:the index of the color palette whose colors - * are being requested. - * @start_offset: the index of the first color being requested. - * @color_count: (inout) (optional): on input, how many colors - * can be maximally stored into the @colors array; - * on output, how many colors were actually stored. - * @colors: (array length=color_count) (out) (optional): - * an array of #hb_color_t records. After calling - * this function, @colors will be filled with - * the palette colors. If @colors is NULL, the function - * will just return the number of total colors - * without storing any actual colors; this can be used - * for allocating a buffer of suitable size before calling - * hb_ot_color_palette_get_colors() a second time. - * - * Retrieves the colors in a color palette. - * - * Returns: the total number of colors in the palette. + * @face: #hb_face_t to work upon + * @palette_index: the index of the color palette to query + * @start_offset: offset of the first color to retrieve + * @color_count: (inout) (optional): Input = the maximum number of colors to return; + * Output = the actual number of colors returned (may be zero) + * @colors: (out) (array length=color_count) (nullable): The array of #hb_color_t records found + * + * Fetches a list of the colors in a color palette. + * + * After calling this function, @colors will be filled with the palette + * colors. If @colors is NULL, the function will just return the number + * of total colors without storing any actual colors; this can be used + * for allocating a buffer of suitable size before calling + * hb_ot_color_palette_get_colors() a second time. + * + * Return value: the total number of colors in the palette * * Since: 2.1.0 */ @@ -177,9 +191,11 @@ hb_ot_color_palette_get_colors (hb_face_t *face, /** * hb_ot_color_has_layers: - * @face: a font face. + * @face: #hb_face_t to work upon + * + * Tests whether a face includes any `COLR` color layers. * - * Returns: whether COLR table is available. + * Return value: true if data found, false otherwise * * Since: 2.1.0 */ @@ -191,14 +207,17 @@ hb_ot_color_has_layers (hb_face_t *face) /** * hb_ot_color_glyph_get_layers: - * @face: a font face. - * @glyph: a layered color glyph id. - * @start_offset: starting offset of layers. - * @count: (inout) (optional): gets number of layers available to be written on buffer - * and returns number of written layers. - * @layers: (array length=count) (out) (optional): layers buffer to buffer. + * @face: #hb_face_t to work upon + * @glyph: The glyph index to query + * @start_offset: offset of the first layer to retrieve + * @layer_count: (inout) (optional): Input = the maximum number of layers to return; + * Output = the actual number of layers returned (may be zero) + * @layers: (out) (array length=layer_count) (nullable): The array of layers found + * + * Fetches a list of all color layers for the specified glyph index in the specified + * face. The list returned will begin at the offset provided. * - * Returns: Total number of layers a layered color glyph have. + * Return value: Total number of layers available for the glyph index queried * * Since: 2.1.0 */ @@ -206,10 +225,10 @@ unsigned int hb_ot_color_glyph_get_layers (hb_face_t *face, hb_codepoint_t glyph, unsigned int start_offset, - unsigned int *count, /* IN/OUT. May be NULL. */ + unsigned int *layer_count, /* IN/OUT. May be NULL. */ hb_ot_color_layer_t *layers /* OUT. May be NULL. */) { - return face->table.COLR->get_glyph_layers (glyph, start_offset, count, layers); + return face->table.COLR->get_glyph_layers (glyph, start_offset, layer_count, layers); } @@ -219,11 +238,11 @@ hb_ot_color_glyph_get_layers (hb_face_t *face, /** * hb_ot_color_has_svg: - * @face: a font face. + * @face: #hb_face_t to work upon. * - * Check whether @face has SVG glyph images. + * Tests whether a face includes any `SVG` glyph images. * - * Returns true if available, false otherwise. + * Return value: true if data found, false otherwise. * * Since: 2.1.0 */ @@ -235,12 +254,12 @@ hb_ot_color_has_svg (hb_face_t *face) /** * hb_ot_color_glyph_reference_svg: - * @face: a font face. - * @glyph: a svg glyph index. + * @face: #hb_face_t to work upon + * @glyph: a svg glyph index * - * Get SVG document for a glyph. The blob may be either plain text or gzip-encoded. + * Fetches the SVG document for a glyph. The blob may be either plain text or gzip-encoded. * - * Returns: (transfer full): respective svg blob of the glyph, if available. + * Return value: (transfer full): An #hb_blob_t containing the SVG document of the glyph, if available * * Since: 2.1.0 */ @@ -257,11 +276,11 @@ hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph) /** * hb_ot_color_has_png: - * @face: a font face. + * @face: #hb_face_t to work upon * - * Check whether @face has PNG glyph images (either CBDT or sbix tables). + * Tests whether a face has PNG glyph images (either in `CBDT` or `sbix` tables). * - * Returns true if available, false otherwise. + * Return value: true if data found, false otherwise * * Since: 2.1.0 */ @@ -273,14 +292,14 @@ hb_ot_color_has_png (hb_face_t *face) /** * hb_ot_color_glyph_reference_png: - * @font: a font object, not face. upem should be set on - * that font object if one wants to get optimal png blob, otherwise - * return the biggest one - * @glyph: a glyph index. + * @font: #hb_font_t to work upon + * @glyph: a glyph index * - * Get PNG image for a glyph. + * Fetches the PNG image for a glyph. This function takes a font object, not a face object, + * as input. To get an optimally sized PNG blob, the UPEM value must be set on the @font + * object. If UPEM is unset, the blob returned will be the largest PNG available. * - * Returns: (transfer full): respective PNG blob of the glyph, if available. + * Return value: (transfer full): An #hb_blob_t containing the PNG image for the glyph, if available * * Since: 2.1.0 */ @@ -297,3 +316,6 @@ hb_ot_color_glyph_reference_png (hb_font_t *font, hb_codepoint_t glyph) return blob; } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.h index 5736890fbe5..593447568dd 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.h @@ -59,11 +59,11 @@ hb_ot_color_palette_color_get_name_id (hb_face_t *face, /** * hb_ot_color_palette_flags_t: - * @HB_OT_COLOR_PALETTE_FLAG_DEFAULT: default indicating that there is nothing special + * @HB_OT_COLOR_PALETTE_FLAG_DEFAULT: Default indicating that there is nothing special * to note about a color palette. - * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND: flag indicating that the color + * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND: Flag indicating that the color * palette is appropriate to use when displaying the font on a light background such as white. - * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND: flag indicating that the color + * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND: Flag indicating that the color * palette is appropriate to use when displaying the font on a dark background such as black. * * Since: 2.1.0 @@ -110,7 +110,7 @@ HB_EXTERN unsigned int hb_ot_color_glyph_get_layers (hb_face_t *face, hb_codepoint_t glyph, unsigned int start_offset, - unsigned int *count, /* IN/OUT. May be NULL. */ + unsigned int *layer_count, /* IN/OUT. May be NULL. */ hb_ot_color_layer_t *layers /* OUT. May be NULL. */); /* diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-deprecated.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-deprecated.h index 2a31b320671..4fdb2b36ab9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-deprecated.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-deprecated.h @@ -40,6 +40,10 @@ HB_BEGIN_DECLS #ifndef HB_DISABLE_DEPRECATED +/* https://github.com/harfbuzz/harfbuzz/issues/1734 */ +#define HB_MATH_GLYPH_PART_FLAG_EXTENDER HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER + + /* Like hb_ot_layout_table_find_script, but takes zero-terminated array of scripts to test */ HB_EXTERN HB_DEPRECATED_FOR (hb_ot_layout_table_select_script) hb_bool_t hb_ot_layout_table_choose_script (hb_face_t *face, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh new file mode 100644 index 00000000000..367e143fdfb --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh @@ -0,0 +1,138 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2012,2013 Google, Inc. + * Copyright © 2019, Facebook Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_FACE_TABLE_LIST_HH +#define HB_OT_FACE_TABLE_LIST_HH +#endif /* HB_OT_FACE_TABLE_LIST_HH */ /* Dummy header guards */ + +#ifndef HB_OT_ACCELERATOR +#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type) +#define _HB_OT_ACCELERATOR_UNDEF +#endif + + +/* This lists font tables that the hb_face_t will contain and lazily + * load. Don't add a table unless it's used though. This is not + * exactly free. */ + +/* v--- Add new tables in the right place here. */ + + +/* OpenType fundamentals. */ +HB_OT_TABLE (OT, head) +#if !defined(HB_NO_FACE_COLLECT_UNICODES) || !defined(HB_NO_OT_FONT) +HB_OT_ACCELERATOR (OT, cmap) +#endif +HB_OT_TABLE (OT, hhea) +HB_OT_ACCELERATOR (OT, hmtx) +HB_OT_TABLE (OT, OS2) +#if !defined(HB_NO_OT_FONT_GLYPH_NAMES) || !defined(HB_NO_METRICS) || !defined(HB_NO_STYLE) +HB_OT_ACCELERATOR (OT, post) +#endif +#ifndef HB_NO_NAME +HB_OT_ACCELERATOR (OT, name) +#endif +#ifndef HB_NO_STYLE +HB_OT_TABLE (OT, STAT) +#endif +#ifndef HB_NO_META +HB_OT_ACCELERATOR (OT, meta) +#endif + +/* Vertical layout. */ +HB_OT_TABLE (OT, vhea) +HB_OT_ACCELERATOR (OT, vmtx) + +/* TrueType outlines. */ +HB_OT_ACCELERATOR (OT, glyf) + +/* CFF outlines. */ +#ifndef HB_NO_CFF +HB_OT_ACCELERATOR (OT, cff1) +HB_OT_ACCELERATOR (OT, cff2) +HB_OT_TABLE (OT, VORG) +#endif + +/* OpenType variations. */ +#ifndef HB_NO_VAR +HB_OT_TABLE (OT, fvar) +HB_OT_TABLE (OT, avar) +HB_OT_ACCELERATOR (OT, gvar) +HB_OT_TABLE (OT, MVAR) +#endif + +/* Legacy kern. */ +#ifndef HB_NO_OT_KERN +HB_OT_TABLE (OT, kern) +#endif + +/* OpenType shaping. */ +#ifndef HB_NO_OT_LAYOUT +HB_OT_ACCELERATOR (OT, GDEF) +HB_OT_ACCELERATOR (OT, GSUB) +HB_OT_ACCELERATOR (OT, GPOS) +//HB_OT_TABLE (OT, JSTF) +#endif + +/* OpenType baseline. */ +#ifndef HB_NO_BASE +HB_OT_TABLE (OT, BASE) +#endif + +/* AAT shaping. */ +#ifndef HB_NO_AAT +HB_OT_TABLE (AAT, morx) +HB_OT_TABLE (AAT, mort) +HB_OT_TABLE (AAT, kerx) +HB_OT_TABLE (AAT, ankr) +HB_OT_TABLE (AAT, trak) +HB_OT_TABLE (AAT, ltag) +HB_OT_TABLE (AAT, feat) +// HB_OT_TABLE (AAT, opbd) +#endif + +/* OpenType color fonts. */ +#ifndef HB_NO_COLOR +HB_OT_TABLE (OT, COLR) +HB_OT_TABLE (OT, CPAL) +HB_OT_ACCELERATOR (OT, CBDT) +HB_OT_ACCELERATOR (OT, sbix) +HB_OT_ACCELERATOR (OT, SVG) +#endif + +/* OpenType math. */ +#ifndef HB_NO_MATH +HB_OT_TABLE (OT, MATH) +#endif + + +#ifdef _HB_OT_ACCELERATOR_UNDEF +#undef HB_OT_ACCELERATOR +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc index 9b17526b7e4..5ef8df43ce7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc @@ -32,6 +32,7 @@ #include "hb-ot-cff2-table.hh" #include "hb-ot-hmtx-table.hh" #include "hb-ot-kern-table.hh" +#include "hb-ot-meta-table.hh" #include "hb-ot-name-table.hh" #include "hb-ot-post-table.hh" #include "hb-ot-color-cbdt-table.hh" @@ -46,16 +47,12 @@ void hb_ot_face_t::init0 (hb_face_t *face) { this->face = face; #define HB_OT_TABLE(Namespace, Type) Type.init0 (); -#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type) - HB_OT_TABLES -#undef HB_OT_ACCELERATOR +#include "hb-ot-face-table-list.hh" #undef HB_OT_TABLE } void hb_ot_face_t::fini () { #define HB_OT_TABLE(Namespace, Type) Type.fini (); -#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type) - HB_OT_TABLES -#undef HB_OT_ACCELERATOR +#include "hb-ot-face-table-list.hh" #undef HB_OT_TABLE } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-face.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-face.hh index 7f47ba6cb8e..e24d380bca8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-face.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-face.hh @@ -38,54 +38,10 @@ * hb_ot_face_t */ -#define HB_OT_TABLES \ - /* OpenType fundamentals. */ \ - HB_OT_TABLE(OT, head) \ - HB_OT_ACCELERATOR(OT, cmap) \ - HB_OT_ACCELERATOR(OT, hmtx) \ - HB_OT_ACCELERATOR(OT, vmtx) \ - HB_OT_ACCELERATOR(OT, post) \ - HB_OT_TABLE(OT, kern) \ - HB_OT_ACCELERATOR(OT, glyf) \ - HB_OT_ACCELERATOR(OT, cff1) \ - HB_OT_ACCELERATOR(OT, cff2) \ - HB_OT_TABLE(OT, VORG) \ - HB_OT_ACCELERATOR(OT, name) \ - HB_OT_TABLE(OT, OS2) \ - HB_OT_TABLE(OT, STAT) \ - /* OpenType shaping. */ \ - HB_OT_ACCELERATOR(OT, GDEF) \ - HB_OT_ACCELERATOR(OT, GSUB) \ - HB_OT_ACCELERATOR(OT, GPOS) \ - HB_OT_TABLE(OT, BASE) \ - HB_OT_TABLE(OT, JSTF) \ - /* AAT shaping. */ \ - HB_OT_TABLE(AAT, mort) \ - HB_OT_TABLE(AAT, morx) \ - HB_OT_TABLE(AAT, kerx) \ - HB_OT_TABLE(AAT, ankr) \ - HB_OT_TABLE(AAT, trak) \ - HB_OT_TABLE(AAT, lcar) \ - HB_OT_TABLE(AAT, ltag) \ - HB_OT_TABLE(AAT, feat) \ - /* OpenType variations. */ \ - HB_OT_TABLE(OT, fvar) \ - HB_OT_TABLE(OT, avar) \ - HB_OT_TABLE(OT, MVAR) \ - /* OpenType math. */ \ - HB_OT_TABLE(OT, MATH) \ - /* OpenType color fonts. */ \ - HB_OT_TABLE(OT, COLR) \ - HB_OT_TABLE(OT, CPAL) \ - HB_OT_ACCELERATOR(OT, CBDT) \ - HB_OT_ACCELERATOR(OT, sbix) \ - HB_OT_ACCELERATOR(OT, SVG) \ - /* */ - /* Declare tables. */ #define HB_OT_TABLE(Namespace, Type) namespace Namespace { struct Type; } #define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type##_accelerator_t) -HB_OT_TABLES +#include "hb-ot-face-table-list.hh" #undef HB_OT_ACCELERATOR #undef HB_OT_TABLE @@ -100,9 +56,7 @@ struct hb_ot_face_t { ORDER_ZERO, #define HB_OT_TABLE(Namespace, Type) HB_OT_TABLE_ORDER (Namespace, Type), -#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type) - HB_OT_TABLES -#undef HB_OT_ACCELERATOR +#include "hb-ot-face-table-list.hh" #undef HB_OT_TABLE }; @@ -111,7 +65,7 @@ struct hb_ot_face_t hb_table_lazy_loader_t Type; #define HB_OT_ACCELERATOR(Namespace, Type) \ hb_face_lazy_loader_t Type; - HB_OT_TABLES +#include "hb-ot-face-table-list.hh" #undef HB_OT_ACCELERATOR #undef HB_OT_TABLE }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc index b290c497762..f28de2af628 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc @@ -26,6 +26,8 @@ #include "hb.hh" +#ifndef HB_NO_OT_FONT + #include "hb-ot.h" #include "hb-font.hh" @@ -37,7 +39,6 @@ #include "hb-ot-cff1-table.hh" #include "hb-ot-cff2-table.hh" #include "hb-ot-hmtx-table.hh" -#include "hb-ot-kern-table.hh" #include "hb-ot-os2-table.hh" #include "hb-ot-post-table.hh" #include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise. @@ -52,7 +53,7 @@ * @short_description: OpenType font implementation * @include: hb-ot.h * - * Functions for using OpenType fonts with hb_shape(). Not that fonts returned + * Functions for using OpenType fonts with hb_shape(). Note that fonts returned * by hb_font_create() default to using these functions, so most clients would * never need to call these functions directly. **/ @@ -149,19 +150,21 @@ hb_ot_get_glyph_v_origin (hb_font_t *font, *x = font->get_glyph_h_advance (glyph) / 2; +#ifndef HB_NO_OT_FONT_CFF const OT::VORG &VORG = *ot_face->VORG; if (VORG.has_data ()) { *y = font->em_scale_y (VORG.get_y_origin (glyph)); return true; } +#endif hb_glyph_extents_t extents = {0}; - if (ot_face->glyf->get_extents (glyph, &extents)) + if (ot_face->glyf->get_extents (font, glyph, &extents)) { const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; - hb_position_t tsb = vmtx.get_side_bearing (glyph); - *y = font->em_scale_y (extents.y_bearing + tsb); + hb_position_t tsb = vmtx.get_side_bearing (font, glyph); + *y = extents.y_bearing + font->em_scale_y (tsb); return true; } @@ -180,23 +183,24 @@ hb_ot_get_glyph_extents (hb_font_t *font, void *user_data HB_UNUSED) { const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; - bool ret = ot_face->sbix->get_extents (font, glyph, extents); - if (!ret) - ret = ot_face->glyf->get_extents (glyph, extents); - if (!ret) - ret = ot_face->cff1->get_extents (glyph, extents); - if (!ret) - ret = ot_face->cff2->get_extents (font, glyph, extents); - if (!ret) - ret = ot_face->CBDT->get_extents (font, glyph, extents); + +#if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR) + if (ot_face->sbix->get_extents (font, glyph, extents)) return true; +#endif + if (ot_face->glyf->get_extents (font, glyph, extents)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff1->get_extents (font, glyph, extents)) return true; + if (ot_face->cff2->get_extents (font, glyph, extents)) return true; +#endif +#if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR) + if (ot_face->CBDT->get_extents (font, glyph, extents)) return true; +#endif + // TODO Hook up side-bearings variations. - extents->x_bearing = font->em_scale_x (extents->x_bearing); - extents->y_bearing = font->em_scale_y (extents->y_bearing); - extents->width = font->em_scale_x (extents->width); - extents->height = font->em_scale_y (extents->height); - return ret; + return false; } +#ifndef HB_NO_OT_FONT_GLYPH_NAMES static hb_bool_t hb_ot_get_glyph_name (hb_font_t *font HB_UNUSED, void *font_data, @@ -205,9 +209,12 @@ hb_ot_get_glyph_name (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; - return ot_face->post->get_glyph_name (glyph, name, size); + if (ot_face->post->get_glyph_name (glyph, name, size)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff1->get_glyph_name (glyph, name, size)) return true; +#endif + return false; } - static hb_bool_t hb_ot_get_glyph_from_name (hb_font_t *font HB_UNUSED, void *font_data, @@ -216,37 +223,34 @@ hb_ot_get_glyph_from_name (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; - return ot_face->post->get_glyph_from_name (name, len, glyph); + if (ot_face->post->get_glyph_from_name (name, len, glyph)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff1->get_glyph_from_name (name, len, glyph)) return true; +#endif + return false; } +#endif static hb_bool_t hb_ot_get_font_h_extents (hb_font_t *font, - void *font_data, + void *font_data HB_UNUSED, hb_font_extents_t *metrics, void *user_data HB_UNUSED) { - const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; - const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx; - metrics->ascender = font->em_scale_y (hmtx.ascender); - metrics->descender = font->em_scale_y (hmtx.descender); - metrics->line_gap = font->em_scale_y (hmtx.line_gap); - // TODO Hook up variations. - return hmtx.has_font_extents; + return _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &metrics->ascender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, &metrics->descender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, &metrics->line_gap); } static hb_bool_t hb_ot_get_font_v_extents (hb_font_t *font, - void *font_data, + void *font_data HB_UNUSED, hb_font_extents_t *metrics, void *user_data HB_UNUSED) { - const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; - const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; - metrics->ascender = font->em_scale_x (vmtx.ascender); - metrics->descender = font->em_scale_x (vmtx.descender); - metrics->line_gap = font->em_scale_x (vmtx.line_gap); - // TODO Hook up variations. - return vmtx.has_font_extents; + return _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_ASCENDER, &metrics->ascender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_DESCENDER, &metrics->descender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_LINE_GAP, &metrics->line_gap); } #if HB_USE_ATEXIT @@ -270,8 +274,10 @@ static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_tface->table, nullptr); } + +#ifndef HB_NO_VAR +int +_glyf_get_side_bearing_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical) +{ + return font->face->table.glyf->get_side_bearing_var (font, glyph, is_vertical); +} + +unsigned +_glyf_get_advance_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical) +{ + return font->face->table.glyf->get_advance_var (font, glyph, is_vertical); +} +#endif + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-glyf-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-glyf-table.hh index 252d0b4eb1a..cd95828e2f5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-glyf-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-glyf-table.hh @@ -1,5 +1,7 @@ /* * Copyright © 2015 Google, Inc. + * Copyright © 2019 Adobe Inc. + * Copyright © 2019 Ebrahim Byagowi * * This is part of HarfBuzz, a text shaping library. * @@ -21,7 +23,8 @@ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * - * Google Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger, Roderick Sheeter + * Adobe Author(s): Michiharu Ariza */ #ifndef HB_OT_GLYF_TABLE_HH @@ -29,7 +32,9 @@ #include "hb-open-type.hh" #include "hb-ot-head-table.hh" -#include "hb-subset-glyf.hh" +#include "hb-ot-hmtx-table.hh" +#include "hb-ot-var-gvar-table.hh" +#include "hb-draw.hh" namespace OT { @@ -54,11 +59,12 @@ struct loca } protected: - UnsizedArrayOf dataZ; /* Location data. */ + UnsizedArrayOf + dataZ; /* Location data. */ public: - DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always - * check the size externally, allow Null() object of it by - * defining it MIN() instead. */ + DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always + * check the size externally, allow Null() object of it by + * defining it _MIN instead. */ }; @@ -76,29 +82,143 @@ struct glyf bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const { TRACE_SANITIZE (this); - /* We don't check for anything specific here. The users of the - * struct do all the hard work... */ + /* Runtime checks as eager sanitizing each glyph is costy */ return_trace (true); } - bool subset (hb_subset_plan_t *plan) const + template + static bool + _add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets) + { + unsigned max_offset = + + padded_offsets + | hb_reduce (hb_add, 0) + ; + unsigned num_offsets = padded_offsets.len () + 1; + bool use_short_loca = max_offset < 0x1FFFF; + unsigned entry_size = use_short_loca ? 2 : 4; + char *loca_prime_data = (char *) calloc (entry_size, num_offsets); + + if (unlikely (!loca_prime_data)) return false; + + DEBUG_MSG (SUBSET, nullptr, "loca entry_size %d num_offsets %d " + "max_offset %d size %d", + entry_size, num_offsets, max_offset, entry_size * num_offsets); + + if (use_short_loca) + _write_loca (padded_offsets, 1, hb_array ((HBUINT16 *) loca_prime_data, num_offsets)); + else + _write_loca (padded_offsets, 0, hb_array ((HBUINT32 *) loca_prime_data, num_offsets)); + + hb_blob_t *loca_blob = hb_blob_create (loca_prime_data, + entry_size * num_offsets, + HB_MEMORY_MODE_WRITABLE, + loca_prime_data, + free); + + bool result = plan->add_table (HB_OT_TAG_loca, loca_blob) + && _add_head_and_set_loca_version (plan, use_short_loca); + + hb_blob_destroy (loca_blob); + return result; + } + + template + static void + _write_loca (IteratorIn it, unsigned right_shift, IteratorOut dest) { - hb_blob_t *glyf_prime = nullptr; - hb_blob_t *loca_prime = nullptr; - - bool success = true; - bool use_short_loca = false; - if (hb_subset_glyf_and_loca (plan, &use_short_loca, &glyf_prime, &loca_prime)) { - success = success && plan->add_table (HB_OT_TAG_glyf, glyf_prime); - success = success && plan->add_table (HB_OT_TAG_loca, loca_prime); - success = success && _add_head_and_set_loca_version (plan, use_short_loca); - } else { - success = false; + unsigned int offset = 0; + dest << 0; + + it + | hb_map ([=, &offset] (unsigned int padded_size) + { + offset += padded_size; + DEBUG_MSG (SUBSET, nullptr, "loca entry offset %d", offset); + return offset >> right_shift; + }) + | hb_sink (dest) + ; + } + + /* requires source of SubsetGlyph complains the identifier isn't declared */ + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + const hb_subset_plan_t *plan) + { + TRACE_SERIALIZE (this); + unsigned init_len = c->length (); + for (const auto &_ : it) _.serialize (c, plan); + + /* As a special case when all glyph in the font are empty, add a zero byte + * to the table, so that OTS doesn’t reject it, and to make the table work + * on Windows as well. + * See https://github.com/khaledhosny/ots/issues/52 */ + if (init_len == c->length ()) + { + HBUINT8 empty_byte; + empty_byte = 0; + c->copy (empty_byte); } - hb_blob_destroy (loca_prime); - hb_blob_destroy (glyf_prime); + return_trace (true); + } - return success; + /* Byte region(s) per glyph to output + unpadded, hints removed if so requested + If we fail to process a glyph we produce an empty (0-length) glyph */ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + glyf *glyf_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false); + + hb_vector_t glyphs; + _populate_subset_glyphs (c->plan, &glyphs); + + glyf_prime->serialize (c->serializer, hb_iter (glyphs), c->plan); + + auto padded_offsets = + + hb_iter (glyphs) + | hb_map (&SubsetGlyph::padded_size) + ; + + if (c->serializer->in_error ()) return_trace (false); + return_trace (c->serializer->check_success (_add_loca_and_head (c->plan, + padded_offsets))); + } + + template + void + _populate_subset_glyphs (const hb_subset_plan_t *plan, + hb_vector_t *glyphs /* OUT */) const + { + OT::glyf::accelerator_t glyf; + glyf.init (plan->source); + + + hb_range (plan->num_output_glyphs ()) + | hb_map ([&] (hb_codepoint_t new_gid) + { + SubsetGlyph subset_glyph = {0}; + subset_glyph.new_gid = new_gid; + + /* should never fail: all old gids should be mapped */ + if (!plan->old_gid_for_new_gid (new_gid, &subset_glyph.old_gid)) + return subset_glyph; + + subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true); + if (plan->drop_hints) subset_glyph.drop_hints_bytes (); + else subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes (); + + return subset_glyph; + }) + | hb_sink (glyphs) + ; + + glyf.fini (); } static bool @@ -112,218 +232,292 @@ struct glyf return false; head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr); - head_prime->indexToLocFormat.set (use_short_loca ? 0 : 1); + head_prime->indexToLocFormat = use_short_loca ? 0 : 1; bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob); hb_blob_destroy (head_prime_blob); return success; } - struct GlyphHeader - { - HBINT16 numberOfContours; /* If the number of contours is - * greater than or equal to zero, - * this is a simple glyph; if negative, - * this is a composite glyph. */ - FWORD xMin; /* Minimum x for coordinate data. */ - FWORD yMin; /* Minimum y for coordinate data. */ - FWORD xMax; /* Maximum x for coordinate data. */ - FWORD yMax; /* Maximum y for coordinate data. */ - - DEFINE_SIZE_STATIC (10); - }; - - struct CompositeGlyphHeader + struct CompositeGlyphChain { - enum composite_glyph_flag_t { - ARG_1_AND_2_ARE_WORDS = 0x0001, - ARGS_ARE_XY_VALUES = 0x0002, - ROUND_XY_TO_GRID = 0x0004, - WE_HAVE_A_SCALE = 0x0008, - MORE_COMPONENTS = 0x0020, - WE_HAVE_AN_X_AND_Y_SCALE = 0x0040, - WE_HAVE_A_TWO_BY_TWO = 0x0080, - WE_HAVE_INSTRUCTIONS = 0x0100, - USE_MY_METRICS = 0x0200, - OVERLAP_COMPOUND = 0x0400, - SCALED_COMPONENT_OFFSET = 0x0800, - UNSCALED_COMPONENT_OFFSET = 0x1000 + protected: + enum composite_glyph_flag_t + { + ARG_1_AND_2_ARE_WORDS = 0x0001, + ARGS_ARE_XY_VALUES = 0x0002, + ROUND_XY_TO_GRID = 0x0004, + WE_HAVE_A_SCALE = 0x0008, + MORE_COMPONENTS = 0x0020, + WE_HAVE_AN_X_AND_Y_SCALE = 0x0040, + WE_HAVE_A_TWO_BY_TWO = 0x0080, + WE_HAVE_INSTRUCTIONS = 0x0100, + USE_MY_METRICS = 0x0200, + OVERLAP_COMPOUND = 0x0400, + SCALED_COMPONENT_OFFSET = 0x0800, + UNSCALED_COMPONENT_OFFSET = 0x1000 }; - HBUINT16 flags; - GlyphID glyphIndex; - + public: unsigned int get_size () const { unsigned int size = min_size; - // arg1 and 2 are int16 + /* arg1 and 2 are int16 */ if (flags & ARG_1_AND_2_ARE_WORDS) size += 4; - // arg1 and 2 are int8 + /* arg1 and 2 are int8 */ else size += 2; - // One x 16 bit (scale) + /* One x 16 bit (scale) */ if (flags & WE_HAVE_A_SCALE) size += 2; - // Two x 16 bit (xscale, yscale) + /* Two x 16 bit (xscale, yscale) */ else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) size += 4; - // Four x 16 bit (xscale, scale01, scale10, yscale) + /* Four x 16 bit (xscale, scale01, scale10, yscale) */ else if (flags & WE_HAVE_A_TWO_BY_TWO) size += 8; return size; } - struct Iterator + void set_glyph_index (hb_codepoint_t new_gid) { glyphIndex = new_gid; } + hb_codepoint_t get_glyph_index () const { return glyphIndex; } + + void drop_instructions_flag () { flags = (uint16_t) flags & ~WE_HAVE_INSTRUCTIONS; } + bool has_instructions () const { return flags & WE_HAVE_INSTRUCTIONS; } + + bool has_more () const { return flags & MORE_COMPONENTS; } + bool is_use_my_metrics () const { return flags & USE_MY_METRICS; } + bool is_anchored () const { return !(flags & ARGS_ARE_XY_VALUES); } + void get_anchor_points (unsigned int &point1, unsigned int &point2) const { - const char *glyph_start; - const char *glyph_end; - const CompositeGlyphHeader *current; + const HBUINT8 *p = &StructAfter (glyphIndex); + if (flags & ARG_1_AND_2_ARE_WORDS) + { + point1 = ((const HBUINT16 *) p)[0]; + point2 = ((const HBUINT16 *) p)[1]; + } + else + { + point1 = p[0]; + point2 = p[1]; + } + } - bool move_to_next () + void transform_points (contour_point_vector_t &points) const + { + float matrix[4]; + contour_point_t trans; + if (get_transformation (matrix, trans)) { - if (current->flags & CompositeGlyphHeader::MORE_COMPONENTS) + if (scaled_offsets ()) { - const CompositeGlyphHeader *possible = - &StructAfter (*current); - if (!in_range (possible)) - return false; - current = possible; - return true; + points.translate (trans); + points.transform (matrix); + } + else + { + points.transform (matrix); + points.translate (trans); } - return false; } + } - bool in_range (const CompositeGlyphHeader *composite) const - { - return (const char *) composite >= glyph_start - && ((const char *) composite + CompositeGlyphHeader::min_size) <= glyph_end - && ((const char *) composite + composite->get_size ()) <= glyph_end; - } - }; + protected: + bool scaled_offsets () const + { return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; } - static bool get_iterator (const char * glyph_data, - unsigned int length, - CompositeGlyphHeader::Iterator *iterator /* OUT */) + bool get_transformation (float (&matrix)[4], contour_point_t &trans) const { - if (length < GlyphHeader::static_size) - return false; /* Empty glyph; zero extents. */ + matrix[0] = matrix[3] = 1.f; + matrix[1] = matrix[2] = 0.f; - const GlyphHeader &glyph_header = StructAtOffset (glyph_data, 0); - if (glyph_header.numberOfContours < 0) + int tx, ty; + const HBINT8 *p = &StructAfter (glyphIndex); + if (flags & ARG_1_AND_2_ARE_WORDS) { - const CompositeGlyphHeader *possible = - &StructAfter (glyph_header); - - iterator->glyph_start = glyph_data; - iterator->glyph_end = (const char *) glyph_data + length; - if (!iterator->in_range (possible)) - return false; - iterator->current = possible; - return true; + tx = *(const HBINT16 *) p; + p += HBINT16::static_size; + ty = *(const HBINT16 *) p; + p += HBINT16::static_size; } + else + { + tx = *p++; + ty = *p++; + } + if (is_anchored ()) tx = ty = 0; - return false; + trans.init ((float) tx, (float) ty); + + { + const F2DOT14 *points = (const F2DOT14 *) p; + if (flags & WE_HAVE_A_SCALE) + { + matrix[0] = matrix[3] = points[0].to_float (); + return true; + } + else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) + { + matrix[0] = points[0].to_float (); + matrix[3] = points[1].to_float (); + return true; + } + else if (flags & WE_HAVE_A_TWO_BY_TWO) + { + matrix[0] = points[0].to_float (); + matrix[1] = points[1].to_float (); + matrix[2] = points[2].to_float (); + matrix[3] = points[3].to_float (); + return true; + } + } + return tx || ty; } + protected: + HBUINT16 flags; + HBGlyphID glyphIndex; + public: DEFINE_SIZE_MIN (4); }; - struct accelerator_t + struct composite_iter_t : hb_iter_with_fallback_t { - void init (hb_face_t *face) + typedef const CompositeGlyphChain *__item_t__; + composite_iter_t (hb_bytes_t glyph_, __item_t__ current_) : + glyph (glyph_), current (current_) + { if (!check_range (current)) current = nullptr; } + composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr) {} + + const CompositeGlyphChain &__item__ () const { return *current; } + bool __more__ () const { return current; } + void __next__ () { - memset (this, 0, sizeof (accelerator_t)); + if (!current->has_more ()) { current = nullptr; return; } - const OT::head &head = *face->table.head; - if (head.indexToLocFormat > 1 || head.glyphDataFormat != 0) - /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ - return; - short_offset = 0 == head.indexToLocFormat; - - loca_table = hb_sanitize_context_t ().reference_table (face); - glyf_table = hb_sanitize_context_t ().reference_table (face); - - num_glyphs = MAX (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1; + const CompositeGlyphChain *possible = &StructAfter (*current); + if (!check_range (possible)) { current = nullptr; return; } + current = possible; } + bool operator != (const composite_iter_t& o) const + { return glyph != o.glyph || current != o.current; } - void fini () + bool check_range (const CompositeGlyphChain *composite) const { - loca_table.destroy (); - glyf_table.destroy (); + return glyph.check_range (composite, CompositeGlyphChain::min_size) + && glyph.check_range (composite, composite->get_size ()); } - /* - * Returns true if the referenced glyph is a valid glyph and a composite glyph. - * If true is returned a pointer to the composite glyph will be written into - * composite. - */ - bool get_composite (hb_codepoint_t glyph, - CompositeGlyphHeader::Iterator *composite /* OUT */) const - { - if (unlikely (!num_glyphs)) - return false; + private: + hb_bytes_t glyph; + __item_t__ current; + }; - unsigned int start_offset, end_offset; - if (!get_offsets (glyph, &start_offset, &end_offset)) - return false; /* glyph not found */ + enum phantom_point_index_t + { + PHANTOM_LEFT = 0, + PHANTOM_RIGHT = 1, + PHANTOM_TOP = 2, + PHANTOM_BOTTOM = 3, + PHANTOM_COUNT = 4 + }; - return CompositeGlyphHeader::get_iterator ((const char *) this->glyf_table + start_offset, - end_offset - start_offset, - composite); - } + struct accelerator_t; - enum simple_glyph_flag_t { - FLAG_ON_CURVE = 0x01, - FLAG_X_SHORT = 0x02, - FLAG_Y_SHORT = 0x04, - FLAG_REPEAT = 0x08, - FLAG_X_SAME = 0x10, - FLAG_Y_SAME = 0x20, + struct Glyph + { + enum simple_glyph_flag_t + { + FLAG_ON_CURVE = 0x01, + FLAG_X_SHORT = 0x02, + FLAG_Y_SHORT = 0x04, + FLAG_REPEAT = 0x08, + FLAG_X_SAME = 0x10, + FLAG_Y_SAME = 0x20, FLAG_RESERVED1 = 0x40, FLAG_RESERVED2 = 0x80 }; - /* based on FontTools _g_l_y_f.py::trim */ - bool remove_padding (unsigned int start_offset, - unsigned int *end_offset) const + private: + struct GlyphHeader { - if (*end_offset - start_offset < GlyphHeader::static_size) return true; + bool has_data () const { return numberOfContours; } - const char *glyph = ((const char *) glyf_table) + start_offset; - const char * const glyph_end = glyph + (*end_offset - start_offset); - const GlyphHeader &glyph_header = StructAtOffset (glyph, 0); - int16_t num_contours = (int16_t) glyph_header.numberOfContours; + bool get_extents (hb_font_t *font, const accelerator_t &glyf_accelerator, + hb_codepoint_t gid, hb_glyph_extents_t *extents) const + { + /* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */ + /* extents->x_bearing = hb_min (glyph_header.xMin, glyph_header.xMax); */ + extents->x_bearing = font->em_scale_x (glyf_accelerator.hmtx->get_side_bearing (gid)); + extents->y_bearing = font->em_scale_y (hb_max (yMin, yMax)); + extents->width = font->em_scale_x (hb_max (xMin, xMax) - hb_min (xMin, xMax)); + extents->height = font->em_scale_y (hb_min (yMin, yMax) - hb_max (yMin, yMax)); - if (num_contours < 0) - /* Trimming for composites not implemented. - * If removing hints it falls out of that. */ return true; - else if (num_contours > 0) + } + + HBINT16 numberOfContours; + /* If the number of contours is + * greater than or equal to zero, + * this is a simple glyph; if negative, + * this is a composite glyph. */ + FWORD xMin; /* Minimum x for coordinate data. */ + FWORD yMin; /* Minimum y for coordinate data. */ + FWORD xMax; /* Maximum x for coordinate data. */ + FWORD yMax; /* Maximum y for coordinate data. */ + public: + DEFINE_SIZE_STATIC (10); + }; + + struct SimpleGlyph + { + const GlyphHeader &header; + hb_bytes_t bytes; + SimpleGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + unsigned int instruction_len_offset () const + { return GlyphHeader::static_size + 2 * header.numberOfContours; } + + unsigned int length (unsigned int instruction_len) const + { return instruction_len_offset () + 2 + instruction_len; } + + unsigned int instructions_length () const { + unsigned int instruction_length_offset = instruction_len_offset (); + if (unlikely (instruction_length_offset + 2 > bytes.length)) return 0; + + const HBUINT16 &instructionLength = StructAtOffset (&bytes, instruction_length_offset); + /* Out of bounds of the current glyph */ + if (unlikely (length (instructionLength) > bytes.length)) return 0; + return instructionLength; + } + + const Glyph trim_padding () const + { + /* based on FontTools _g_l_y_f.py::trim */ + const char *glyph = bytes.arrayZ; + const char *glyph_end = glyph + bytes.length; /* simple glyph w/contours, possibly trimmable */ - glyph += GlyphHeader::static_size + 2 * num_contours; + glyph += instruction_len_offset (); - if (unlikely (glyph + 2 >= glyph_end)) return false; - uint16_t nCoordinates = (uint16_t) StructAtOffset (glyph - 2, 0) + 1; - uint16_t nInstructions = (uint16_t) StructAtOffset (glyph, 0); + if (unlikely (glyph + 2 >= glyph_end)) return Glyph (); + unsigned int num_coordinates = StructAtOffset (glyph - 2, 0) + 1; + unsigned int num_instructions = StructAtOffset (glyph, 0); - glyph += 2 + nInstructions; - if (unlikely (glyph + 2 >= glyph_end)) return false; + glyph += 2 + num_instructions; - unsigned int coordBytes = 0; - unsigned int coordsWithFlags = 0; + unsigned int coord_bytes = 0; + unsigned int coords_with_flags = 0; while (glyph < glyph_end) { - uint8_t flag = (uint8_t) *glyph; + uint8_t flag = *glyph; glyph++; unsigned int repeat = 1; if (flag & FLAG_REPEAT) { - if (glyph >= glyph_end) - { - DEBUG_MSG(SUBSET, nullptr, "Bad flag"); - return false; - } - repeat = ((uint8_t) *glyph) + 1; + if (unlikely (glyph >= glyph_end)) return Glyph (); + repeat = *glyph + 1; glyph++; } @@ -335,143 +529,728 @@ struct glyf if (flag & FLAG_Y_SHORT) yBytes = 1; else if ((flag & FLAG_Y_SAME) == 0) yBytes = 2; - coordBytes += (xBytes + yBytes) * repeat; - coordsWithFlags += repeat; - if (coordsWithFlags >= nCoordinates) - break; + coord_bytes += (xBytes + yBytes) * repeat; + coords_with_flags += repeat; + if (coords_with_flags >= num_coordinates) break; } - if (coordsWithFlags != nCoordinates) + if (unlikely (coords_with_flags != num_coordinates)) return Glyph (); + return Glyph (bytes.sub_array (0, bytes.length + coord_bytes - (glyph_end - glyph))); + } + + /* zero instruction length */ + void drop_hints () + { + GlyphHeader &glyph_header = const_cast (header); + (HBUINT16 &) StructAtOffset (&glyph_header, instruction_len_offset ()) = 0; + } + + void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const + { + unsigned int instructions_len = instructions_length (); + unsigned int glyph_length = length (instructions_len); + dest_start = bytes.sub_array (0, glyph_length - instructions_len); + dest_end = bytes.sub_array (glyph_length, bytes.length - glyph_length); + } + + static bool read_points (const HBUINT8 *&p /* IN/OUT */, + contour_point_vector_t &points_ /* IN/OUT */, + const hb_bytes_t &bytes, + void (* setter) (contour_point_t &_, float v), + const simple_glyph_flag_t short_flag, + const simple_glyph_flag_t same_flag) + { + float v = 0; + for (unsigned i = 0; i < points_.length; i++) { - DEBUG_MSG(SUBSET, nullptr, "Expect %d coords to have flags, got flags for %d", nCoordinates, coordsWithFlags); - return false; + uint8_t flag = points_[i].flag; + if (flag & short_flag) + { + if (unlikely (!bytes.check_range (p))) return false; + if (flag & same_flag) + v += *p++; + else + v -= *p++; + } + else + { + if (!(flag & same_flag)) + { + if (unlikely (!bytes.check_range ((const HBUINT16 *) p))) return false; + v += *(const HBINT16 *) p; + p += HBINT16::static_size; + } + } + setter (points_[i], v); } - glyph += coordBytes; + return true; + } + + bool get_contour_points (contour_point_vector_t &points_ /* OUT */, + bool phantom_only = false) const + { + const HBUINT16 *endPtsOfContours = &StructAfter (header); + int num_contours = header.numberOfContours; + if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours + 1]))) return false; + unsigned int num_points = endPtsOfContours[num_contours - 1] + 1; + + points_.resize (num_points); + for (unsigned int i = 0; i < points_.length; i++) points_[i].init (); + if (phantom_only) return true; - if (glyph < glyph_end) - *end_offset -= glyph_end - glyph; + for (int i = 0; i < num_contours; i++) + points_[endPtsOfContours[i]].is_end_point = true; + + /* Skip instructions */ + const HBUINT8 *p = &StructAtOffset (&endPtsOfContours[num_contours + 1], + endPtsOfContours[num_contours]); + + /* Read flags */ + for (unsigned int i = 0; i < num_points; i++) + { + if (unlikely (!bytes.check_range (p))) return false; + uint8_t flag = *p++; + points_[i].flag = flag; + if (flag & FLAG_REPEAT) + { + if (unlikely (!bytes.check_range (p))) return false; + unsigned int repeat_count = *p++; + while ((repeat_count-- > 0) && (++i < num_points)) + points_[i].flag = flag; + } + } + + /* Read x & y coordinates */ + return read_points (p, points_, bytes, [] (contour_point_t &p, float v) { p.x = v; }, + FLAG_X_SHORT, FLAG_X_SAME) + && read_points (p, points_, bytes, [] (contour_point_t &p, float v) { p.y = v; }, + FLAG_Y_SHORT, FLAG_Y_SAME); } - return true; - } + }; - bool get_offsets (hb_codepoint_t glyph, - unsigned int *start_offset /* OUT */, - unsigned int *end_offset /* OUT */) const + struct CompositeGlyph { - if (unlikely (glyph >= num_glyphs)) - return false; + const GlyphHeader &header; + hb_bytes_t bytes; + CompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} - if (short_offset) + composite_iter_t get_iterator () const + { return composite_iter_t (bytes, &StructAfter (header)); } + + unsigned int instructions_length (hb_bytes_t bytes) const { - const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ; - *start_offset = 2 * offsets[glyph]; - *end_offset = 2 * offsets[glyph + 1]; + unsigned int start = bytes.length; + unsigned int end = bytes.length; + const CompositeGlyphChain *last = nullptr; + for (auto &item : get_iterator ()) + last = &item; + if (unlikely (!last)) return 0; + + if (last->has_instructions ()) + start = (char *) last - &bytes + last->get_size (); + if (unlikely (start > end)) return 0; + return end - start; } - else + + /* Trimming for composites not implemented. + * If removing hints it falls out of that. */ + const Glyph trim_padding () const { return Glyph (bytes); } + + void drop_hints () { - const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ; + for (const auto &_ : get_iterator ()) + const_cast (_).drop_instructions_flag (); + } + + /* Chop instructions off the end */ + void drop_hints_bytes (hb_bytes_t &dest_start) const + { dest_start = bytes.sub_array (0, bytes.length - instructions_length (bytes)); } + }; + + enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE }; + + public: + composite_iter_t get_composite_iterator () const + { + if (type != COMPOSITE) return composite_iter_t (); + return CompositeGlyph (*header, bytes).get_iterator (); + } - *start_offset = offsets[glyph]; - *end_offset = offsets[glyph + 1]; + const Glyph trim_padding () const + { + switch (type) { + case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding (); + case SIMPLE: return SimpleGlyph (*header, bytes).trim_padding (); + default: return bytes; } + } - if (*start_offset > *end_offset || *end_offset > glyf_table.get_length ()) - return false; + void drop_hints () + { + switch (type) { + case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return; + case SIMPLE: SimpleGlyph (*header, bytes).drop_hints (); return; + default: return; + } + } - return true; + void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const + { + switch (type) { + case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return; + case SIMPLE: SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return; + default: return; + } } - bool get_instruction_offsets (unsigned int start_offset, - unsigned int end_offset, - unsigned int *instruction_start /* OUT */, - unsigned int *instruction_end /* OUT */) const + /* Note: Recursively calls itself. + * all_points includes phantom points + */ + bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator, + contour_point_vector_t &all_points /* OUT */, + bool phantom_only = false, + unsigned int depth = 0) const { - if (end_offset - start_offset < GlyphHeader::static_size) + if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false; + contour_point_vector_t points; + + switch (type) { + case COMPOSITE: { - *instruction_start = 0; - *instruction_end = 0; - return true; /* Empty glyph; no instructions. */ + /* pseudo component points for each component in composite glyph */ + unsigned num_points = hb_len (CompositeGlyph (*header, bytes).get_iterator ()); + if (unlikely (!points.resize (num_points))) return false; + for (unsigned i = 0; i < points.length; i++) + points[i].init (); + break; } - const GlyphHeader &glyph_header = StructAtOffset (glyf_table, start_offset); - int16_t num_contours = (int16_t) glyph_header.numberOfContours; - if (num_contours < 0) + case SIMPLE: + if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only))) + return false; + break; + } + + /* Init phantom points */ + if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; + hb_array_t phantoms = points.sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); { - CompositeGlyphHeader::Iterator composite_it; - if (unlikely (!CompositeGlyphHeader::get_iterator ( - (const char*) this->glyf_table + start_offset, - end_offset - start_offset, &composite_it))) return false; - const CompositeGlyphHeader *last; - do { - last = composite_it.current; - } while (composite_it.move_to_next ()); - - if ((uint16_t) last->flags & CompositeGlyphHeader::WE_HAVE_INSTRUCTIONS) - *instruction_start = ((char *) last - (char *) glyf_table->dataZ.arrayZ) + last->get_size (); - else - *instruction_start = end_offset; - *instruction_end = end_offset; - if (unlikely (*instruction_start > *instruction_end)) + for (unsigned i = 0; i < PHANTOM_COUNT; ++i) phantoms[i].init (); + int h_delta = (int) header->xMin - glyf_accelerator.hmtx->get_side_bearing (gid); + int v_orig = (int) header->yMax + glyf_accelerator.vmtx->get_side_bearing (gid); + unsigned h_adv = glyf_accelerator.hmtx->get_advance (gid); + unsigned v_adv = glyf_accelerator.vmtx->get_advance (gid); + phantoms[PHANTOM_LEFT].x = h_delta; + phantoms[PHANTOM_RIGHT].x = h_adv + h_delta; + phantoms[PHANTOM_TOP].y = v_orig; + phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv; + } + +#ifndef HB_NO_VAR + if (unlikely (!glyf_accelerator.gvar->apply_deltas_to_points (gid, font, points.as_array ()))) + return false; +#endif + + switch (type) { + case SIMPLE: + all_points.extend (points.as_array ()); + break; + case COMPOSITE: + { + unsigned int comp_index = 0; + for (auto &item : get_composite_iterator ()) { - DEBUG_MSG(SUBSET, nullptr, "Invalid instruction offset, %d is outside [%d, %d]", *instruction_start, start_offset, end_offset); - return false; + contour_point_vector_t comp_points; + if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_glyph_index ()) + .get_points (font, glyf_accelerator, comp_points, + phantom_only, depth + 1) + || comp_points.length < PHANTOM_COUNT)) + return false; + + /* Copy phantom points from component if USE_MY_METRICS flag set */ + if (item.is_use_my_metrics ()) + for (unsigned int i = 0; i < PHANTOM_COUNT; i++) + phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; + + /* Apply component transformation & translation */ + item.transform_points (comp_points); + + /* Apply translation from gvar */ + comp_points.translate (points[comp_index]); + + if (item.is_anchored ()) + { + unsigned int p1, p2; + item.get_anchor_points (p1, p2); + if (likely (p1 < all_points.length && p2 < comp_points.length)) + { + contour_point_t delta; + delta.init (all_points[p1].x - comp_points[p2].x, + all_points[p1].y - comp_points[p2].y); + + comp_points.translate (delta); + } + } + + all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT)); + + comp_index++; } + + all_points.extend (phantoms); + } break; + default: + all_points.extend (phantoms); } - else + + if (depth == 0) /* Apply at top level */ { - unsigned int instruction_length_offset = start_offset + GlyphHeader::static_size + 2 * num_contours; - if (unlikely (instruction_length_offset + 2 > end_offset)) + /* Undocumented rasterizer behavior: + * Shift points horizontally by the updated left side bearing + */ + contour_point_t delta; + delta.init (-phantoms[PHANTOM_LEFT].x, 0.f); + if (delta.x) all_points.translate (delta); + } + + return true; + } + + bool get_extents (hb_font_t *font, const accelerator_t &glyf_accelerator, + hb_glyph_extents_t *extents) const + { + if (type == EMPTY) return true; /* Empty glyph; zero extents. */ + return header->get_extents (font, glyf_accelerator, gid, extents); + } + + hb_bytes_t get_bytes () const { return bytes; } + + Glyph (hb_bytes_t bytes_ = hb_bytes_t (), + hb_codepoint_t gid_ = (hb_codepoint_t) -1) : bytes (bytes_), gid (gid_), + header (bytes.as ()) + { + int num_contours = header->numberOfContours; + if (unlikely (num_contours == 0)) type = EMPTY; + else if (num_contours > 0) type = SIMPLE; + else type = COMPOSITE; /* negative numbers */ + } + + protected: + hb_bytes_t bytes; + hb_codepoint_t gid; + const GlyphHeader *header; + unsigned type; + }; + + struct accelerator_t + { + void init (hb_face_t *face_) + { + short_offset = false; + num_glyphs = 0; + loca_table = nullptr; + glyf_table = nullptr; +#ifndef HB_NO_VAR + gvar = nullptr; +#endif + hmtx = nullptr; + vmtx = nullptr; + face = face_; + const OT::head &head = *face->table.head; + if (head.indexToLocFormat > 1 || head.glyphDataFormat > 0) + /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ + return; + short_offset = 0 == head.indexToLocFormat; + + loca_table = hb_sanitize_context_t ().reference_table (face); + glyf_table = hb_sanitize_context_t ().reference_table (face); +#ifndef HB_NO_VAR + gvar = face->table.gvar; +#endif + hmtx = face->table.hmtx; + vmtx = face->table.vmtx; + + num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1; + num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ()); + } + + void fini () + { + loca_table.destroy (); + glyf_table.destroy (); + } + + protected: + template + bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const + { + if (gid >= num_glyphs) return false; + + /* Making this alloc free is not that easy + https://github.com/harfbuzz/harfbuzz/issues/2095 + mostly because of gvar handling in VF fonts, + perhaps a separate path for non-VF fonts can be considered */ + contour_point_vector_t all_points; + + bool phantom_only = !consumer.is_consuming_contour_points (); + if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, phantom_only))) + return false; + + if (consumer.is_consuming_contour_points ()) + { + for (unsigned point_index = 0; point_index + 4 < all_points.length; ++point_index) + consumer.consume_point (all_points[point_index]); + consumer.points_end (); + } + + /* Where to write phantoms, nullptr if not requested */ + contour_point_t *phantoms = consumer.get_phantoms_sink (); + if (phantoms) + for (unsigned i = 0; i < PHANTOM_COUNT; ++i) + phantoms[i] = all_points[all_points.length - PHANTOM_COUNT + i]; + + return true; + } + +#ifndef HB_NO_VAR + struct points_aggregator_t + { + hb_font_t *font; + hb_glyph_extents_t *extents; + contour_point_t *phantoms; + + struct contour_bounds_t + { + contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; } + + void add (const contour_point_t &p) { - DEBUG_MSG(SUBSET, nullptr, "Glyph size is too short, missing field instructionLength."); - return false; + min_x = hb_min (min_x, p.x); + min_y = hb_min (min_y, p.y); + max_x = hb_max (max_x, p.x); + max_y = hb_max (max_y, p.y); } - const HBUINT16 &instruction_length = StructAtOffset (glyf_table, instruction_length_offset); - unsigned int start = instruction_length_offset + 2; - unsigned int end = start + (uint16_t) instruction_length; - if (unlikely (end > end_offset)) // Out of bounds of the current glyph + bool empty () const { return (min_x >= max_x) || (min_y >= max_y); } + + void get_extents (hb_font_t *font, hb_glyph_extents_t *extents) { - DEBUG_MSG(SUBSET, nullptr, "The instructions array overruns the glyph's boundaries."); - return false; + if (unlikely (empty ())) + { + extents->width = 0; + extents->x_bearing = 0; + extents->height = 0; + extents->y_bearing = 0; + return; + } + extents->x_bearing = font->em_scalef_x (min_x); + extents->width = font->em_scalef_x (max_x - min_x); + extents->y_bearing = font->em_scalef_y (max_y); + extents->height = font->em_scalef_y (min_y - max_y); } - *instruction_start = start; - *instruction_end = end; + protected: + float min_x, min_y, max_x, max_y; + } bounds; + + points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_) + { + font = font_; + extents = extents_; + phantoms = phantoms_; + if (extents) bounds = contour_bounds_t (); } - return true; + + void consume_point (const contour_point_t &point) { bounds.add (point); } + void points_end () { bounds.get_extents (font, extents); } + + bool is_consuming_contour_points () { return extents; } + contour_point_t *get_phantoms_sink () { return phantoms; } + }; + + public: + unsigned + get_advance_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const + { + if (unlikely (gid >= num_glyphs)) return 0; + + bool success = false; + + contour_point_t phantoms[PHANTOM_COUNT]; + if (likely (font->num_coords == gvar->get_axis_count ())) + success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms)); + + if (unlikely (!success)) + return is_vertical ? vmtx->get_advance (gid) : hmtx->get_advance (gid); + + float result = is_vertical + ? phantoms[PHANTOM_TOP].y - phantoms[PHANTOM_BOTTOM].y + : phantoms[PHANTOM_RIGHT].x - phantoms[PHANTOM_LEFT].x; + return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2); } - bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const + int get_side_bearing_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const { + if (unlikely (gid >= num_glyphs)) return 0; + + hb_glyph_extents_t extents; + + contour_point_t phantoms[PHANTOM_COUNT]; + if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms)))) + return is_vertical ? vmtx->get_side_bearing (gid) : hmtx->get_side_bearing (gid); + + return is_vertical + ? ceilf (phantoms[PHANTOM_TOP].y) - extents.y_bearing + : floorf (phantoms[PHANTOM_LEFT].x); + } +#endif + + public: + bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const + { + if (unlikely (gid >= num_glyphs)) return false; + +#ifndef HB_NO_VAR + if (font->num_coords && font->num_coords == gvar->get_axis_count ()) + return get_points (font, gid, points_aggregator_t (font, extents, nullptr)); +#endif + return glyph_for_gid (gid).get_extents (font, *this, extents); + } + + const Glyph + glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const + { + if (unlikely (gid >= num_glyphs)) return Glyph (); + unsigned int start_offset, end_offset; - if (!get_offsets (glyph, &start_offset, &end_offset)) - return false; - if (end_offset - start_offset < GlyphHeader::static_size) - return true; /* Empty glyph; zero extents. */ + if (short_offset) + { + const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ; + start_offset = 2 * offsets[gid]; + end_offset = 2 * offsets[gid + 1]; + } + else + { + const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ; + start_offset = offsets[gid]; + end_offset = offsets[gid + 1]; + } + + if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ())) + return Glyph (); - const GlyphHeader &glyph_header = StructAtOffset (glyf_table, start_offset); + Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset, + end_offset - start_offset), gid); + return needs_padding_removal ? glyph.trim_padding () : glyph; + } - extents->x_bearing = MIN (glyph_header.xMin, glyph_header.xMax); - extents->y_bearing = MAX (glyph_header.yMin, glyph_header.yMax); - extents->width = MAX (glyph_header.xMin, glyph_header.xMax) - extents->x_bearing; - extents->height = MIN (glyph_header.yMin, glyph_header.yMax) - extents->y_bearing; + void + add_gid_and_children (hb_codepoint_t gid, hb_set_t *gids_to_retain, + unsigned int depth = 0) const + { + if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return; + /* Check if is already visited */ + if (gids_to_retain->has (gid)) return; - return true; + gids_to_retain->add (gid); + + for (auto &item : glyph_for_gid (gid).get_composite_iterator ()) + add_gid_and_children (item.get_glyph_index (), gids_to_retain, depth); } +#ifdef HB_EXPERIMENTAL_API + struct path_builder_t + { + hb_font_t *font; + draw_helper_t *draw_helper; + + struct optional_point_t + { + optional_point_t () { has_data = false; } + optional_point_t (float x_, float y_) { x = x_; y = y_; has_data = true; } + + bool has_data; + float x; + float y; + + optional_point_t lerp (optional_point_t p, float t) + { return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); } + } first_oncurve, first_offcurve, last_offcurve; + + path_builder_t (hb_font_t *font_, draw_helper_t &draw_helper_) + { + font = font_; + draw_helper = &draw_helper_; + first_oncurve = first_offcurve = last_offcurve = optional_point_t (); + } + + /* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287 + See also: + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html + * https://stackoverflow.com/a/20772557 */ + void consume_point (const contour_point_t &point) + { + /* Skip empty contours */ + if (unlikely (point.is_end_point && !first_oncurve.has_data && !first_offcurve.has_data)) + return; + + bool is_on_curve = point.flag & Glyph::FLAG_ON_CURVE; + optional_point_t p (point.x, point.y); + if (!first_oncurve.has_data) + { + if (is_on_curve) + { + first_oncurve = p; + draw_helper->move_to (font->em_scalef_x (p.x), font->em_scalef_y (p.y)); + } + else + { + if (first_offcurve.has_data) + { + optional_point_t mid = first_offcurve.lerp (p, .5f); + first_oncurve = mid; + last_offcurve = p; + draw_helper->move_to (font->em_scalef_x (mid.x), font->em_scalef_y (mid.y)); + } + else + first_offcurve = p; + } + } + else + { + if (last_offcurve.has_data) + { + if (is_on_curve) + { + draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), + font->em_scalef_x (p.x), font->em_scalef_y (p.y)); + last_offcurve = optional_point_t (); + } + else + { + optional_point_t mid = last_offcurve.lerp (p, .5f); + draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), + font->em_scalef_x (mid.x), font->em_scalef_y (mid.y)); + last_offcurve = p; + } + } + else + { + if (is_on_curve) + draw_helper->line_to (font->em_scalef_x (p.x), font->em_scalef_y (p.y)); + else + last_offcurve = p; + } + } + + if (point.is_end_point) + { + if (first_offcurve.has_data && last_offcurve.has_data) + { + optional_point_t mid = last_offcurve.lerp (first_offcurve, .5f); + draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), + font->em_scalef_x (mid.x), font->em_scalef_y (mid.y)); + last_offcurve = optional_point_t (); + /* now check the rest */ + } + + if (first_offcurve.has_data && first_oncurve.has_data) + draw_helper->quadratic_to (font->em_scalef_x (first_offcurve.x), font->em_scalef_y (first_offcurve.y), + font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y)); + else if (last_offcurve.has_data && first_oncurve.has_data) + draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), + font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y)); + else if (first_oncurve.has_data) + draw_helper->line_to (font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y)); + + /* Getting ready for the next contour */ + first_oncurve = first_offcurve = last_offcurve = optional_point_t (); + draw_helper->end_path (); + } + } + void points_end () {} + + bool is_consuming_contour_points () { return true; } + contour_point_t *get_phantoms_sink () { return nullptr; } + }; + + bool + get_path (hb_font_t *font, hb_codepoint_t gid, draw_helper_t &draw_helper) const + { return get_points (font, gid, path_builder_t (font, draw_helper)); } +#endif + +#ifndef HB_NO_VAR + const gvar_accelerator_t *gvar; +#endif + const hmtx_accelerator_t *hmtx; + const vmtx_accelerator_t *vmtx; + private: bool short_offset; unsigned int num_glyphs; hb_blob_ptr_t loca_table; hb_blob_ptr_t glyf_table; + hb_face_t *face; + }; + + struct SubsetGlyph + { + hb_codepoint_t new_gid; + hb_codepoint_t old_gid; + Glyph source_glyph; + hb_bytes_t dest_start; /* region of source_glyph to copy first */ + hb_bytes_t dest_end; /* region of source_glyph to copy second */ + + bool serialize (hb_serialize_context_t *c, + const hb_subset_plan_t *plan) const + { + TRACE_SERIALIZE (this); + + hb_bytes_t dest_glyph = dest_start.copy (c); + dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length); + unsigned int pad_length = padding (); + DEBUG_MSG (SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d", dest_glyph.length, dest_glyph.length + pad_length, pad_length); + + HBUINT8 pad; + pad = 0; + while (pad_length > 0) + { + c->embed (pad); + pad_length--; + } + + if (unlikely (!dest_glyph.length)) return_trace (true); + + /* update components gids */ + for (auto &_ : Glyph (dest_glyph).get_composite_iterator ()) + { + hb_codepoint_t new_gid; + if (plan->new_gid_for_old_gid (_.get_glyph_index (), &new_gid)) + const_cast (_).set_glyph_index (new_gid); + } + + if (plan->drop_hints) Glyph (dest_glyph).drop_hints (); + + return_trace (true); + } + + void drop_hints_bytes () + { source_glyph.drop_hints_bytes (dest_start, dest_end); } + + unsigned int length () const { return dest_start.length + dest_end.length; } + /* pad to 2 to ensure 2-byte loca will be ok */ + unsigned int padding () const { return length () % 2; } + unsigned int padded_size () const { return length () + padding (); } }; protected: - UnsizedArrayOf dataZ; /* Glyphs data. */ + UnsizedArrayOf + dataZ; /* Glyphs data. */ public: - DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always - * check the size externally, allow Null() object of it by - * defining it MIN() instead. */ + DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always + * check the size externally, allow Null() object of it by + * defining it _MIN instead. */ }; struct glyf_accelerator_t : glyf::accelerator_t {}; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-hdmx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-hdmx-table.hh index d27d098b69f..201ffc50be7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-hdmx-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-hdmx-table.hh @@ -41,68 +41,31 @@ namespace OT { struct DeviceRecord { - struct SubsetView - { - const DeviceRecord *source_device_record; - unsigned int sizeDeviceRecord; - hb_subset_plan_t *subset_plan; - - void init (const DeviceRecord *source_device_record, - unsigned int sizeDeviceRecord, - hb_subset_plan_t *subset_plan) - { - this->source_device_record = source_device_record; - this->sizeDeviceRecord = sizeDeviceRecord; - this->subset_plan = subset_plan; - } - - unsigned int len () const - { return this->subset_plan->glyphs.length; } - - const HBUINT8* operator [] (unsigned int i) const - { - if (unlikely (i >= len ())) return nullptr; - hb_codepoint_t gid = this->subset_plan->glyphs [i]; - - if (gid >= sizeDeviceRecord - DeviceRecord::min_size) - return nullptr; - return &(this->source_device_record->widthsZ[gid]); - } - }; - - static unsigned int get_size (unsigned int count) + static unsigned int get_size (unsigned count) { return hb_ceil_to_4 (min_size + count * HBUINT8::static_size); } - bool serialize (hb_serialize_context_t *c, const SubsetView &subset_view) + template + bool serialize (hb_serialize_context_t *c, unsigned pixelSize, Iterator it) { TRACE_SERIALIZE (this); - unsigned int size = get_size (subset_view.len ()); - if (unlikely (!c->allocate_size (size))) - { - DEBUG_MSG(SUBSET, nullptr, "Couldn't allocate enough space for DeviceRecord: %d.", - size); - return_trace (false); - } - - this->pixelSize.set (subset_view.source_device_record->pixelSize); - this->maxWidth.set (subset_view.source_device_record->maxWidth); - - for (unsigned int i = 0; i < subset_view.len (); i++) - { - const HBUINT8 *width = subset_view[i]; - if (!width) - { - DEBUG_MSG(SUBSET, nullptr, "HDMX width for new gid %d is missing.", i); - return_trace (false); - } - widthsZ[i].set (*width); - } + unsigned length = it.len (); + + if (unlikely (!c->extend (*this, length))) return_trace (false); + + this->pixelSize = pixelSize; + this->maxWidth = + + it + | hb_reduce (hb_max, 0u); + + + it + | hb_sink (widthsZ.as_array (length)); return_trace (true); } - bool sanitize (hb_sanitize_context_t *c, unsigned int sizeDeviceRecord) const + bool sanitize (hb_sanitize_context_t *c, unsigned sizeDeviceRecord) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && @@ -132,62 +95,60 @@ struct hdmx return StructAtOffset (&this->firstDeviceRecord, i * sizeDeviceRecord); } - bool serialize (hb_serialize_context_t *c, const hdmx *source_hdmx, hb_subset_plan_t *plan) + template + bool serialize (hb_serialize_context_t *c, unsigned version, Iterator it) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min ((*this)))) return_trace (false); - this->version.set (source_hdmx->version); - this->numRecords.set (source_hdmx->numRecords); - this->sizeDeviceRecord.set (DeviceRecord::get_size (plan->glyphs.length)); + this->version = version; + this->numRecords = it.len (); + this->sizeDeviceRecord = DeviceRecord::get_size (it ? (*it).second.len () : 0); - for (unsigned int i = 0; i < source_hdmx->numRecords; i++) - { - DeviceRecord::SubsetView subset_view; - subset_view.init (&(*source_hdmx)[i], source_hdmx->sizeDeviceRecord, plan); + for (const hb_item_type& _ : +it) + c->start_embed ()->serialize (c, _.first, _.second); - if (!c->start_embed ()->serialize (c, subset_view)) - return_trace (false); - } - - return_trace (true); + return_trace (c->successful); } - static size_t get_subsetted_size (const hdmx *source_hdmx, hb_subset_plan_t *plan) + + bool subset (hb_subset_context_t *c) const { - return min_size + source_hdmx->numRecords * DeviceRecord::get_size (plan->glyphs.length); + TRACE_SUBSET (this); + + hdmx *hdmx_prime = c->serializer->start_embed (); + if (unlikely (!hdmx_prime)) return_trace (false); + + auto it = + + hb_range ((unsigned) numRecords) + | hb_map ([c, this] (unsigned _) + { + const DeviceRecord *device_record = + &StructAtOffset (&firstDeviceRecord, + _ * sizeDeviceRecord); + auto row = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map (c->plan->reverse_glyph_map) + | hb_map ([this, c, device_record] (hb_codepoint_t _) + { + if (c->plan->is_empty_glyph (_)) + return Null (HBUINT8); + return device_record->widthsZ.as_array (get_num_glyphs ()) [_]; + }) + ; + return hb_pair ((unsigned) device_record->pixelSize, +row); + }) + ; + + hdmx_prime->serialize (c->serializer, version, it); + return_trace (true); } - bool subset (hb_subset_plan_t *plan) const + unsigned get_num_glyphs () const { - size_t dest_size = get_subsetted_size (this, plan); - hdmx *dest = (hdmx *) malloc (dest_size); - if (unlikely (!dest)) - { - DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %lu for hdmx subset output.", (unsigned long) dest_size); - return false; - } - - hb_serialize_context_t c (dest, dest_size); - hdmx *hdmx_prime = c.start_serialize (); - if (!hdmx_prime || !hdmx_prime->serialize (&c, this, plan)) - { - free (dest); - DEBUG_MSG(SUBSET, nullptr, "Failed to serialize write new hdmx."); - return false; - } - c.end_serialize (); - - hb_blob_t *hdmx_prime_blob = hb_blob_create ((const char *) dest, - dest_size, - HB_MEMORY_MODE_READONLY, - dest, - free); - bool result = plan->add_table (HB_OT_TAG_hdmx, hdmx_prime_blob); - hb_blob_destroy (hdmx_prime_blob); - - return result; + return sizeDeviceRecord - DeviceRecord::min_size; } bool sanitize (hb_sanitize_context_t *c) const @@ -200,10 +161,12 @@ struct hdmx } protected: - HBUINT16 version; /* Table version number (0) */ - HBUINT16 numRecords; /* Number of device records. */ - HBUINT32 sizeDeviceRecord; /* Size of a device record, 32-bit aligned. */ - DeviceRecord firstDeviceRecord; /* Array of device records. */ + HBUINT16 version; /* Table version number (0) */ + HBUINT16 numRecords; /* Number of device records. */ + HBUINT32 sizeDeviceRecord; + /* Size of a device record, 32-bit aligned. */ + DeviceRecord firstDeviceRecord; + /* Array of device records. */ public: DEFINE_SIZE_MIN (8); }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh index d7448d2dfce..3f4af706bc8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh @@ -54,6 +54,18 @@ struct head return 16 <= upem && upem <= 16384 ? upem : 1000; } + bool serialize (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace ((bool) c->embed (this)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace (serialize (c->serializer)); + } + enum mac_style_flag_t { BOLD = 1u<<0, ITALIC = 1u<<1, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-hhea-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-hhea-table.hh index 66879a085a0..37ef8744572 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-hhea-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-hhea-table.hh @@ -45,6 +45,8 @@ namespace OT { template struct _hea { + bool has_data () const { return version.major; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -52,35 +54,38 @@ struct _hea } public: - FixedVersion<>version; /* 0x00010000u for version 1.0. */ - FWORD ascender; /* Typographic ascent. */ - FWORD descender; /* Typographic descent. */ - FWORD lineGap; /* Typographic line gap. */ - UFWORD advanceMax; /* Maximum advance width/height value in - * metrics table. */ - FWORD minLeadingBearing; /* Minimum left/top sidebearing value in - * metrics table. */ - FWORD minTrailingBearing; /* Minimum right/bottom sidebearing value; - * calculated as Min(aw - lsb - - * (xMax - xMin)) for horizontal. */ - FWORD maxExtent; /* horizontal: Max(lsb + (xMax - xMin)), - * vertical: minLeadingBearing+(yMax-yMin). */ - HBINT16 caretSlopeRise; /* Used to calculate the slope of the - * cursor (rise/run); 1 for vertical caret, - * 0 for horizontal.*/ - HBINT16 caretSlopeRun; /* 0 for vertical caret, 1 for horizontal. */ - HBINT16 caretOffset; /* The amount by which a slanted - * highlight on a glyph needs - * to be shifted to produce the - * best appearance. Set to 0 for - * non-slanted fonts. */ - HBINT16 reserved1; /* Set to 0. */ - HBINT16 reserved2; /* Set to 0. */ - HBINT16 reserved3; /* Set to 0. */ - HBINT16 reserved4; /* Set to 0. */ - HBINT16 metricDataFormat; /* 0 for current format. */ - HBUINT16 numberOfLongMetrics; /* Number of LongMetric entries in metric - * table. */ + FixedVersion<>version; /* 0x00010000u for version 1.0. */ + FWORD ascender; /* Typographic ascent. */ + FWORD descender; /* Typographic descent. */ + FWORD lineGap; /* Typographic line gap. */ + UFWORD advanceMax; /* Maximum advance width/height value in + * metrics table. */ + FWORD minLeadingBearing; + /* Minimum left/top sidebearing value in + * metrics table. */ + FWORD minTrailingBearing; + /* Minimum right/bottom sidebearing value; + * calculated as Min(aw - lsb - + * (xMax - xMin)) for horizontal. */ + FWORD maxExtent; /* horizontal: Max(lsb + (xMax - xMin)), + * vertical: minLeadingBearing+(yMax-yMin). */ + HBINT16 caretSlopeRise; /* Used to calculate the slope of the + * cursor (rise/run); 1 for vertical caret, + * 0 for horizontal.*/ + HBINT16 caretSlopeRun; /* 0 for vertical caret, 1 for horizontal. */ + HBINT16 caretOffset; /* The amount by which a slanted + * highlight on a glyph needs + * to be shifted to produce the + * best appearance. Set to 0 for + * non-slanted fonts. */ + HBINT16 reserved1; /* Set to 0. */ + HBINT16 reserved2; /* Set to 0. */ + HBINT16 reserved3; /* Set to 0. */ + HBINT16 reserved4; /* Set to 0. */ + HBINT16 metricDataFormat;/* 0 for current format. */ + HBUINT16 numberOfLongMetrics; + /* Number of LongMetric entries in metric + * table. */ public: DEFINE_SIZE_STATIC (36); }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh index dfb0f78d643..0a2973d8eb8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh @@ -29,8 +29,8 @@ #include "hb-open-type.hh" #include "hb-ot-hhea-table.hh" -#include "hb-ot-os2-table.hh" #include "hb-ot-var-hvar-table.hh" +#include "hb-ot-metrics.hh" /* * hmtx -- Horizontal Metrics @@ -42,6 +42,13 @@ #define HB_OT_TAG_vmtx HB_TAG('v','m','t','x') +HB_INTERNAL int +_glyf_get_side_bearing_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical); + +HB_INTERNAL unsigned +_glyf_get_advance_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical); + + namespace OT { @@ -53,6 +60,7 @@ struct LongMetric DEFINE_SIZE_STATIC (4); }; + template struct hmtxvmtx { @@ -66,7 +74,7 @@ struct hmtxvmtx bool subset_update_header (hb_subset_plan_t *plan, - unsigned int num_hmetrics) const + unsigned int num_hmetrics) const { hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table (plan->source, H::tableTag); hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail (src_blob); @@ -78,7 +86,7 @@ struct hmtxvmtx unsigned int length; H *table = (H *) hb_blob_get_data (dest_blob, &length); - table->numberOfLongMetrics.set (num_hmetrics); + table->numberOfLongMetrics = num_hmetrics; bool result = plan->add_table (H::tableTag, dest_blob); hb_blob_destroy (dest_blob); @@ -86,100 +94,66 @@ struct hmtxvmtx return result; } - bool subset (hb_subset_plan_t *plan) const + template + void serialize (hb_serialize_context_t *c, + Iterator it, + unsigned num_advances) { - typename T::accelerator_t _mtx; - _mtx.init (plan->source); - - /* All the trailing glyphs with the same advance can use one LongMetric - * and just keep LSB */ - hb_vector_t &gids = plan->glyphs; - unsigned int num_advances = gids.length; - unsigned int last_advance = _mtx.get_advance (gids[num_advances - 1]); - while (num_advances > 1 && - last_advance == _mtx.get_advance (gids[num_advances - 2])) - { - num_advances--; - } - - /* alloc the new table */ - size_t dest_sz = num_advances * 4 - + (gids.length - num_advances) * 2; - void *dest = (void *) malloc (dest_sz); - if (unlikely (!dest)) + unsigned idx = 0; + for (auto _ : it) { - return false; - } - DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in src has %d advances, %d lsbs", HB_UNTAG(T::tableTag), _mtx.num_advances, _mtx.num_metrics - _mtx.num_advances); - DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in dest has %d advances, %d lsbs, %u bytes", HB_UNTAG(T::tableTag), num_advances, gids.length - num_advances, (unsigned int) dest_sz); - - const char *source_table = hb_blob_get_data (_mtx.table.get_blob (), nullptr); - // Copy everything over - LongMetric * old_metrics = (LongMetric *) source_table; - FWORD *lsbs = (FWORD *) (old_metrics + _mtx.num_advances); - char * dest_pos = (char *) dest; - - bool failed = false; - for (unsigned int i = 0; i < gids.length; i++) - { - /* the last metric or the one for gids[i] */ - LongMetric *src_metric = old_metrics + MIN ((hb_codepoint_t) _mtx.num_advances - 1, gids[i]); - if (gids[i] < _mtx.num_advances) + if (idx < num_advances) { - /* src is a LongMetric */ - if (i < num_advances) - { - /* dest is a LongMetric, copy it */ - *((LongMetric *) dest_pos) = *src_metric; - } - else - { - /* dest just sb */ - *((FWORD *) dest_pos) = src_metric->sb; - } + LongMetric lm; + lm.advance = _.first; + lm.sb = _.second; + if (unlikely (!c->embed (&lm))) return; } else { - if (gids[i] >= _mtx.num_metrics) - { - DEBUG_MSG(SUBSET, nullptr, "gid %d is >= number of source metrics %d", - gids[i], _mtx.num_metrics); - failed = true; - break; - } - FWORD src_sb = *(lsbs + gids[i] - _mtx.num_advances); - if (i < num_advances) - { - /* dest needs a full LongMetric */ - LongMetric *metric = (LongMetric *)dest_pos; - metric->advance = src_metric->advance; - metric->sb = src_sb; - } - else - { - /* dest just needs an sb */ - *((FWORD *) dest_pos) = src_sb; - } + FWORD *sb = c->allocate_size (FWORD::static_size); + if (unlikely (!sb)) return; + *sb = _.second; } - dest_pos += (i < num_advances ? 4 : 2); + idx++; } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + T *table_prime = c->serializer->start_embed (); + if (unlikely (!table_prime)) return_trace (false); + + accelerator_t _mtx; + _mtx.init (c->plan->source); + unsigned num_advances = _mtx.num_advances_for_subset (c->plan); + + auto it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map ([c, &_mtx] (unsigned _) + { + hb_codepoint_t old_gid; + if (!c->plan->old_gid_for_new_gid (_, &old_gid)) + return hb_pair (0u, 0); + return hb_pair (_mtx.get_advance (old_gid), _mtx.get_side_bearing (old_gid)); + }) + ; + + table_prime->serialize (c->serializer, it, num_advances); + _mtx.fini (); + if (unlikely (c->serializer->ran_out_of_room || c->serializer->in_error ())) + return_trace (false); + // Amend header num hmetrics - if (failed || unlikely (!subset_update_header (plan, num_advances))) - { - free (dest); - return false; - } + if (unlikely (!subset_update_header (c->plan, num_advances))) + return_trace (false); - hb_blob_t *result = hb_blob_create ((const char *)dest, - dest_sz, - HB_MEMORY_MODE_READONLY, - dest, - free); - bool success = plan->add_table (T::tableTag, result); - hb_blob_destroy (result); - return success; + return_trace (true); } struct accelerator_t @@ -187,34 +161,13 @@ struct hmtxvmtx friend struct hmtxvmtx; void init (hb_face_t *face, - unsigned int default_advance_ = 0) + unsigned int default_advance_ = 0) { default_advance = default_advance_ ? default_advance_ : hb_face_get_upem (face); - bool got_font_extents = false; - if (T::os2Tag != HB_TAG_NONE && face->table.OS2->is_typo_metrics ()) - { - ascender = abs (face->table.OS2->sTypoAscender); - descender = -abs (face->table.OS2->sTypoDescender); - line_gap = face->table.OS2->sTypoLineGap; - got_font_extents = (ascender | descender) != 0; - } + num_advances = T::is_horizontal ? face->table.hhea->numberOfLongMetrics : face->table.vhea->numberOfLongMetrics; - hb_blob_t *_hea_blob = hb_sanitize_context_t().reference_table (face); - const H *_hea_table = _hea_blob->as (); - num_advances = _hea_table->numberOfLongMetrics; - if (!got_font_extents) - { - ascender = abs (_hea_table->ascender); - descender = -abs (_hea_table->descender); - line_gap = _hea_table->lineGap; - got_font_extents = (ascender | descender) != 0; - } - hb_blob_destroy (_hea_blob); - - has_font_extents = got_font_extents; - - table = hb_sanitize_context_t().reference_table (face, T::tableTag); + table = hb_sanitize_context_t ().reference_table (face, T::tableTag); /* Cap num_metrics() and num_advances() based on table length. */ unsigned int len = table.get_length (); @@ -231,7 +184,7 @@ struct hmtxvmtx table = hb_blob_get_empty (); } - var_table = hb_sanitize_context_t().reference_table (face, T::variationsTag); + var_table = hb_sanitize_context_t ().reference_table (face, T::variationsTag); } void fini () @@ -240,8 +193,7 @@ struct hmtxvmtx var_table.destroy (); } - /* TODO Add variations version. */ - unsigned int get_side_bearing (hb_codepoint_t glyph) const + int get_side_bearing (hb_codepoint_t glyph) const { if (glyph < num_advances) return table->longMetricZ[glyph].sb; @@ -253,6 +205,23 @@ struct hmtxvmtx return bearings[glyph - num_advances]; } + int get_side_bearing (hb_font_t *font, hb_codepoint_t glyph) const + { + int side_bearing = get_side_bearing (glyph); + +#ifndef HB_NO_VAR + if (unlikely (glyph >= num_metrics) || !font->num_coords) + return side_bearing; + + if (var_table.get_length ()) + return side_bearing + var_table->get_side_bearing_var (glyph, font->coords, font->num_coords); // TODO Optimize?! + + return _glyf_get_side_bearing_var (font, glyph, T::tableTag == HB_OT_TAG_vmtx); +#else + return side_bearing; +#endif + } + unsigned int get_advance (hb_codepoint_t glyph) const { if (unlikely (glyph >= num_metrics)) @@ -266,25 +235,52 @@ struct hmtxvmtx return default_advance; } - return table->longMetricZ[MIN (glyph, (uint32_t) num_advances - 1)].advance; + return table->longMetricZ[hb_min (glyph, (uint32_t) num_advances - 1)].advance; } unsigned int get_advance (hb_codepoint_t glyph, hb_font_t *font) const { unsigned int advance = get_advance (glyph); - if (likely (glyph < num_metrics)) + +#ifndef HB_NO_VAR + if (unlikely (glyph >= num_metrics) || !font->num_coords) + return advance; + + if (var_table.get_length ()) + return advance + roundf (var_table->get_advance_var (glyph, font)); // TODO Optimize?! + + return _glyf_get_advance_var (font, glyph, T::tableTag == HB_OT_TAG_vmtx); +#else + return advance; +#endif + } + + unsigned int num_advances_for_subset (const hb_subset_plan_t *plan) const + { + unsigned int num_advances = plan->num_output_glyphs (); + unsigned int last_advance = _advance_for_new_gid (plan, + num_advances - 1); + while (num_advances > 1 && + last_advance == _advance_for_new_gid (plan, + num_advances - 2)) { - advance += (font->num_coords ? var_table->get_advance_var (glyph, font->coords, font->num_coords) : 0); // TODO Optimize?! + num_advances--; } - return advance; + + return num_advances; } - public: - bool has_font_extents; - int ascender; - int descender; - int line_gap; + private: + unsigned int _advance_for_new_gid (const hb_subset_plan_t *plan, + hb_codepoint_t new_gid) const + { + hb_codepoint_t old_gid; + if (!plan->old_gid_for_new_gid (new_gid, &old_gid)) + return 0; + + return get_advance (old_gid); + } protected: unsigned int num_metrics; @@ -297,27 +293,29 @@ struct hmtxvmtx }; protected: - UnsizedArrayOflongMetricZ;/* Paired advance width and leading - * bearing values for each glyph. The - * value numOfHMetrics comes from - * the 'hhea' table. If the font is - * monospaced, only one entry need - * be in the array, but that entry is - * required. The last entry applies to - * all subsequent glyphs. */ -/*UnsizedArrayOf leadingBearingX;*//* Here the advance is assumed - * to be the same as the advance - * for the last entry above. The - * number of entries in this array is - * derived from numGlyphs (from 'maxp' - * table) minus numberOfLongMetrics. - * This generally is used with a run - * of monospaced glyphs (e.g., Kanji - * fonts or Courier fonts). Only one - * run is allowed and it must be at - * the end. This allows a monospaced - * font to vary the side bearing - * values for each glyph. */ + UnsizedArrayOf + longMetricZ; /* Paired advance width and leading + * bearing values for each glyph. The + * value numOfHMetrics comes from + * the 'hhea' table. If the font is + * monospaced, only one entry need + * be in the array, but that entry is + * required. The last entry applies to + * all subsequent glyphs. */ +/*UnsizedArrayOf leadingBearingX;*/ + /* Here the advance is assumed + * to be the same as the advance + * for the last entry above. The + * number of entries in this array is + * derived from numGlyphs (from 'maxp' + * table) minus numberOfLongMetrics. + * This generally is used with a run + * of monospaced glyphs (e.g., Kanji + * fonts or Courier fonts). Only one + * run is allowed and it must be at + * the end. This allows a monospaced + * font to vary the side bearing + * values for each glyph. */ public: DEFINE_SIZE_ARRAY (0, longMetricZ); }; @@ -325,12 +323,12 @@ struct hmtxvmtx struct hmtx : hmtxvmtx { static constexpr hb_tag_t tableTag = HB_OT_TAG_hmtx; static constexpr hb_tag_t variationsTag = HB_OT_TAG_HVAR; - static constexpr hb_tag_t os2Tag = HB_OT_TAG_OS2; + static constexpr bool is_horizontal = true; }; struct vmtx : hmtxvmtx { static constexpr hb_tag_t tableTag = HB_OT_TAG_vmtx; static constexpr hb_tag_t variationsTag = HB_OT_TAG_VVAR; - static constexpr hb_tag_t os2Tag = HB_TAG_NONE; + static constexpr bool is_horizontal = false; }; struct hmtx_accelerator_t : hmtx::accelerator_t {}; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh index 9d870ecfc8a..37ea1276ea6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh @@ -47,9 +47,9 @@ struct KernSubTableFormat3 int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const { hb_array_t kernValue = kernValueZ.as_array (kernValueCount); - hb_array_t leftClass = StructAfter > (kernValue).as_array (glyphCount); - hb_array_t rightClass = StructAfter > (leftClass).as_array (glyphCount); - hb_array_t kernIndex = StructAfter > (rightClass).as_array (leftClassCount * rightClassCount); + hb_array_t leftClass = StructAfter> (kernValue).as_array (glyphCount); + hb_array_t rightClass = StructAfter> (leftClass).as_array (glyphCount); + hb_array_t kernIndex = StructAfter> (rightClass).as_array (leftClassCount * rightClassCount); unsigned int leftC = leftClass[left]; unsigned int rightC = rightClass[right]; @@ -86,21 +86,26 @@ struct KernSubTableFormat3 } protected: - KernSubTableHeader header; - HBUINT16 glyphCount; /* The number of glyphs in this font. */ - HBUINT8 kernValueCount; /* The number of kerning values. */ - HBUINT8 leftClassCount; /* The number of left-hand classes. */ - HBUINT8 rightClassCount;/* The number of right-hand classes. */ - HBUINT8 flags; /* Set to zero (reserved for future use). */ - UnsizedArrayOf kernValueZ; /* The kerning values. - * Length kernValueCount. */ + KernSubTableHeader + header; + HBUINT16 glyphCount; /* The number of glyphs in this font. */ + HBUINT8 kernValueCount; /* The number of kerning values. */ + HBUINT8 leftClassCount; /* The number of left-hand classes. */ + HBUINT8 rightClassCount;/* The number of right-hand classes. */ + HBUINT8 flags; /* Set to zero (reserved for future use). */ + UnsizedArrayOf + kernValueZ; /* The kerning values. + * Length kernValueCount. */ #if 0 - UnsizedArrayOfleftClass; /* The left-hand classes. - * Length glyphCount. */ - UnsizedArrayOfrightClass; /* The right-hand classes. - * Length glyphCount. */ - UnsizedArrayOfkernIndex; /* The indices into the kernValue array. - * Length leftClassCount * rightClassCount */ + UnsizedArrayOf + leftClass; /* The left-hand classes. + * Length glyphCount. */ + UnsizedArrayOf + rightClass; /* The right-hand classes. + * Length glyphCount. */ + UnsizedArrayOfkernIndex; + /* The indices into the kernValue array. + * Length leftClassCount * rightClassCount */ #endif public: DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 6, kernValueZ); @@ -121,16 +126,20 @@ struct KernSubTable } } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { unsigned int subtable_type = get_type (); TRACE_DISPATCH (this, subtable_type); switch (subtable_type) { case 0: return_trace (c->dispatch (u.format0)); - case 1: return_trace (u.header.apple ? c->dispatch (u.format1) : c->default_return_value ()); +#ifndef HB_NO_AAT_SHAPE + case 1: return_trace (u.header.apple ? c->dispatch (u.format1, hb_forward (ds)...) : c->default_return_value ()); +#endif case 2: return_trace (c->dispatch (u.format2)); - case 3: return_trace (u.header.apple ? c->dispatch (u.format3) : c->default_return_value ()); +#ifndef HB_NO_AAT_SHAPE + case 3: return_trace (u.header.apple ? c->dispatch (u.format3, hb_forward (ds)...) : c->default_return_value ()); +#endif default: return_trace (c->default_return_value ()); } } @@ -163,8 +172,8 @@ struct KernOTSubTableHeader static constexpr bool apple = false; typedef AAT::ObsoleteTypes Types; - unsigned int tuple_count () const { return 0; } - bool is_horizontal () const { return (coverage & Horizontal); } + unsigned tuple_count () const { return 0; } + bool is_horizontal () const { return (coverage & Horizontal); } enum Coverage { @@ -218,8 +227,8 @@ struct KernAATSubTableHeader static constexpr bool apple = true; typedef AAT::ObsoleteTypes Types; - unsigned int tuple_count () const { return 0; } - bool is_horizontal () const { return !(coverage & Vertical); } + unsigned tuple_count () const { return 0; } + bool is_horizontal () const { return !(coverage & Vertical); } enum Coverage { @@ -242,8 +251,8 @@ struct KernAATSubTableHeader HBUINT8 coverage; /* Coverage bits. */ HBUINT8 format; /* Subtable format. */ HBUINT16 tupleIndex; /* The tuple index (used for variations fonts). - * This value specifies which tuple this subtable covers. - * Note: We don't implement. */ + * This value specifies which tuple this subtable covers. + * Note: We don't implement. */ public: DEFINE_SIZE_STATIC (8); }; @@ -271,14 +280,16 @@ struct kern { static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; - bool has_data () const { return u.version32; } - unsigned int get_type () const { return u.major; } + bool has_data () const { return u.version32; } + unsigned get_type () const { return u.major; } bool has_state_machine () const { switch (get_type ()) { case 0: return u.ot.has_state_machine (); +#ifndef HB_NO_AAT_SHAPE case 1: return u.aat.has_state_machine (); +#endif default:return false; } } @@ -287,7 +298,9 @@ struct kern { switch (get_type ()) { case 0: return u.ot.has_cross_stream (); +#ifndef HB_NO_AAT_SHAPE case 1: return u.aat.has_cross_stream (); +#endif default:return false; } } @@ -296,7 +309,9 @@ struct kern { switch (get_type ()) { case 0: return u.ot.get_h_kerning (left, right); +#ifndef HB_NO_AAT_SHAPE case 1: return u.aat.get_h_kerning (left, right); +#endif default:return 0; } } @@ -304,14 +319,16 @@ struct kern bool apply (AAT::hb_aat_apply_context_t *c) const { return dispatch (c); } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { unsigned int subtable_type = get_type (); TRACE_DISPATCH (this, subtable_type); switch (subtable_type) { - case 0: return_trace (c->dispatch (u.ot)); - case 1: return_trace (c->dispatch (u.aat)); + case 0: return_trace (c->dispatch (u.ot, hb_forward (ds)...)); +#ifndef HB_NO_AAT_SHAPE + case 1: return_trace (c->dispatch (u.aat, hb_forward (ds)...)); +#endif default: return_trace (c->default_return_value ()); } } @@ -328,7 +345,9 @@ struct kern HBUINT32 version32; HBUINT16 major; KernOT ot; +#ifndef HB_NO_AAT_SHAPE KernAAT aat; +#endif } u; public: DEFINE_SIZE_UNION (4, version32); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh index fe30e278795..f8cdb046910 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh @@ -1,7 +1,7 @@ /* - * Copyright © 2016 Elie Roux + * Copyright © 2016 Elie Roux * Copyright © 2018 Google, Inc. - * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2018-2019 Ebrahim Byagowi * * This is part of HarfBuzz, a text shaping library. * @@ -32,9 +32,6 @@ #include "hb-open-type.hh" #include "hb-ot-layout-common.hh" -/* To be removed */ -typedef hb_tag_t hb_ot_layout_baseline_t; - namespace OT { /* @@ -76,7 +73,7 @@ struct BaseCoordFormat2 protected: HBUINT16 format; /* Format identifier--format = 2 */ FWORD coordinate; /* X or Y value, in design units */ - GlyphID referenceGlyph; /* Glyph ID of control glyph */ + HBGlyphID referenceGlyph; /* Glyph ID of control glyph */ HBUINT16 coordPoint; /* Index of contour point on the * reference glyph */ public: @@ -116,9 +113,11 @@ struct BaseCoordFormat3 struct BaseCoord { - hb_position_t get_coord (hb_font_t *font, + bool has_data () const { return u.format; } + + hb_position_t get_coord (hb_font_t *font, const VariationStore &var_store, - hb_direction_t direction) const + hb_direction_t direction) const { switch (u.format) { case 1: return u.format1.get_coord (); @@ -142,10 +141,10 @@ struct BaseCoord protected: union { - HBUINT16 format; - BaseCoordFormat1 format1; - BaseCoordFormat2 format2; - BaseCoordFormat3 format3; + HBUINT16 format; + BaseCoordFormat1 format1; + BaseCoordFormat2 format2; + BaseCoordFormat3 format3; } u; public: DEFINE_SIZE_UNION (2, format); @@ -153,14 +152,9 @@ struct BaseCoord struct FeatMinMaxRecord { - static int cmp (const void *key_, const void *entry_) - { - hb_tag_t key = * (hb_tag_t *) key_; - const FeatMinMaxRecord &entry = * (const FeatMinMaxRecord *) entry_; - return key < (unsigned int) entry.tag ? -1 : - key > (unsigned int) entry.tag ? 1 : - 0; - } + int cmp (hb_tag_t key) const { return tag.cmp (key); } + + bool has_data () const { return tag; } void get_min_max (const BaseCoord **min, const BaseCoord **max) const { @@ -195,17 +189,12 @@ struct FeatMinMaxRecord struct MinMax { void get_min_max (hb_tag_t feature_tag, - const BaseCoord **min, - const BaseCoord **max) const + const BaseCoord **min, + const BaseCoord **max) const { - /* TODO Replace hb_bsearch() with .bsearch(). */ - const FeatMinMaxRecord *minMaxCoord = (const FeatMinMaxRecord *) - hb_bsearch (&feature_tag, featMinMaxRecords.arrayZ, - featMinMaxRecords.len, - FeatMinMaxRecord::static_size, - FeatMinMaxRecord::cmp); - if (minMaxCoord) - minMaxCoord->get_min_max (min, max); + const FeatMinMaxRecord &minMaxCoord = featMinMaxRecords.bsearch (feature_tag); + if (minMaxCoord.has_data ()) + minMaxCoord.get_min_max (min, max); else { if (likely (min)) *min = &(this+minCoord); @@ -271,17 +260,11 @@ struct BaseValues struct BaseLangSysRecord { - static int cmp (const void *key_, const void *entry_) - { - hb_tag_t key = * (hb_tag_t *) key_; - const BaseLangSysRecord &entry = * (const BaseLangSysRecord *) entry_; - return key < (unsigned int) entry.baseLangSysTag ? -1 : - key > (unsigned int) entry.baseLangSysTag ? 1 : - 0; - } + int cmp (hb_tag_t key) const { return baseLangSysTag.cmp (key); } + + bool has_data () const { return baseLangSysTag; } - const MinMax &get_min_max () const - { return this+minMax; } + const MinMax &get_min_max () const { return this+minMax; } bool sanitize (hb_sanitize_context_t *c, const void *base) const { @@ -303,19 +286,14 @@ struct BaseScript { const MinMax &get_min_max (hb_tag_t language_tag) const { - /* TODO Replace hb_bsearch() with .bsearch(). */ - const BaseLangSysRecord* record = (const BaseLangSysRecord *) - hb_bsearch (&language_tag, baseLangSysRecords.arrayZ, - baseLangSysRecords.len, - BaseLangSysRecord::static_size, - BaseLangSysRecord::cmp); - return record ? record->get_min_max () : this+defaultMinMax; + const BaseLangSysRecord& record = baseLangSysRecords.bsearch (language_tag); + return record.has_data () ? record.get_min_max () : this+defaultMinMax; } const BaseCoord &get_base_coord (int baseline_tag_index) const { return (this+baseValues).get_base_coord (baseline_tag_index); } - bool is_empty () const { return !baseValues; } + bool has_data () const { return baseValues; } bool sanitize (hb_sanitize_context_t *c) const { @@ -345,14 +323,9 @@ struct BaseScript struct BaseScriptList; struct BaseScriptRecord { - static int cmp (const void *key_, const void *entry_) - { - hb_tag_t key = * (hb_tag_t *) key_; - const BaseScriptRecord &entry = * (const BaseScriptRecord *) entry_; - return key < (unsigned int) entry.baseScriptTag ? -1 : - key > (unsigned int) entry.baseScriptTag ? 1 : - 0; - } + int cmp (hb_tag_t key) const { return baseScriptTag.cmp (key); } + + bool has_data () const { return baseScriptTag; } const BaseScript &get_base_script (const BaseScriptList *list) const { return list+baseScript; } @@ -376,22 +349,11 @@ struct BaseScriptRecord struct BaseScriptList { - const BaseScriptRecord *find_record (hb_tag_t script) const - { - /* TODO Replace hb_bsearch() with .bsearch(). */ - return (const BaseScriptRecord *) hb_bsearch (&script, baseScriptRecords.arrayZ, - baseScriptRecords.len, - BaseScriptRecord::static_size, - BaseScriptRecord::cmp); - } - - /* TODO: Or client should handle fallback? */ const BaseScript &get_base_script (hb_tag_t script) const { - const BaseScriptRecord *record = find_record (script); - if (!record) record = find_record ((hb_script_t) HB_TAG ('D','F','L','T')); - - return record ? record->get_base_script (this) : Null (BaseScript); + const BaseScriptRecord *record = &baseScriptRecords.bsearch (script); + if (!record->has_data ()) record = &baseScriptRecords.bsearch (HB_TAG ('D','F','L','T')); + return record->has_data () ? record->get_base_script (this) : Null (BaseScript); } bool sanitize (hb_sanitize_context_t *c) const @@ -411,15 +373,20 @@ struct BaseScriptList struct Axis { - bool get_baseline (hb_ot_layout_baseline_t baseline, - hb_tag_t script_tag, - hb_tag_t language_tag, - const BaseCoord **coord) const + bool get_baseline (hb_tag_t baseline_tag, + hb_tag_t script_tag, + hb_tag_t language_tag, + const BaseCoord **coord) const { const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag); - if (base_script.is_empty ()) return false; + if (!base_script.has_data ()) return false; - if (likely (coord)) *coord = &base_script.get_base_coord ((this+baseTagList).bsearch (baseline)); + if (likely (coord)) + { + unsigned int tag_index = 0; + (this+baseTagList).bfind (baseline_tag, &tag_index); + *coord = &base_script.get_base_coord (tag_index); + } return true; } @@ -431,7 +398,7 @@ struct Axis const BaseCoord **max_coord) const { const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag); - if (base_script.is_empty ()) return false; + if (!base_script.has_data ()) return false; base_script.get_min_max (language_tag).get_min_max (feature_tag, min_coord, max_coord); @@ -447,7 +414,7 @@ struct Axis } protected: - OffsetTo > + OffsetTo> baseTagList; /* Offset to BaseTagList table, from beginning * of Axis table (may be NULL) * Array of 4-byte baseline identification tags — must @@ -472,20 +439,21 @@ struct BASE const VariationStore &get_var_store () const { return version.to_int () < 0x00010001u ? Null (VariationStore) : this+varStore; } - bool get_baseline (hb_font_t *font, - hb_ot_layout_baseline_t baseline, - hb_direction_t direction, - hb_tag_t script_tag, - hb_tag_t language_tag, - hb_position_t *base) const + bool get_baseline (hb_font_t *font, + hb_tag_t baseline_tag, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_position_t *base) const { - const BaseCoord *base_coord; - if (!get_axis (direction).get_baseline (baseline, script_tag, language_tag, &base_coord)) + const BaseCoord *base_coord = nullptr; + if (unlikely (!get_axis (direction).get_baseline (baseline_tag, script_tag, language_tag, &base_coord) || + !base_coord || !base_coord->has_data ())) return false; - if (likely (base && base_coord)) *base = base_coord->get_coord (font, - get_var_store (), - direction); + if (likely (base)) + *base = base_coord->get_coord (font, get_var_store (), direction); + return true; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh index 7618057ed1c..56773e68e36 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh @@ -33,6 +33,7 @@ #include "hb-ot-layout.hh" #include "hb-open-type.hh" #include "hb-set.hh" +#include "hb-bimap.hh" #ifndef HB_MAX_NESTING_LEVEL @@ -59,6 +60,18 @@ #define HB_MAX_LANGSYS 2000 #endif +#ifndef HB_MAX_FEATURES +#define HB_MAX_FEATURES 750 +#endif + +#ifndef HB_MAX_FEATURE_INDICES +#define HB_MAX_FEATURE_INDICES 1500 +#endif + +#ifndef HB_MAX_LOOKUP_INDICES +#define HB_MAX_LOOKUP_INDICES 20000 +#endif + namespace OT { @@ -66,6 +79,213 @@ namespace OT { #define NOT_COVERED ((unsigned int) -1) +template +static inline void Coverage_serialize (hb_serialize_context_t *c, + Iterator it); + +template +static inline void ClassDef_serialize (hb_serialize_context_t *c, + Iterator it); + +static void ClassDef_remap_and_serialize (hb_serialize_context_t *c, + const hb_set_t &glyphset, + const hb_map_t &gid_klass_map, + hb_sorted_vector_t &glyphs, + const hb_set_t &klasses, + hb_map_t *klass_map /*INOUT*/); + +struct hb_subset_layout_context_t : + hb_dispatch_context_t +{ + const char *get_name () { return "SUBSET_LAYOUT"; } + static return_t default_return_value () { return hb_empty_t (); } + + bool visitScript () + { + return script_count++ < HB_MAX_SCRIPTS; + } + + bool visitLangSys () + { + return langsys_count++ < HB_MAX_LANGSYS; + } + + bool visitFeatureIndex (int count) + { + feature_index_count += count; + return feature_index_count < HB_MAX_FEATURE_INDICES; + } + + bool visitLookupIndex() + { + lookup_index_count++; + return lookup_index_count < HB_MAX_LOOKUP_INDICES; + } + + hb_subset_context_t *subset_context; + const hb_tag_t table_tag; + const hb_map_t *lookup_index_map; + const hb_map_t *feature_index_map; + + hb_subset_layout_context_t (hb_subset_context_t *c_, + hb_tag_t tag_, + hb_map_t *lookup_map_, + hb_map_t *feature_map_) : + subset_context (c_), + table_tag (tag_), + lookup_index_map (lookup_map_), + feature_index_map (feature_map_), + script_count (0), + langsys_count (0), + feature_index_count (0), + lookup_index_count (0) + {} + + private: + unsigned script_count; + unsigned langsys_count; + unsigned feature_index_count; + unsigned lookup_index_count; +}; + +struct hb_collect_variation_indices_context_t : + hb_dispatch_context_t +{ + template + return_t dispatch (const T &obj) { obj.collect_variation_indices (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } + + hb_set_t *layout_variation_indices; + const hb_set_t *glyph_set; + const hb_map_t *gpos_lookups; + + hb_collect_variation_indices_context_t (hb_set_t *layout_variation_indices_, + const hb_set_t *glyph_set_, + const hb_map_t *gpos_lookups_) : + layout_variation_indices (layout_variation_indices_), + glyph_set (glyph_set_), + gpos_lookups (gpos_lookups_) {} +}; + +template +struct subset_offset_array_t +{ + subset_offset_array_t (hb_subset_context_t *subset_context_, + OutputArray& out_, + const void *base_) : subset_context (subset_context_), + out (out_), base (base_) {} + + template + bool operator () (T&& offset) + { + auto *o = out.serialize_append (subset_context->serializer); + if (unlikely (!o)) return false; + auto snap = subset_context->serializer->snapshot (); + bool ret = o->serialize_subset (subset_context, offset, base); + if (!ret) + { + out.pop (); + subset_context->serializer->revert (snap); + } + return ret; + } + + private: + hb_subset_context_t *subset_context; + OutputArray &out; + const void *base; +}; + + +template +struct subset_offset_array_arg_t +{ + subset_offset_array_arg_t (hb_subset_context_t *subset_context_, + OutputArray& out_, + const void *base_, + Arg &&arg_) : subset_context (subset_context_), out (out_), + base (base_), arg (arg_) {} + + template + bool operator () (T&& offset) + { + auto *o = out.serialize_append (subset_context->serializer); + if (unlikely (!o)) return false; + auto snap = subset_context->serializer->snapshot (); + bool ret = o->serialize_subset (subset_context, offset, base, arg); + if (!ret) + { + out.pop (); + subset_context->serializer->revert (snap); + } + return ret; + } + + private: + hb_subset_context_t *subset_context; + OutputArray &out; + const void *base; + Arg &&arg; +}; + +/* + * Helper to subset an array of offsets. Subsets the thing pointed to by each offset + * and discards the offset in the array if the subset operation results in an empty + * thing. + */ +struct +{ + template + subset_offset_array_t + operator () (hb_subset_context_t *subset_context, OutputArray& out, + const void *base) const + { return subset_offset_array_t (subset_context, out, base); } + + /* Variant with one extra argument passed to serialize_subset */ + template + subset_offset_array_arg_t + operator () (hb_subset_context_t *subset_context, OutputArray& out, + const void *base, Arg &&arg) const + { return subset_offset_array_arg_t (subset_context, out, base, arg); } +} +HB_FUNCOBJ (subset_offset_array); + +template +struct subset_record_array_t +{ + subset_record_array_t (hb_subset_layout_context_t *c_, OutputArray* out_, + const void *base_) : subset_layout_context (c_), + out (out_), base (base_) {} + + template + void + operator () (T&& record) + { + auto snap = subset_layout_context->subset_context->serializer->snapshot (); + bool ret = record.subset (subset_layout_context, base); + if (!ret) subset_layout_context->subset_context->serializer->revert (snap); + else out->len++; + } + + private: + hb_subset_layout_context_t *subset_layout_context; + OutputArray *out; + const void *base; +}; + +/* + * Helper to subset a RecordList/record array. Subsets each Record in the array and + * discards the record if the subset operation returns false. + */ +struct +{ + template + subset_record_array_t + operator () (hb_subset_layout_context_t *c, OutputArray* out, + const void *base) const + { return subset_record_array_t (c, out, base); } +} +HB_FUNCOBJ (subset_record_array); /* * @@ -88,6 +308,15 @@ struct Record { int cmp (hb_tag_t a) const { return tag.cmp (a); } + bool subset (hb_subset_layout_context_t *c, const void *base) const + { + TRACE_SUBSET (this); + auto *out = c->subset_context->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + bool ret = out->offset.serialize_subset (c->subset_context, offset, base, c, &tag); + return_trace (ret); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); @@ -104,7 +333,7 @@ struct Record }; template -struct RecordArrayOf : SortedArrayOf > +struct RecordArrayOf : SortedArrayOf> { const OffsetTo& get_offset (unsigned int i) const { return (*this)[i].offset; } @@ -116,11 +345,12 @@ struct RecordArrayOf : SortedArrayOf > unsigned int *record_count /* IN/OUT */, hb_tag_t *record_tags /* OUT */) const { - if (record_count) { - const Record *arr = this->sub_array (start_offset, record_count); - unsigned int count = *record_count; - for (unsigned int i = 0; i < count; i++) - record_tags[i] = arr[i].tag; + if (record_count) + { + + this->sub_array (start_offset, record_count) + | hb_map (&Record::tag) + | hb_sink (hb_array (record_tags, *record_count)) + ; } return this->len; } @@ -136,14 +366,16 @@ struct RecordListOf : RecordArrayOf const Type& operator [] (unsigned int i) const { return this+this->get_offset (i); } - bool subset (hb_subset_context_t *c) const + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const { TRACE_SUBSET (this); - struct RecordListOf *out = c->serializer->embed (*this); - if (unlikely (!out)) return_trace (false); - unsigned int count = this->len; - for (unsigned int i = 0; i < count; i++) - out->get_offset (i).serialize_subset (c, (*this)[i], out); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + this->iter () + | hb_apply (subset_record_array (l, out, this)) + ; return_trace (true); } @@ -154,11 +386,31 @@ struct RecordListOf : RecordArrayOf } }; +struct Feature; + +struct RecordListOfFeature : RecordListOf +{ + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + unsigned count = this->len; + + hb_zip (*this, hb_range (count)) + | hb_filter (l->feature_index_map, hb_second) + | hb_map (hb_first) + | hb_apply (subset_record_array (l, out, this)) + ; + return_trace (true); + } +}; struct RangeRecord { int cmp (hb_codepoint_t g) const - { return g < start ? -1 : g <= end ? 0 : +1; } + { return g < first ? -1 : g <= last ? 0 : +1; } bool sanitize (hb_sanitize_context_t *c) const { @@ -167,14 +419,14 @@ struct RangeRecord } bool intersects (const hb_set_t *glyphs) const - { return glyphs->intersects (start, end); } + { return glyphs->intersects (first, last); } template - bool add_coverage (set_t *glyphs) const - { return glyphs->add_range (start, end); } + bool collect_coverage (set_t *glyphs) const + { return glyphs->add_range (first, last); } - GlyphID start; /* First GlyphID in the range */ - GlyphID end; /* Last GlyphID in the range */ + HBGlyphID first; /* First GlyphID in the range */ + HBGlyphID last; /* Last GlyphID in the range */ HBUINT16 value; /* Value */ public: DEFINE_SIZE_STATIC (6); @@ -184,15 +436,38 @@ DECLARE_NULL_NAMESPACE_BYTES (OT, RangeRecord); struct IndexArray : ArrayOf { + bool intersects (const hb_map_t *indexes) const + { return hb_any (*this, indexes); } + + template + void serialize (hb_serialize_context_t *c, + hb_subset_layout_context_t *l, + Iterator it) + { + if (!it) return; + if (unlikely (!c->extend_min ((*this)))) return; + + for (const auto _ : it) + { + if (!l->visitLookupIndex()) break; + + Index i; + i = _; + c->copy (i); + this->len++; + } + } + unsigned int get_indexes (unsigned int start_offset, unsigned int *_count /* IN/OUT */, unsigned int *_indexes /* OUT */) const { - if (_count) { - const HBUINT16 *arr = this->sub_array (start_offset, _count); - unsigned int count = *_count; - for (unsigned int i = 0; i < count; i++) - _indexes[i] = arr[i]; + if (_count) + { + + this->sub_array (start_offset, _count) + | hb_sink (hb_array (_indexes, *_count)) + ; } return this->len; } @@ -204,11 +479,6 @@ struct IndexArray : ArrayOf }; -struct Script; -struct LangSys; -struct Feature; - - struct LangSys { unsigned int get_feature_count () const @@ -227,13 +497,49 @@ struct LangSys { if (reqFeatureIndex == 0xFFFFu) return Index::NOT_FOUND_INDEX; - return reqFeatureIndex;; + return reqFeatureIndex; } - bool subset (hb_subset_context_t *c) const + LangSys* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (*this)); + } + + bool operator == (const LangSys& o) const + { + if (featureIndex.len != o.featureIndex.len || + reqFeatureIndex != o.reqFeatureIndex) + return false; + + for (const auto _ : + hb_zip (featureIndex, o.featureIndex)) + if (_.first != _.second) return false; + + return true; + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + const Tag *tag = nullptr) const { TRACE_SUBSET (this); - return_trace (c->serializer->embed (*this)); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + out->reqFeatureIndex = l->feature_index_map->has (reqFeatureIndex) ? l->feature_index_map->get (reqFeatureIndex) : 0xFFFFu; + + if (!l->visitFeatureIndex (featureIndex.len)) + return_trace (false); + + auto it = + + hb_iter (featureIndex) + | hb_filter (l->feature_index_map) + | hb_map (l->feature_index_map) + ; + + bool ret = bool (it); + out->featureIndex.serialize (c->serializer, l, it); + return_trace (ret); } bool sanitize (hb_sanitize_context_t *c, @@ -275,16 +581,46 @@ struct Script bool has_default_lang_sys () const { return defaultLangSys != 0; } const LangSys& get_default_lang_sys () const { return this+defaultLangSys; } - bool subset (hb_subset_context_t *c) const + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + const Tag *tag) const { TRACE_SUBSET (this); - struct Script *out = c->serializer->embed (*this); - if (unlikely (!out)) return_trace (false); - out->defaultLangSys.serialize_subset (c, this+defaultLangSys, out); - unsigned int count = langSys.len; - for (unsigned int i = 0; i < count; i++) - out->langSys.arrayZ[i].offset.serialize_subset (c, this+langSys[i].offset, out); - return_trace (true); + if (!l->visitScript ()) return_trace (false); + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + bool defaultLang = false; + if (has_default_lang_sys ()) + { + c->serializer->push (); + const LangSys& ls = this+defaultLangSys; + bool ret = ls.subset (c, l); + if (!ret && tag && *tag != HB_TAG ('D', 'F', 'L', 'T')) + { + c->serializer->pop_discard (); + out->defaultLangSys = 0; + } + else + { + c->serializer->add_link (out->defaultLangSys, c->serializer->pop_pack ()); + defaultLang = true; + } + } + + + langSys.iter () + | hb_filter ([=] (const Record& record) {return l->visitLangSys (); }) + | hb_filter ([&] (const Record& record) + { + const LangSys& d = this+defaultLangSys; + const LangSys& l = this+record.offset; + return !(l == d); + }) + | hb_apply (subset_record_array (l, &(out->langSys), this)) + ; + + return_trace (bool (out->langSys.len) || defaultLang || l->table_tag == HB_OT_TAG_GSUB); } bool sanitize (hb_sanitize_context_t *c, @@ -381,6 +717,12 @@ struct FeatureParamsSize return_trace (true); } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace ((bool) c->serializer->embed (*this)); + } + HBUINT16 designSize; /* Represents the design size in 720/inch * units (decipoints). The design size entry * must be non-zero. When there is a design @@ -431,6 +773,12 @@ struct FeatureParamsStylisticSet return_trace (c->check_struct (this)); } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace ((bool) c->serializer->embed (*this)); + } + HBUINT16 version; /* (set to 0): This corresponds to a “minor” * version number. Additional data may be * added to the end of this Feature Parameters @@ -457,6 +805,27 @@ struct FeatureParamsStylisticSet /* https://docs.microsoft.com/en-us/typography/opentype/spec/features_ae#cv01-cv99 */ struct FeatureParamsCharacterVariants { + unsigned + get_characters (unsigned start_offset, unsigned *char_count, hb_codepoint_t *chars) const + { + if (char_count) + { + + characters.sub_array (start_offset, char_count) + | hb_sink (hb_array (chars, *char_count)) + ; + } + return characters.len; + } + + unsigned get_size () const + { return min_size + characters.len * HBUINT24::static_size; } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace ((bool) c->serializer->embed (*this)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -500,6 +869,9 @@ struct FeatureParams { bool sanitize (hb_sanitize_context_t *c, hb_tag_t tag) const { +#ifdef HB_NO_LAYOUT_FEATURE_PARAMS + return true; +#endif TRACE_SANITIZE (this); if (tag == HB_TAG ('s','i','z','e')) return_trace (u.size.sanitize (c)); @@ -510,26 +882,39 @@ struct FeatureParams return_trace (true); } + bool subset (hb_subset_context_t *c, const Tag* tag) const + { + TRACE_SUBSET (this); + if (!tag) return_trace (false); + if (*tag == HB_TAG ('s','i','z','e')) + return_trace (u.size.subset (c)); + if ((*tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */ + return_trace (u.stylisticSet.subset (c)); + if ((*tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */ + return_trace (u.characterVariants.subset (c)); + return_trace (false); + } + +#ifndef HB_NO_LAYOUT_FEATURE_PARAMS const FeatureParamsSize& get_size_params (hb_tag_t tag) const { if (tag == HB_TAG ('s','i','z','e')) return u.size; return Null (FeatureParamsSize); } - const FeatureParamsStylisticSet& get_stylistic_set_params (hb_tag_t tag) const { if ((tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */ return u.stylisticSet; return Null (FeatureParamsStylisticSet); } - const FeatureParamsCharacterVariants& get_character_variants_params (hb_tag_t tag) const { if ((tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */ return u.characterVariants; return Null (FeatureParamsCharacterVariants); } +#endif private: union { @@ -538,7 +923,7 @@ struct FeatureParams FeatureParamsCharacterVariants characterVariants; } u; public: - DEFINE_SIZE_STATIC (17); + DEFINE_SIZE_MIN (0); }; struct Feature @@ -557,13 +942,28 @@ struct Feature const FeatureParams &get_feature_params () const { return this+featureParams; } - bool subset (hb_subset_context_t *c) const + bool intersects_lookup_indexes (const hb_map_t *lookup_indexes) const + { return lookupIndex.intersects (lookup_indexes); } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + const Tag *tag = nullptr) const { TRACE_SUBSET (this); - struct Feature *out = c->serializer->embed (*this); - if (unlikely (!out)) return_trace (false); - out->featureParams.set (0); /* TODO(subset) FeatureParams. */ - return_trace (true); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + bool subset_featureParams = out->featureParams.serialize_subset (c, featureParams, this, tag); + + auto it = + + hb_iter (lookupIndex) + | hb_filter (l->lookup_index_map) + | hb_map (l->lookup_index_map) + ; + + out->lookupIndex.serialize (c->serializer, l, it); + return_trace (bool (it) || subset_featureParams + || (tag && *tag == HB_TAG ('p', 'r', 'e', 'f'))); } bool sanitize (hb_sanitize_context_t *c, @@ -584,25 +984,25 @@ struct Feature * Adobe tools, only the 'size' feature had FeatureParams defined. */ - OffsetTo orig_offset = featureParams; + if (likely (featureParams.is_null ())) + return_trace (true); + + unsigned int orig_offset = featureParams; if (unlikely (!featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE))) return_trace (false); - if (likely (orig_offset.is_null ())) - return_trace (true); - if (featureParams == 0 && closure && closure->tag == HB_TAG ('s','i','z','e') && closure->list_base && closure->list_base < this) { - unsigned int new_offset_int = (unsigned int) orig_offset - + unsigned int new_offset_int = orig_offset - (((char *) this) - ((char *) closure->list_base)); OffsetTo new_offset; - /* Check that it did not overflow. */ - new_offset.set (new_offset_int); + /* Check that it would not overflow. */ + new_offset = new_offset_int; if (new_offset == new_offset_int && - c->try_set (&featureParams, new_offset) && + c->try_set (&featureParams, new_offset_int) && !featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE)) return_trace (false); } @@ -648,16 +1048,19 @@ struct Lookup { unsigned int get_subtable_count () const { return subTable.len; } - template - const TSubTable& get_subtable (unsigned int i) const - { return this+CastR > (subTable)[i]; } - template const OffsetArrayOf& get_subtables () const - { return CastR > (subTable); } + { return reinterpret_cast &> (subTable); } template OffsetArrayOf& get_subtables () - { return CastR > (subTable); } + { return reinterpret_cast &> (subTable); } + + template + const TSubTable& get_subtable (unsigned int i) const + { return this+get_subtables ()[i]; } + template + TSubTable& get_subtable (unsigned int i) + { return this+get_subtables ()[i]; } unsigned int get_size () const { @@ -683,14 +1086,14 @@ struct Lookup return flag; } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { unsigned int lookup_type = get_type (); TRACE_DISPATCH (this, lookup_type); unsigned int count = get_subtable_count (); for (unsigned int i = 0; i < count; i++) { - typename context_t::return_t r = get_subtable (i).dispatch (c, lookup_type); + typename context_t::return_t r = get_subtable (i).dispatch (c, lookup_type, hb_forward (ds)...); if (c->stop_sublookup_iteration (r)) return_trace (r); } @@ -704,95 +1107,73 @@ struct Lookup { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - lookupType.set (lookup_type); - lookupFlag.set (lookup_props & 0xFFFFu); + lookupType = lookup_type; + lookupFlag = lookup_props & 0xFFFFu; if (unlikely (!subTable.serialize (c, num_subtables))) return_trace (false); if (lookupFlag & LookupFlag::UseMarkFilteringSet) { if (unlikely (!c->extend (*this))) return_trace (false); HBUINT16 &markFilteringSet = StructAfter (subTable); - markFilteringSet.set (lookup_props >> 16); + markFilteringSet = lookup_props >> 16; } return_trace (true); } - /* Older compilers need this to NOT be locally defined in a function. */ - template - struct SubTableSubsetWrapper - { - SubTableSubsetWrapper (const TSubTable &subtable_, - unsigned int lookup_type_) : - subtable (subtable_), - lookup_type (lookup_type_) {} - - bool subset (hb_subset_context_t *c) const - { return subtable.dispatch (c, lookup_type); } - - private: - const TSubTable &subtable; - unsigned int lookup_type; - }; - template bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - struct Lookup *out = c->serializer->embed (*this); - if (unlikely (!out)) return_trace (false); - - /* Subset the actual subtables. */ - /* TODO Drop empty ones, either by calling intersects() beforehand, - * or just dropping null offsets after. */ - const OffsetArrayOf& subtables = get_subtables (); - OffsetArrayOf& out_subtables = out->get_subtables (); - unsigned int count = subTable.len; - for (unsigned int i = 0; i < count; i++) - { - SubTableSubsetWrapper wrapper (this+subtables[i], get_type ()); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + out->lookupType = lookupType; + out->lookupFlag = lookupFlag; - out_subtables[i].serialize_subset (c, wrapper, out); - } + const hb_set_t *glyphset = c->plan->glyphset (); + unsigned int lookup_type = get_type (); + + hb_iter (get_subtables ()) + | hb_filter ([this, glyphset, lookup_type] (const OffsetTo &_) { return (this+_).intersects (glyphset, lookup_type); }) + | hb_apply (subset_offset_array (c, out->get_subtables (), this, lookup_type)) + ; return_trace (true); } - /* Older compilers need this to NOT be locally defined in a function. */ - template - struct SubTableSanitizeWrapper : TSubTable - { - bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) const - { return this->dispatch (c, lookup_type); } - }; - template bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (!(c->check_struct (this) && subTable.sanitize (c))) return_trace (false); + + unsigned subtables = get_subtable_count (); + if (unlikely (!c->visit_subtables (subtables))) return_trace (false); + if (lookupFlag & LookupFlag::UseMarkFilteringSet) { const HBUINT16 &markFilteringSet = StructAfter (subTable); if (!markFilteringSet.sanitize (c)) return_trace (false); } - if (unlikely (!CastR > > (subTable) - .sanitize (c, this, get_type ()))) + if (unlikely (!get_subtables ().sanitize (c, this, get_type ()))) return_trace (false); - if (unlikely (get_type () == TSubTable::Extension)) + if (unlikely (get_type () == TSubTable::Extension && !c->get_edit_count ())) { /* The spec says all subtables of an Extension lookup should * have the same type, which shall not be the Extension type * itself (but we already checked for that). - * This is specially important if one has a reverse type! */ + * This is specially important if one has a reverse type! + * + * We only do this if sanitizer edit_count is zero. Otherwise, + * some of the subtables might have become insane after they + * were sanity-checked by the edits of subsequent subtables. + * https://bugs.chromium.org/p/chromium/issues/detail?id=960331 + */ unsigned int type = get_subtable (0).u.extension.get_type (); - unsigned int count = get_subtable_count (); - for (unsigned int i = 1; i < count; i++) + for (unsigned int i = 1; i < subtables; i++) if (get_subtable (i).u.extension.get_type () != type) return_trace (false); } return_trace (true); - return_trace (true); } private: @@ -800,7 +1181,7 @@ struct Lookup HBUINT16 lookupFlag; /* Lookup qualifiers */ ArrayOf subTable; /* Array of SubTables */ -/*HBUINT16 markFilteringSetX[VAR];*//* Index (base 0) into GDEF mark glyph sets +/*HBUINT16 markFilteringSetX[HB_VAR_ARRAY];*//* Index (base 0) into GDEF mark glyph sets * structure. This field is only present if bit * UseMarkFilteringSet of lookup flags is set. */ public: @@ -809,6 +1190,32 @@ struct Lookup typedef OffsetListOf LookupList; +template +struct LookupOffsetList : OffsetListOf +{ + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + unsigned count = this->len; + + hb_zip (*this, hb_range (count)) + | hb_filter (l->lookup_index_map, hb_second) + | hb_map (hb_first) + | hb_apply (subset_offset_array (c, *out, this)) + ; + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (OffsetListOf::sanitize (c, this)); + } +}; + /* * Coverage Table @@ -826,8 +1233,9 @@ struct CoverageFormat1 return i; } - bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs) + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) { TRACE_SERIALIZE (this); return_trace (glyphArray.serialize (c, glyphs)); @@ -852,20 +1260,20 @@ struct CoverageFormat1 { return glyphs->has (glyphArray[index]); } template - bool add_coverage (set_t *glyphs) const - { - return glyphs->add_sorted_array (glyphArray.arrayZ, glyphArray.len); - } + bool collect_coverage (set_t *glyphs) const + { return glyphs->add_sorted_array (glyphArray.arrayZ, glyphArray.len); } public: /* Older compilers need this to be public. */ - struct Iter { + struct iter_t + { void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; } void fini () {} - bool more () { return i < c->glyphArray.len; } + bool more () const { return i < c->glyphArray.len; } void next () { i++; } - hb_codepoint_t get_glyph () { return c->glyphArray[i]; } - unsigned int get_coverage () { return i; } + hb_codepoint_t get_glyph () const { return c->glyphArray[i]; } + bool operator != (const iter_t& o) const + { return i != o.i || c != o.c; } private: const struct CoverageFormat1 *c; @@ -875,7 +1283,7 @@ struct CoverageFormat1 protected: HBUINT16 coverageFormat; /* Format identifier--format = 1 */ - SortedArrayOf + SortedArrayOf glyphArray; /* Array of GlyphIDs--in numerical order */ public: DEFINE_SIZE_ARRAY (4, glyphArray); @@ -889,43 +1297,53 @@ struct CoverageFormat2 unsigned int get_coverage (hb_codepoint_t glyph_id) const { const RangeRecord &range = rangeRecord.bsearch (glyph_id); - return likely (range.start <= range.end) ? - (unsigned int) range.value + (glyph_id - range.start) : - NOT_COVERED; + return likely (range.first <= range.last) + ? (unsigned int) range.value + (glyph_id - range.first) + : NOT_COVERED; } - bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs) + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - if (unlikely (!glyphs.length)) + if (unlikely (!glyphs)) { - rangeRecord.len.set (0); + rangeRecord.len = 0; return_trace (true); } - unsigned int num_ranges = 1; - for (unsigned int i = 1; i < glyphs.length; i++) - if (glyphs[i - 1] + 1 != glyphs[i]) + /* TODO(iter) Write more efficiently? */ + + unsigned num_ranges = 0; + hb_codepoint_t last = (hb_codepoint_t) -2; + for (auto g: glyphs) + { + if (last + 1 != g) num_ranges++; - rangeRecord.len.set (num_ranges); - if (unlikely (!c->extend (rangeRecord))) return_trace (false); + last = g; + } + + if (unlikely (!rangeRecord.serialize (c, num_ranges))) return_trace (false); - unsigned int range = 0; - rangeRecord[range].start = glyphs[0]; - rangeRecord[range].value.set (0); - for (unsigned int i = 1; i < glyphs.length; i++) + unsigned count = 0; + unsigned range = (unsigned) -1; + last = (hb_codepoint_t) -2; + for (auto g: glyphs) { - if (glyphs[i - 1] + 1 != glyphs[i]) + if (last + 1 != g) { range++; - rangeRecord[range].start = glyphs[i]; - rangeRecord[range].value.set (i); + rangeRecord[range].first = g; + rangeRecord[range].value = count; } - rangeRecord[range].end = glyphs[i]; + rangeRecord[range].last = g; + last = g; + count++; } + return_trace (true); } @@ -951,7 +1369,7 @@ struct CoverageFormat2 for (i = 0; i < count; i++) { const RangeRecord &range = rangeRecord[i]; if (range.value <= index && - index < (unsigned int) range.value + (range.end - range.start) && + index < (unsigned int) range.value + (range.last - range.first) && range.intersects (glyphs)) return true; else if (index < range.value) @@ -961,57 +1379,61 @@ struct CoverageFormat2 } template - bool add_coverage (set_t *glyphs) const + bool collect_coverage (set_t *glyphs) const { unsigned int count = rangeRecord.len; for (unsigned int i = 0; i < count; i++) - if (unlikely (!rangeRecord[i].add_coverage (glyphs))) + if (unlikely (!rangeRecord[i].collect_coverage (glyphs))) return false; return true; } public: /* Older compilers need this to be public. */ - struct Iter + struct iter_t { void init (const CoverageFormat2 &c_) { c = &c_; coverage = 0; i = 0; - j = c->rangeRecord.len ? c->rangeRecord[0].start : 0; - if (unlikely (c->rangeRecord[0].start > c->rangeRecord[0].end)) + j = c->rangeRecord.len ? c->rangeRecord[0].first : 0; + if (unlikely (c->rangeRecord[0].first > c->rangeRecord[0].last)) { /* Broken table. Skip. */ i = c->rangeRecord.len; } } void fini () {} - bool more () { return i < c->rangeRecord.len; } + bool more () const { return i < c->rangeRecord.len; } void next () { - if (j >= c->rangeRecord[i].end) + if (j >= c->rangeRecord[i].last) { i++; if (more ()) { - hb_codepoint_t old = j; - j = c->rangeRecord[i].start; - if (unlikely (j <= old)) + unsigned int old = coverage; + j = c->rangeRecord[i].first; + coverage = c->rangeRecord[i].value; + if (unlikely (coverage != old + 1)) { - /* Broken table. Skip. Important to avoid DoS. */ + /* Broken table. Skip. Important to avoid DoS. + * Also, our callers depend on coverage being + * consecutive and monotonically increasing, + * ie. iota(). */ i = c->rangeRecord.len; return; } - coverage = c->rangeRecord[i].value; } return; } coverage++; j++; } - hb_codepoint_t get_glyph () { return j; } - unsigned int get_coverage () { return coverage; } + hb_codepoint_t get_glyph () const { return j; } + bool operator != (const iter_t& o) const + { return i != o.i || j != o.j || c != o.c; } private: const struct CoverageFormat2 *c; @@ -1032,6 +1454,15 @@ struct CoverageFormat2 struct Coverage { + /* Has interface. */ + static constexpr unsigned SENTINEL = NOT_COVERED; + typedef unsigned int value_t; + value_t operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + /* Predicate. */ + bool operator () (hb_codepoint_t k) const { return has (k); } + + unsigned int get (hb_codepoint_t k) const { return get_coverage (k); } unsigned int get_coverage (hb_codepoint_t glyph_id) const { switch (u.format) { @@ -1041,17 +1472,24 @@ struct Coverage } } - bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs) + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - unsigned int num_ranges = 1; - for (unsigned int i = 1; i < glyphs.length; i++) - if (glyphs[i - 1] + 1 != glyphs[i]) + unsigned count = 0; + unsigned num_ranges = 0; + hb_codepoint_t last = (hb_codepoint_t) -2; + for (auto g: glyphs) + { + if (last + 1 != g) num_ranges++; - u.format.set (glyphs.length * 2 < num_ranges * 3 ? 1 : 2); + last = g; + count++; + } + u.format = count <= num_ranges * 3 ? 1 : 2; switch (u.format) { @@ -1061,6 +1499,23 @@ struct Coverage } } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + iter () + | hb_filter (glyphset) + | hb_map_retains_sorting (glyph_map) + ; + + bool ret = bool (it); + Coverage_serialize (c->serializer, it); + return_trace (ret); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -1095,19 +1550,20 @@ struct Coverage /* Might return false if array looks unsorted. * Used for faster rejection of corrupt data. */ template - bool add_coverage (set_t *glyphs) const + bool collect_coverage (set_t *glyphs) const { switch (u.format) { - case 1: return u.format1.add_coverage (glyphs); - case 2: return u.format2.add_coverage (glyphs); + case 1: return u.format1.collect_coverage (glyphs); + case 2: return u.format2.collect_coverage (glyphs); default:return false; } } - struct Iter + struct iter_t : hb_iter_with_fallback_t { - Iter (const Coverage &c_) + static constexpr bool is_sorted_iterator = true; + iter_t (const Coverage &c_ = Null (Coverage)) { memset (this, 0, sizeof (*this)); format = c_.u.format; @@ -1118,7 +1574,7 @@ struct Coverage default: return; } } - bool more () + bool __more__ () const { switch (format) { @@ -1127,7 +1583,7 @@ struct Coverage default:return false; } } - void next () + void __next__ () { switch (format) { @@ -1136,7 +1592,10 @@ struct Coverage default: break; } } - hb_codepoint_t get_glyph () + typedef hb_codepoint_t __item_t__; + __item_t__ __item__ () const { return get_glyph (); } + + hb_codepoint_t get_glyph () const { switch (format) { @@ -1145,23 +1604,25 @@ struct Coverage default:return 0; } } - unsigned int get_coverage () + bool operator != (const iter_t& o) const { + if (format != o.format) return true; switch (format) { - case 1: return u.format1.get_coverage (); - case 2: return u.format2.get_coverage (); - default:return -1; + case 1: return u.format1 != o.u.format1; + case 2: return u.format2 != o.u.format2; + default:return false; } } private: unsigned int format; union { - CoverageFormat2::Iter format2; /* Put this one first since it's larger; helps shut up compiler. */ - CoverageFormat1::Iter format1; + CoverageFormat2::iter_t format2; /* Put this one first since it's larger; helps shut up compiler. */ + CoverageFormat1::iter_t format1; } u; }; + iter_t iter () const { return iter_t (*this); } protected: union { @@ -1173,15 +1634,56 @@ struct Coverage DEFINE_SIZE_UNION (2, format); }; +template +static inline void +Coverage_serialize (hb_serialize_context_t *c, + Iterator it) +{ c->start_embed ()->serialize (c, it); } + +static void ClassDef_remap_and_serialize (hb_serialize_context_t *c, + const hb_set_t &glyphset, + const hb_map_t &gid_klass_map, + hb_sorted_vector_t &glyphs, + const hb_set_t &klasses, + hb_map_t *klass_map /*INOUT*/) +{ + if (!klass_map) + { + ClassDef_serialize (c, hb_zip (glyphs.iter (), + glyphs.iter () + | hb_map (gid_klass_map))); + return; + } + + /* any glyph not assigned a class value falls into Class zero (0), + * if any glyph assigned to class 0, remapping must start with 0->0*/ + if (glyphset.get_population () > gid_klass_map.get_population ()) + klass_map->set (0, 0); + + unsigned idx = klass_map->has (0) ? 1 : 0; + for (const unsigned k: klasses.iter ()) + { + if (klass_map->has (k)) continue; + klass_map->set (k, idx); + idx++; + } + + auto it = + + glyphs.iter () + | hb_map_retains_sorting ([&] (const HBGlyphID& gid) -> hb_pair_t + { + unsigned new_klass = klass_map->get (gid_klass_map[gid]); + return hb_pair ((hb_codepoint_t)gid, new_klass); + }) + ; + + c->propagate_error (glyphs, klasses); + ClassDef_serialize (c, it); +} /* * Class Definition Table */ -static inline void ClassDef_serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t klasses); - struct ClassDefFormat1 { friend struct ClassDef; @@ -1192,54 +1694,64 @@ struct ClassDefFormat1 return classValue[(unsigned int) (glyph_id - startGlyph)]; } + template bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t klasses) + Iterator it) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - if (unlikely (!glyphs.length)) + if (unlikely (!it)) { - startGlyph.set (0); - classValue.len.set (0); + startGlyph = 0; + classValue.len = 0; return_trace (true); } - hb_codepoint_t glyph_min = glyphs[0]; - hb_codepoint_t glyph_max = glyphs[glyphs.length - 1]; - - startGlyph.set (glyph_min); - classValue.len.set (glyph_max - glyph_min + 1); - if (unlikely (!c->extend (classValue))) return_trace (false); - - for (unsigned int i = 0; i < glyphs.length; i++) - classValue[glyphs[i] - glyph_min] = klasses[i]; + hb_codepoint_t glyph_min = (*it).first; + hb_codepoint_t glyph_max = + it + | hb_map (hb_first) + | hb_reduce (hb_max, 0u); + unsigned glyph_count = glyph_max - glyph_min + 1; + startGlyph = glyph_min; + if (unlikely (!classValue.serialize (c, glyph_count))) return_trace (false); + for (const hb_pair_t gid_klass_pair : + it) + { + unsigned idx = gid_klass_pair.first - glyph_min; + classValue[idx] = gid_klass_pair.second; + } return_trace (true); } - bool subset (hb_subset_context_t *c) const + bool subset (hb_subset_context_t *c, + hb_map_t *klass_map = nullptr /*OUT*/) const { TRACE_SUBSET (this); - const hb_set_t &glyphset = *c->plan->glyphset; + const hb_set_t &glyphset = *c->plan->_glyphset_gsub; const hb_map_t &glyph_map = *c->plan->glyph_map; - hb_vector_t glyphs; - hb_vector_t klasses; + + hb_sorted_vector_t glyphs; + hb_set_t orig_klasses; + hb_map_t gid_org_klass_map; hb_codepoint_t start = startGlyph; hb_codepoint_t end = start + classValue.len; - for (hb_codepoint_t g = start; g < end; g++) + for (const hb_codepoint_t gid : + hb_range (start, end) + | hb_filter (glyphset)) { - unsigned int value = classValue[g - start]; - if (!value) continue; - if (!glyphset.has (g)) continue; - glyphs.push()->set (glyph_map[g]); - klasses.push()->set (value); + unsigned klass = classValue[gid - start]; + if (!klass) continue; + + glyphs.push (glyph_map[gid]); + gid_org_klass_map.set (glyph_map[gid], klass); + orig_klasses.add (klass); } - c->serializer->propagate_error (glyphs, klasses); - ClassDef_serialize (c->serializer, glyphs, klasses); - return_trace (glyphs.length); + + ClassDef_remap_and_serialize (c->serializer, glyphset, gid_org_klass_map, + glyphs, orig_klasses, klass_map); + return_trace ((bool) glyphs); } bool sanitize (hb_sanitize_context_t *c) const @@ -1249,7 +1761,7 @@ struct ClassDefFormat1 } template - bool add_coverage (set_t *glyphs) const + bool collect_coverage (set_t *glyphs) const { unsigned int start = 0; unsigned int count = classValue.len; @@ -1272,7 +1784,7 @@ struct ClassDefFormat1 } template - bool add_class (set_t *glyphs, unsigned int klass) const + bool collect_class (set_t *glyphs, unsigned int klass) const { unsigned int count = classValue.len; for (unsigned int i = 0; i < count; i++) @@ -1311,7 +1823,7 @@ struct ClassDefFormat1 protected: HBUINT16 classFormat; /* Format identifier--format = 1 */ - GlyphID startGlyph; /* First GlyphID of the classValueArray */ + HBGlyphID startGlyph; /* First GlyphID of the classValueArray */ ArrayOf classValue; /* Array of Class Values--one per GlyphID */ public: @@ -1328,69 +1840,90 @@ struct ClassDefFormat2 return rangeRecord.bsearch (glyph_id).value; } + template bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t klasses) + Iterator it) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - if (unlikely (!glyphs.length)) + if (unlikely (!it)) { - rangeRecord.len.set (0); + rangeRecord.len = 0; return_trace (true); } - unsigned int num_ranges = 1; - for (unsigned int i = 1; i < glyphs.length; i++) - if (glyphs[i - 1] + 1 != glyphs[i] || - klasses[i - 1] != klasses[i]) - num_ranges++; - rangeRecord.len.set (num_ranges); - if (unlikely (!c->extend (rangeRecord))) return_trace (false); + unsigned num_ranges = 1; + hb_codepoint_t prev_gid = (*it).first; + unsigned prev_klass = (*it).second; + + RangeRecord range_rec; + range_rec.first = prev_gid; + range_rec.last = prev_gid; + range_rec.value = prev_klass; - unsigned int range = 0; - rangeRecord[range].start = glyphs[0]; - rangeRecord[range].value.set (klasses[0]); - for (unsigned int i = 1; i < glyphs.length; i++) + RangeRecord *record = c->copy (range_rec); + if (unlikely (!record)) return_trace (false); + + for (const auto gid_klass_pair : + (++it)) { - if (glyphs[i - 1] + 1 != glyphs[i] || - klasses[i - 1] != klasses[i]) + hb_codepoint_t cur_gid = gid_klass_pair.first; + unsigned cur_klass = gid_klass_pair.second; + + if (cur_gid != prev_gid + 1 || + cur_klass != prev_klass) { - range++; - rangeRecord[range].start = glyphs[i]; - rangeRecord[range].value = klasses[i]; + if (unlikely (!record)) break; + record->last = prev_gid; + num_ranges++; + + range_rec.first = cur_gid; + range_rec.last = cur_gid; + range_rec.value = cur_klass; + + record = c->copy (range_rec); } - rangeRecord[range].end = glyphs[i]; + + prev_klass = cur_klass; + prev_gid = cur_gid; } + + if (likely (record)) record->last = prev_gid; + rangeRecord.len = num_ranges; return_trace (true); } - bool subset (hb_subset_context_t *c) const + bool subset (hb_subset_context_t *c, + hb_map_t *klass_map = nullptr /*OUT*/) const { TRACE_SUBSET (this); - const hb_set_t &glyphset = *c->plan->glyphset; + const hb_set_t &glyphset = *c->plan->_glyphset_gsub; const hb_map_t &glyph_map = *c->plan->glyph_map; - hb_vector_t glyphs; - hb_vector_t klasses; - unsigned int count = rangeRecord.len; - for (unsigned int i = 0; i < count; i++) + hb_sorted_vector_t glyphs; + hb_set_t orig_klasses; + hb_map_t gid_org_klass_map; + + unsigned count = rangeRecord.len; + for (unsigned i = 0; i < count; i++) { - unsigned int value = rangeRecord[i].value; - if (!value) continue; - hb_codepoint_t start = rangeRecord[i].start; - hb_codepoint_t end = rangeRecord[i].end + 1; + unsigned klass = rangeRecord[i].value; + if (!klass) continue; + hb_codepoint_t start = rangeRecord[i].first; + hb_codepoint_t end = rangeRecord[i].last + 1; for (hb_codepoint_t g = start; g < end; g++) { if (!glyphset.has (g)) continue; - glyphs.push ()->set (glyph_map[g]); - klasses.push ()->set (value); + glyphs.push (glyph_map[g]); + gid_org_klass_map.set (glyph_map[g], klass); + orig_klasses.add (klass); } } - c->serializer->propagate_error (glyphs, klasses); - ClassDef_serialize (c->serializer, glyphs, klasses); - return_trace (glyphs.length); + + ClassDef_remap_and_serialize (c->serializer, glyphset, gid_org_klass_map, + glyphs, orig_klasses, klass_map); + return_trace ((bool) glyphs); } bool sanitize (hb_sanitize_context_t *c) const @@ -1400,24 +1933,24 @@ struct ClassDefFormat2 } template - bool add_coverage (set_t *glyphs) const + bool collect_coverage (set_t *glyphs) const { unsigned int count = rangeRecord.len; for (unsigned int i = 0; i < count; i++) if (rangeRecord[i].value) - if (unlikely (!rangeRecord[i].add_coverage (glyphs))) + if (unlikely (!rangeRecord[i].collect_coverage (glyphs))) return false; return true; } template - bool add_class (set_t *glyphs, unsigned int klass) const + bool collect_class (set_t *glyphs, unsigned int klass) const { unsigned int count = rangeRecord.len; for (unsigned int i = 0; i < count; i++) { if (rangeRecord[i].value == klass) - if (unlikely (!rangeRecord[i].add_coverage (glyphs))) + if (unlikely (!rangeRecord[i].collect_coverage (glyphs))) return false; } return true; @@ -1443,9 +1976,9 @@ struct ClassDefFormat2 { if (!hb_set_next (glyphs, &g)) break; - if (g < rangeRecord[i].start) + if (g < rangeRecord[i].first) return true; - g = rangeRecord[i].end; + g = rangeRecord[i].last; } if (g != HB_SET_VALUE_INVALID && hb_set_next (glyphs, &g)) return true; @@ -1468,6 +2001,15 @@ struct ClassDefFormat2 struct ClassDef { + /* Has interface. */ + static constexpr unsigned SENTINEL = 0; + typedef unsigned int value_t; + value_t operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + /* Projection. */ + hb_codepoint_t operator () (hb_codepoint_t k) const { return get (k); } + + unsigned int get (hb_codepoint_t k) const { return get_class (k); } unsigned int get_class (hb_codepoint_t glyph_id) const { switch (u.format) { @@ -1477,44 +2019,58 @@ struct ClassDef } } - bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t klasses) + template + bool serialize (hb_serialize_context_t *c, Iterator it) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - unsigned int format = 2; - if (glyphs.length) + unsigned format = 2; + if (likely (it)) { - hb_codepoint_t glyph_min = glyphs[0]; - hb_codepoint_t glyph_max = glyphs[glyphs.length - 1]; + hb_codepoint_t glyph_min = (*it).first; + hb_codepoint_t glyph_max = + it + | hb_map (hb_first) + | hb_reduce (hb_max, 0u); + + unsigned num_ranges = 1; + hb_codepoint_t prev_gid = glyph_min; + unsigned prev_klass = (*it).second; - unsigned int num_ranges = 1; - for (unsigned int i = 1; i < glyphs.length; i++) - if (glyphs[i - 1] + 1 != glyphs[i] || - klasses[i - 1] != klasses[i]) + for (const auto gid_klass_pair : it) + { + hb_codepoint_t cur_gid = gid_klass_pair.first; + unsigned cur_klass = gid_klass_pair.second; + if (cur_gid == glyph_min || !cur_klass) continue; + if (cur_gid != prev_gid + 1 || + cur_klass != prev_klass) num_ranges++; - if (1 + (glyph_max - glyph_min + 1) < num_ranges * 3) + prev_gid = cur_gid; + prev_klass = cur_klass; + } + + if (1 + (glyph_max - glyph_min + 1) <= num_ranges * 3) format = 1; } - u.format.set (format); + u.format = format; switch (u.format) { - case 1: return_trace (u.format1.serialize (c, glyphs, klasses)); - case 2: return_trace (u.format2.serialize (c, glyphs, klasses)); + case 1: return_trace (u.format1.serialize (c, it)); + case 2: return_trace (u.format2.serialize (c, it)); default:return_trace (false); } } - bool subset (hb_subset_context_t *c) const + bool subset (hb_subset_context_t *c, + hb_map_t *klass_map = nullptr /*OUT*/) const { TRACE_SUBSET (this); switch (u.format) { - case 1: return_trace (u.format1.subset (c)); - case 2: return_trace (u.format2.subset (c)); + case 1: return_trace (u.format1.subset (c, klass_map)); + case 2: return_trace (u.format2.subset (c, klass_map)); default:return_trace (false); } } @@ -1533,11 +2089,11 @@ struct ClassDef /* Might return false if array looks unsorted. * Used for faster rejection of corrupt data. */ template - bool add_coverage (set_t *glyphs) const + bool collect_coverage (set_t *glyphs) const { switch (u.format) { - case 1: return u.format1.add_coverage (glyphs); - case 2: return u.format2.add_coverage (glyphs); + case 1: return u.format1.collect_coverage (glyphs); + case 2: return u.format2.collect_coverage (glyphs); default:return false; } } @@ -1545,11 +2101,11 @@ struct ClassDef /* Might return false if array looks unsorted. * Used for faster rejection of corrupt data. */ template - bool add_class (set_t *glyphs, unsigned int klass) const + bool collect_class (set_t *glyphs, unsigned int klass) const { switch (u.format) { - case 1: return u.format1.add_class (glyphs, klass); - case 2: return u.format2.add_class (glyphs, klass); + case 1: return u.format1.collect_class (glyphs, klass); + case 2: return u.format2.collect_class (glyphs, klass); default:return false; } } @@ -1581,10 +2137,10 @@ struct ClassDef DEFINE_SIZE_UNION (2, format); }; +template static inline void ClassDef_serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t klasses) -{ c->start_embed ()->serialize (c, glyphs, klasses); } + Iterator it) +{ c->start_embed ()->serialize (c, it); } /* @@ -1635,7 +2191,7 @@ struct VarRegionAxis struct VarRegionList { float evaluate (unsigned int region_index, - const int *coords, unsigned int coord_len) const + const int *coords, unsigned int coord_len) const { if (unlikely (region_index >= regionCount)) return 0.; @@ -1662,6 +2218,26 @@ struct VarRegionList axesZ.sanitize (c, (unsigned int) axisCount * (unsigned int) regionCount)); } + bool serialize (hb_serialize_context_t *c, const VarRegionList *src, const hb_bimap_t ®ion_map) + { + TRACE_SERIALIZE (this); + VarRegionList *out = c->allocate_min (); + if (unlikely (!out)) return_trace (false); + axisCount = src->axisCount; + regionCount = region_map.get_population (); + if (unlikely (!c->allocate_size (get_size () - min_size))) return_trace (false); + unsigned int region_count = src->get_region_count (); + for (unsigned int r = 0; r < regionCount; r++) + { + unsigned int backward = region_map.backward (r); + if (backward >= region_count) return_trace (false); + memcpy (&axesZ[axisCount * r], &src->axesZ[axisCount * backward], VarRegionAxis::static_size * axisCount); + } + + return_trace (true); + } + + unsigned int get_size () const { return min_size + VarRegionAxis::static_size * axisCount * regionCount; } unsigned int get_region_count () const { return regionCount; } protected: @@ -1685,8 +2261,8 @@ struct VarData { return itemCount * get_row_size (); } float get_delta (unsigned int inner, - const int *coords, unsigned int coord_count, - const VarRegionList ®ions) const + const int *coords, unsigned int coord_count, + const VarRegionList ®ions) const { if (unlikely (inner >= itemCount)) return 0.; @@ -1694,7 +2270,7 @@ struct VarData unsigned int count = regionIndices.len; unsigned int scount = shortCount; - const HBUINT8 *bytes = &StructAfter (regionIndices); + const HBUINT8 *bytes = get_delta_bytes (); const HBUINT8 *row = bytes + inner * (scount + count); float delta = 0.; @@ -1716,16 +2292,16 @@ struct VarData return delta; } - void get_scalars (int *coords, unsigned int coord_count, + void get_scalars (const int *coords, unsigned int coord_count, const VarRegionList ®ions, float *scalars /*OUT */, unsigned int num_scalars) const { - assert (num_scalars == regionIndices.len); - for (unsigned int i = 0; i < num_scalars; i++) - { - scalars[i] = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count); - } + unsigned count = hb_min (num_scalars, regionIndices.len); + for (unsigned int i = 0; i < count; i++) + scalars[i] = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count); + for (unsigned int i = count; i < num_scalars; i++) + scalars[i] = 0.f; } bool sanitize (hb_sanitize_context_t *c) const @@ -1734,11 +2310,117 @@ struct VarData return_trace (c->check_struct (this) && regionIndices.sanitize (c) && shortCount <= regionIndices.len && - c->check_range (&StructAfter (regionIndices), + c->check_range (get_delta_bytes (), itemCount, get_row_size ())); } + bool serialize (hb_serialize_context_t *c, + const VarData *src, + const hb_inc_bimap_t &inner_map, + const hb_bimap_t ®ion_map) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + itemCount = inner_map.get_next_value (); + + /* Optimize short count */ + unsigned short ri_count = src->regionIndices.len; + enum delta_size_t { kZero=0, kByte, kShort }; + hb_vector_t delta_sz; + hb_vector_t ri_map; /* maps old index to new index */ + delta_sz.resize (ri_count); + ri_map.resize (ri_count); + unsigned int new_short_count = 0; + unsigned int r; + for (r = 0; r < ri_count; r++) + { + delta_sz[r] = kZero; + for (unsigned int i = 0; i < inner_map.get_next_value (); i++) + { + unsigned int old = inner_map.backward (i); + int16_t delta = src->get_item_delta (old, r); + if (delta < -128 || 127 < delta) + { + delta_sz[r] = kShort; + new_short_count++; + break; + } + else if (delta != 0) + delta_sz[r] = kByte; + } + } + unsigned int short_index = 0; + unsigned int byte_index = new_short_count; + unsigned int new_ri_count = 0; + for (r = 0; r < ri_count; r++) + if (delta_sz[r]) + { + ri_map[r] = (delta_sz[r] == kShort)? short_index++ : byte_index++; + new_ri_count++; + } + + shortCount = new_short_count; + regionIndices.len = new_ri_count; + + unsigned int size = regionIndices.get_size () - HBUINT16::static_size/*regionIndices.len*/ + (get_row_size () * itemCount); + if (unlikely (!c->allocate_size (size))) + return_trace (false); + + for (r = 0; r < ri_count; r++) + if (delta_sz[r]) regionIndices[ri_map[r]] = region_map[src->regionIndices[r]]; + + for (unsigned int i = 0; i < itemCount; i++) + { + unsigned int old = inner_map.backward (i); + for (unsigned int r = 0; r < ri_count; r++) + if (delta_sz[r]) set_item_delta (i, ri_map[r], src->get_item_delta (old, r)); + } + + return_trace (true); + } + + void collect_region_refs (hb_inc_bimap_t ®ion_map, const hb_inc_bimap_t &inner_map) const + { + for (unsigned int r = 0; r < regionIndices.len; r++) + { + unsigned int region = regionIndices[r]; + if (region_map.has (region)) continue; + for (unsigned int i = 0; i < inner_map.get_next_value (); i++) + if (get_item_delta (inner_map.backward (i), r) != 0) + { + region_map.add (region); + break; + } + } + } + + protected: + const HBUINT8 *get_delta_bytes () const + { return &StructAfter (regionIndices); } + + HBUINT8 *get_delta_bytes () + { return &StructAfter (regionIndices); } + + int16_t get_item_delta (unsigned int item, unsigned int region) const + { + if ( item >= itemCount || unlikely (region >= regionIndices.len)) return 0; + const HBINT8 *p = (const HBINT8 *)get_delta_bytes () + item * get_row_size (); + if (region < shortCount) + return ((const HBINT16 *)p)[region]; + else + return (p + HBINT16::static_size * shortCount)[region - shortCount]; + } + + void set_item_delta (unsigned int item, unsigned int region, int16_t delta) + { + HBINT8 *p = (HBINT8 *)get_delta_bytes () + item * get_row_size (); + if (region < shortCount) + ((HBINT16 *)p)[region] = delta; + else + (p + HBINT16::static_size * shortCount)[region - shortCount] = delta; + } + protected: HBUINT16 itemCount; HBUINT16 shortCount; @@ -1753,8 +2435,12 @@ struct VariationStore float get_delta (unsigned int outer, unsigned int inner, const int *coords, unsigned int coord_count) const { +#ifdef HB_NO_VAR + return 0.f; +#endif + if (unlikely (outer >= dataSets.len)) - return 0.; + return 0.f; return (this+dataSets[outer]).get_delta (inner, coords, coord_count, @@ -1771,6 +2457,10 @@ struct VariationStore bool sanitize (hb_sanitize_context_t *c) const { +#ifdef HB_NO_VAR + return true; +#endif + TRACE_SANITIZE (this); return_trace (c->check_struct (this) && format == 1 && @@ -1778,18 +2468,98 @@ struct VariationStore dataSets.sanitize (c, this)); } + bool serialize (hb_serialize_context_t *c, + const VariationStore *src, + const hb_array_t &inner_maps) + { + TRACE_SERIALIZE (this); + unsigned int set_count = 0; + for (unsigned int i = 0; i < inner_maps.length; i++) + if (inner_maps[i].get_population () > 0) set_count++; + + unsigned int size = min_size + HBUINT32::static_size * set_count; + if (unlikely (!c->allocate_size (size))) return_trace (false); + format = 1; + + hb_inc_bimap_t region_map; + for (unsigned int i = 0; i < inner_maps.length; i++) + (src+src->dataSets[i]).collect_region_refs (region_map, inner_maps[i]); + region_map.sort (); + + if (unlikely (!regions.serialize (c, this) + .serialize (c, &(src+src->regions), region_map))) return_trace (false); + + /* TODO: The following code could be simplified when + * OffsetListOf::subset () can take a custom param to be passed to VarData::serialize () + */ + dataSets.len = set_count; + unsigned int set_index = 0; + for (unsigned int i = 0; i < inner_maps.length; i++) + { + if (inner_maps[i].get_population () == 0) continue; + if (unlikely (!dataSets[set_index++].serialize (c, this) + .serialize (c, &(src+src->dataSets[i]), inner_maps[i], region_map))) + return_trace (false); + } + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + VariationStore *varstore_prime = c->serializer->start_embed (); + if (unlikely (!varstore_prime)) return_trace (false); + + const hb_set_t *variation_indices = c->plan->layout_variation_indices; + if (variation_indices->is_empty ()) return_trace (false); + + hb_vector_t inner_maps; + inner_maps.resize ((unsigned) dataSets.len); + for (unsigned i = 0; i < inner_maps.length; i++) + inner_maps[i].init (); + + for (unsigned idx : c->plan->layout_variation_indices->iter ()) + { + uint16_t major = idx >> 16; + uint16_t minor = idx & 0xFFFF; + + if (major >= inner_maps.length) + { + for (unsigned i = 0; i < inner_maps.length; i++) + inner_maps[i].fini (); + return_trace (false); + } + inner_maps[major].add (minor); + } + varstore_prime->serialize (c->serializer, this, inner_maps.as_array ()); + + for (unsigned i = 0; i < inner_maps.length; i++) + inner_maps[i].fini (); + return_trace (bool (varstore_prime->dataSets)); + } + unsigned int get_region_index_count (unsigned int ivs) const { return (this+dataSets[ivs]).get_region_index_count (); } void get_scalars (unsigned int ivs, - int *coords, unsigned int coord_count, + const int *coords, unsigned int coord_count, float *scalars /*OUT*/, unsigned int num_scalars) const { +#ifdef HB_NO_VAR + for (unsigned i = 0; i < num_scalars; i++) + scalars[i] = 0.f; + return; +#endif + (this+dataSets[ivs]).get_scalars (coords, coord_count, this+regions, &scalars[0], num_scalars); } + unsigned int get_sub_table_count () const { return dataSets.len; } + protected: HBUINT16 format; LOffsetTo regions; @@ -1806,6 +2576,14 @@ struct ConditionFormat1 { friend struct Condition; + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + return_trace (true); + } + private: bool evaluate (const int *coords, unsigned int coord_len) const { @@ -1838,6 +2616,17 @@ struct Condition } } + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -1868,6 +2657,18 @@ struct ConditionSet return true; } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + + conditions.iter () + | hb_apply (subset_offset_array (c, out->conditions, this)) + ; + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -1884,6 +2685,30 @@ struct FeatureTableSubstitutionRecord { friend struct FeatureTableSubstitution; + void collect_lookups (const void *base, hb_set_t *lookup_indexes /* OUT */) const + { + return (base+feature).add_lookup_indexes_to (lookup_indexes); + } + + void closure_features (const void *base, + const hb_map_t *lookup_indexes, + hb_set_t *feature_indexes /* OUT */) const + { + if ((base+feature).intersects_lookup_indexes (lookup_indexes)) + feature_indexes->add (featureIndex); + } + + bool subset (hb_subset_layout_context_t *c, const void *base) const + { + TRACE_SUBSET (this); + auto *out = c->subset_context->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + out->featureIndex = c->feature_index_map->get (featureIndex); + bool ret = out->feature.serialize_subset (c->subset_context, feature, base, c); + return_trace (ret); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); @@ -1911,6 +2736,39 @@ struct FeatureTableSubstitution return nullptr; } + void collect_lookups (const hb_set_t *feature_indexes, + hb_set_t *lookup_indexes /* OUT */) const + { + + hb_iter (substitutions) + | hb_filter (feature_indexes, &FeatureTableSubstitutionRecord::featureIndex) + | hb_apply ([this, lookup_indexes] (const FeatureTableSubstitutionRecord& r) + { r.collect_lookups (this, lookup_indexes); }) + ; + } + + void closure_features (const hb_map_t *lookup_indexes, + hb_set_t *feature_indexes /* OUT */) const + { + for (const FeatureTableSubstitutionRecord& record : substitutions) + record.closure_features (this, lookup_indexes, feature_indexes); + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + out->version.major = version.major; + out->version.minor = version.minor; + + + substitutions.iter () + | hb_apply (subset_record_array (l, &(out->substitutions), this)) + ; + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -1931,6 +2789,32 @@ struct FeatureVariationRecord { friend struct FeatureVariations; + void collect_lookups (const void *base, + const hb_set_t *feature_indexes, + hb_set_t *lookup_indexes /* OUT */) const + { + return (base+substitutions).collect_lookups (feature_indexes, lookup_indexes); + } + + void closure_features (const void *base, + const hb_map_t *lookup_indexes, + hb_set_t *feature_indexes /* OUT */) const + { + (base+substitutions).closure_features (lookup_indexes, feature_indexes); + } + + bool subset (hb_subset_layout_context_t *c, const void *base) const + { + TRACE_SUBSET (this); + auto *out = c->subset_context->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + out->conditions.serialize_subset (c->subset_context, conditions, base); + out->substitutions.serialize_subset (c->subset_context, substitutions, base, c); + + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); @@ -1952,7 +2836,7 @@ struct FeatureVariations static constexpr unsigned NOT_FOUND_INDEX = 0xFFFFFFFFu; bool find_index (const int *coords, unsigned int coord_len, - unsigned int *index) const + unsigned int *index) const { unsigned int count = varRecords.len; for (unsigned int i = 0; i < count; i++) @@ -1975,10 +2859,40 @@ struct FeatureVariations return (this+record.substitutions).find_substitute (feature_index); } - bool subset (hb_subset_context_t *c) const + FeatureVariations* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (*this)); + } + + void collect_lookups (const hb_set_t *feature_indexes, + hb_set_t *lookup_indexes /* OUT */) const + { + for (const FeatureVariationRecord& r : varRecords) + r.collect_lookups (this, feature_indexes, lookup_indexes); + } + + void closure_features (const hb_map_t *lookup_indexes, + hb_set_t *feature_indexes /* OUT */) const + { + for (const FeatureVariationRecord& record : varRecords) + record.closure_features (this, lookup_indexes, feature_indexes); + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const { TRACE_SUBSET (this); - return_trace (c->serializer->embed (*this)); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + out->version.major = version.major; + out->version.minor = version.minor; + + + varRecords.iter () + | hb_apply (subset_record_array (l, &(out->varRecords), this)) + ; + return_trace (bool (out->varRecords)); } bool sanitize (hb_sanitize_context_t *c) const @@ -2014,6 +2928,8 @@ struct HintingDevice hb_position_t get_y_delta (hb_font_t *font) const { return get_delta (font->y_ppem, font->y_scale); } + public: + unsigned int get_size () const { unsigned int f = deltaFormat; @@ -2027,6 +2943,12 @@ struct HintingDevice return_trace (c->check_struct (this) && c->check_range (this, this->get_size ())); } + HintingDevice* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + private: int get_delta (unsigned int ppem, int scale) const @@ -2088,6 +3010,32 @@ struct VariationDevice hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store) const { return font->em_scalef_y (get_delta (font, store)); } + VariationDevice* copy (hb_serialize_context_t *c, const hb_map_t *layout_variation_idx_map) const + { + TRACE_SERIALIZE (this); + auto snap = c->snapshot (); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + if (!layout_variation_idx_map || layout_variation_idx_map->is_empty ()) return_trace (out); + + unsigned org_idx = (outerIndex << 16) + innerIndex; + if (!layout_variation_idx_map->has (org_idx)) + { + c->revert (snap); + return_trace (nullptr); + } + unsigned new_idx = layout_variation_idx_map->get (org_idx); + out->outerIndex = new_idx >> 16; + out->innerIndex = new_idx & 0xFFFF; + return_trace (out); + } + + void record_variation_index (hb_set_t *layout_variation_indices) const + { + unsigned var_idx = (outerIndex << 16) + innerIndex; + layout_variation_indices->add (var_idx); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -2126,10 +3074,14 @@ struct Device { switch (u.b.format) { +#ifndef HB_NO_HINTING case 1: case 2: case 3: return u.hinting.get_x_delta (font); +#endif +#ifndef HB_NO_VAR case 0x8000: return u.variation.get_x_delta (font, store); +#endif default: return 0; } @@ -2139,9 +3091,13 @@ struct Device switch (u.b.format) { case 1: case 2: case 3: +#ifndef HB_NO_HINTING return u.hinting.get_y_delta (font); +#endif +#ifndef HB_NO_VAR case 0x8000: return u.variation.get_y_delta (font, store); +#endif default: return 0; } @@ -2152,20 +3108,64 @@ struct Device TRACE_SANITIZE (this); if (!u.b.format.sanitize (c)) return_trace (false); switch (u.b.format) { +#ifndef HB_NO_HINTING case 1: case 2: case 3: return_trace (u.hinting.sanitize (c)); +#endif +#ifndef HB_NO_VAR case 0x8000: return_trace (u.variation.sanitize (c)); +#endif default: return_trace (true); } } + Device* copy (hb_serialize_context_t *c, const hb_map_t *layout_variation_idx_map=nullptr) const + { + TRACE_SERIALIZE (this); + switch (u.b.format) { +#ifndef HB_NO_HINTING + case 1: + case 2: + case 3: + return_trace (reinterpret_cast (u.hinting.copy (c))); +#endif +#ifndef HB_NO_VAR + case 0x8000: + return_trace (reinterpret_cast (u.variation.copy (c, layout_variation_idx_map))); +#endif + default: + return_trace (nullptr); + } + } + + void collect_variation_indices (hb_set_t *layout_variation_indices) const + { + switch (u.b.format) { +#ifndef HB_NO_HINTING + case 1: + case 2: + case 3: + return; +#endif +#ifndef HB_NO_VAR + case 0x8000: + u.variation.record_variation_index (layout_variation_indices); + return; +#endif + default: + return; + } + } + protected: union { DeviceHeader b; HintingDevice hinting; +#ifndef HB_NO_VAR VariationDevice variation; +#endif } u; public: DEFINE_SIZE_UNION (6, b); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gdef-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gdef-table.hh index 533c95a41e6..201a6c980fc 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gdef-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gdef-table.hh @@ -41,8 +41,18 @@ namespace OT { * Attachment List Table */ -typedef ArrayOf AttachPoint; /* Array of contour point indices--in - * increasing numerical order */ +/* Array of contour point indices--in increasing numerical order */ +struct AttachPoint : ArrayOf +{ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->serialize (c->serializer, + iter ())); + } +}; struct AttachList { @@ -63,15 +73,36 @@ struct AttachList if (point_count) { - hb_array_t array = points.sub_array (start_offset, point_count); - unsigned int count = array.length; - for (unsigned int i = 0; i < count; i++) - point_array[i] = array[i]; + + points.sub_array (start_offset, point_count) + | hb_sink (hb_array (point_array, *point_count)) + ; } return points.len; } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, attachPoint) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->attachPoint, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -96,6 +127,13 @@ struct AttachList struct CaretValueFormat1 { friend struct CaretValue; + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + return_trace (true); + } private: hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction) const @@ -119,6 +157,13 @@ struct CaretValueFormat1 struct CaretValueFormat2 { friend struct CaretValue; + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + return_trace (true); + } private: hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id) const @@ -153,6 +198,19 @@ struct CaretValueFormat3 font->em_scale_y (coordinate) + (this+deviceTable).get_y_delta (font, var_store); } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->deviceTable.serialize_copy (c->serializer, deviceTable, this, c->serializer->to_bias (out), + hb_serialize_context_t::Head, c->plan->layout_variation_idx_map)); + } + + void collect_variation_indices (hb_set_t *layout_variation_indices) const + { (this+deviceTable).collect_variation_indices (layout_variation_indices); } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -173,9 +231,9 @@ struct CaretValueFormat3 struct CaretValue { hb_position_t get_caret_value (hb_font_t *font, - hb_direction_t direction, - hb_codepoint_t glyph_id, - const VariationStore &var_store) const + hb_direction_t direction, + hb_codepoint_t glyph_id, + const VariationStore &var_store) const { switch (u.format) { case 1: return u.format1.get_caret_value (font, direction); @@ -185,6 +243,32 @@ struct CaretValue } } + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, hb_forward (ds)...)); + case 3: return_trace (c->dispatch (u.format3, hb_forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + + void collect_variation_indices (hb_set_t *layout_variation_indices) const + { + switch (u.format) { + case 1: + case 2: + return; + case 3: + u.format3.collect_variation_indices (layout_variation_indices); + return; + default: return; + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -210,25 +294,45 @@ struct CaretValue struct LigGlyph { - unsigned int get_lig_carets (hb_font_t *font, - hb_direction_t direction, - hb_codepoint_t glyph_id, - const VariationStore &var_store, - unsigned int start_offset, - unsigned int *caret_count /* IN/OUT */, - hb_position_t *caret_array /* OUT */) const + unsigned get_lig_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + const VariationStore &var_store, + unsigned start_offset, + unsigned *caret_count /* IN/OUT */, + hb_position_t *caret_array /* OUT */) const { if (caret_count) { - hb_array_t > array = carets.sub_array (start_offset, caret_count); - unsigned int count = array.length; - for (unsigned int i = 0; i < count; i++) - caret_array[i] = (this+array[i]).get_caret_value (font, direction, glyph_id, var_store); + + carets.sub_array (start_offset, caret_count) + | hb_map (hb_add (this)) + | hb_map ([&] (const CaretValue &value) { return value.get_caret_value (font, direction, glyph_id, var_store); }) + | hb_sink (hb_array (caret_array, *caret_count)) + ; } return carets.len; } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + hb_iter (carets) + | hb_apply (subset_offset_array (c, out->carets, this)) + ; + + return_trace (bool (out->carets)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + for (const OffsetTo& offset : carets.iter ()) + (this+offset).collect_variation_indices (c->layout_variation_indices); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -265,6 +369,38 @@ struct LigCaretList return lig_glyph.get_lig_carets (font, direction, glyph_id, var_store, start_offset, caret_count, caret_array); } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, ligGlyph) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->ligGlyph, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+coverage, ligGlyph) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const LigGlyph& _) { _.collect_variation_indices (c); }) + ; + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -288,6 +424,34 @@ struct MarkGlyphSetsFormat1 bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const { return (this+coverage[set_index]).get_coverage (glyph_id) != NOT_COVERED; } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + bool ret = true; + for (const LOffsetTo& offset : coverage.iter ()) + { + auto *o = out->coverage.serialize_append (c->serializer); + if (unlikely (!o)) + { + ret = false; + break; + } + + //not using o->serialize_subset (c, offset, this, out) here because + //OTS doesn't allow null offset. + //See issue: https://github.com/khaledhosny/ots/issues/172 + c->serializer->push (); + c->dispatch (this+offset); + c->serializer->add_link (*o, c->serializer->pop_pack ()); + } + + return_trace (ret && out->coverage.len); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -296,7 +460,7 @@ struct MarkGlyphSetsFormat1 protected: HBUINT16 format; /* Format identifier--format = 1 */ - ArrayOf > + ArrayOf> coverage; /* Array of long offsets to mark set * coverage tables */ public: @@ -313,6 +477,15 @@ struct MarkGlyphSets } } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + switch (u.format) { + case 1: return_trace (u.format1.subset (c)); + default:return_trace (false); + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -356,7 +529,7 @@ struct GDEF unsigned int get_glyph_class (hb_codepoint_t glyph) const { return (this+glyphClassDef).get_class (glyph); } void get_glyphs_in_class (unsigned int klass, hb_set_t *glyphs) const - { (this+glyphClassDef).add_class (glyphs, klass); } + { (this+glyphClassDef).collect_class (glyphs, klass); } bool has_mark_attachment_types () const { return markAttachClassDef != 0; } unsigned int get_mark_attachment_type (hb_codepoint_t glyph) const @@ -386,7 +559,7 @@ struct GDEF bool has_var_store () const { return version.to_int () >= 0x00010003u && varStore != 0; } const VariationStore &get_var_store () const - { return version.to_int () >= 0x00010003u ? this+varStore : Null(VariationStore); } + { return version.to_int () >= 0x00010003u ? this+varStore : Null (VariationStore); } /* glyph_props is a 16-bit integer where the lower 8-bit have bits representing * glyph class and other bits, and high 8-bit the mark attachment type (if any). @@ -409,15 +582,15 @@ struct GDEF } } - HB_INTERNAL bool is_blacklisted (hb_blob_t *blob, + HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, hb_face_t *face) const; struct accelerator_t { void init (hb_face_t *face) { - this->table = hb_sanitize_context_t().reference_table (face); - if (unlikely (this->table->is_blacklisted (this->table.get_blob (), face))) + this->table = hb_sanitize_context_t ().reference_table (face); + if (unlikely (this->table->is_blocklisted (this->table.get_blob (), face))) { hb_blob_destroy (this->table.get_blob ()); this->table = hb_blob_get_empty (); @@ -436,24 +609,66 @@ struct GDEF (version.to_int () >= 0x00010003u ? varStore.static_size : 0); } + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { (this+ligCaretList).collect_variation_indices (c); } + + void remap_layout_variation_indices (const hb_set_t *layout_variation_indices, + hb_map_t *layout_variation_idx_map /* OUT */) const + { + if (version.to_int () < 0x00010003u || !varStore) return; + if (layout_variation_indices->is_empty ()) return; + + unsigned new_major = 0, new_minor = 0; + unsigned last_major = (layout_variation_indices->get_min ()) >> 16; + for (unsigned idx : layout_variation_indices->iter ()) + { + uint16_t major = idx >> 16; + if (major >= (this+varStore).get_sub_table_count ()) break; + if (major != last_major) + { + new_minor = 0; + ++new_major; + } + + unsigned new_idx = (new_major << 16) + new_minor; + layout_variation_idx_map->set (idx, new_idx); + ++new_minor; + last_major = major; + } + } + bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - struct GDEF *out = c->serializer->embed (*this); + auto *out = c->serializer->embed (*this); if (unlikely (!out)) return_trace (false); - out->glyphClassDef.serialize_subset (c, this+glyphClassDef, out); - out->attachList.set (0);//TODO(subset) serialize_subset (c, this+attachList, out); - out->ligCaretList.set (0);//TODO(subset) serialize_subset (c, this+ligCaretList, out); - out->markAttachClassDef.serialize_subset (c, this+markAttachClassDef, out); + bool subset_glyphclassdef = out->glyphClassDef.serialize_subset (c, glyphClassDef, this); + bool subset_attachlist = out->attachList.serialize_subset (c, attachList, this); + bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this); + bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this); + bool subset_markglyphsetsdef = true; if (version.to_int () >= 0x00010002u) - out->markGlyphSetsDef.set (0);// TODO(subset) serialize_subset (c, this+markGlyphSetsDef, out); + { + subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this); + if (!subset_markglyphsetsdef && + version.to_int () == 0x00010002u) + out->version.minor = 0; + } + bool subset_varstore = true; if (version.to_int () >= 0x00010003u) - out->varStore.set (0);// TODO(subset) serialize_subset (c, this+varStore, out); + { + subset_varstore = out->varStore.serialize_subset (c, varStore, this); + if (!subset_varstore && version.to_int () == 0x00010003u) + out->version.minor = 2; + } - return_trace (true); + return_trace (subset_glyphclassdef || subset_attachlist || + subset_ligcaretlist || subset_markattachclassdef || + (out->version.to_int () >= 0x00010002u && subset_markglyphsetsdef) || + (out->version.to_int () >= 0x00010003u && subset_varstore)); } bool sanitize (hb_sanitize_context_t *c) const diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gpos-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gpos-table.hh index 2a516500581..eddae150e5f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gpos-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gpos-table.hh @@ -34,6 +34,11 @@ namespace OT { +struct MarkArray; +static void Markclass_closure_and_remap_indexes (const Coverage &mark_coverage, + const MarkArray &mark_array, + const hb_set_t &glyphset, + hb_map_t* klass_mapping /* INOUT */); /* buffer **position** var allocations */ #define attach_chain() var.i16[0] /* glyph to which this attaches to, relative to current glyphs; negative for going back, positive for forward. */ @@ -74,14 +79,14 @@ struct ValueFormat : HBUINT16 /* All fields are options. Only those available advance the value pointer. */ #if 0 - HBINT16 xPlacement; /* Horizontal adjustment for + HBINT16 xPlacement; /* Horizontal adjustment for * placement--in design units */ - HBINT16 yPlacement; /* Vertical adjustment for + HBINT16 yPlacement; /* Vertical adjustment for * placement--in design units */ - HBINT16 xAdvance; /* Horizontal adjustment for + HBINT16 xAdvance; /* Horizontal adjustment for * advance--in design units (only used * for horizontal writing) */ - HBINT16 yAdvance; /* Vertical adjustment for advance--in + HBINT16 yAdvance; /* Vertical adjustment for advance--in * design units (only used for vertical * writing) */ OffsetTo xPlaDevice; /* Offset to Device table for @@ -101,10 +106,10 @@ struct ValueFormat : HBUINT16 unsigned int get_len () const { return hb_popcount ((unsigned int) *this); } unsigned int get_size () const { return get_len () * Value::static_size; } - bool apply_value (hb_ot_apply_context_t *c, - const void *base, - const Value *values, - hb_glyph_position_t &glyph_pos) const + bool apply_value (hb_ot_apply_context_t *c, + const void *base, + const Value *values, + hb_glyph_position_t &glyph_pos) const { bool ret = false; unsigned int format = *this; @@ -155,6 +160,60 @@ struct ValueFormat : HBUINT16 return ret; } + void serialize_copy (hb_serialize_context_t *c, const void *base, + const Value *values, const hb_map_t *layout_variation_idx_map) const + { + unsigned int format = *this; + if (!format) return; + + if (format & xPlacement) c->copy (*values++); + if (format & yPlacement) c->copy (*values++); + if (format & xAdvance) c->copy (*values++); + if (format & yAdvance) c->copy (*values++); + + if (format & xPlaDevice) copy_device (c, base, values++, layout_variation_idx_map); + if (format & yPlaDevice) copy_device (c, base, values++, layout_variation_idx_map); + if (format & xAdvDevice) copy_device (c, base, values++, layout_variation_idx_map); + if (format & yAdvDevice) copy_device (c, base, values++, layout_variation_idx_map); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *base, + const hb_array_t& values) const + { + unsigned format = *this; + unsigned i = 0; + if (format & xPlacement) i++; + if (format & yPlacement) i++; + if (format & xAdvance) i++; + if (format & yAdvance) i++; + if (format & xPlaDevice) + { + (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices); + i++; + } + + if (format & ValueFormat::yPlaDevice) + { + (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices); + i++; + } + + if (format & ValueFormat::xAdvDevice) + { + + (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices); + i++; + } + + if (format & ValueFormat::yAdvDevice) + { + + (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices); + i++; + } + } + private: bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const { @@ -173,18 +232,42 @@ struct ValueFormat : HBUINT16 return true; } - static OffsetTo& get_device (Value* value) - { return *CastP > (value); } - static const OffsetTo& get_device (const Value* value, bool *worked=nullptr) + static inline OffsetTo& get_device (Value* value) + { + return *static_cast *> (value); + } + static inline const OffsetTo& get_device (const Value* value, bool *worked=nullptr) { if (worked) *worked |= bool (*value); - return *CastP > (value); + return *static_cast *> (value); + } + + bool copy_device (hb_serialize_context_t *c, const void *base, + const Value *src_value, const hb_map_t *layout_variation_idx_map) const + { + Value *dst_value = c->copy (*src_value); + + if (!dst_value) return false; + if (*dst_value == 0) return true; + + *dst_value = 0; + c->push (); + if ((base + get_device (src_value)).copy (c, layout_variation_idx_map)) + { + c->add_link (*dst_value, c->pop_pack ()); + return true; + } + else + { + c->pop_discard (); + return false; + } } - static const HBINT16& get_short (const Value* value, bool *worked=nullptr) + static inline const HBINT16& get_short (const Value* value, bool *worked=nullptr) { if (worked) *worked |= bool (*value); - return *CastP (value); + return *reinterpret_cast (value); } public: @@ -236,6 +319,13 @@ struct ValueFormat : HBUINT16 } }; +template +static void SinglePos_serialize (hb_serialize_context_t *c, + const void *src, + Iterator it, + ValueFormat valFormat, + const hb_map_t *layout_variation_idx_map); + struct AnchorFormat1 { @@ -253,6 +343,12 @@ struct AnchorFormat1 return_trace (c->check_struct (this)); } + AnchorFormat1* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + protected: HBUINT16 format; /* Format identifier--format = 1 */ FWORD xCoordinate; /* Horizontal value--in design units */ @@ -267,6 +363,13 @@ struct AnchorFormat2 float *x, float *y) const { hb_font_t *font = c->font; + +#ifdef HB_NO_HINTING + *x = font->em_fscale_x (xCoordinate); + *y = font->em_fscale_y (yCoordinate); + return; +#endif + unsigned int x_ppem = font->x_ppem; unsigned int y_ppem = font->y_ppem; hb_position_t cx = 0, cy = 0; @@ -284,6 +387,12 @@ struct AnchorFormat2 return_trace (c->check_struct (this)); } + AnchorFormat2* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + protected: HBUINT16 format; /* Format identifier--format = 2 */ FWORD xCoordinate; /* Horizontal value--in design units */ @@ -314,6 +423,26 @@ struct AnchorFormat3 return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this)); } + AnchorFormat3* copy (hb_serialize_context_t *c, + const hb_map_t *layout_variation_idx_map) const + { + TRACE_SERIALIZE (this); + if (!layout_variation_idx_map) return_trace (nullptr); + + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + + out->xDeviceTable.serialize_copy (c, xDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map); + out->yDeviceTable.serialize_copy (c, yDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map); + return_trace (out); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + (this+xDeviceTable).collect_variation_indices (c->layout_variation_indices); + (this+yDeviceTable).collect_variation_indices (c->layout_variation_indices); + } + protected: HBUINT16 format; /* Format identifier--format = 3 */ FWORD xCoordinate; /* Horizontal value--in design units */ @@ -356,6 +485,29 @@ struct Anchor } } + Anchor* copy (hb_serialize_context_t *c, const hb_map_t *layout_variation_idx_map) const + { + TRACE_SERIALIZE (this); + switch (u.format) { + case 1: return_trace (reinterpret_cast (u.format1.copy (c))); + case 2: return_trace (reinterpret_cast (u.format2.copy (c))); + case 3: return_trace (reinterpret_cast (u.format3.copy (c, layout_variation_idx_map))); + default:return_trace (nullptr); + } + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + switch (u.format) { + case 1: case 2: + return; + case 3: + u.format3.collect_variation_indices (c); + return; + default: return; + } + } + protected: union { HBUINT16 format; /* Format identifier */ @@ -374,11 +526,46 @@ struct AnchorMatrix unsigned int cols, bool *found) const { *found = false; - if (unlikely (row >= rows || col >= cols)) return Null(Anchor); + if (unlikely (row >= rows || col >= cols)) return Null (Anchor); *found = !matrixZ[row * cols + col].is_null (); return this+matrixZ[row * cols + col]; } + template + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + Iterator index_iter) const + { + for (unsigned i : index_iter) + (this+matrixZ[i]).collect_variation_indices (c); + } + + template + bool serialize (hb_serialize_context_t *c, + unsigned num_rows, + AnchorMatrix const *offset_matrix, + const hb_map_t *layout_variation_idx_map, + Iterator index_iter) + { + TRACE_SERIALIZE (this); + if (!index_iter) return_trace (false); + if (unlikely (!c->extend_min ((*this)))) return_trace (false); + + this->rows = num_rows; + for (const unsigned i : index_iter) + { + auto *offset = c->embed (offset_matrix->matrixZ[i]); + if (!offset) return_trace (false); + offset->serialize_copy (c, offset_matrix->matrixZ[i], + offset_matrix, c->to_bias (this), + hb_serialize_context_t::Head, + layout_variation_idx_map); + } + + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const { TRACE_SANITIZE (this); @@ -392,8 +579,7 @@ struct AnchorMatrix } HBUINT16 rows; /* Number of rows */ - protected: - UnsizedArrayOf > + UnsizedArrayOf> matrixZ; /* Matrix of offsets to Anchor tables-- * from beginning of AnchorMatrix table */ public: @@ -405,12 +591,34 @@ struct MarkRecord { friend struct MarkArray; + unsigned get_class () const { return (unsigned) klass; } bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && markAnchor.sanitize (c, base)); } + MarkRecord *copy (hb_serialize_context_t *c, + const void *src_base, + unsigned dst_bias, + const hb_map_t *klass_mapping, + const hb_map_t *layout_variation_idx_map) const + { + TRACE_SERIALIZE (this); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + + out->klass = klass_mapping->get (klass); + out->markAnchor.serialize_copy (c, markAnchor, src_base, dst_bias, hb_serialize_context_t::Head, layout_variation_idx_map); + return_trace (out); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *src_base) const + { + (src_base+markAnchor).collect_variation_indices (c); + } + protected: HBUINT16 klass; /* Class defined for this mark */ OffsetTo @@ -446,8 +654,8 @@ struct MarkArray : ArrayOf /* Array of MarkRecords--in Coverage ord glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y); hb_glyph_position_t &o = buffer->cur_pos(); - o.x_offset = round (base_x - mark_x); - o.y_offset = round (base_y - mark_y); + o.x_offset = roundf (base_x - mark_x); + o.y_offset = roundf (base_y - mark_y); o.attach_type() = ATTACH_TYPE_MARK; o.attach_chain() = (int) glyph_pos - (int) buffer->idx; buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; @@ -456,6 +664,21 @@ struct MarkArray : ArrayOf /* Array of MarkRecords--in Coverage ord return_trace (true); } + template + bool serialize (hb_serialize_context_t *c, + const hb_map_t *klass_mapping, + const hb_map_t *layout_variation_idx_map, + const void *base, + Iterator it) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->check_assign (len, it.len ()))) return_trace (false); + c->copy_all (it, base, c->to_bias (this), klass_mapping, layout_variation_idx_map); + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -471,8 +694,22 @@ struct SinglePosFormat1 bool intersects (const hb_set_t *glyphs) const { return (this+coverage).intersects (glyphs); } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if (!valueFormat.has_device ()) return; + + auto it = + + hb_iter (this+coverage) + | hb_filter (c->glyph_set) + ; + + if (!it) return; + valueFormat.collect_variation_indices (c, this, values.as_array (valueFormat.get_len ())); + } + void collect_glyphs (hb_collect_glyphs_context_t *c) const - { if (unlikely (!(this+coverage).add_coverage (c->input))) return; } + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } const Coverage &get_coverage () const { return this+coverage; } @@ -489,11 +726,48 @@ struct SinglePosFormat1 return_trace (true); } + template + void serialize (hb_serialize_context_t *c, + const void *src, + Iterator it, + ValueFormat valFormat, + const hb_map_t *layout_variation_idx_map) + { + auto out = c->extend_min (*this); + if (unlikely (!out)) return; + if (unlikely (!c->check_assign (valueFormat, valFormat))) return; + + + it + | hb_map (hb_second) + | hb_apply ([&] (hb_array_t _) + { valFormat.serialize_copy (c, src, &_, layout_variation_idx_map); }) + ; + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize (c, this).serialize (c, glyphs); + } + bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_iter (this+coverage) + | hb_filter (glyphset) + | hb_map_retains_sorting (glyph_map) + | hb_zip (hb_repeat (values.as_array (valueFormat.get_len ()))) + ; + + bool ret = bool (it); + SinglePos_serialize (c->serializer, this, it, valueFormat, c->plan->layout_variation_idx_map); + return_trace (ret); } bool sanitize (hb_sanitize_context_t *c) const @@ -523,8 +797,29 @@ struct SinglePosFormat2 bool intersects (const hb_set_t *glyphs) const { return (this+coverage).intersects (glyphs); } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if (!valueFormat.has_device ()) return; + + auto it = + + hb_zip (this+coverage, hb_range ((unsigned) valueCount)) + | hb_filter (c->glyph_set, hb_first) + ; + + if (!it) return; + + unsigned sub_length = valueFormat.get_len (); + const hb_array_t values_array = values.as_array (valueCount * sub_length); + + for (unsigned i : + it + | hb_map (hb_second)) + valueFormat.collect_variation_indices (c, this, values_array.sub_array (i * sub_length, sub_length)); + + } + void collect_glyphs (hb_collect_glyphs_context_t *c) const - { if (unlikely (!(this+coverage).add_coverage (c->input))) return; } + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } const Coverage &get_coverage () const { return this+coverage; } @@ -545,11 +840,56 @@ struct SinglePosFormat2 return_trace (true); } + template + void serialize (hb_serialize_context_t *c, + const void *src, + Iterator it, + ValueFormat valFormat, + const hb_map_t *layout_variation_idx_map) + { + auto out = c->extend_min (*this); + if (unlikely (!out)) return; + if (unlikely (!c->check_assign (valueFormat, valFormat))) return; + if (unlikely (!c->check_assign (valueCount, it.len ()))) return; + + + it + | hb_map (hb_second) + | hb_apply ([&] (hb_array_t _) + { valFormat.serialize_copy (c, src, &_, layout_variation_idx_map); }) + ; + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize (c, this).serialize (c, glyphs); + } + bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + unsigned sub_length = valueFormat.get_len (); + auto values_array = values.as_array (valueCount * sub_length); + + auto it = + + hb_zip (this+coverage, hb_range ((unsigned) valueCount)) + | hb_filter (glyphset, hb_first) + | hb_map_retains_sorting ([&] (const hb_pair_t& _) + { + return hb_pair (glyph_map[_.first], + values_array.sub_array (_.second * sub_length, + sub_length)); + }) + ; + + bool ret = bool (it); + SinglePos_serialize (c->serializer, this, it, valueFormat, c->plan->layout_variation_idx_map); + return_trace (ret); } bool sanitize (hb_sanitize_context_t *c) const @@ -576,14 +916,52 @@ struct SinglePosFormat2 struct SinglePos { - template - typename context_t::return_t dispatch (context_t *c) const + template + unsigned get_format (Iterator glyph_val_iter_pairs) + { + hb_array_t first_val_iter = hb_second (*glyph_val_iter_pairs); + + for (const auto iter : glyph_val_iter_pairs) + for (const auto _ : hb_zip (iter.second, first_val_iter)) + if (_.first != _.second) + return 2; + + return 1; + } + + + template + void serialize (hb_serialize_context_t *c, + const void *src, + Iterator glyph_val_iter_pairs, + ValueFormat valFormat, + const hb_map_t *layout_variation_idx_map) + { + if (unlikely (!c->extend_min (u.format))) return; + unsigned format = 2; + + if (glyph_val_iter_pairs) format = get_format (glyph_val_iter_pairs); + + u.format = format; + switch (u.format) { + case 1: u.format1.serialize (c, src, glyph_val_iter_pairs, valFormat, layout_variation_idx_map); + return; + case 2: u.format2.serialize (c, src, glyph_val_iter_pairs, valFormat, layout_variation_idx_map); + return; + default:return; + } + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); - case 2: return_trace (c->dispatch (u.format2)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -596,13 +974,64 @@ struct SinglePos } u; }; +template +static void +SinglePos_serialize (hb_serialize_context_t *c, + const void *src, + Iterator it, + ValueFormat valFormat, + const hb_map_t *layout_variation_idx_map) +{ c->start_embed ()->serialize (c, src, it, valFormat, layout_variation_idx_map); } + struct PairValueRecord { friend struct PairSet; + int cmp (hb_codepoint_t k) const + { return secondGlyph.cmp (k); } + + struct serialize_closure_t + { + const void *base; + const ValueFormat *valueFormats; + unsigned len1; /* valueFormats[0].get_len() */ + const hb_map_t *glyph_map; + const hb_map_t *layout_variation_idx_map; + }; + + bool serialize (hb_serialize_context_t *c, + serialize_closure_t *closure) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (*this); + if (unlikely (!c->extend_min (out))) return_trace (false); + + out->secondGlyph = (*closure->glyph_map)[secondGlyph]; + + closure->valueFormats[0].serialize_copy (c, closure->base, &values[0], closure->layout_variation_idx_map); + closure->valueFormats[1].serialize_copy (c, closure->base, &values[closure->len1], closure->layout_variation_idx_map); + + return_trace (true); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const ValueFormat *valueFormats, + const void *base) const + { + unsigned record1_len = valueFormats[0].get_len (); + unsigned record2_len = valueFormats[1].get_len (); + const hb_array_t values_array = values.as_array (record1_len + record2_len); + + if (valueFormats[0].has_device ()) + valueFormats[0].collect_variation_indices (c, base, values_array.sub_array (0, record1_len)); + + if (valueFormats[1].has_device ()) + valueFormats[1].collect_variation_indices (c, base, values_array.sub_array (record1_len, record2_len)); + } + protected: - GlyphID secondGlyph; /* GlyphID of second glyph in the + HBGlyphID secondGlyph; /* GlyphID of second glyph in the * pair--first glyph is listed in the * Coverage table */ ValueRecord values; /* Positioning data for the first glyph @@ -616,7 +1045,7 @@ struct PairSet friend struct PairPosFormat1; bool intersects (const hb_set_t *glyphs, - const ValueFormat *valueFormats) const + const ValueFormat *valueFormats) const { unsigned int len1 = valueFormats[0].get_len (); unsigned int len2 = valueFormats[1].get_len (); @@ -634,7 +1063,7 @@ struct PairSet } void collect_glyphs (hb_collect_glyphs_context_t *c, - const ValueFormat *valueFormats) const + const ValueFormat *valueFormats) const { unsigned int len1 = valueFormats[0].get_len (); unsigned int len2 = valueFormats[1].get_len (); @@ -644,9 +1073,27 @@ struct PairSet c->input->add_array (&record->secondGlyph, len, record_size); } + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const ValueFormat *valueFormats) const + { + unsigned len1 = valueFormats[0].get_len (); + unsigned len2 = valueFormats[1].get_len (); + unsigned record_size = HBUINT16::static_size * (1 + len1 + len2); + + const PairValueRecord *record = &firstPairValueRecord; + unsigned count = len; + for (unsigned i = 0; i < count; i++) + { + if (c->glyph_set->has (record->secondGlyph)) + { record->collect_variation_indices (c, valueFormats, this); } + + record = &StructAtOffset (record, record_size); + } + } + bool apply (hb_ot_apply_context_t *c, - const ValueFormat *valueFormats, - unsigned int pos) const + const ValueFormat *valueFormats, + unsigned int pos) const { TRACE_APPLY (this); hb_buffer_t *buffer = c->buffer; @@ -654,41 +1101,66 @@ struct PairSet unsigned int len2 = valueFormats[1].get_len (); unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2); - unsigned int count = len; + const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint, + &firstPairValueRecord, + len, + record_size); + if (record) + { + /* Note the intentional use of "|" instead of short-circuit "||". */ + if (valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos()) | + valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos])) + buffer->unsafe_to_break (buffer->idx, pos + 1); + if (len2) + pos++; + buffer->idx = pos; + return_trace (true); + } + return_trace (false); + } - /* Hand-coded bsearch. */ - if (unlikely (!count)) - return_trace (false); - hb_codepoint_t x = buffer->info[pos].codepoint; - int min = 0, max = (int) count - 1; - while (min <= max) + bool subset (hb_subset_context_t *c, + const ValueFormat valueFormats[2]) const + { + TRACE_SUBSET (this); + auto snap = c->serializer->snapshot (); + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->len = 0; + + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + unsigned len1 = valueFormats[0].get_len (); + unsigned len2 = valueFormats[1].get_len (); + unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2); + + PairValueRecord::serialize_closure_t closure = { - int mid = ((unsigned int) min + (unsigned int) max) / 2; - const PairValueRecord *record = &StructAtOffset (&firstPairValueRecord, record_size * mid); - hb_codepoint_t mid_x = record->secondGlyph; - if (x < mid_x) - max = mid - 1; - else if (x > mid_x) - min = mid + 1; - else - { - /* Note the intentional use of "|" instead of short-circuit "||". */ - if (valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos()) | - valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos])) - buffer->unsafe_to_break (buffer->idx, pos + 1); - if (len2) - pos++; - buffer->idx = pos; - return_trace (true); - } + this, + valueFormats, + len1, + &glyph_map, + c->plan->layout_variation_idx_map + }; + + const PairValueRecord *record = &firstPairValueRecord; + unsigned count = len, num = 0; + for (unsigned i = 0; i < count; i++) + { + if (glyphset.has (record->secondGlyph) + && record->serialize (c->serializer, &closure)) num++; + record = &StructAtOffset (record, record_size); } - return_trace (false); + out->len = num; + if (!num) c->serializer->revert (snap); + return_trace (num); } struct sanitize_closure_t { - const void *base; const ValueFormat *valueFormats; unsigned int len1; /* valueFormats[0].get_len() */ unsigned int stride; /* 1 + len1 + len2 */ @@ -705,8 +1177,8 @@ struct PairSet unsigned int count = len; const PairValueRecord *record = &firstPairValueRecord; - return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride) && - closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride)); + return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) && + closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride)); } protected: @@ -722,21 +1194,37 @@ struct PairPosFormat1 { bool intersects (const hb_set_t *glyphs) const { - unsigned int count = pairSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (glyphs->has (iter.get_glyph ()) && - (this+pairSet[iter.get_coverage ()]).intersects (glyphs, valueFormat)) - return true; - } - return false; + return + + hb_zip (this+coverage, pairSet) + | hb_filter (*glyphs, hb_first) + | hb_map (hb_second) + | hb_map ([glyphs, this] (const OffsetTo &_) + { return (this+_).intersects (glyphs, valueFormat); }) + | hb_any + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if ((!valueFormat[0].has_device ()) && (!valueFormat[1].has_device ())) return; + + auto it = + + hb_zip (this+coverage, pairSet) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + if (!it) return; + + it + | hb_map (hb_add (this)) + | hb_apply ([&] (const PairSet& _) { _.collect_variation_indices (c, valueFormat); }) + ; } void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+coverage).add_coverage (c->input))) return; + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; unsigned int count = pairSet.len; for (unsigned int i = 0; i < count; i++) (this+pairSet[i]).collect_glyphs (c, valueFormat); @@ -761,8 +1249,43 @@ struct PairPosFormat1 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + out->valueFormat[0] = valueFormat[0]; + out->valueFormat[1] = valueFormat[1]; + + hb_sorted_vector_t new_coverage; + + + hb_zip (this+coverage, pairSet) + | hb_filter (glyphset, hb_first) + | hb_filter ([this, c, out] (const OffsetTo& _) + { + auto *o = out->pairSet.serialize_append (c->serializer); + if (unlikely (!o)) return false; + auto snap = c->serializer->snapshot (); + bool ret = o->serialize_subset (c, _, this, valueFormat); + if (!ret) + { + out->pairSet.pop (); + c->serializer->revert (snap); + } + return ret; + }, + hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + out->coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ()); + + return_trace (bool (new_coverage)); } bool sanitize (hb_sanitize_context_t *c) const @@ -775,7 +1298,6 @@ struct PairPosFormat1 unsigned int len2 = valueFormat[1].get_len (); PairSet::sanitize_closure_t closure = { - this, valueFormat, len1, 1 + len1 + len2 @@ -810,10 +1332,43 @@ struct PairPosFormat2 (this+classDef2).intersects (glyphs); } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if ((!valueFormat1.has_device ()) && (!valueFormat2.has_device ())) return; + + hb_set_t class1_set, class2_set; + for (const unsigned cp : c->glyph_set->iter ()) + { + unsigned klass1 = (this+classDef1).get (cp); + unsigned klass2 = (this+classDef2).get (cp); + class1_set.add (klass1); + class2_set.add (klass2); + } + + if (class1_set.is_empty () || class2_set.is_empty ()) return; + + unsigned len1 = valueFormat1.get_len (); + unsigned len2 = valueFormat2.get_len (); + const hb_array_t values_array = values.as_array ((unsigned)class1Count * (unsigned) class2Count * (len1 + len2)); + for (const unsigned class1_idx : class1_set.iter ()) + { + for (const unsigned class2_idx : class2_set.iter ()) + { + unsigned start_offset = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2); + if (valueFormat1.has_device ()) + valueFormat1.collect_variation_indices (c, this, values_array.sub_array (start_offset, len1)); + + if (valueFormat2.has_device ()) + valueFormat2.collect_variation_indices (c, this, values_array.sub_array (start_offset+len1, len2)); + } + } + } + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+coverage).add_coverage (c->input))) return; - if (unlikely (!(this+classDef2).add_coverage (c->input))) return; + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + if (unlikely (!(this+classDef2).collect_coverage (c->input))) return; } const Coverage &get_coverage () const { return this+coverage; } @@ -853,8 +1408,50 @@ struct PairPosFormat2 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + out->valueFormat1 = valueFormat1; + out->valueFormat2 = valueFormat2; + + hb_map_t klass1_map; + out->classDef1.serialize_subset (c, classDef1, this, &klass1_map); + out->class1Count = klass1_map.get_population (); + + hb_map_t klass2_map; + out->classDef2.serialize_subset (c, classDef2, this, &klass2_map); + out->class2Count = klass2_map.get_population (); + + unsigned len1 = valueFormat1.get_len (); + unsigned len2 = valueFormat2.get_len (); + + + hb_range ((unsigned) class1Count) + | hb_filter (klass1_map) + | hb_apply ([&] (const unsigned class1_idx) + { + + hb_range ((unsigned) class2Count) + | hb_filter (klass2_map) + | hb_apply ([&] (const unsigned class2_idx) + { + unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2); + valueFormat1.serialize_copy (c->serializer, this, &values[idx], c->plan->layout_variation_idx_map); + valueFormat2.serialize_copy (c->serializer, this, &values[idx + len1], c->plan->layout_variation_idx_map); + }) + ; + }) + ; + + const hb_set_t &glyphset = *c->plan->_glyphset_gsub; + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_iter (this+coverage) + | hb_filter (glyphset) + | hb_map_retains_sorting (glyph_map) + ; + + out->coverage.serialize (c->serializer, out).serialize (c->serializer, it); + return_trace (out->class1Count && out->class2Count && bool (it)); } bool sanitize (hb_sanitize_context_t *c) const @@ -909,14 +1506,14 @@ struct PairPosFormat2 struct PairPos { - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); - case 2: return_trace (c->dispatch (u.format2)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -940,6 +1537,27 @@ struct EntryExitRecord return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base)); } + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *src_base) const + { + (src_base+entryAnchor).collect_variation_indices (c); + (src_base+exitAnchor).collect_variation_indices (c); + } + + EntryExitRecord* copy (hb_serialize_context_t *c, + const void *src_base, + const void *dst_base, + const hb_map_t *layout_variation_idx_map) const + { + TRACE_SERIALIZE (this); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + + out->entryAnchor.serialize_copy (c, entryAnchor, src_base, c->to_bias (dst_base), hb_serialize_context_t::Head, layout_variation_idx_map); + out->exitAnchor.serialize_copy (c, exitAnchor, src_base, c->to_bias (dst_base), hb_serialize_context_t::Head, layout_variation_idx_map); + return_trace (out); + } + protected: OffsetTo entryAnchor; /* Offset to EntryAnchor table--from @@ -961,8 +1579,19 @@ struct CursivePosFormat1 bool intersects (const hb_set_t *glyphs) const { return (this+coverage).intersects (glyphs); } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+coverage, entryExitRecord) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const EntryExitRecord& record) { record.collect_variation_indices (c, this); }) + ; + } + void collect_glyphs (hb_collect_glyphs_context_t *c) const - { if (unlikely (!(this+coverage).add_coverage (c->input))) return; } + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } const Coverage &get_coverage () const { return this+coverage; } @@ -995,32 +1624,32 @@ struct CursivePosFormat1 /* Main-direction adjustment */ switch (c->direction) { case HB_DIRECTION_LTR: - pos[i].x_advance = round (exit_x) + pos[i].x_offset; + pos[i].x_advance = roundf (exit_x) + pos[i].x_offset; - d = round (entry_x) + pos[j].x_offset; + d = roundf (entry_x) + pos[j].x_offset; pos[j].x_advance -= d; pos[j].x_offset -= d; break; case HB_DIRECTION_RTL: - d = round (exit_x) + pos[i].x_offset; + d = roundf (exit_x) + pos[i].x_offset; pos[i].x_advance -= d; pos[i].x_offset -= d; - pos[j].x_advance = round (entry_x) + pos[j].x_offset; + pos[j].x_advance = roundf (entry_x) + pos[j].x_offset; break; case HB_DIRECTION_TTB: - pos[i].y_advance = round (exit_y) + pos[i].y_offset; + pos[i].y_advance = roundf (exit_y) + pos[i].y_offset; - d = round (entry_y) + pos[j].y_offset; + d = roundf (entry_y) + pos[j].y_offset; pos[j].y_advance -= d; pos[j].y_offset -= d; break; case HB_DIRECTION_BTT: - d = round (exit_y) + pos[i].y_offset; + d = roundf (exit_y) + pos[i].y_offset; pos[i].y_advance -= d; pos[i].y_offset -= d; - pos[j].y_advance = round (entry_y); + pos[j].y_advance = roundf (entry_y); break; case HB_DIRECTION_INVALID: default: @@ -1063,15 +1692,58 @@ struct CursivePosFormat1 else pos[child].x_offset = x_offset; + /* If parent was attached to child, break them free. + * https://github.com/harfbuzz/harfbuzz/issues/2469 + */ + if (unlikely (pos[parent].attach_chain() == -pos[child].attach_chain())) + pos[parent].attach_chain() = 0; + buffer->idx++; return_trace (true); } + template + void serialize (hb_serialize_context_t *c, + Iterator it, + const void *src_base, + const hb_map_t *layout_variation_idx_map) + { + if (unlikely (!c->extend_min ((*this)))) return; + this->format = 1; + this->entryExitRecord.len = it.len (); + + for (const EntryExitRecord& entry_record : + it + | hb_map (hb_second)) + c->copy (entry_record, src_base, this, layout_variation_idx_map); + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize (c, this).serialize (c, glyphs); + } + bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out)) return_trace (false); + + auto it = + + hb_zip (this+coverage, entryExitRecord) + | hb_filter (glyphset, hb_first) + | hb_map_retains_sorting ([&] (hb_pair_t p) -> hb_pair_t + { return hb_pair (glyph_map[p.first], p.second);}) + ; + + bool ret = bool (it); + out->serialize (c->serializer, it, this, c->plan->layout_variation_idx_map); + return_trace (ret); } bool sanitize (hb_sanitize_context_t *c) const @@ -1094,13 +1766,13 @@ struct CursivePosFormat1 struct CursivePos { - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -1118,16 +1790,73 @@ typedef AnchorMatrix BaseArray; /* base-major-- * mark-minor-- * ordered by class--zero-based. */ +static void Markclass_closure_and_remap_indexes (const Coverage &mark_coverage, + const MarkArray &mark_array, + const hb_set_t &glyphset, + hb_map_t* klass_mapping /* INOUT */) +{ + hb_set_t orig_classes; + + + hb_zip (mark_coverage, mark_array) + | hb_filter (glyphset, hb_first) + | hb_map (hb_second) + | hb_map (&MarkRecord::get_class) + | hb_sink (orig_classes) + ; + + unsigned idx = 0; + for (auto klass : orig_classes.iter ()) + { + if (klass_mapping->has (klass)) continue; + klass_mapping->set (klass, idx); + idx++; + } +} + struct MarkBasePosFormat1 { bool intersects (const hb_set_t *glyphs) const - { return (this+markCoverage).intersects (glyphs) && - (this+baseCoverage).intersects (glyphs); } + { + return (this+markCoverage).intersects (glyphs) && + (this+baseCoverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping); + + unsigned basecount = (this+baseArray).rows; + auto base_iter = + + hb_zip (this+baseCoverage, hb_range (basecount)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + hb_sorted_vector_t base_indexes; + for (const unsigned row : base_iter) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (base_indexes) + ; + } + (this+baseArray).collect_variation_indices (c, base_indexes.iter ()); + } void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+markCoverage).add_coverage (c->input))) return; - if (unlikely (!(this+baseCoverage).add_coverage (c->input))) return; + if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return; + if (unlikely (!(this+baseCoverage).collect_coverage (c->input))) return; } const Coverage &get_coverage () const { return this+markCoverage; } @@ -1175,8 +1904,70 @@ struct MarkBasePosFormat1 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping); + + if (!klass_mapping.get_population ()) return_trace (false); + out->classCount = klass_mapping.get_population (); + + auto mark_iter = + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (glyphset, hb_first) + ; + + hb_sorted_vector_t new_coverage; + + mark_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->markCoverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + out->markArray.serialize (c->serializer, out) + .serialize (c->serializer, &klass_mapping, c->plan->layout_variation_idx_map, &(this+markArray), + mark_iter + | hb_map (hb_second)); + + unsigned basecount = (this+baseArray).rows; + auto base_iter = + + hb_zip (this+baseCoverage, hb_range (basecount)) + | hb_filter (glyphset, hb_first) + ; + + new_coverage.reset (); + + base_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->baseCoverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + hb_sorted_vector_t base_indexes; + for (const unsigned row : + base_iter + | hb_map (hb_second)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (base_indexes) + ; + } + out->baseArray.serialize (c->serializer, out) + .serialize (c->serializer, base_iter.len (), &(this+baseArray), c->plan->layout_variation_idx_map, base_indexes.iter ()); + + return_trace (true); } bool sanitize (hb_sanitize_context_t *c) const @@ -1210,13 +2001,13 @@ struct MarkBasePosFormat1 struct MarkBasePos { - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -1242,13 +2033,53 @@ typedef OffsetListOf LigatureArray; struct MarkLigPosFormat1 { bool intersects (const hb_set_t *glyphs) const - { return (this+markCoverage).intersects (glyphs) && - (this+ligatureCoverage).intersects (glyphs); } + { + return (this+markCoverage).intersects (glyphs) && + (this+ligatureCoverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping); + + unsigned ligcount = (this+ligatureArray).len; + auto lig_iter = + + hb_zip (this+ligatureCoverage, hb_range (ligcount)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + const LigatureArray& lig_array = this+ligatureArray; + for (const unsigned i : lig_iter) + { + hb_sorted_vector_t lig_indexes; + unsigned row_count = lig_array[i].rows; + for (unsigned row : + hb_range (row_count)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (lig_indexes) + ; + } + + lig_array[i].collect_variation_indices (c, lig_indexes.iter ()); + } + } void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+markCoverage).add_coverage (c->input))) return; - if (unlikely (!(this+ligatureCoverage).add_coverage (c->input))) return; + if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return; + if (unlikely (!(this+ligatureCoverage).collect_coverage (c->input))) return; } const Coverage &get_coverage () const { return this+markCoverage; } @@ -1289,7 +2120,7 @@ struct MarkLigPosFormat1 unsigned int mark_id = _hb_glyph_info_get_lig_id (&buffer->cur()); unsigned int mark_comp = _hb_glyph_info_get_lig_comp (&buffer->cur()); if (lig_id && lig_id == mark_id && mark_comp > 0) - comp_index = MIN (comp_count, _hb_glyph_info_get_lig_comp (&buffer->cur())) - 1; + comp_index = hb_min (comp_count, _hb_glyph_info_get_lig_comp (&buffer->cur())) - 1; else comp_index = comp_count - 1; @@ -1335,13 +2166,13 @@ struct MarkLigPosFormat1 struct MarkLigPos { - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -1362,13 +2193,47 @@ typedef AnchorMatrix Mark2Array; /* mark2-major-- struct MarkMarkPosFormat1 { bool intersects (const hb_set_t *glyphs) const - { return (this+mark1Coverage).intersects (glyphs) && - (this+mark2Coverage).intersects (glyphs); } + { + return (this+mark1Coverage).intersects (glyphs) && + (this+mark2Coverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+mark1Coverage, this+mark1Array) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+mark1Array)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, *c->glyph_set, &klass_mapping); + + unsigned mark2_count = (this+mark2Array).rows; + auto mark2_iter = + + hb_zip (this+mark2Coverage, hb_range (mark2_count)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + hb_sorted_vector_t mark2_indexes; + for (const unsigned row : mark2_iter) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (mark2_indexes) + ; + } + (this+mark2Array).collect_variation_indices (c, mark2_indexes.iter ()); + } void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+mark1Coverage).add_coverage (c->input))) return; - if (unlikely (!(this+mark2Coverage).add_coverage (c->input))) return; + if (unlikely (!(this+mark1Coverage).collect_coverage (c->input))) return; + if (unlikely (!(this+mark2Coverage).collect_coverage (c->input))) return; } const Coverage &get_coverage () const { return this+mark1Coverage; } @@ -1395,12 +2260,15 @@ struct MarkMarkPosFormat1 unsigned int comp1 = _hb_glyph_info_get_lig_comp (&buffer->cur()); unsigned int comp2 = _hb_glyph_info_get_lig_comp (&buffer->info[j]); - if (likely (id1 == id2)) { + if (likely (id1 == id2)) + { if (id1 == 0) /* Marks belonging to the same base. */ goto good; else if (comp1 == comp2) /* Marks belonging to the same ligature component. */ goto good; - } else { + } + else + { /* If ligature ids don't match, it may be the case that one of the marks * itself is a ligature. In which case match. */ if ((id1 > 0 && !comp1) || (id2 > 0 && !comp2)) @@ -1420,8 +2288,70 @@ struct MarkMarkPosFormat1 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, glyphset, &klass_mapping); + + if (!klass_mapping.get_population ()) return_trace (false); + out->classCount = klass_mapping.get_population (); + + auto mark1_iter = + + hb_zip (this+mark1Coverage, this+mark1Array) + | hb_filter (glyphset, hb_first) + ; + + hb_sorted_vector_t new_coverage; + + mark1_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->mark1Coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + out->mark1Array.serialize (c->serializer, out) + .serialize (c->serializer, &klass_mapping, c->plan->layout_variation_idx_map, &(this+mark1Array), + mark1_iter + | hb_map (hb_second)); + + unsigned mark2count = (this+mark2Array).rows; + auto mark2_iter = + + hb_zip (this+mark2Coverage, hb_range (mark2count)) + | hb_filter (glyphset, hb_first) + ; + + new_coverage.reset (); + + mark2_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->mark2Coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + hb_sorted_vector_t mark2_indexes; + for (const unsigned row : + mark2_iter + | hb_map (hb_second)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (mark2_indexes) + ; + } + out->mark2Array.serialize (c->serializer, out) + .serialize (c->serializer, mark2_iter.len (), &(this+mark2Array), c->plan->layout_variation_idx_map, mark2_indexes.iter ()); + + return_trace (true); } bool sanitize (hb_sanitize_context_t *c) const @@ -1457,13 +2387,13 @@ struct MarkMarkPosFormat1 struct MarkMarkPos { - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -1509,24 +2439,30 @@ struct PosLookupSubTable Extension = 9 }; - template - typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const + template + typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const { TRACE_DISPATCH (this, lookup_type); switch (lookup_type) { - case Single: return_trace (u.single.dispatch (c)); - case Pair: return_trace (u.pair.dispatch (c)); - case Cursive: return_trace (u.cursive.dispatch (c)); - case MarkBase: return_trace (u.markBase.dispatch (c)); - case MarkLig: return_trace (u.markLig.dispatch (c)); - case MarkMark: return_trace (u.markMark.dispatch (c)); - case Context: return_trace (u.context.dispatch (c)); - case ChainContext: return_trace (u.chainContext.dispatch (c)); - case Extension: return_trace (u.extension.dispatch (c)); + case Single: return_trace (u.single.dispatch (c, hb_forward (ds)...)); + case Pair: return_trace (u.pair.dispatch (c, hb_forward (ds)...)); + case Cursive: return_trace (u.cursive.dispatch (c, hb_forward (ds)...)); + case MarkBase: return_trace (u.markBase.dispatch (c, hb_forward (ds)...)); + case MarkLig: return_trace (u.markLig.dispatch (c, hb_forward (ds)...)); + case MarkMark: return_trace (u.markMark.dispatch (c, hb_forward (ds)...)); + case Context: return_trace (u.context.dispatch (c, hb_forward (ds)...)); + case ChainContext: return_trace (u.chainContext.dispatch (c, hb_forward (ds)...)); + case Extension: return_trace (u.extension.dispatch (c, hb_forward (ds)...)); default: return_trace (c->default_return_value ()); } } + bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c, lookup_type); + } + protected: union { SinglePos single; @@ -1571,21 +2507,40 @@ struct PosLookup : Lookup hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const { return dispatch (c); } + hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const + { + if (c->is_lookup_visited (this_index)) + return hb_closure_lookups_context_t::default_return_value (); + + c->set_lookup_visited (this_index); + if (!intersects (c->glyphs)) + { + c->set_lookup_inactive (this_index); + return hb_closure_lookups_context_t::default_return_value (); + } + c->set_recurse_func (dispatch_closure_lookups_recurse_func); + + hb_closure_lookups_context_t::return_t ret = dispatch (c); + return ret; + } + template - void add_coverage (set_t *glyphs) const + void collect_coverage (set_t *glyphs) const { - hb_add_coverage_context_t c (glyphs); + hb_collect_coverage_context_t c (glyphs); dispatch (&c); } - static bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index); + static inline bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index); template static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); - template - typename context_t::return_t dispatch (context_t *c) const - { return Lookup::dispatch (c); } + HB_INTERNAL static hb_closure_lookups_context_t::return_t dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned this_index); + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { return Lookup::dispatch (c, hb_forward (ds)...); } bool subset (hb_subset_context_t *c) const { return Lookup::subset (c); } @@ -1604,21 +2559,39 @@ struct GPOS : GSUBGPOS static constexpr hb_tag_t tableTag = HB_OT_TAG_GPOS; const PosLookup& get_lookup (unsigned int i) const - { return CastR (GSUBGPOS::get_lookup (i)); } + { return static_cast (GSUBGPOS::get_lookup (i)); } static inline void position_start (hb_font_t *font, hb_buffer_t *buffer); static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer); static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer); bool subset (hb_subset_context_t *c) const - { return GSUBGPOS::subset (c); } + { + hb_subset_layout_context_t l (c, tableTag, c->plan->gpos_lookups, c->plan->gpos_features); + return GSUBGPOS::subset (&l); + } bool sanitize (hb_sanitize_context_t *c) const { return GSUBGPOS::sanitize (c); } - HB_INTERNAL bool is_blacklisted (hb_blob_t *blob, + HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, hb_face_t *face) const; + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + for (unsigned i = 0; i < GSUBGPOS::get_lookup_count (); i++) + { + if (!c->gpos_lookups->has (i)) continue; + const PosLookup &l = get_lookup (i); + l.dispatch (c); + } + } + + void closure_lookups (hb_face_t *face, + const hb_set_t *glyphs, + hb_set_t *lookup_indexes /* IN/OUT */) const + { GSUBGPOS::closure_lookups (face, glyphs, lookup_indexes); } + typedef GSUBGPOS::accelerator_t accelerator_t; }; @@ -1732,14 +2705,21 @@ struct GPOS_accelerator_t : GPOS::accelerator_t {}; /* Out-of-class implementation for methods recursing */ +#ifndef HB_NO_OT_LAYOUT template -/*static*/ inline typename context_t::return_t PosLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index) +/*static*/ typename context_t::return_t PosLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index) { const PosLookup &l = c->face->table.GPOS.get_relaxed ()->table->get_lookup (lookup_index); return l.dispatch (c); } -/*static*/ inline bool PosLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index) +/*static*/ inline hb_closure_lookups_context_t::return_t PosLookup::dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned this_index) +{ + const PosLookup &l = c->face->table.GPOS.get_relaxed ()->table->get_lookup (this_index); + return l.closure_lookups (c, this_index); +} + +/*static*/ bool PosLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index) { const PosLookup &l = c->face->table.GPOS.get_relaxed ()->table->get_lookup (lookup_index); unsigned int saved_lookup_props = c->lookup_props; @@ -1751,6 +2731,7 @@ template c->set_lookup_props (saved_lookup_props); return ret; } +#endif } /* namespace OT */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsub-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsub-table.hh index 288c07b552e..de49c4e2084 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsub-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsub-table.hh @@ -34,10 +34,12 @@ namespace OT { +typedef hb_pair_t hb_codepoint_pair_t; + +template +static void SingleSubst_serialize (hb_serialize_context_t *c, + Iterator it); -static inline void SingleSubst_serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t substitutes); struct SingleSubstFormat1 { @@ -46,35 +48,30 @@ struct SingleSubstFormat1 void closure (hb_closure_context_t *c) const { - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - /* TODO Switch to range-based API to work around malicious fonts. - * https://github.com/harfbuzz/harfbuzz/issues/363 */ - hb_codepoint_t glyph_id = iter.get_glyph (); - if (c->glyphs->has (glyph_id)) - c->out->add ((glyph_id + deltaGlyphID) & 0xFFFFu); - } + unsigned d = deltaGlyphID; + + hb_iter (this+coverage) + | hb_filter (*c->glyphs) + | hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; }) + | hb_sink (c->output) + ; } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+coverage).add_coverage (c->input))) return; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - /* TODO Switch to range-based API to work around malicious fonts. - * https://github.com/harfbuzz/harfbuzz/issues/363 */ - hb_codepoint_t glyph_id = iter.get_glyph (); - c->output->add ((glyph_id + deltaGlyphID) & 0xFFFFu); - } + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + unsigned d = deltaGlyphID; + + hb_iter (this+coverage) + | hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; }) + | hb_sink (c->output) + ; } const Coverage &get_coverage () const { return this+coverage; } bool would_apply (hb_would_apply_context_t *c) const - { - TRACE_WOULD_APPLY (this); - return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); - } + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } bool apply (hb_ot_apply_context_t *c) const { @@ -91,34 +88,41 @@ struct SingleSubstFormat1 return_trace (true); } + template bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - int delta) + Iterator glyphs, + unsigned delta) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false); - deltaGlyphID.set (delta); /* TODO(serialize) overflow? */ + c->check_assign (deltaGlyphID, delta); return_trace (true); } bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - const hb_set_t &glyphset = *c->plan->glyphset; + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); const hb_map_t &glyph_map = *c->plan->glyph_map; - hb_vector_t from; - hb_vector_t to; + hb_codepoint_t delta = deltaGlyphID; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (!glyphset.has (iter.get_glyph ())) continue; - from.push ()->set (glyph_map[iter.get_glyph ()]); - to.push ()->set (glyph_map[(iter.get_glyph () + delta) & 0xFFFF]); - } - c->serializer->propagate_error (from, to); - SingleSubst_serialize (c->serializer, from, to); - return_trace (from.length); + + auto it = + + hb_iter (this+coverage) + | hb_filter (glyphset) + | hb_map_retains_sorting ([&] (hb_codepoint_t g) { + return hb_codepoint_pair_t (g, + (g + delta) & 0xFFFF); }) + | hb_filter (glyphset, hb_second) + | hb_map_retains_sorting ([&] (hb_codepoint_pair_t p) -> hb_codepoint_pair_t + { return hb_pair (glyph_map[p.first], glyph_map[p.second]); }) + ; + + bool ret = bool (it); + SingleSubst_serialize (c->serializer, it); + return_trace (ret); } bool sanitize (hb_sanitize_context_t *c) const @@ -132,8 +136,8 @@ struct SingleSubstFormat1 OffsetTo coverage; /* Offset to Coverage table--from * beginning of Substitution table */ - HBINT16 deltaGlyphID; /* Add to original GlyphID to get - * substitute GlyphID */ + HBUINT16 deltaGlyphID; /* Add to original GlyphID to get + * substitute GlyphID, modulo 0x10000 */ public: DEFINE_SIZE_STATIC (6); }; @@ -145,35 +149,28 @@ struct SingleSubstFormat2 void closure (hb_closure_context_t *c) const { - unsigned int count = substitute.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (c->glyphs->has (iter.get_glyph ())) - c->out->add (substitute[iter.get_coverage ()]); - } + + hb_zip (this+coverage, substitute) + | hb_filter (*c->glyphs, hb_first) + | hb_map (hb_second) + | hb_sink (c->output) + ; } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+coverage).add_coverage (c->input))) return; - unsigned int count = substitute.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - c->output->add (substitute[iter.get_coverage ()]); - } + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + hb_zip (this+coverage, substitute) + | hb_map (hb_second) + | hb_sink (c->output) + ; } const Coverage &get_coverage () const { return this+coverage; } bool would_apply (hb_would_apply_context_t *c) const - { - TRACE_WOULD_APPLY (this); - return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); - } + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } bool apply (hb_ot_apply_context_t *c) const { @@ -188,11 +185,21 @@ struct SingleSubstFormat2 return_trace (true); } + template bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t substitutes) + Iterator it) { TRACE_SERIALIZE (this); + auto substitutes = + + it + | hb_map (hb_second) + ; + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; if (unlikely (!c->extend_min (*this))) return_trace (false); if (unlikely (!substitute.serialize (c, substitutes))) return_trace (false); if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false); @@ -202,19 +209,20 @@ struct SingleSubstFormat2 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - const hb_set_t &glyphset = *c->plan->glyphset; + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); const hb_map_t &glyph_map = *c->plan->glyph_map; - hb_vector_t from; - hb_vector_t to; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (!glyphset.has (iter.get_glyph ())) continue; - from.push ()->set (glyph_map[iter.get_glyph ()]); - to.push ()->set (glyph_map[substitute[iter.get_coverage ()]]); - } - c->serializer->propagate_error (from, to); - SingleSubst_serialize (c->serializer, from, to); - return_trace (from.length); + + auto it = + + hb_zip (this+coverage, substitute) + | hb_filter (glyphset, hb_first) + | hb_filter (glyphset, hb_second) + | hb_map_retains_sorting ([&] (hb_pair_t p) -> hb_codepoint_pair_t + { return hb_pair (glyph_map[p.first], glyph_map[p.second]); }) + ; + + bool ret = bool (it); + SingleSubst_serialize (c->serializer, it); + return_trace (ret); } bool sanitize (hb_sanitize_context_t *c) const @@ -228,7 +236,7 @@ struct SingleSubstFormat2 OffsetTo coverage; /* Offset to Coverage table--from * beginning of Substitution table */ - ArrayOf + ArrayOf substitute; /* Array of substitute * GlyphIDs--ordered by Coverage Index */ public: @@ -237,41 +245,44 @@ struct SingleSubstFormat2 struct SingleSubst { + + template bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t substitutes) + Iterator glyphs) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (u.format))) return_trace (false); - unsigned int format = 2; - int delta = 0; - if (glyphs.length) + unsigned format = 2; + unsigned delta = 0; + if (glyphs) { format = 1; - /* TODO(serialize) check for wrap-around */ - delta = substitutes[0] - glyphs[0]; - for (unsigned int i = 1; i < glyphs.length; i++) - if (delta != (int) (substitutes[i] - glyphs[i])) { - format = 2; - break; - } + auto get_delta = [=] (hb_codepoint_pair_t _) + { return (unsigned) (_.second - _.first) & 0xFFFF; }; + delta = get_delta (*glyphs); + if (!hb_all (++(+glyphs), delta, get_delta)) format = 2; } - u.format.set (format); + u.format = format; switch (u.format) { - case 1: return_trace (u.format1.serialize (c, glyphs, delta)); - case 2: return_trace (u.format2.serialize (c, glyphs, substitutes)); + case 1: return_trace (u.format1.serialize (c, + + glyphs + | hb_map_retains_sorting (hb_first), + delta)); + case 2: return_trace (u.format2.serialize (c, glyphs)); default:return_trace (false); } } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); - case 2: return_trace (c->dispatch (u.format2)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -284,20 +295,19 @@ struct SingleSubst } u; }; -static inline void +template +static void SingleSubst_serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t substitutes) -{ c->start_embed ()->serialize (c, glyphs, substitutes); } + Iterator it) +{ c->start_embed ()->serialize (c, it); } struct Sequence { + bool intersects (const hb_set_t *glyphs) const + { return hb_all (substitute, glyphs); } + void closure (hb_closure_context_t *c) const - { - unsigned int count = substitute.len; - for (unsigned int i = 0; i < count; i++) - c->out->add (substitute[i]); - } + { c->output->add_array (substitute.arrayZ, substitute.len); } void collect_glyphs (hb_collect_glyphs_context_t *c) const { c->output->add_array (substitute.arrayZ, substitute.len); } @@ -334,11 +344,30 @@ struct Sequence return_trace (true); } + template bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs) + Iterator subst) { TRACE_SERIALIZE (this); - return_trace (substitute.serialize (c, glyphs)); + return_trace (substitute.serialize (c, subst)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + if (!intersects (&glyphset)) return_trace (false); + + auto it = + + hb_iter (substitute) + | hb_map (glyph_map) + ; + + auto *out = c->serializer->start_embed (*this); + return_trace (out->serialize (c->serializer, it)); } bool sanitize (hb_sanitize_context_t *c) const @@ -348,7 +377,7 @@ struct Sequence } protected: - ArrayOf + ArrayOf substitute; /* String of GlyphIDs to substitute */ public: DEFINE_SIZE_ARRAY (2, substitute); @@ -361,31 +390,30 @@ struct MultipleSubstFormat1 void closure (hb_closure_context_t *c) const { - unsigned int count = sequence.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (c->glyphs->has (iter.get_glyph ())) - (this+sequence[iter.get_coverage ()]).closure (c); - } + + hb_zip (this+coverage, sequence) + | hb_filter (*c->glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Sequence &_) { _.closure (c); }) + ; } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+coverage).add_coverage (c->input))) return; - unsigned int count = sequence.len; - for (unsigned int i = 0; i < count; i++) - (this+sequence[i]).collect_glyphs (c); + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + hb_zip (this+coverage, sequence) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Sequence &_) { _.collect_glyphs (c); }) + ; } const Coverage &get_coverage () const { return this+coverage; } bool would_apply (hb_would_apply_context_t *c) const - { - TRACE_WOULD_APPLY (this); - return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); - } + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } bool apply (hb_ot_apply_context_t *c) const { @@ -398,9 +426,9 @@ struct MultipleSubstFormat1 } bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, + hb_sorted_array_t glyphs, hb_array_t substitute_len_list, - hb_array_t substitute_glyphs_list) + hb_array_t substitute_glyphs_list) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); @@ -419,8 +447,24 @@ struct MultipleSubstFormat1 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, sequence) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->sequence, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); } bool sanitize (hb_sanitize_context_t *c) const @@ -444,27 +488,27 @@ struct MultipleSubstFormat1 struct MultipleSubst { bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, + hb_sorted_array_t glyphs, hb_array_t substitute_len_list, - hb_array_t substitute_glyphs_list) + hb_array_t substitute_glyphs_list) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (u.format))) return_trace (false); unsigned int format = 1; - u.format.set (format); + u.format = format; switch (u.format) { case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, substitute_glyphs_list)); default:return_trace (false); } } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -478,12 +522,11 @@ struct MultipleSubst struct AlternateSet { + bool intersects (const hb_set_t *glyphs) const + { return hb_any (alternates, glyphs); } + void closure (hb_closure_context_t *c) const - { - unsigned int count = alternates.len; - for (unsigned int i = 0; i < count; i++) - c->out->add (alternates[i]); - } + { c->output->add_array (alternates.arrayZ, alternates.len); } void collect_glyphs (hb_collect_glyphs_context_t *c) const { c->output->add_array (alternates.arrayZ, alternates.len); } @@ -502,7 +545,7 @@ struct AlternateSet unsigned int shift = hb_ctz (lookup_mask); unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift); - /* If alt_index is MAX, randomize feature if it is the rand feature. */ + /* If alt_index is MAX_VALUE, randomize feature if it is the rand feature. */ if (alt_index == HB_OT_MAP_MAX_VALUE && c->random) alt_index = c->random_number () % count + 1; @@ -513,11 +556,44 @@ struct AlternateSet return_trace (true); } + unsigned + get_alternates (unsigned start_offset, + unsigned *alternate_count /* IN/OUT. May be NULL. */, + hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const + { + if (alternates.len && alternate_count) + { + + alternates.sub_array (start_offset, alternate_count) + | hb_sink (hb_array (alternate_glyphs, *alternate_count)) + ; + } + return alternates.len; + } + + template bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs) + Iterator alts) { TRACE_SERIALIZE (this); - return_trace (alternates.serialize (c, glyphs)); + return_trace (alternates.serialize (c, alts)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_iter (alternates) + | hb_filter (glyphset) + | hb_map (glyph_map) + ; + + auto *out = c->serializer->start_embed (*this); + return_trace (out->serialize (c->serializer, it) && + out->alternates); } bool sanitize (hb_sanitize_context_t *c) const @@ -527,7 +603,7 @@ struct AlternateSet } protected: - ArrayOf + ArrayOf alternates; /* Array of alternate GlyphIDs--in * arbitrary order */ public: @@ -541,35 +617,38 @@ struct AlternateSubstFormat1 void closure (hb_closure_context_t *c) const { - unsigned int count = alternateSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (c->glyphs->has (iter.get_glyph ())) - (this+alternateSet[iter.get_coverage ()]).closure (c); - } + + hb_zip (this+coverage, alternateSet) + | hb_filter (c->glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const AlternateSet &_) { _.closure (c); }) + ; } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+coverage).add_coverage (c->input))) return; - unsigned int count = alternateSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - (this+alternateSet[iter.get_coverage ()]).collect_glyphs (c); - } + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + hb_zip (this+coverage, alternateSet) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const AlternateSet &_) { _.collect_glyphs (c); }) + ; } const Coverage &get_coverage () const { return this+coverage; } bool would_apply (hb_would_apply_context_t *c) const - { - TRACE_WOULD_APPLY (this); - return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); - } + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } + + unsigned + get_glyph_alternates (hb_codepoint_t gid, + unsigned start_offset, + unsigned *alternate_count /* IN/OUT. May be NULL. */, + hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const + { return (this+alternateSet[(this+coverage).get_coverage (gid)]) + .get_alternates (start_offset, alternate_count, alternate_glyphs); } bool apply (hb_ot_apply_context_t *c) const { @@ -582,9 +661,9 @@ struct AlternateSubstFormat1 } bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, + hb_sorted_array_t glyphs, hb_array_t alternate_len_list, - hb_array_t alternate_glyphs_list) + hb_array_t alternate_glyphs_list) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); @@ -603,8 +682,24 @@ struct AlternateSubstFormat1 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, alternateSet) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->alternateSet, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); } bool sanitize (hb_sanitize_context_t *c) const @@ -628,27 +723,27 @@ struct AlternateSubstFormat1 struct AlternateSubst { bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, + hb_sorted_array_t glyphs, hb_array_t alternate_len_list, - hb_array_t alternate_glyphs_list) + hb_array_t alternate_glyphs_list) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (u.format))) return_trace (false); unsigned int format = 1; - u.format.set (format); + u.format = format; switch (u.format) { case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list)); default:return_trace (false); } } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -664,40 +759,30 @@ struct AlternateSubst struct Ligature { bool intersects (const hb_set_t *glyphs) const - { - unsigned int count = component.lenP1; - for (unsigned int i = 1; i < count; i++) - if (!glyphs->has (component[i])) - return false; - return true; - } + { return hb_all (component, glyphs); } void closure (hb_closure_context_t *c) const { - unsigned int count = component.lenP1; - for (unsigned int i = 1; i < count; i++) - if (!c->glyphs->has (component[i])) - return; - c->out->add (ligGlyph); + if (!intersects (c->glyphs)) return; + c->output->add (ligGlyph); } void collect_glyphs (hb_collect_glyphs_context_t *c) const { - c->input->add_array (component.arrayZ, component.lenP1 ? component.lenP1 - 1 : 0); + c->input->add_array (component.arrayZ, component.get_length ()); c->output->add (ligGlyph); } bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); if (c->len != component.lenP1) - return_trace (false); + return false; for (unsigned int i = 1; i < c->len; i++) if (likely (c->glyphs[i] != component[i])) - return_trace (false); + return false; - return_trace (true); + return true; } bool apply (hb_ot_apply_context_t *c) const @@ -739,9 +824,11 @@ struct Ligature return_trace (true); } + template bool serialize (hb_serialize_context_t *c, - GlyphID ligature, - hb_array_t components /* Starting from second */) + hb_codepoint_t ligature, + Iterator components /* Starting from second */) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); @@ -750,6 +837,25 @@ struct Ligature return_trace (true); } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false); + + auto it = + + hb_iter (component) + | hb_map (glyph_map) + ; + + auto *out = c->serializer->start_embed (*this); + return_trace (out->serialize (c->serializer, + glyph_map[ligGlyph], + it)); + } + public: bool sanitize (hb_sanitize_context_t *c) const { @@ -758,8 +864,8 @@ struct Ligature } protected: - GlyphID ligGlyph; /* GlyphID of ligature to substitute */ - HeadlessArrayOf + HBGlyphID ligGlyph; /* GlyphID of ligature to substitute */ + HeadlessArrayOf component; /* Array of component GlyphIDs--start * with the second component--ordered * in writing direction */ @@ -771,38 +877,38 @@ struct LigatureSet { bool intersects (const hb_set_t *glyphs) const { - unsigned int num_ligs = ligature.len; - for (unsigned int i = 0; i < num_ligs; i++) - if ((this+ligature[i]).intersects (glyphs)) - return true; - return false; + return + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_map ([glyphs] (const Ligature &_) { return _.intersects (glyphs); }) + | hb_any + ; } void closure (hb_closure_context_t *c) const { - unsigned int num_ligs = ligature.len; - for (unsigned int i = 0; i < num_ligs; i++) - (this+ligature[i]).closure (c); + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Ligature &_) { _.closure (c); }) + ; } void collect_glyphs (hb_collect_glyphs_context_t *c) const { - unsigned int num_ligs = ligature.len; - for (unsigned int i = 0; i < num_ligs; i++) - (this+ligature[i]).collect_glyphs (c); + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Ligature &_) { _.collect_glyphs (c); }) + ; } bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); - unsigned int num_ligs = ligature.len; - for (unsigned int i = 0; i < num_ligs; i++) - { - const Ligature &lig = this+ligature[i]; - if (lig.would_apply (c)) - return_trace (true); - } - return_trace (false); + return + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_map ([c] (const Ligature &_) { return _.would_apply (c); }) + | hb_any + ; } bool apply (hb_ot_apply_context_t *c) const @@ -819,16 +925,16 @@ struct LigatureSet } bool serialize (hb_serialize_context_t *c, - hb_array_t ligatures, + hb_array_t ligatures, hb_array_t component_count_list, - hb_array_t &component_list /* Starting from second for each ligature */) + hb_array_t &component_list /* Starting from second for each ligature */) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); if (unlikely (!ligature.serialize (c, ligatures.length))) return_trace (false); for (unsigned int i = 0; i < ligatures.length; i++) { - unsigned int component_count = MAX (component_count_list[i] - 1, 0); + unsigned int component_count = (unsigned) hb_max ((int) component_count_list[i] - 1, 0); if (unlikely (!ligature[i].serialize (c, this) .serialize (c, ligatures[i], @@ -839,6 +945,19 @@ struct LigatureSet return_trace (true); } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + hb_iter (ligature) + | hb_filter (subset_offset_array (c, out->ligature, this)) + | hb_drain + ; + return_trace (bool (out->ligature)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -857,59 +976,55 @@ struct LigatureSubstFormat1 { bool intersects (const hb_set_t *glyphs) const { - unsigned int count = ligatureSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (glyphs->has (iter.get_glyph ()) && - (this+ligatureSet[iter.get_coverage ()]).intersects (glyphs)) - return true; - } - return false; + return + + hb_zip (this+coverage, ligatureSet) + | hb_filter (*glyphs, hb_first) + | hb_map (hb_second) + | hb_map ([this, glyphs] (const OffsetTo &_) + { return (this+_).intersects (glyphs); }) + | hb_any + ; } void closure (hb_closure_context_t *c) const { - unsigned int count = ligatureSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (c->glyphs->has (iter.get_glyph ())) - (this+ligatureSet[iter.get_coverage ()]).closure (c); - } + + hb_zip (this+coverage, ligatureSet) + | hb_filter (*c->glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const LigatureSet &_) { _.closure (c); }) + ; } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+coverage).add_coverage (c->input))) return; - unsigned int count = ligatureSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - (this+ligatureSet[iter.get_coverage ()]).collect_glyphs (c); - } + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + + hb_zip (this+coverage, ligatureSet) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const LigatureSet &_) { _.collect_glyphs (c); }) + ; } const Coverage &get_coverage () const { return this+coverage; } bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); unsigned int index = (this+coverage).get_coverage (c->glyphs[0]); - if (likely (index == NOT_COVERED)) return_trace (false); + if (likely (index == NOT_COVERED)) return false; const LigatureSet &lig_set = this+ligatureSet[index]; - return_trace (lig_set.would_apply (c)); + return lig_set.would_apply (c); } bool apply (hb_ot_apply_context_t *c) const { TRACE_APPLY (this); - unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint); if (likely (index == NOT_COVERED)) return_trace (false); const LigatureSet &lig_set = this+ligatureSet[index]; @@ -917,11 +1032,11 @@ struct LigatureSubstFormat1 } bool serialize (hb_serialize_context_t *c, - hb_array_t first_glyphs, + hb_sorted_array_t first_glyphs, hb_array_t ligature_per_first_glyph_count_list, - hb_array_t ligatures_list, + hb_array_t ligatures_list, hb_array_t component_count_list, - hb_array_t component_list /* Starting from second for each ligature */) + hb_array_t component_list /* Starting from second for each ligature */) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); @@ -943,8 +1058,24 @@ struct LigatureSubstFormat1 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, ligatureSet) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->ligatureSet, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); } bool sanitize (hb_sanitize_context_t *c) const @@ -968,16 +1099,16 @@ struct LigatureSubstFormat1 struct LigatureSubst { bool serialize (hb_serialize_context_t *c, - hb_array_t first_glyphs, + hb_sorted_array_t first_glyphs, hb_array_t ligature_per_first_glyph_count_list, - hb_array_t ligatures_list, + hb_array_t ligatures_list, hb_array_t component_count_list, - hb_array_t component_list /* Starting from second for each ligature */) + hb_array_t component_list /* Starting from second for each ligature */) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (u.format))) return_trace (false); unsigned int format = 1; - u.format.set (format); + u.format = format; switch (u.format) { case 1: return_trace (u.format1.serialize (c, first_glyphs, @@ -989,13 +1120,13 @@ struct LigatureSubst } } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -1015,7 +1146,6 @@ struct ChainContextSubst : ChainContext {}; struct ExtensionSubst : Extension { typedef struct SubstLookupSubTable SubTable; - bool is_reverse () const; }; @@ -1027,7 +1157,7 @@ struct ReverseChainSingleSubstFormat1 if (!(this+coverage).intersects (glyphs)) return false; - const OffsetArrayOf &lookahead = StructAfter > (backtrack); + const OffsetArrayOf &lookahead = StructAfter> (backtrack); unsigned int count; @@ -1046,47 +1176,36 @@ struct ReverseChainSingleSubstFormat1 void closure (hb_closure_context_t *c) const { - const OffsetArrayOf &lookahead = StructAfter > (backtrack); + if (!intersects (c->glyphs)) return; - unsigned int count; - - count = backtrack.len; - for (unsigned int i = 0; i < count; i++) - if (!(this+backtrack[i]).intersects (c->glyphs)) - return; - - count = lookahead.len; - for (unsigned int i = 0; i < count; i++) - if (!(this+lookahead[i]).intersects (c->glyphs)) - return; + const OffsetArrayOf &lookahead = StructAfter> (backtrack); + const ArrayOf &substitute = StructAfter> (lookahead); - const ArrayOf &substitute = StructAfter > (lookahead); - count = substitute.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (c->glyphs->has (iter.get_glyph ())) - c->out->add (substitute[iter.get_coverage ()]); - } + + hb_zip (this+coverage, substitute) + | hb_filter (*c->glyphs, hb_first) + | hb_map (hb_second) + | hb_sink (c->output) + ; } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+coverage).add_coverage (c->input))) return; + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; unsigned int count; count = backtrack.len; for (unsigned int i = 0; i < count; i++) - if (unlikely (!(this+backtrack[i]).add_coverage (c->before))) return; + if (unlikely (!(this+backtrack[i]).collect_coverage (c->before))) return; - const OffsetArrayOf &lookahead = StructAfter > (backtrack); + const OffsetArrayOf &lookahead = StructAfter> (backtrack); count = lookahead.len; for (unsigned int i = 0; i < count; i++) - if (unlikely (!(this+lookahead[i]).add_coverage (c->after))) return; + if (unlikely (!(this+lookahead[i]).collect_coverage (c->after))) return; - const ArrayOf &substitute = StructAfter > (lookahead); + const ArrayOf &substitute = StructAfter> (lookahead); count = substitute.len; c->output->add_array (substitute.arrayZ, substitute.len); } @@ -1094,10 +1213,7 @@ struct ReverseChainSingleSubstFormat1 const Coverage &get_coverage () const { return this+coverage; } bool would_apply (hb_would_apply_context_t *c) const - { - TRACE_WOULD_APPLY (this); - return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); - } + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } bool apply (hb_ot_apply_context_t *c) const { @@ -1105,13 +1221,15 @@ struct ReverseChainSingleSubstFormat1 if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL)) return_trace (false); /* No chaining to this type */ - unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint); if (likely (index == NOT_COVERED)) return_trace (false); - const OffsetArrayOf &lookahead = StructAfter > (backtrack); - const ArrayOf &substitute = StructAfter > (lookahead); + const OffsetArrayOf &lookahead = StructAfter> (backtrack); + const ArrayOf &substitute = StructAfter> (lookahead); + + if (unlikely (index >= substitute.len)) return_trace (false); - unsigned int start_index = 0, end_index = 0; + unsigned int start_index = 0, end_index = 0; if (match_backtrack (c, backtrack.len, (HBUINT16 *) backtrack.arrayZ, match_coverage, this, @@ -1144,10 +1262,10 @@ struct ReverseChainSingleSubstFormat1 TRACE_SANITIZE (this); if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this))) return_trace (false); - const OffsetArrayOf &lookahead = StructAfter > (backtrack); + const OffsetArrayOf &lookahead = StructAfter> (backtrack); if (!lookahead.sanitize (c, this)) return_trace (false); - const ArrayOf &substitute = StructAfter > (lookahead); + const ArrayOf &substitute = StructAfter> (lookahead); return_trace (substitute.sanitize (c)); } @@ -1164,7 +1282,7 @@ struct ReverseChainSingleSubstFormat1 lookaheadX; /* Array of coverage tables * in lookahead sequence, in glyph * sequence order */ - ArrayOf + ArrayOf substituteX; /* Array of substitute * GlyphIDs--ordered by Coverage Index */ public: @@ -1173,13 +1291,13 @@ struct ReverseChainSingleSubstFormat1 struct ReverseChainSingleSubst { - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -1213,23 +1331,29 @@ struct SubstLookupSubTable ReverseChainSingle = 8 }; - template - typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const + template + typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const { TRACE_DISPATCH (this, lookup_type); switch (lookup_type) { - case Single: return_trace (u.single.dispatch (c)); - case Multiple: return_trace (u.multiple.dispatch (c)); - case Alternate: return_trace (u.alternate.dispatch (c)); - case Ligature: return_trace (u.ligature.dispatch (c)); - case Context: return_trace (u.context.dispatch (c)); - case ChainContext: return_trace (u.chainContext.dispatch (c)); - case Extension: return_trace (u.extension.dispatch (c)); - case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c)); + case Single: return_trace (u.single.dispatch (c, hb_forward (ds)...)); + case Multiple: return_trace (u.multiple.dispatch (c, hb_forward (ds)...)); + case Alternate: return_trace (u.alternate.dispatch (c, hb_forward (ds)...)); + case Ligature: return_trace (u.ligature.dispatch (c, hb_forward (ds)...)); + case Context: return_trace (u.context.dispatch (c, hb_forward (ds)...)); + case ChainContext: return_trace (u.chainContext.dispatch (c, hb_forward (ds)...)); + case Extension: return_trace (u.extension.dispatch (c, hb_forward (ds)...)); + case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c, hb_forward (ds)...)); default: return_trace (c->default_return_value ()); } } + bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c, lookup_type); + } + protected: union { SingleSubst single; @@ -1253,14 +1377,14 @@ struct SubstLookup : Lookup const SubTable& get_subtable (unsigned int i) const { return Lookup::get_subtable (i); } - static bool lookup_type_is_reverse (unsigned int lookup_type) + static inline bool lookup_type_is_reverse (unsigned int lookup_type) { return lookup_type == SubTable::ReverseChainSingle; } bool is_reverse () const { unsigned int type = get_type (); if (unlikely (type == SubTable::Extension)) - return CastR (get_subtable(0)).is_reverse (); + return reinterpret_cast (get_subtable (0)).is_reverse (); return lookup_type_is_reverse (type); } @@ -1290,6 +1414,24 @@ struct SubstLookup : Lookup return ret; } + hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const + { + if (c->is_lookup_visited (this_index)) + return hb_closure_lookups_context_t::default_return_value (); + + c->set_lookup_visited (this_index); + if (!intersects (c->glyphs)) + { + c->set_lookup_inactive (this_index); + return hb_closure_lookups_context_t::default_return_value (); + } + + c->set_recurse_func (dispatch_closure_lookups_recurse_func); + + hb_closure_lookups_context_t::return_t ret = dispatch (c); + return ret; + } + hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const { c->set_recurse_func (dispatch_recurse_func); @@ -1297,90 +1439,93 @@ struct SubstLookup : Lookup } template - void add_coverage (set_t *glyphs) const + void collect_coverage (set_t *glyphs) const { - hb_add_coverage_context_t c (glyphs); + hb_collect_coverage_context_t c (glyphs); dispatch (&c); } bool would_apply (hb_would_apply_context_t *c, const hb_ot_layout_lookup_accelerator_t *accel) const { - TRACE_WOULD_APPLY (this); - if (unlikely (!c->len)) return_trace (false); - if (!accel->may_have (c->glyphs[0])) return_trace (false); - return_trace (dispatch (c)); + if (unlikely (!c->len)) return false; + if (!accel->may_have (c->glyphs[0])) return false; + return dispatch (c); } - static bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index); + static inline bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index); SubTable& serialize_subtable (hb_serialize_context_t *c, - unsigned int i) + unsigned int i) { return get_subtables ()[i].serialize (c, this); } bool serialize_single (hb_serialize_context_t *c, uint32_t lookup_props, - hb_array_t glyphs, - hb_array_t substitutes) + hb_sorted_array_t glyphs, + hb_array_t substitutes) { TRACE_SERIALIZE (this); if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false); - return_trace (serialize_subtable (c, 0).u.single.serialize (c, glyphs, substitutes)); + return_trace (serialize_subtable (c, 0).u.single. + serialize (c, hb_zip (glyphs, substitutes))); } bool serialize_multiple (hb_serialize_context_t *c, uint32_t lookup_props, - hb_array_t glyphs, + hb_sorted_array_t glyphs, hb_array_t substitute_len_list, - hb_array_t substitute_glyphs_list) + hb_array_t substitute_glyphs_list) { TRACE_SERIALIZE (this); if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false); - return_trace (serialize_subtable (c, 0).u.multiple.serialize (c, - glyphs, - substitute_len_list, - substitute_glyphs_list)); + return_trace (serialize_subtable (c, 0).u.multiple. + serialize (c, + glyphs, + substitute_len_list, + substitute_glyphs_list)); } bool serialize_alternate (hb_serialize_context_t *c, uint32_t lookup_props, - hb_array_t glyphs, + hb_sorted_array_t glyphs, hb_array_t alternate_len_list, - hb_array_t alternate_glyphs_list) + hb_array_t alternate_glyphs_list) { TRACE_SERIALIZE (this); if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false); - return_trace (serialize_subtable (c, 0).u.alternate.serialize (c, - glyphs, - alternate_len_list, - alternate_glyphs_list)); + return_trace (serialize_subtable (c, 0).u.alternate. + serialize (c, + glyphs, + alternate_len_list, + alternate_glyphs_list)); } bool serialize_ligature (hb_serialize_context_t *c, uint32_t lookup_props, - hb_array_t first_glyphs, + hb_sorted_array_t first_glyphs, hb_array_t ligature_per_first_glyph_count_list, - hb_array_t ligatures_list, + hb_array_t ligatures_list, hb_array_t component_count_list, - hb_array_t component_list /* Starting from second for each ligature */) + hb_array_t component_list /* Starting from second for each ligature */) { TRACE_SERIALIZE (this); if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false); - return_trace (serialize_subtable (c, 0).u.ligature.serialize (c, - first_glyphs, - ligature_per_first_glyph_count_list, - ligatures_list, - component_count_list, - component_list)); + return_trace (serialize_subtable (c, 0).u.ligature. + serialize (c, + first_glyphs, + ligature_per_first_glyph_count_list, + ligatures_list, + component_count_list, + component_list)); } template - static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); + static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); - static hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned int lookup_index) + static inline hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned int lookup_index) { if (!c->should_visit_lookup (lookup_index)) - return HB_VOID; + return hb_empty_t (); hb_closure_context_t::return_t ret = dispatch_recurse_func (c, lookup_index); @@ -1392,9 +1537,11 @@ struct SubstLookup : Lookup return ret; } - template - typename context_t::return_t dispatch (context_t *c) const - { return Lookup::dispatch (c); } + HB_INTERNAL static hb_closure_lookups_context_t::return_t dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned lookup_index); + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { return Lookup::dispatch (c, hb_forward (ds)...); } bool subset (hb_subset_context_t *c) const { return Lookup::subset (c); } @@ -1413,17 +1560,25 @@ struct GSUB : GSUBGPOS static constexpr hb_tag_t tableTag = HB_OT_TAG_GSUB; const SubstLookup& get_lookup (unsigned int i) const - { return CastR (GSUBGPOS::get_lookup (i)); } + { return static_cast (GSUBGPOS::get_lookup (i)); } bool subset (hb_subset_context_t *c) const - { return GSUBGPOS::subset (c); } + { + hb_subset_layout_context_t l (c, tableTag, c->plan->gsub_lookups, c->plan->gsub_features); + return GSUBGPOS::subset (&l); + } bool sanitize (hb_sanitize_context_t *c) const { return GSUBGPOS::sanitize (c); } - HB_INTERNAL bool is_blacklisted (hb_blob_t *blob, + HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, hb_face_t *face) const; + void closure_lookups (hb_face_t *face, + const hb_set_t *glyphs, + hb_set_t *lookup_indexes /* IN/OUT */) const + { GSUBGPOS::closure_lookups (face, glyphs, lookup_indexes); } + typedef GSUBGPOS::accelerator_t accelerator_t; }; @@ -1433,22 +1588,25 @@ struct GSUB_accelerator_t : GSUB::accelerator_t {}; /* Out-of-class implementation for methods recursing */ +#ifndef HB_NO_OT_LAYOUT /*static*/ inline bool ExtensionSubst::is_reverse () const { - unsigned int type = get_type (); - if (unlikely (type == SubTable::Extension)) - return CastR (get_subtable()).is_reverse (); - return SubstLookup::lookup_type_is_reverse (type); + return SubstLookup::lookup_type_is_reverse (get_type ()); } - template -/*static*/ inline typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index) +/*static*/ typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index) { const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index); return l.dispatch (c); } -/*static*/ inline bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index) +/*static*/ inline hb_closure_lookups_context_t::return_t SubstLookup::dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned this_index) +{ + const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (this_index); + return l.closure_lookups (c, this_index); +} + +/*static*/ bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index) { const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index); unsigned int saved_lookup_props = c->lookup_props; @@ -1460,6 +1618,8 @@ template c->set_lookup_props (saved_lookup_props); return ret; } +#endif + } /* namespace OT */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh index 11d757a4512..1e7d76dd47b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh @@ -42,30 +42,26 @@ namespace OT { struct hb_intersects_context_t : - hb_dispatch_context_t + hb_dispatch_context_t { - const char *get_name () { return "INTERSECTS"; } template return_t dispatch (const T &obj) { return obj.intersects (this->glyphs); } static return_t default_return_value () { return false; } bool stop_sublookup_iteration (return_t r) const { return r; } const hb_set_t *glyphs; - unsigned int debug_depth; hb_intersects_context_t (const hb_set_t *glyphs_) : - glyphs (glyphs_), - debug_depth (0) {} + glyphs (glyphs_) {} }; struct hb_closure_context_t : - hb_dispatch_context_t + hb_dispatch_context_t { - const char *get_name () { return "CLOSURE"; } typedef return_t (*recurse_func_t) (hb_closure_context_t *c, unsigned int lookup_index); template - return_t dispatch (const T &obj) { obj.closure (this); return HB_VOID; } - static return_t default_return_value () { return HB_VOID; } + return_t dispatch (const T &obj) { obj.closure (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } void recurse (unsigned int lookup_index) { if (unlikely (nesting_level_left == 0 || !recurse_func)) @@ -76,26 +72,35 @@ struct hb_closure_context_t : nesting_level_left++; } + bool lookup_limit_exceeded () + { return lookup_count > HB_MAX_LOOKUP_INDICES; } + bool should_visit_lookup (unsigned int lookup_index) { + if (lookup_count++ > HB_MAX_LOOKUP_INDICES) + return false; + if (is_lookup_done (lookup_index)) return false; + done_lookups->set (lookup_index, glyphs->get_population ()); return true; } bool is_lookup_done (unsigned int lookup_index) { + if (done_lookups->in_error ()) + return true; + /* Have we visited this lookup with the current set of glyphs? */ return done_lookups->get (lookup_index) == glyphs->get_population (); } hb_face_t *face; hb_set_t *glyphs; - hb_set_t out[1]; + hb_set_t output[1]; recurse_func_t recurse_func; unsigned int nesting_level_left; - unsigned int debug_depth; hb_closure_context_t (hb_face_t *face_, hb_set_t *glyphs_, @@ -105,8 +110,9 @@ struct hb_closure_context_t : glyphs (glyphs_), recurse_func (nullptr), nesting_level_left (nesting_level_left_), - debug_depth (0), - done_lookups (done_lookups_) {} + done_lookups (done_lookups_), + lookup_count (0) + {} ~hb_closure_context_t () { flush (); } @@ -114,19 +120,87 @@ struct hb_closure_context_t : void flush () { - hb_set_union (glyphs, out); - hb_set_clear (out); + hb_set_del_range (output, face->get_num_glyphs (), hb_set_get_max (output)); /* Remove invalid glyphs. */ + hb_set_union (glyphs, output); + hb_set_clear (output); } private: hb_map_t *done_lookups; + unsigned int lookup_count; }; +struct hb_closure_lookups_context_t : + hb_dispatch_context_t +{ + typedef return_t (*recurse_func_t) (hb_closure_lookups_context_t *c, unsigned lookup_index); + template + return_t dispatch (const T &obj) { obj.closure_lookups (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } + void recurse (unsigned lookup_index) + { + if (unlikely (nesting_level_left == 0 || !recurse_func)) + return; + + /* Return if new lookup was recursed to before. */ + if (is_lookup_visited (lookup_index)) + return; + + set_lookup_visited (lookup_index); + nesting_level_left--; + recurse_func (this, lookup_index); + nesting_level_left++; + } + + void set_lookup_visited (unsigned lookup_index) + { visited_lookups->add (lookup_index); } + + void set_lookup_inactive (unsigned lookup_index) + { inactive_lookups->add (lookup_index); } + + bool lookup_limit_exceeded () + { return lookup_count > HB_MAX_LOOKUP_INDICES; } + + bool is_lookup_visited (unsigned lookup_index) + { + if (lookup_count++ > HB_MAX_LOOKUP_INDICES) + return true; + + if (visited_lookups->in_error ()) + return true; + + return visited_lookups->has (lookup_index); + } + + hb_face_t *face; + const hb_set_t *glyphs; + recurse_func_t recurse_func; + unsigned int nesting_level_left; + + hb_closure_lookups_context_t (hb_face_t *face_, + const hb_set_t *glyphs_, + hb_set_t *visited_lookups_, + hb_set_t *inactive_lookups_, + unsigned nesting_level_left_ = HB_MAX_NESTING_LEVEL) : + face (face_), + glyphs (glyphs_), + recurse_func (nullptr), + nesting_level_left (nesting_level_left_), + visited_lookups (visited_lookups_), + inactive_lookups (inactive_lookups_), + lookup_count (0) {} + + void set_recurse_func (recurse_func_t func) { recurse_func = func; } + + private: + hb_set_t *visited_lookups; + hb_set_t *inactive_lookups; + unsigned int lookup_count; +}; struct hb_would_apply_context_t : - hb_dispatch_context_t + hb_dispatch_context_t { - const char *get_name () { return "WOULD_APPLY"; } template return_t dispatch (const T &obj) { return obj.would_apply (this); } static return_t default_return_value () { return false; } @@ -136,7 +210,6 @@ struct hb_would_apply_context_t : const hb_codepoint_t *glyphs; unsigned int len; bool zero_context; - unsigned int debug_depth; hb_would_apply_context_t (hb_face_t *face_, const hb_codepoint_t *glyphs_, @@ -145,19 +218,16 @@ struct hb_would_apply_context_t : face (face_), glyphs (glyphs_), len (len_), - zero_context (zero_context_), - debug_depth (0) {} + zero_context (zero_context_) {} }; - struct hb_collect_glyphs_context_t : - hb_dispatch_context_t + hb_dispatch_context_t { - const char *get_name () { return "COLLECT_GLYPHS"; } typedef return_t (*recurse_func_t) (hb_collect_glyphs_context_t *c, unsigned int lookup_index); template - return_t dispatch (const T &obj) { obj.collect_glyphs (this); return HB_VOID; } - static return_t default_return_value () { return HB_VOID; } + return_t dispatch (const T &obj) { obj.collect_glyphs (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } void recurse (unsigned int lookup_index) { if (unlikely (nesting_level_left == 0 || !recurse_func)) @@ -204,7 +274,6 @@ struct hb_collect_glyphs_context_t : recurse_func_t recurse_func; hb_set_t *recursed_lookups; unsigned int nesting_level_left; - unsigned int debug_depth; hb_collect_glyphs_context_t (hb_face_t *face_, hb_set_t *glyphs_before, /* OUT. May be NULL */ @@ -219,8 +288,7 @@ struct hb_collect_glyphs_context_t : output (glyphs_output ? glyphs_output : hb_set_get_empty ()), recurse_func (nullptr), recursed_lookups (hb_set_create ()), - nesting_level_left (nesting_level_left_), - debug_depth (0) {} + nesting_level_left (nesting_level_left_) {} ~hb_collect_glyphs_context_t () { hb_set_destroy (recursed_lookups); } void set_recurse_func (recurse_func_t func) { recurse_func = func; } @@ -229,26 +297,23 @@ struct hb_collect_glyphs_context_t : template -struct hb_add_coverage_context_t : - hb_dispatch_context_t, const Coverage &, HB_DEBUG_GET_COVERAGE> +struct hb_collect_coverage_context_t : + hb_dispatch_context_t, const Coverage &> { - const char *get_name () { return "GET_COVERAGE"; } - typedef const Coverage &return_t; + typedef const Coverage &return_t; // Stoopid that we have to dupe this here. template return_t dispatch (const T &obj) { return obj.get_coverage (); } - static return_t default_return_value () { return Null(Coverage); } + static return_t default_return_value () { return Null (Coverage); } bool stop_sublookup_iteration (return_t r) const { - r.add_coverage (set); + r.collect_coverage (set); return false; } - hb_add_coverage_context_t (set_t *set_) : - set (set_), - debug_depth (0) {} + hb_collect_coverage_context_t (set_t *set_) : + set (set_) {} set_t *set; - unsigned int debug_depth; }; @@ -276,7 +341,7 @@ struct hb_ot_apply_context_t : void set_mask (hb_mask_t mask_) { mask = mask_; } void set_syllable (uint8_t syllable_) { syllable = syllable_; } void set_match_func (match_func_t match_func_, - const void *match_data_) + const void *match_data_) { match_func = match_func_; match_data = match_data_; } enum may_match_t { @@ -286,7 +351,7 @@ struct hb_ot_apply_context_t : }; may_match_t may_match (const hb_glyph_info_t &info, - const HBUINT16 *glyph_data) const + const HBUINT16 *glyph_data) const { if (!(info.mask & mask) || (syllable && syllable != info.syllable ())) @@ -355,7 +420,7 @@ struct hb_ot_apply_context_t : } void reset (unsigned int start_index_, - unsigned int num_items_) + unsigned int num_items_) { idx = start_index_; num_items = num_items_; @@ -363,7 +428,11 @@ struct hb_ot_apply_context_t : matcher.set_syllable (start_index_ == c->buffer->idx ? c->buffer->cur().syllable () : 0); } - void reject () { num_items++; match_glyph_data--; } + void reject () + { + num_items++; + if (match_glyph_data) match_glyph_data--; + } matcher_t::may_skip_t may_skip (const hb_glyph_info_t &info) const @@ -387,7 +456,7 @@ struct hb_ot_apply_context_t : skip == matcher_t::SKIP_NO)) { num_items--; - match_glyph_data++; + if (match_glyph_data) match_glyph_data++; return true; } @@ -414,7 +483,7 @@ struct hb_ot_apply_context_t : skip == matcher_t::SKIP_NO)) { num_items--; - match_glyph_data++; + if (match_glyph_data) match_glyph_data++; return true; } @@ -467,7 +536,6 @@ struct hb_ot_apply_context_t : unsigned int lookup_index; unsigned int lookup_props; unsigned int nesting_level_left; - unsigned int debug_depth; bool has_glyph_classes; bool auto_zwnj; @@ -478,12 +546,18 @@ struct hb_ot_apply_context_t : hb_ot_apply_context_t (unsigned int table_index_, - hb_font_t *font_, - hb_buffer_t *buffer_) : + hb_font_t *font_, + hb_buffer_t *buffer_) : iter_input (), iter_context (), font (font_), face (font->face), buffer (buffer_), recurse_func (nullptr), - gdef (*face->table.GDEF->table), + gdef ( +#ifndef HB_NO_OT_LAYOUT + *face->table.GDEF->table +#else + Null (GDEF) +#endif + ), var_store (gdef.get_var_store ()), direction (buffer_->props.direction), lookup_mask (1), @@ -491,7 +565,6 @@ struct hb_ot_apply_context_t : lookup_index ((unsigned int) -1), lookup_props (0), nesting_level_left (HB_MAX_NESTING_LEVEL), - debug_depth (0), has_glyph_classes (gdef.has_glyph_classes ()), auto_zwnj (true), auto_zwj (true), @@ -595,13 +668,13 @@ struct hb_ot_apply_context_t : buffer->cur().codepoint = glyph_index; } void replace_glyph_with_ligature (hb_codepoint_t glyph_index, - unsigned int class_guess) const + unsigned int class_guess) const { _set_glyph_props (glyph_index, class_guess, true); buffer->replace_glyph (glyph_index); } void output_glyph_for_component (hb_codepoint_t glyph_index, - unsigned int class_guess) const + unsigned int class_guess) const { _set_glyph_props (glyph_index, class_guess, false, true); buffer->output_glyph (glyph_index); @@ -610,10 +683,10 @@ struct hb_ot_apply_context_t : struct hb_get_subtables_context_t : - hb_dispatch_context_t + hb_dispatch_context_t { template - static bool apply_to (const void *obj, OT::hb_ot_apply_context_t *c) + static inline bool apply_to (const void *obj, OT::hb_ot_apply_context_t *c) { const Type *typed_obj = (const Type *) obj; return typed_obj->apply (c); @@ -629,7 +702,7 @@ struct hb_get_subtables_context_t : obj = &obj_; apply_func = apply_func_; digest.init (); - obj_.get_coverage ().add_coverage (&digest); + obj_.get_coverage ().collect_coverage (&digest); } bool apply (OT::hb_ot_apply_context_t *c) const @@ -646,22 +719,19 @@ struct hb_get_subtables_context_t : typedef hb_vector_t array_t; /* Dispatch interface. */ - const char *get_name () { return "GET_SUBTABLES"; } template return_t dispatch (const T &obj) { hb_applicable_t *entry = array.push(); entry->init (obj, apply_to); - return HB_VOID; + return hb_empty_t (); } - static return_t default_return_value () { return HB_VOID; } + static return_t default_return_value () { return hb_empty_t (); } hb_get_subtables_context_t (array_t &array_) : - array (array_), - debug_depth (0) {} + array (array_) {} array_t &array; - unsigned int debug_depth; }; @@ -700,15 +770,14 @@ static inline bool intersects_coverage (const hb_set_t *glyphs, const HBUINT16 & return (data+coverage).intersects (glyphs); } -static inline bool intersects_array (const hb_set_t *glyphs, - unsigned int count, - const HBUINT16 values[], - intersects_func_t intersects_func, - const void *intersects_data) +static inline bool array_is_subset_of (const hb_set_t *glyphs, + unsigned int count, + const HBUINT16 values[], + intersects_func_t intersects_func, + const void *intersects_data) { - for (unsigned int i = 0; i < count; i++) - if (likely (!intersects_func (glyphs, values[i], intersects_data))) - return false; + for (const HBUINT16 &_ : + hb_iter (values, count)) + if (!intersects_func (glyphs, _, intersects_data)) return false; return true; } @@ -720,12 +789,12 @@ static inline void collect_glyph (hb_set_t *glyphs, const HBUINT16 &value, const static inline void collect_class (hb_set_t *glyphs, const HBUINT16 &value, const void *data) { const ClassDef &class_def = *reinterpret_cast(data); - class_def.add_class (glyphs, value); + class_def.collect_class (glyphs, value); } static inline void collect_coverage (hb_set_t *glyphs, const HBUINT16 &value, const void *data) { const OffsetTo &coverage = (const OffsetTo&)value; - (data+coverage).add_coverage (glyphs); + (data+coverage).collect_coverage (glyphs); } static inline void collect_array (hb_collect_glyphs_context_t *c HB_UNUSED, hb_set_t *glyphs, @@ -734,8 +803,10 @@ static inline void collect_array (hb_collect_glyphs_context_t *c HB_UNUSED, collect_glyphs_func_t collect_func, const void *collect_data) { - for (unsigned int i = 0; i < count; i++) - collect_func (glyphs, values[i], collect_data); + return + + hb_iter (values, count) + | hb_apply ([&] (const HBUINT16 &_) { collect_func (glyphs, _, collect_data); }) + ; } @@ -846,7 +917,7 @@ static inline bool match_input (hb_ot_apply_context_t *c, if (ligbase == LIGBASE_NOT_CHECKED) { bool found = false; - const hb_glyph_info_t *out = buffer->out_info; + const auto *out = buffer->out_info; unsigned int j = buffer->out_len; while (j && _hb_glyph_info_get_lig_id (&out[j - 1]) == first_lig_id) { @@ -970,7 +1041,7 @@ static inline bool ligate_input (hb_ot_apply_context_t *c, if (this_comp == 0) this_comp = last_num_components; unsigned int new_lig_comp = components_so_far - last_num_components + - MIN (this_comp, last_num_components); + hb_min (this_comp, last_num_components); _hb_glyph_info_set_lig_props_for_mark (&buffer->cur(), lig_id, new_lig_comp); } buffer->next_glyph (); @@ -984,18 +1055,19 @@ static inline bool ligate_input (hb_ot_apply_context_t *c, buffer->idx++; } - if (!is_mark_ligature && last_lig_id) { + if (!is_mark_ligature && last_lig_id) + { /* Re-adjust components for any marks following. */ - for (unsigned int i = buffer->idx; i < buffer->len; i++) { - if (last_lig_id == _hb_glyph_info_get_lig_id (&buffer->info[i])) { - unsigned int this_comp = _hb_glyph_info_get_lig_comp (&buffer->info[i]); - if (!this_comp) - break; - unsigned int new_lig_comp = components_so_far - last_num_components + - MIN (this_comp, last_num_components); - _hb_glyph_info_set_lig_props_for_mark (&buffer->info[i], lig_id, new_lig_comp); - } else - break; + for (unsigned i = buffer->idx; i < buffer->len; ++i) + { + if (last_lig_id != _hb_glyph_info_get_lig_id (&buffer->info[i])) break; + + unsigned this_comp = _hb_glyph_info_get_lig_comp (&buffer->info[i]); + if (!this_comp) break; + + unsigned new_lig_comp = components_so_far - last_num_components + + hb_min (this_comp, last_num_components); + _hb_glyph_info_set_lig_props_for_mark (&buffer->info[i], lig_id, new_lig_comp); } } return_trace (true); @@ -1050,6 +1122,17 @@ static inline bool match_lookahead (hb_ot_apply_context_t *c, struct LookupRecord { + LookupRecord* copy (hb_serialize_context_t *c, + const hb_map_t *lookup_map) const + { + TRACE_SERIALIZE (this); + auto *out = c->embed (*this); + if (unlikely (!out)) return_trace (nullptr); + + out->lookupListIndex = hb_map_get (lookup_map, lookupListIndex); + return_trace (out); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -1170,7 +1253,7 @@ static inline bool apply_lookup (hb_ot_apply_context_t *c, else { /* NOTE: delta is negative. */ - delta = MAX (delta, (int) next - (int) count); + delta = hb_max (delta, (int) next - (int) count); next -= delta; } @@ -1221,9 +1304,9 @@ static inline bool context_intersects (const hb_set_t *glyphs, const HBUINT16 input[], /* Array of input values--start with second glyph */ ContextClosureLookupContext &lookup_context) { - return intersects_array (glyphs, - inputCount ? inputCount - 1 : 0, input, - lookup_context.funcs.intersects, lookup_context.intersects_data); + return array_is_subset_of (glyphs, + inputCount ? inputCount - 1 : 0, input, + lookup_context.funcs.intersects, lookup_context.intersects_data); } static inline void context_closure_lookup (hb_closure_context_t *c, @@ -1296,7 +1379,9 @@ struct Rule void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const { - const UnsizedArrayOf &lookupRecord = StructAfter > + if (unlikely (c->lookup_limit_exceeded ())) return; + + const UnsizedArrayOf &lookupRecord = StructAfter> (inputZ.as_array ((inputCount ? inputCount - 1 : 0))); context_closure_lookup (c, inputCount, inputZ.arrayZ, @@ -1304,10 +1389,19 @@ struct Rule lookup_context); } + void closure_lookups (hb_closure_lookups_context_t *c) const + { + if (unlikely (c->lookup_limit_exceeded ())) return; + + const UnsizedArrayOf &lookupRecord = StructAfter> + (inputZ.as_array (inputCount ? inputCount - 1 : 0)); + recurse_lookups (c, lookupCount, lookupRecord.arrayZ); + } + void collect_glyphs (hb_collect_glyphs_context_t *c, ContextCollectGlyphsLookupContext &lookup_context) const { - const UnsizedArrayOf &lookupRecord = StructAfter > + const UnsizedArrayOf &lookupRecord = StructAfter> (inputZ.as_array (inputCount ? inputCount - 1 : 0)); context_collect_glyphs_lookup (c, inputCount, inputZ.arrayZ, @@ -1318,21 +1412,64 @@ struct Rule bool would_apply (hb_would_apply_context_t *c, ContextApplyLookupContext &lookup_context) const { - TRACE_WOULD_APPLY (this); - const UnsizedArrayOf &lookupRecord = StructAfter > + const UnsizedArrayOf &lookupRecord = StructAfter> (inputZ.as_array (inputCount ? inputCount - 1 : 0)); - return_trace (context_would_apply_lookup (c, inputCount, inputZ.arrayZ, lookupCount, lookupRecord.arrayZ, lookup_context)); + return context_would_apply_lookup (c, + inputCount, inputZ.arrayZ, + lookupCount, lookupRecord.arrayZ, + lookup_context); } bool apply (hb_ot_apply_context_t *c, ContextApplyLookupContext &lookup_context) const { TRACE_APPLY (this); - const UnsizedArrayOf &lookupRecord = StructAfter > + const UnsizedArrayOf &lookupRecord = StructAfter> (inputZ.as_array (inputCount ? inputCount - 1 : 0)); return_trace (context_apply_lookup (c, inputCount, inputZ.arrayZ, lookupCount, lookupRecord.arrayZ, lookup_context)); } + bool serialize (hb_serialize_context_t *c, + const hb_map_t *input_mapping, /* old->new glyphid or class mapping */ + const hb_map_t *lookup_map) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!c->extend_min (out))) return_trace (false); + + out->inputCount = inputCount; + out->lookupCount = lookupCount; + + const hb_array_t input = inputZ.as_array (inputCount - 1); + for (const auto org : input) + { + HBUINT16 d; + d = input_mapping->get (org); + c->copy (d); + } + + const UnsizedArrayOf &lookupRecord = StructAfter> + (inputZ.as_array ((inputCount ? inputCount - 1 : 0))); + for (unsigned i = 0; i < (unsigned) lookupCount; i++) + c->copy (lookupRecord[i], lookup_map); + + return_trace (true); + } + + bool subset (hb_subset_context_t *c, + const hb_map_t *lookup_map, + const hb_map_t *klass_map = nullptr) const + { + TRACE_SUBSET (this); + + const hb_array_t input = inputZ.as_array ((inputCount ? inputCount - 1 : 0)); + if (!input.length) return_trace (false); + + const hb_map_t *mapping = klass_map == nullptr ? c->plan->glyph_map : klass_map; + if (!hb_all (input, mapping)) return_trace (false); + return_trace (serialize (c->serializer, mapping, lookup_map)); + } + public: bool sanitize (hb_sanitize_context_t *c) const { @@ -1364,53 +1501,99 @@ struct RuleSet bool intersects (const hb_set_t *glyphs, ContextClosureLookupContext &lookup_context) const { - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - if ((this+rule[i]).intersects (glyphs, lookup_context)) - return true; - return false; + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const Rule &_) { return _.intersects (glyphs, lookup_context); }) + | hb_any + ; } void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const { - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - (this+rule[i]).closure (c, lookup_context); + if (unlikely (c->lookup_limit_exceeded ())) return; + + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const Rule &_) { _.closure (c, lookup_context); }) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const + { + if (unlikely (c->lookup_limit_exceeded ())) return; + + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const Rule &_) { _.closure_lookups (c); }) + ; } void collect_glyphs (hb_collect_glyphs_context_t *c, ContextCollectGlyphsLookupContext &lookup_context) const { - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - (this+rule[i]).collect_glyphs (c, lookup_context); + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const Rule &_) { _.collect_glyphs (c, lookup_context); }) + ; } bool would_apply (hb_would_apply_context_t *c, ContextApplyLookupContext &lookup_context) const { - TRACE_WOULD_APPLY (this); - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - { - if ((this+rule[i]).would_apply (c, lookup_context)) - return_trace (true); - } - return_trace (false); + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const Rule &_) { return _.would_apply (c, lookup_context); }) + | hb_any + ; } bool apply (hb_ot_apply_context_t *c, ContextApplyLookupContext &lookup_context) const { TRACE_APPLY (this); - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) + return_trace ( + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const Rule &_) { return _.apply (c, lookup_context); }) + | hb_any + ) + ; + } + + bool subset (hb_subset_context_t *c, + const hb_map_t *lookup_map, + const hb_map_t *klass_map = nullptr) const + { + TRACE_SUBSET (this); + + auto snap = c->serializer->snapshot (); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + for (const OffsetTo& _ : rule) { - if ((this+rule[i]).apply (c, lookup_context)) - return_trace (true); + if (!_) continue; + auto *o = out->rule.serialize_append (c->serializer); + if (unlikely (!o)) continue; + + auto o_snap = c->serializer->snapshot (); + if (!o->serialize_subset (c, _, this, lookup_map, klass_map)) + { + out->rule.pop (); + c->serializer->revert (o_snap); + } } - return_trace (false); + + bool ret = bool (out->rule); + if (!ret) c->serializer->revert (snap); + + return_trace (ret); } bool sanitize (hb_sanitize_context_t *c) const @@ -1437,16 +1620,14 @@ struct ContextFormat1 nullptr }; - unsigned int count = ruleSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (glyphs->has (iter.get_glyph ()) && - (this+ruleSet[iter.get_coverage ()]).intersects (glyphs, lookup_context)) - return true; - } - return false; + return + + hb_zip (this+coverage, ruleSet) + | hb_filter (*glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_map ([&] (const RuleSet &_) { return _.intersects (glyphs, lookup_context); }) + | hb_any + ; } void closure (hb_closure_context_t *c) const @@ -1456,40 +1637,47 @@ struct ContextFormat1 nullptr }; - unsigned int count = ruleSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (c->glyphs->has (iter.get_glyph ())) - (this+ruleSet[iter.get_coverage ()]).closure (c, lookup_context); - } + + hb_zip (this+coverage, ruleSet) + | hb_filter (*c->glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([&] (const RuleSet &_) { _.closure (c, lookup_context); }) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const + { + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const RuleSet &_) { _.closure_lookups (c); }) + ; } + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - (this+coverage).add_coverage (c->input); + (this+coverage).collect_coverage (c->input); struct ContextCollectGlyphsLookupContext lookup_context = { {collect_glyph}, nullptr }; - unsigned int count = ruleSet.len; - for (unsigned int i = 0; i < count; i++) - (this+ruleSet[i]).collect_glyphs (c, lookup_context); + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const RuleSet &_) { _.collect_glyphs (c, lookup_context); }) + ; } bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); - const RuleSet &rule_set = this+ruleSet[(this+coverage).get_coverage (c->glyphs[0])]; struct ContextApplyLookupContext lookup_context = { {match_glyph}, nullptr }; - return_trace (rule_set.would_apply (c, lookup_context)); + return rule_set.would_apply (c, lookup_context); } const Coverage &get_coverage () const { return this+coverage; } @@ -1512,8 +1700,26 @@ struct ContextFormat1 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups; + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, ruleSet) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->ruleSet, this, lookup_map), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + out->coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); } bool sanitize (hb_sanitize_context_t *c) const @@ -1549,13 +1755,15 @@ struct ContextFormat2 &class_def }; - unsigned int count = ruleSet.len; - for (unsigned int i = 0; i < count; i++) - if (class_def.intersects_class (glyphs, i) && - (this+ruleSet[i]).intersects (glyphs, lookup_context)) - return true; - - return false; + return + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_enumerate + | hb_map ([&] (const hb_pair_t p) + { return class_def.intersects_class (glyphs, p.first) && + p.second.intersects (glyphs, lookup_context); }) + | hb_any + ; } void closure (hb_closure_context_t *c) const @@ -1570,17 +1778,30 @@ struct ContextFormat2 &class_def }; - unsigned int count = ruleSet.len; - for (unsigned int i = 0; i < count; i++) - if (class_def.intersects_class (c->glyphs, i)) { - const RuleSet &rule_set = this+ruleSet[i]; - rule_set.closure (c, lookup_context); - } + return + + hb_enumerate (ruleSet) + | hb_filter ([&] (unsigned _) + { return class_def.intersects_class (c->glyphs, _); }, + hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([&] (const RuleSet &_) { _.closure (c, lookup_context); }) + ; } + void closure_lookups (hb_closure_lookups_context_t *c) const + { + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const RuleSet &_) { _.closure_lookups (c); }) + ; + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - (this+coverage).add_coverage (c->input); + (this+coverage).collect_coverage (c->input); const ClassDef &class_def = this+classDef; struct ContextCollectGlyphsLookupContext lookup_context = { @@ -1588,15 +1809,14 @@ struct ContextFormat2 &class_def }; - unsigned int count = ruleSet.len; - for (unsigned int i = 0; i < count; i++) - (this+ruleSet[i]).collect_glyphs (c, lookup_context); + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const RuleSet &_) { _.collect_glyphs (c, lookup_context); }) + ; } bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); - const ClassDef &class_def = this+classDef; unsigned int index = class_def.get_class (c->glyphs[0]); const RuleSet &rule_set = this+ruleSet[index]; @@ -1604,7 +1824,7 @@ struct ContextFormat2 {match_class}, &class_def }; - return_trace (rule_set.would_apply (c, lookup_context)); + return rule_set.would_apply (c, lookup_context); } const Coverage &get_coverage () const { return this+coverage; } @@ -1628,8 +1848,45 @@ struct ContextFormat2 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + if (unlikely (!out->coverage.serialize_subset (c, coverage, this))) + return_trace (false); + + hb_map_t klass_map; + out->classDef.serialize_subset (c, classDef, this, &klass_map); + + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups; + bool ret = true; + unsigned non_zero_index = 0, index = 0; + for (const hb_pair_t&> _ : + hb_enumerate (ruleSet) + | hb_filter (klass_map, hb_first)) + { + auto *o = out->ruleSet.serialize_append (c->serializer); + if (unlikely (!o)) + { + ret = false; + break; + } + + if (o->serialize_subset (c, _.second, this, lookup_map, &klass_map)) + non_zero_index = index; + + index++; + } + + if (!ret) return_trace (ret); + + //prune empty trailing ruleSets + --index; + while (index > non_zero_index) + { + out->ruleSet.pop (); + index--; + } + + return_trace (bool (out->ruleSet)); } bool sanitize (hb_sanitize_context_t *c) const @@ -1686,9 +1943,17 @@ struct ContextFormat3 lookup_context); } + void closure_lookups (hb_closure_lookups_context_t *c) const + { + const LookupRecord *lookupRecord = &StructAfter (coverageZ.as_array (glyphCount)); + recurse_lookups (c, lookupCount, lookupRecord); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - (this+coverageZ[0]).add_coverage (c->input); + (this+coverageZ[0]).collect_coverage (c->input); const LookupRecord *lookupRecord = &StructAfter (coverageZ.as_array (glyphCount)); struct ContextCollectGlyphsLookupContext lookup_context = { @@ -1704,14 +1969,15 @@ struct ContextFormat3 bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); - const LookupRecord *lookupRecord = &StructAfter (coverageZ.as_array (glyphCount)); struct ContextApplyLookupContext lookup_context = { {match_coverage}, this }; - return_trace (context_would_apply_lookup (c, glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1), lookupCount, lookupRecord, lookup_context)); + return context_would_apply_lookup (c, + glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1), + lookupCount, lookupRecord, + lookup_context); } const Coverage &get_coverage () const { return this+coverageZ[0]; } @@ -1733,8 +1999,28 @@ struct ContextFormat3 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + out->format = format; + out->glyphCount = glyphCount; + out->lookupCount = lookupCount; + + auto coverages = coverageZ.as_array (glyphCount); + + for (const OffsetTo& offset : coverages) + { + auto *o = c->serializer->allocate_size> (OffsetTo::static_size); + if (unlikely (!o)) return_trace (false); + if (!o->serialize_subset (c, offset, this)) return_trace (false); + } + + const LookupRecord *lookupRecord = &StructAfter (coverageZ.as_array (glyphCount)); + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups; + for (unsigned i = 0; i < (unsigned) lookupCount; i++) + c->serializer->copy (lookupRecord[i], lookup_map); + + return_trace (true); } bool sanitize (hb_sanitize_context_t *c) const @@ -1755,7 +2041,7 @@ struct ContextFormat3 HBUINT16 glyphCount; /* Number of glyphs in the input glyph * sequence */ HBUINT16 lookupCount; /* Number of LookupRecords */ - UnsizedArrayOf > + UnsizedArrayOf> coverageZ; /* Array of offsets to Coverage * table in glyph sequence order */ /*UnsizedArrayOf @@ -1767,15 +2053,15 @@ struct ContextFormat3 struct Context { - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); - case 2: return_trace (c->dispatch (u.format2)); - case 3: return_trace (c->dispatch (u.format3)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, hb_forward (ds)...)); + case 3: return_trace (c->dispatch (u.format3, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -1819,15 +2105,15 @@ static inline bool chain_context_intersects (const hb_set_t *glyphs, const HBUINT16 lookahead[], ChainContextClosureLookupContext &lookup_context) { - return intersects_array (glyphs, - backtrackCount, backtrack, - lookup_context.funcs.intersects, lookup_context.intersects_data[0]) - && intersects_array (glyphs, - inputCount ? inputCount - 1 : 0, input, - lookup_context.funcs.intersects, lookup_context.intersects_data[1]) - && intersects_array (glyphs, - lookaheadCount, lookahead, - lookup_context.funcs.intersects, lookup_context.intersects_data[2]); + return array_is_subset_of (glyphs, + backtrackCount, backtrack, + lookup_context.funcs.intersects, lookup_context.intersects_data[0]) + && array_is_subset_of (glyphs, + inputCount ? inputCount - 1 : 0, input, + lookup_context.funcs.intersects, lookup_context.intersects_data[1]) + && array_is_subset_of (glyphs, + lookaheadCount, lookahead, + lookup_context.funcs.intersects, lookup_context.intersects_data[2]); } static inline void chain_context_closure_lookup (hb_closure_context_t *c, @@ -1927,8 +2213,8 @@ struct ChainRule { bool intersects (const hb_set_t *glyphs, ChainContextClosureLookupContext &lookup_context) const { - const HeadlessArrayOf &input = StructAfter > (backtrack); - const ArrayOf &lookahead = StructAfter > (input); + const HeadlessArrayOf &input = StructAfter> (backtrack); + const ArrayOf &lookahead = StructAfter> (input); return chain_context_intersects (glyphs, backtrack.len, backtrack.arrayZ, input.lenP1, input.arrayZ, @@ -1939,9 +2225,11 @@ struct ChainRule void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const { - const HeadlessArrayOf &input = StructAfter > (backtrack); - const ArrayOf &lookahead = StructAfter > (input); - const ArrayOf &lookup = StructAfter > (lookahead); + if (unlikely (c->lookup_limit_exceeded ())) return; + + const HeadlessArrayOf &input = StructAfter> (backtrack); + const ArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); chain_context_closure_lookup (c, backtrack.len, backtrack.arrayZ, input.lenP1, input.arrayZ, @@ -1950,12 +2238,22 @@ struct ChainRule lookup_context); } + void closure_lookups (hb_closure_lookups_context_t *c) const + { + if (unlikely (c->lookup_limit_exceeded ())) return; + + const HeadlessArrayOf &input = StructAfter> (backtrack); + const ArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); + recurse_lookups (c, lookup.len, lookup.arrayZ); + } + void collect_glyphs (hb_collect_glyphs_context_t *c, ChainContextCollectGlyphsLookupContext &lookup_context) const { - const HeadlessArrayOf &input = StructAfter > (backtrack); - const ArrayOf &lookahead = StructAfter > (input); - const ArrayOf &lookup = StructAfter > (lookahead); + const HeadlessArrayOf &input = StructAfter> (backtrack); + const ArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); chain_context_collect_glyphs_lookup (c, backtrack.len, backtrack.arrayZ, input.lenP1, input.arrayZ, @@ -1967,23 +2265,22 @@ struct ChainRule bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const { - TRACE_WOULD_APPLY (this); - const HeadlessArrayOf &input = StructAfter > (backtrack); - const ArrayOf &lookahead = StructAfter > (input); - const ArrayOf &lookup = StructAfter > (lookahead); - return_trace (chain_context_would_apply_lookup (c, - backtrack.len, backtrack.arrayZ, - input.lenP1, input.arrayZ, - lookahead.len, lookahead.arrayZ, lookup.len, - lookup.arrayZ, lookup_context)); + const HeadlessArrayOf &input = StructAfter> (backtrack); + const ArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); + return chain_context_would_apply_lookup (c, + backtrack.len, backtrack.arrayZ, + input.lenP1, input.arrayZ, + lookahead.len, lookahead.arrayZ, lookup.len, + lookup.arrayZ, lookup_context); } bool apply (hb_ot_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const { TRACE_APPLY (this); - const HeadlessArrayOf &input = StructAfter > (backtrack); - const ArrayOf &lookahead = StructAfter > (input); - const ArrayOf &lookup = StructAfter > (lookahead); + const HeadlessArrayOf &input = StructAfter> (backtrack); + const ArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); return_trace (chain_context_apply_lookup (c, backtrack.len, backtrack.arrayZ, input.lenP1, input.arrayZ, @@ -1991,15 +2288,99 @@ struct ChainRule lookup.arrayZ, lookup_context)); } + template + void serialize_array (hb_serialize_context_t *c, + HBUINT16 len, + Iterator it) const + { + c->copy (len); + for (const auto g : it) + { + HBUINT16 gid; + gid = g; + c->copy (gid); + } + } + + ChainRule* copy (hb_serialize_context_t *c, + const hb_map_t *lookup_map, + const hb_map_t *backtrack_map, + const hb_map_t *input_map = nullptr, + const hb_map_t *lookahead_map = nullptr) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!out)) return_trace (nullptr); + + const hb_map_t *mapping = backtrack_map; + serialize_array (c, backtrack.len, + backtrack.iter () + | hb_map (mapping)); + + const HeadlessArrayOf &input = StructAfter> (backtrack); + if (input_map) mapping = input_map; + serialize_array (c, input.lenP1, + input.iter () + | hb_map (mapping)); + + const ArrayOf &lookahead = StructAfter> (input); + if (lookahead_map) mapping = lookahead_map; + serialize_array (c, lookahead.len, + lookahead.iter () + | hb_map (mapping)); + + const ArrayOf &lookupRecord = StructAfter> (lookahead); + HBUINT16 lookupCount; + lookupCount = lookupRecord.len; + if (!c->copy (lookupCount)) return_trace (nullptr); + + for (unsigned i = 0; i < (unsigned) lookupCount; i++) + if (!c->copy (lookupRecord[i], lookup_map)) return_trace (nullptr); + + return_trace (out); + } + + bool subset (hb_subset_context_t *c, + const hb_map_t *lookup_map, + const hb_map_t *backtrack_map = nullptr, + const hb_map_t *input_map = nullptr, + const hb_map_t *lookahead_map = nullptr) const + { + TRACE_SUBSET (this); + + const HeadlessArrayOf &input = StructAfter> (backtrack); + const ArrayOf &lookahead = StructAfter> (input); + + if (!backtrack_map) + { + const hb_set_t &glyphset = *c->plan->glyphset (); + if (!hb_all (backtrack, glyphset) || + !hb_all (input, glyphset) || + !hb_all (lookahead, glyphset)) + return_trace (false); + + copy (c->serializer, lookup_map, c->plan->glyph_map); + } + else + { + if (!hb_all (backtrack, backtrack_map) || + !hb_all (input, input_map) || + !hb_all (lookahead, lookahead_map)) + return_trace (false); + + copy (c->serializer, lookup_map, backtrack_map, input_map, lookahead_map); + } + + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (!backtrack.sanitize (c)) return_trace (false); - const HeadlessArrayOf &input = StructAfter > (backtrack); + const HeadlessArrayOf &input = StructAfter> (backtrack); if (!input.sanitize (c)) return_trace (false); - const ArrayOf &lookahead = StructAfter > (input); + const ArrayOf &lookahead = StructAfter> (input); if (!lookahead.sanitize (c)) return_trace (false); - const ArrayOf &lookup = StructAfter > (lookahead); + const ArrayOf &lookup = StructAfter> (lookahead); return_trace (lookup.sanitize (c)); } @@ -2025,46 +2406,100 @@ struct ChainRuleSet { bool intersects (const hb_set_t *glyphs, ChainContextClosureLookupContext &lookup_context) const { - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - if ((this+rule[i]).intersects (glyphs, lookup_context)) - return true; - return false; + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const ChainRule &_) { return _.intersects (glyphs, lookup_context); }) + | hb_any + ; } void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const { - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - (this+rule[i]).closure (c, lookup_context); + if (unlikely (c->lookup_limit_exceeded ())) return; + + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRule &_) { _.closure (c, lookup_context); }) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const + { + if (unlikely (c->lookup_limit_exceeded ())) return; + + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRule &_) { _.closure_lookups (c); }) + ; } void collect_glyphs (hb_collect_glyphs_context_t *c, ChainContextCollectGlyphsLookupContext &lookup_context) const { - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - (this+rule[i]).collect_glyphs (c, lookup_context); + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRule &_) { _.collect_glyphs (c, lookup_context); }) + ; } bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const { - TRACE_WOULD_APPLY (this); - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - if ((this+rule[i]).would_apply (c, lookup_context)) - return_trace (true); - - return_trace (false); + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const ChainRule &_) { return _.would_apply (c, lookup_context); }) + | hb_any + ; } bool apply (hb_ot_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const { TRACE_APPLY (this); - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - if ((this+rule[i]).apply (c, lookup_context)) - return_trace (true); + return_trace ( + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const ChainRule &_) { return _.apply (c, lookup_context); }) + | hb_any + ) + ; + } + + bool subset (hb_subset_context_t *c, + const hb_map_t *lookup_map, + const hb_map_t *backtrack_klass_map = nullptr, + const hb_map_t *input_klass_map = nullptr, + const hb_map_t *lookahead_klass_map = nullptr) const + { + TRACE_SUBSET (this); + + auto snap = c->serializer->snapshot (); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + for (const OffsetTo& _ : rule) + { + if (!_) continue; + auto *o = out->rule.serialize_append (c->serializer); + if (unlikely (!o)) continue; + + auto o_snap = c->serializer->snapshot (); + if (!o->serialize_subset (c, _, this, + lookup_map, + backtrack_klass_map, + input_klass_map, + lookahead_klass_map)) + { + out->rule.pop (); + c->serializer->revert (o_snap); + } + } + + bool ret = bool (out->rule); + if (!ret) c->serializer->revert (snap); - return_trace (false); + return_trace (ret); } bool sanitize (hb_sanitize_context_t *c) const @@ -2090,16 +2525,14 @@ struct ChainContextFormat1 {nullptr, nullptr, nullptr} }; - unsigned int count = ruleSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (glyphs->has (iter.get_glyph ()) && - (this+ruleSet[iter.get_coverage ()]).intersects (glyphs, lookup_context)) - return true; - } - return false; + return + + hb_zip (this+coverage, ruleSet) + | hb_filter (*glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_map ([&] (const ChainRuleSet &_) { return _.intersects (glyphs, lookup_context); }) + | hb_any + ; } void closure (hb_closure_context_t *c) const @@ -2109,40 +2542,47 @@ struct ChainContextFormat1 {nullptr, nullptr, nullptr} }; - unsigned int count = ruleSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (c->glyphs->has (iter.get_glyph ())) - (this+ruleSet[iter.get_coverage ()]).closure (c, lookup_context); - } + + hb_zip (this+coverage, ruleSet) + | hb_filter (*c->glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRuleSet &_) { _.closure (c, lookup_context); }) + ; } + void closure_lookups (hb_closure_lookups_context_t *c) const + { + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRuleSet &_) { _.closure_lookups (c); }) + ; + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - (this+coverage).add_coverage (c->input); + (this+coverage).collect_coverage (c->input); struct ChainContextCollectGlyphsLookupContext lookup_context = { {collect_glyph}, {nullptr, nullptr, nullptr} }; - unsigned int count = ruleSet.len; - for (unsigned int i = 0; i < count; i++) - (this+ruleSet[i]).collect_glyphs (c, lookup_context); + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRuleSet &_) { _.collect_glyphs (c, lookup_context); }) + ; } bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); - const ChainRuleSet &rule_set = this+ruleSet[(this+coverage).get_coverage (c->glyphs[0])]; struct ChainContextApplyLookupContext lookup_context = { {match_glyph}, {nullptr, nullptr, nullptr} }; - return_trace (rule_set.would_apply (c, lookup_context)); + return rule_set.would_apply (c, lookup_context); } const Coverage &get_coverage () const { return this+coverage; } @@ -2164,8 +2604,26 @@ struct ChainContextFormat1 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups; + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, ruleSet) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->ruleSet, this, lookup_map), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + out->coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); } bool sanitize (hb_sanitize_context_t *c) const @@ -2204,13 +2662,15 @@ struct ChainContextFormat2 &lookahead_class_def} }; - unsigned int count = ruleSet.len; - for (unsigned int i = 0; i < count; i++) - if (input_class_def.intersects_class (glyphs, i) && - (this+ruleSet[i]).intersects (glyphs, lookup_context)) - return true; - - return false; + return + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_enumerate + | hb_map ([&] (const hb_pair_t p) + { return input_class_def.intersects_class (glyphs, p.first) && + p.second.intersects (glyphs, lookup_context); }) + | hb_any + ; } void closure (hb_closure_context_t *c) const { @@ -2228,17 +2688,30 @@ struct ChainContextFormat2 &lookahead_class_def} }; - unsigned int count = ruleSet.len; - for (unsigned int i = 0; i < count; i++) - if (input_class_def.intersects_class (c->glyphs, i)) { - const ChainRuleSet &rule_set = this+ruleSet[i]; - rule_set.closure (c, lookup_context); - } + return + + hb_enumerate (ruleSet) + | hb_filter ([&] (unsigned _) + { return input_class_def.intersects_class (c->glyphs, _); }, + hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRuleSet &_) { _.closure (c, lookup_context); }) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const + { + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRuleSet &_) { _.closure_lookups (c); }) + ; } + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - (this+coverage).add_coverage (c->input); + (this+coverage).collect_coverage (c->input); const ClassDef &backtrack_class_def = this+backtrackClassDef; const ClassDef &input_class_def = this+inputClassDef; @@ -2251,15 +2724,14 @@ struct ChainContextFormat2 &lookahead_class_def} }; - unsigned int count = ruleSet.len; - for (unsigned int i = 0; i < count; i++) - (this+ruleSet[i]).collect_glyphs (c, lookup_context); + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRuleSet &_) { _.collect_glyphs (c, lookup_context); }) + ; } bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); - const ClassDef &backtrack_class_def = this+backtrackClassDef; const ClassDef &input_class_def = this+inputClassDef; const ClassDef &lookahead_class_def = this+lookaheadClassDef; @@ -2272,7 +2744,7 @@ struct ChainContextFormat2 &input_class_def, &lookahead_class_def} }; - return_trace (rule_set.would_apply (c, lookup_context)); + return rule_set.would_apply (c, lookup_context); } const Coverage &get_coverage () const { return this+coverage; } @@ -2301,8 +2773,61 @@ struct ChainContextFormat2 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + out->coverage.serialize_subset (c, coverage, this); + + hb_map_t backtrack_klass_map; + out->backtrackClassDef.serialize_subset (c, backtrackClassDef, this, &backtrack_klass_map); + if (unlikely (!c->serializer->check_success (!backtrack_klass_map.in_error ()))) + return_trace (false); + + // subset inputClassDef based on glyphs survived in Coverage subsetting + hb_map_t input_klass_map; + out->inputClassDef.serialize_subset (c, inputClassDef, this, &input_klass_map); + if (unlikely (!c->serializer->check_success (!input_klass_map.in_error ()))) + return_trace (false); + + hb_map_t lookahead_klass_map; + out->lookaheadClassDef.serialize_subset (c, lookaheadClassDef, this, &lookahead_klass_map); + if (unlikely (!c->serializer->check_success (!lookahead_klass_map.in_error ()))) + return_trace (false); + + unsigned non_zero_index = 0, index = 0; + bool ret = true; + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups; + for (const OffsetTo& _ : + hb_enumerate (ruleSet) + | hb_filter (input_klass_map, hb_first) + | hb_map (hb_second)) + { + auto *o = out->ruleSet.serialize_append (c->serializer); + if (unlikely (!o)) + { + ret = false; + break; + } + if (o->serialize_subset (c, _, this, + lookup_map, + &backtrack_klass_map, + &input_klass_map, + &lookahead_klass_map)) + non_zero_index = index; + + index++; + } + + if (!ret) return_trace (ret); + + //prune empty trailing ruleSets + --index; + while (index > non_zero_index) + { + out->ruleSet.pop (); + index--; + } + + return_trace (bool (out->ruleSet)); } bool sanitize (hb_sanitize_context_t *c) const @@ -2343,12 +2868,12 @@ struct ChainContextFormat3 { bool intersects (const hb_set_t *glyphs) const { - const OffsetArrayOf &input = StructAfter > (backtrack); + const OffsetArrayOf &input = StructAfter> (backtrack); if (!(this+input[0]).intersects (glyphs)) return false; - const OffsetArrayOf &lookahead = StructAfter > (input); + const OffsetArrayOf &lookahead = StructAfter> (input); struct ChainContextClosureLookupContext lookup_context = { {intersects_coverage}, {this, this, this} @@ -2362,13 +2887,13 @@ struct ChainContextFormat3 void closure (hb_closure_context_t *c) const { - const OffsetArrayOf &input = StructAfter > (backtrack); + const OffsetArrayOf &input = StructAfter> (backtrack); if (!(this+input[0]).intersects (c->glyphs)) return; - const OffsetArrayOf &lookahead = StructAfter > (input); - const ArrayOf &lookup = StructAfter > (lookahead); + const OffsetArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); struct ChainContextClosureLookupContext lookup_context = { {intersects_coverage}, {this, this, this} @@ -2381,14 +2906,24 @@ struct ChainContextFormat3 lookup_context); } + void closure_lookups (hb_closure_lookups_context_t *c) const + { + const OffsetArrayOf &input = StructAfter> (backtrack); + const OffsetArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); + recurse_lookups (c, lookup.len, lookup.arrayZ); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - const OffsetArrayOf &input = StructAfter > (backtrack); + const OffsetArrayOf &input = StructAfter> (backtrack); - (this+input[0]).add_coverage (c->input); + (this+input[0]).collect_coverage (c->input); - const OffsetArrayOf &lookahead = StructAfter > (input); - const ArrayOf &lookup = StructAfter > (lookahead); + const OffsetArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); struct ChainContextCollectGlyphsLookupContext lookup_context = { {collect_coverage}, {this, this, this} @@ -2403,38 +2938,36 @@ struct ChainContextFormat3 bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); - - const OffsetArrayOf &input = StructAfter > (backtrack); - const OffsetArrayOf &lookahead = StructAfter > (input); - const ArrayOf &lookup = StructAfter > (lookahead); + const OffsetArrayOf &input = StructAfter> (backtrack); + const OffsetArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); struct ChainContextApplyLookupContext lookup_context = { {match_coverage}, {this, this, this} }; - return_trace (chain_context_would_apply_lookup (c, - backtrack.len, (const HBUINT16 *) backtrack.arrayZ, - input.len, (const HBUINT16 *) input.arrayZ + 1, - lookahead.len, (const HBUINT16 *) lookahead.arrayZ, - lookup.len, lookup.arrayZ, lookup_context)); + return chain_context_would_apply_lookup (c, + backtrack.len, (const HBUINT16 *) backtrack.arrayZ, + input.len, (const HBUINT16 *) input.arrayZ + 1, + lookahead.len, (const HBUINT16 *) lookahead.arrayZ, + lookup.len, lookup.arrayZ, lookup_context); } const Coverage &get_coverage () const { - const OffsetArrayOf &input = StructAfter > (backtrack); + const OffsetArrayOf &input = StructAfter> (backtrack); return this+input[0]; } bool apply (hb_ot_apply_context_t *c) const { TRACE_APPLY (this); - const OffsetArrayOf &input = StructAfter > (backtrack); + const OffsetArrayOf &input = StructAfter> (backtrack); unsigned int index = (this+input[0]).get_coverage (c->buffer->cur().codepoint); if (likely (index == NOT_COVERED)) return_trace (false); - const OffsetArrayOf &lookahead = StructAfter > (input); - const ArrayOf &lookup = StructAfter > (lookahead); + const OffsetArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); struct ChainContextApplyLookupContext lookup_context = { {match_coverage}, {this, this, this} @@ -2446,23 +2979,63 @@ struct ChainContextFormat3 lookup.len, lookup.arrayZ, lookup_context)); } + template + bool serialize_coverage_offsets (hb_subset_context_t *c, Iterator it, const void* base) const + { + TRACE_SERIALIZE (this); + auto *out = c->serializer->start_embed> (); + + if (unlikely (!c->serializer->allocate_size (HBUINT16::static_size))) return_trace (false); + + + it + | hb_apply (subset_offset_array (c, *out, base)) + ; + + return_trace (out->len); + } + bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + + auto *out = c->serializer->start_embed (this); + if (unlikely (!out)) return_trace (false); + if (unlikely (!c->serializer->embed (this->format))) return_trace (false); + + if (!serialize_coverage_offsets (c, backtrack.iter (), this)) + return_trace (false); + + const OffsetArrayOf &input = StructAfter> (backtrack); + if (!serialize_coverage_offsets (c, input.iter (), this)) + return_trace (false); + + const OffsetArrayOf &lookahead = StructAfter> (input); + if (!serialize_coverage_offsets (c, lookahead.iter (), this)) + return_trace (false); + + const ArrayOf &lookupRecord = StructAfter> (lookahead); + HBUINT16 lookupCount; + lookupCount = lookupRecord.len; + if (!c->serializer->copy (lookupCount)) return_trace (false); + + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups; + for (unsigned i = 0; i < (unsigned) lookupCount; i++) + if (!c->serializer->copy (lookupRecord[i], lookup_map)) return_trace (false); + + return_trace (true); } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (!backtrack.sanitize (c, this)) return_trace (false); - const OffsetArrayOf &input = StructAfter > (backtrack); + const OffsetArrayOf &input = StructAfter> (backtrack); if (!input.sanitize (c, this)) return_trace (false); if (!input.len) return_trace (false); /* To be consistent with Context. */ - const OffsetArrayOf &lookahead = StructAfter > (input); + const OffsetArrayOf &lookahead = StructAfter> (input); if (!lookahead.sanitize (c, this)) return_trace (false); - const ArrayOf &lookup = StructAfter > (lookahead); + const ArrayOf &lookup = StructAfter> (lookahead); return_trace (lookup.sanitize (c)); } @@ -2489,15 +3062,15 @@ struct ChainContextFormat3 struct ChainContext { - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); - case 2: return_trace (c->dispatch (u.format2)); - case 3: return_trace (c->dispatch (u.format3)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, hb_forward (ds)...)); + case 3: return_trace (c->dispatch (u.format3, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -2519,26 +3092,24 @@ struct ExtensionFormat1 template const X& get_subtable () const - { - unsigned int offset = extensionOffset; - if (unlikely (!offset)) return Null(typename T::SubTable); - return StructAtOffset (this, offset); - } + { return this + reinterpret_cast &> (extensionOffset); } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, format); if (unlikely (!c->may_dispatch (this, this))) return_trace (c->no_dispatch_return_value ()); - return_trace (get_subtable ().dispatch (c, get_type ())); + return_trace (get_subtable ().dispatch (c, get_type (), hb_forward (ds)...)); } + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { dispatch (c); } + /* This is called from may_dispatch() above with hb_sanitize_context_t. */ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && - extensionOffset != 0 && extensionLookupType != T::SubTable::Extension); } @@ -2547,7 +3118,7 @@ struct ExtensionFormat1 HBUINT16 extensionLookupType; /* Lookup type of subtable referenced * by ExtensionOffset (i.e. the * extension subtable). */ - HBUINT32 extensionOffset; /* Offset to the extension subtable, + Offset32 extensionOffset; /* Offset to the extension subtable, * of lookup type subtable. */ public: DEFINE_SIZE_STATIC (8); @@ -2568,17 +3139,17 @@ struct Extension { switch (u.format) { case 1: return u.format1.template get_subtable (); - default:return Null(typename T::SubTable); + default:return Null (typename T::SubTable); } } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (u.format1.dispatch (c)); + case 1: return_trace (u.format1.dispatch (c, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -2601,7 +3172,7 @@ struct hb_ot_layout_lookup_accelerator_t void init (const TLookup &lookup) { digest.init (); - lookup.add_coverage (&digest); + lookup.collect_coverage (&digest); subtables.init (); OT::hb_get_subtables_context_t c_get_subtables (subtables); @@ -2661,11 +3232,18 @@ struct GSUBGPOS bool find_variations_index (const int *coords, unsigned int num_coords, unsigned int *index) const - { return (version.to_int () >= 0x00010001u ? this+featureVars : Null(FeatureVariations)) - .find_index (coords, num_coords, index); } + { +#ifdef HB_NO_VAR + *index = FeatureVariations::NOT_FOUND_INDEX; + return false; +#endif + return (version.to_int () >= 0x00010001u ? this+featureVars : Null (FeatureVariations)) + .find_index (coords, num_coords, index); + } const Feature& get_feature_variation (unsigned int feature_index, unsigned int variations_index) const { +#ifndef HB_NO_VAR if (FeatureVariations::NOT_FOUND_INDEX != variations_index && version.to_int () >= 0x00010001u) { @@ -2674,32 +3252,90 @@ struct GSUBGPOS if (feature) return *feature; } +#endif return get_feature (feature_index); } + void feature_variation_collect_lookups (const hb_set_t *feature_indexes, + hb_set_t *lookup_indexes /* OUT */) const + { +#ifndef HB_NO_VAR + if (version.to_int () >= 0x00010001u) + (this+featureVars).collect_lookups (feature_indexes, lookup_indexes); +#endif + } + template - bool subset (hb_subset_context_t *c) const + void closure_lookups (hb_face_t *face, + const hb_set_t *glyphs, + hb_set_t *lookup_indexes /* IN/OUT */) const { - TRACE_SUBSET (this); - struct GSUBGPOS *out = c->serializer->embed (*this); - if (unlikely (!out)) return_trace (false); + hb_set_t visited_lookups, inactive_lookups; + OT::hb_closure_lookups_context_t c (face, glyphs, &visited_lookups, &inactive_lookups); - out->scriptList.serialize_subset (c, this+scriptList, out); - out->featureList.serialize_subset (c, this+featureList, out); + for (unsigned lookup_index : + hb_iter (lookup_indexes)) + reinterpret_cast (get_lookup (lookup_index)).closure_lookups (&c, lookup_index); - typedef OffsetListOf TLookupList; - /* TODO Use intersects() to count how many subtables survive? */ - CastR > (out->lookupList) - .serialize_subset (c, - this+CastR > (lookupList), - out); + hb_set_union (lookup_indexes, &visited_lookups); + hb_set_subtract (lookup_indexes, &inactive_lookups); + } + + template + bool subset (hb_subset_layout_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->subset_context->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + typedef LookupOffsetList TLookupList; + reinterpret_cast &> (out->lookupList) + .serialize_subset (c->subset_context, + reinterpret_cast &> (lookupList), + this, + c); + + reinterpret_cast &> (out->featureList) + .serialize_subset (c->subset_context, + reinterpret_cast &> (featureList), + this, + c); + + out->scriptList.serialize_subset (c->subset_context, + scriptList, + this, + c); + +#ifndef HB_NO_VAR if (version.to_int () >= 0x00010001u) - out->featureVars.serialize_subset (c, this+featureVars, out); + { + bool ret = out->featureVars.serialize_subset (c->subset_context, featureVars, this, c); + if (!ret) + { + out->version.major = 1; + out->version.minor = 0; + } + } +#endif return_trace (true); } + void closure_features (const hb_map_t *lookup_indexes, /* IN */ + hb_set_t *feature_indexes /* OUT */) const + { + unsigned int feature_count = hb_min (get_feature_count (), (unsigned) HB_MAX_FEATURES); + for (unsigned i = 0; i < feature_count; i++) + { + const Feature& f = get_feature (i); + if ((!f.featureParams.is_null ()) || f.intersects_lookup_indexes (lookup_indexes)) + feature_indexes->add (i); + } +#ifndef HB_NO_VAR + if (version.to_int () >= 0x00010001u) + (this+featureVars).closure_features (lookup_indexes, feature_indexes); +#endif + } + unsigned int get_size () const { return min_size + @@ -2711,12 +3347,19 @@ struct GSUBGPOS { TRACE_SANITIZE (this); typedef OffsetListOf TLookupList; - return_trace (version.sanitize (c) && - likely (version.major == 1) && - scriptList.sanitize (c, this) && - featureList.sanitize (c, this) && - CastR > (lookupList).sanitize (c, this) && - (version.to_int () < 0x00010001u || featureVars.sanitize (c, this))); + if (unlikely (!(version.sanitize (c) && + likely (version.major == 1) && + scriptList.sanitize (c, this) && + featureList.sanitize (c, this) && + reinterpret_cast &> (lookupList).sanitize (c, this)))) + return_trace (false); + +#ifndef HB_NO_VAR + if (unlikely (!(version.to_int () < 0x00010001u || featureVars.sanitize (c, this)))) + return_trace (false); +#endif + + return_trace (true); } template @@ -2724,8 +3367,8 @@ struct GSUBGPOS { void init (hb_face_t *face) { - this->table = hb_sanitize_context_t().reference_table (face); - if (unlikely (this->table->is_blacklisted (this->table.get_blob (), face))) + this->table = hb_sanitize_context_t ().reference_table (face); + if (unlikely (this->table->is_blocklisted (this->table.get_blob (), face))) { hb_blob_destroy (this->table.get_blob ()); this->table = hb_blob_get_empty (); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-jstf-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-jstf-table.hh index 7a543b0aa40..f8cd27f9221 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-jstf-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-jstf-table.hh @@ -136,7 +136,7 @@ struct JstfLangSys : OffsetListOf * ExtenderGlyphs -- Extender Glyph Table */ -typedef SortedArrayOf ExtenderGlyphs; +typedef SortedArrayOf ExtenderGlyphs; /* diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc index a8e579b4472..c326120628b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc @@ -28,6 +28,14 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_LAYOUT + +#ifdef HB_NO_OT_TAG +#error "Cannot compile hb-ot-layout.cc with HB_NO_OT_TAG." +#endif + #include "hb-open-type.hh" #include "hb-ot-layout.hh" #include "hb-ot-face.hh" @@ -35,7 +43,6 @@ #include "hb-map.hh" #include "hb-ot-kern-table.hh" -#include "hb-ot-gasp-table.hh" // Just so we compile it; unused otherwise. #include "hb-ot-layout-gdef-table.hh" #include "hb-ot-layout-gsub-table.hh" #include "hb-ot-layout-gpos-table.hh" @@ -44,9 +51,8 @@ #include "hb-ot-name-table.hh" #include "hb-ot-os2-table.hh" -#include "hb-aat-layout-lcar-table.hh" #include "hb-aat-layout-morx-table.hh" - +#include "hb-aat-layout-opbd-table.hh" // Just so we compile it; unused otherwise. /** * SECTION:hb-ot-layout @@ -62,18 +68,53 @@ * kern */ +#ifndef HB_NO_OT_KERN +/** + * hb_ot_layout_has_kerning: + * @face: The #hb_face_t to work on + * + * Tests whether a face includes any kerning data in the 'kern' table. + * Does NOT test for kerning lookups in the GPOS table. + * + * Return value: true if data found, false otherwise + * + **/ bool hb_ot_layout_has_kerning (hb_face_t *face) { return face->table.kern->has_data (); } +/** + * hb_ot_layout_has_machine_kerning: + * @face: The #hb_face_t to work on + * + * Tests whether a face includes any state-machine kerning in the 'kern' table. + * Does NOT examine the GPOS table. + * + * Return value: true if data found, false otherwise + * + **/ bool hb_ot_layout_has_machine_kerning (hb_face_t *face) { return face->table.kern->has_state_machine (); } +/** + * hb_ot_layout_has_cross_kerning: + * @face: The #hb_face_t to work on + * + * Tests whether a face has any cross-stream kerning (i.e., kerns + * that make adjustments perpendicular to the direction of the text + * flow: Y adjustments in horizontal text or X adjustments in + * vertical text) in the 'kern' table. + * + * Does NOT examine the GPOS table. + * + * Return value: true is data found, false otherwise + * + **/ bool hb_ot_layout_has_cross_kerning (hb_face_t *face) { @@ -92,6 +133,7 @@ hb_ot_layout_kern (const hb_ot_shape_plan_t *plan, kern.apply (&c); } +#endif /* @@ -99,10 +141,13 @@ hb_ot_layout_kern (const hb_ot_shape_plan_t *plan, */ bool -OT::GDEF::is_blacklisted (hb_blob_t *blob, +OT::GDEF::is_blocklisted (hb_blob_t *blob, hb_face_t *face) const { - /* The ugly business of blacklisting individual fonts' tables happen here! +#ifdef HB_NO_OT_LAYOUT_BLACKLIST + return false; +#endif + /* The ugly business of blocklisting individual fonts' tables happen here! * See this thread for why we finally had to bend in and do this: * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html * @@ -119,84 +164,82 @@ OT::GDEF::is_blacklisted (hb_blob_t *blob, * https://bugzilla.mozilla.org/show_bug.cgi?id=1279693 * https://bugzilla.mozilla.org/show_bug.cgi?id=1279875 */ -#define ENCODE(x,y,z) (((uint64_t) (x) << 48) | ((uint64_t) (y) << 24) | (uint64_t) (z)) - switch ENCODE(blob->length, - face->table.GSUB->table.get_length (), - face->table.GPOS->table.get_length ()) + switch HB_CODEPOINT_ENCODE3(blob->length, + face->table.GSUB->table.get_length (), + face->table.GPOS->table.get_length ()) { /* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */ - case ENCODE (442, 2874, 42038): + case HB_CODEPOINT_ENCODE3 (442, 2874, 42038): /* sha1sum:37fc8c16a0894ab7b749e35579856c73c840867b Windows 7? timesbi.ttf */ - case ENCODE (430, 2874, 40662): + case HB_CODEPOINT_ENCODE3 (430, 2874, 40662): /* sha1sum:19fc45110ea6cd3cdd0a5faca256a3797a069a80 Windows 7 timesi.ttf */ - case ENCODE (442, 2874, 39116): + case HB_CODEPOINT_ENCODE3 (442, 2874, 39116): /* sha1sum:6d2d3c9ed5b7de87bc84eae0df95ee5232ecde26 Windows 7 timesbi.ttf */ - case ENCODE (430, 2874, 39374): + case HB_CODEPOINT_ENCODE3 (430, 2874, 39374): /* sha1sum:8583225a8b49667c077b3525333f84af08c6bcd8 OS X 10.11.3 Times New Roman Italic.ttf */ - case ENCODE (490, 3046, 41638): + case HB_CODEPOINT_ENCODE3 (490, 3046, 41638): /* sha1sum:ec0f5a8751845355b7c3271d11f9918a966cb8c9 OS X 10.11.3 Times New Roman Bold Italic.ttf */ - case ENCODE (478, 3046, 41902): + case HB_CODEPOINT_ENCODE3 (478, 3046, 41902): /* sha1sum:96eda93f7d33e79962451c6c39a6b51ee893ce8c tahoma.ttf from Windows 8 */ - case ENCODE (898, 12554, 46470): + case HB_CODEPOINT_ENCODE3 (898, 12554, 46470): /* sha1sum:20928dc06014e0cd120b6fc942d0c3b1a46ac2bc tahomabd.ttf from Windows 8 */ - case ENCODE (910, 12566, 47732): + case HB_CODEPOINT_ENCODE3 (910, 12566, 47732): /* sha1sum:4f95b7e4878f60fa3a39ca269618dfde9721a79e tahoma.ttf from Windows 8.1 */ - case ENCODE (928, 23298, 59332): + case HB_CODEPOINT_ENCODE3 (928, 23298, 59332): /* sha1sum:6d400781948517c3c0441ba42acb309584b73033 tahomabd.ttf from Windows 8.1 */ - case ENCODE (940, 23310, 60732): + case HB_CODEPOINT_ENCODE3 (940, 23310, 60732): /* tahoma.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ - case ENCODE (964, 23836, 60072): + case HB_CODEPOINT_ENCODE3 (964, 23836, 60072): /* tahomabd.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ - case ENCODE (976, 23832, 61456): + case HB_CODEPOINT_ENCODE3 (976, 23832, 61456): /* sha1sum:e55fa2dfe957a9f7ec26be516a0e30b0c925f846 tahoma.ttf from Windows 10 */ - case ENCODE (994, 24474, 60336): + case HB_CODEPOINT_ENCODE3 (994, 24474, 60336): /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343 tahomabd.ttf from Windows 10 */ - case ENCODE (1006, 24470, 61740): + case HB_CODEPOINT_ENCODE3 (1006, 24470, 61740): /* tahoma.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ - case ENCODE (1006, 24576, 61346): + case HB_CODEPOINT_ENCODE3 (1006, 24576, 61346): /* tahomabd.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ - case ENCODE (1018, 24572, 62828): + case HB_CODEPOINT_ENCODE3 (1018, 24572, 62828): /* sha1sum:b9c84d820c49850d3d27ec498be93955b82772b5 tahoma.ttf from Windows 10 AU */ - case ENCODE (1006, 24576, 61352): + case HB_CODEPOINT_ENCODE3 (1006, 24576, 61352): /* sha1sum:2bdfaab28174bdadd2f3d4200a30a7ae31db79d2 tahomabd.ttf from Windows 10 AU */ - case ENCODE (1018, 24572, 62834): + case HB_CODEPOINT_ENCODE3 (1018, 24572, 62834): /* sha1sum:b0d36cf5a2fbe746a3dd277bffc6756a820807a7 Tahoma.ttf from Mac OS X 10.9 */ - case ENCODE (832, 7324, 47162): + case HB_CODEPOINT_ENCODE3 (832, 7324, 47162): /* sha1sum:12fc4538e84d461771b30c18b5eb6bd434e30fba Tahoma Bold.ttf from Mac OS X 10.9 */ - case ENCODE (844, 7302, 45474): + case HB_CODEPOINT_ENCODE3 (844, 7302, 45474): /* sha1sum:eb8afadd28e9cf963e886b23a30b44ab4fd83acc himalaya.ttf from Windows 7 */ - case ENCODE (180, 13054, 7254): + case HB_CODEPOINT_ENCODE3 (180, 13054, 7254): /* sha1sum:73da7f025b238a3f737aa1fde22577a6370f77b0 himalaya.ttf from Windows 8 */ - case ENCODE (192, 12638, 7254): + case HB_CODEPOINT_ENCODE3 (192, 12638, 7254): /* sha1sum:6e80fd1c0b059bbee49272401583160dc1e6a427 himalaya.ttf from Windows 8.1 */ - case ENCODE (192, 12690, 7254): + case HB_CODEPOINT_ENCODE3 (192, 12690, 7254): /* 8d9267aea9cd2c852ecfb9f12a6e834bfaeafe44 cantarell-fonts-0.0.21/otf/Cantarell-Regular.otf */ /* 983988ff7b47439ab79aeaf9a45bd4a2c5b9d371 cantarell-fonts-0.0.21/otf/Cantarell-Oblique.otf */ - case ENCODE (188, 248, 3852): + case HB_CODEPOINT_ENCODE3 (188, 248, 3852): /* 2c0c90c6f6087ffbfea76589c93113a9cbb0e75f cantarell-fonts-0.0.21/otf/Cantarell-Bold.otf */ /* 55461f5b853c6da88069ffcdf7f4dd3f8d7e3e6b cantarell-fonts-0.0.21/otf/Cantarell-Bold-Oblique.otf */ - case ENCODE (188, 264, 3426): + case HB_CODEPOINT_ENCODE3 (188, 264, 3426): /* d125afa82a77a6475ac0e74e7c207914af84b37a padauk-2.80/Padauk.ttf RHEL 7.2 */ - case ENCODE (1058, 47032, 11818): + case HB_CODEPOINT_ENCODE3 (1058, 47032, 11818): /* 0f7b80437227b90a577cc078c0216160ae61b031 padauk-2.80/Padauk-Bold.ttf RHEL 7.2*/ - case ENCODE (1046, 47030, 12600): + case HB_CODEPOINT_ENCODE3 (1046, 47030, 12600): /* d3dde9aa0a6b7f8f6a89ef1002e9aaa11b882290 padauk-2.80/Padauk.ttf Ubuntu 16.04 */ - case ENCODE (1058, 71796, 16770): + case HB_CODEPOINT_ENCODE3 (1058, 71796, 16770): /* 5f3c98ccccae8a953be2d122c1b3a77fd805093f padauk-2.80/Padauk-Bold.ttf Ubuntu 16.04 */ - case ENCODE (1046, 71790, 17862): + case HB_CODEPOINT_ENCODE3 (1046, 71790, 17862): /* 6c93b63b64e8b2c93f5e824e78caca555dc887c7 padauk-2.80/Padauk-book.ttf */ - case ENCODE (1046, 71788, 17112): + case HB_CODEPOINT_ENCODE3 (1046, 71788, 17112): /* d89b1664058359b8ec82e35d3531931125991fb9 padauk-2.80/Padauk-bookbold.ttf */ - case ENCODE (1058, 71794, 17514): + case HB_CODEPOINT_ENCODE3 (1058, 71794, 17514): /* 824cfd193aaf6234b2b4dc0cf3c6ef576c0d00ef padauk-3.0/Padauk-book.ttf */ - case ENCODE (1330, 109904, 57938): + case HB_CODEPOINT_ENCODE3 (1330, 109904, 57938): /* 91fcc10cf15e012d27571e075b3b4dfe31754a8a padauk-3.0/Padauk-bookbold.ttf */ - case ENCODE (1330, 109904, 58972): + case HB_CODEPOINT_ENCODE3 (1330, 109904, 58972): /* sha1sum: c26e41d567ed821bed997e937bc0c41435689e85 Padauk.ttf * "Padauk Regular" "Version 2.5", see https://crbug.com/681813 */ - case ENCODE (1004, 59092, 14836): + case HB_CODEPOINT_ENCODE3 (1004, 59092, 14836): return true; -#undef ENCODE } return false; } @@ -219,6 +262,15 @@ _hb_ot_layout_set_glyph_props (hb_font_t *font, /* Public API */ +/** + * hb_ot_layout_has_glyph_classes: + * @face: #hb_face_t to work upon + * + * Tests whether a face has any glyph classes defined in its GDEF table. + * + * Return value: true if data found, false otherwise + * + **/ hb_bool_t hb_ot_layout_has_glyph_classes (hb_face_t *face) { @@ -227,6 +279,13 @@ hb_ot_layout_has_glyph_classes (hb_face_t *face) /** * hb_ot_layout_get_glyph_class: + * @face: The #hb_face_t to work on + * @glyph: The #hb_codepoint_t code point to query + * + * Fetches the GDEF class of the requested glyph in the specified face. + * + * Return value: The #hb_ot_layout_glyph_class_t glyph class of the given code + * point in the GDEF table of the face. * * Since: 0.9.7 **/ @@ -239,6 +298,13 @@ hb_ot_layout_get_glyph_class (hb_face_t *face, /** * hb_ot_layout_get_glyphs_in_class: + * @face: The #hb_face_t to work on + * @klass: The #hb_ot_layout_glyph_class_t GDEF class to retrieve + * @glyphs: (out): The #hb_set_t set of all glyphs belonging to the requested + * class. + * + * Retrieves the set of all glyphs from the face that belong to the requested + * glyph class in the face's GDEF table. * * Since: 0.9.7 **/ @@ -250,6 +316,22 @@ hb_ot_layout_get_glyphs_in_class (hb_face_t *face, return face->table.GDEF->table->get_glyphs_in_class (klass, glyphs); } +#ifndef HB_NO_LAYOUT_UNUSED +/** + * hb_ot_layout_get_attach_points: + * @face: The #hb_face_t to work on + * @glyph: The #hb_codepoint_t code point to query + * @start_offset: offset of the first attachment point to retrieve + * @point_count: (inout) (allow-none): Input = the maximum number of attachment points to return; + * Output = the actual number of attachment points returned (may be zero) + * @point_array: (out) (array length=point_count): The array of attachment points found for the query + * + * Fetches a list of all attachment points for the specified glyph in the GDEF + * table of the face. The list returned will begin at the offset provided. + * + * Useful if the client program wishes to cache the list. + * + **/ unsigned int hb_ot_layout_get_attach_points (hb_face_t *face, hb_codepoint_t glyph, @@ -262,7 +344,20 @@ hb_ot_layout_get_attach_points (hb_face_t *face, point_count, point_array); } - +/** + * hb_ot_layout_get_ligature_carets: + * @font: The #hb_font_t to work on + * @direction: The #hb_direction_t text direction to use + * @glyph: The #hb_codepoint_t code point to query + * @start_offset: offset of the first caret position to retrieve + * @caret_count: (inout) (allow-none): Input = the maximum number of caret positions to return; + * Output = the actual number of caret positions returned (may be zero) + * @caret_array: (out) (array length=caret_count): The array of caret positions found for the query + * + * Fetches a list of the caret positions defined for a ligature glyph in the GDEF + * table of the font. The list returned will begin at the offset provided. + * + **/ unsigned int hb_ot_layout_get_ligature_carets (hb_font_t *font, hb_direction_t direction, @@ -271,16 +366,9 @@ hb_ot_layout_get_ligature_carets (hb_font_t *font, unsigned int *caret_count /* IN/OUT */, hb_position_t *caret_array /* OUT */) { - unsigned int result_caret_count = 0; - unsigned int result = font->face->table.GDEF->table->get_lig_carets (font, direction, glyph, start_offset, &result_caret_count, caret_array); - if (result) - { - if (caret_count) *caret_count = result_caret_count; - } - else - result = font->face->table.lcar->get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array); - return result; + return font->face->table.GDEF->table->get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array); } +#endif /* @@ -288,34 +376,22 @@ hb_ot_layout_get_ligature_carets (hb_font_t *font, */ bool -OT::GSUB::is_blacklisted (hb_blob_t *blob HB_UNUSED, +OT::GSUB::is_blocklisted (hb_blob_t *blob HB_UNUSED, hb_face_t *face) const { - /* Mac OS X prefers morx over GSUB. It also ships with various Indic fonts, - * all by 'MUTF' foundry (Tamil MN, Tamil Sangam MN, etc.), that have broken - * GSUB/GPOS tables. Some have GSUB with zero scripts, those are ignored by - * our morx/GSUB preference code. But if GSUB has non-zero scripts, we tend - * to prefer it over morx because we want to be consistent with other OpenType - * shapers. - * - * To work around broken Indic Mac system fonts, we ignore GSUB table if - * OS/2 VendorId is 'MUTF' and font has morx table as well. - * - * https://github.com/harfbuzz/harfbuzz/issues/1410 - * https://github.com/harfbuzz/harfbuzz/issues/1348 - * https://github.com/harfbuzz/harfbuzz/issues/1391 - */ - if (unlikely (face->table.OS2->achVendID == HB_TAG ('M','U','T','F') && - face->table.morx->has_data ())) - return true; - +#ifdef HB_NO_OT_LAYOUT_BLACKLIST + return false; +#endif return false; } bool -OT::GPOS::is_blacklisted (hb_blob_t *blob HB_UNUSED, +OT::GPOS::is_blocklisted (hb_blob_t *blob HB_UNUSED, hb_face_t *face HB_UNUSED) const { +#ifdef HB_NO_OT_LAYOUT_BLACKLIST + return false; +#endif return false; } @@ -326,11 +402,24 @@ get_gsubgpos_table (hb_face_t *face, switch (table_tag) { case HB_OT_TAG_GSUB: return *face->table.GSUB->table; case HB_OT_TAG_GPOS: return *face->table.GPOS->table; - default: return Null(OT::GSUBGPOS); + default: return Null (OT::GSUBGPOS); } } +/** + * hb_ot_layout_table_get_script_tags: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @start_offset: offset of the first script tag to retrieve + * @script_count: (inout) (allow-none): Input = the maximum number of script tags to return; + * Output = the actual number of script tags returned (may be zero) + * @script_tags: (out) (array length=script_count): The array of #hb_tag_t script tags found for the query + * + * Fetches a list of all scripts enumerated in the specified face's GSUB table + * or GPOS table. The list returned will begin at the offset provided. + * + **/ unsigned int hb_ot_layout_table_get_script_tags (hb_face_t *face, hb_tag_t table_tag, @@ -345,11 +434,24 @@ hb_ot_layout_table_get_script_tags (hb_face_t *face, #define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n') +/** + * hb_ot_layout_table_find_script: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_tag: #hb_tag_t of the script tag requested + * @script_index: (out): The index of the requested script tag + * + * Fetches the index if a given script tag in the specified face's GSUB table + * or GPOS table. + * + * Return value: true if the script is found, false otherwise + * + **/ hb_bool_t hb_ot_layout_table_find_script (hb_face_t *face, hb_tag_t table_tag, hb_tag_t script_tag, - unsigned int *script_index) + unsigned int *script_index /* OUT */) { static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX), ""); const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); @@ -375,20 +477,38 @@ hb_ot_layout_table_find_script (hb_face_t *face, return false; } +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_ot_layout_table_choose_script: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_tags: Array of #hb_tag_t script tags + * @script_index: (out): The index of the requested script tag + * @chosen_script: (out): #hb_tag_t of the script tag requested + * + * Deprecated since 2.0.0 + **/ hb_bool_t hb_ot_layout_table_choose_script (hb_face_t *face, hb_tag_t table_tag, const hb_tag_t *script_tags, - unsigned int *script_index, - hb_tag_t *chosen_script) + unsigned int *script_index /* OUT */, + hb_tag_t *chosen_script /* OUT */) { const hb_tag_t *t; for (t = script_tags; *t; t++); return hb_ot_layout_table_select_script (face, table_tag, t - script_tags, script_tags, script_index, chosen_script); } +#endif /** * hb_ot_layout_table_select_script: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_count: Number of script tags in the array + * @script_tags: Array of #hb_tag_t script tags + * @script_index: (out): The index of the requested script + * @chosen_script: (out): #hb_tag_t of the requested script * * Since: 2.0.0 **/ @@ -442,6 +562,19 @@ hb_ot_layout_table_select_script (hb_face_t *face, return false; } + +/** + * hb_ot_layout_table_get_feature_tags: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @start_offset: offset of the first feature tag to retrieve + * @feature_count: (inout) (allow-none): Input = the maximum number of feature tags to return; + * Output = the actual number of feature tags returned (may be zero) + * @feature_tags: (out) (array length=feature_count): Array of feature tags found in the table + * + * Fetches a list of all feature tags in the given face's GSUB or GPOS table. + * + **/ unsigned int hb_ot_layout_table_get_feature_tags (hb_face_t *face, hb_tag_t table_tag, @@ -454,11 +587,24 @@ hb_ot_layout_table_get_feature_tags (hb_face_t *face, return g.get_feature_tags (start_offset, feature_count, feature_tags); } + +/** + * hb_ot_layout_table_find_feature: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @feature_tag: The #hb_tag_t og the requested feature tag + * @feature_index: (out): The index of the requested feature + * + * Fetches the index for a given feature tag in the specified face's GSUB table + * or GPOS table. + * + * Return value: true if the feature is found, false otherwise + **/ bool hb_ot_layout_table_find_feature (hb_face_t *face, hb_tag_t table_tag, hb_tag_t feature_tag, - unsigned int *feature_index) + unsigned int *feature_index /* OUT */) { static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), ""); const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); @@ -477,6 +623,20 @@ hb_ot_layout_table_find_feature (hb_face_t *face, } +/** + * hb_ot_layout_script_get_language_tags: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @start_offset: offset of the first language tag to retrieve + * @language_count: (inout) (allow-none): Input = the maximum number of language tags to return; + * Output = the actual number of language tags returned (may be zero) + * @language_tags: (out) (array length=language_count): Array of language tags found in the table + * + * Fetches a list of language tags in the given face's GSUB or GPOS table, underneath + * the specified script index. The list returned will begin at the offset provided. + * + **/ unsigned int hb_ot_layout_script_get_language_tags (hb_face_t *face, hb_tag_t table_tag, @@ -490,6 +650,24 @@ hb_ot_layout_script_get_language_tags (hb_face_t *face, return s.get_lang_sys_tags (start_offset, language_count, language_tags); } + +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_ot_layout_script_find_language: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_tag: The #hb_tag_t of the requested language + * @language_index: The index of the requested language + * + * Fetches the index of a given language tag in the specified face's GSUB table + * or GPOS table, underneath the specified script tag. + * + * Return value: true if the language tag is found, false otherwise + * + * Since: ?? + * Deprecated: ?? + **/ hb_bool_t hb_ot_layout_script_find_language (hb_face_t *face, hb_tag_t table_tag, @@ -504,9 +682,22 @@ hb_ot_layout_script_find_language (hb_face_t *face, &language_tag, language_index); } +#endif + /** * hb_ot_layout_script_select_language: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_count: The number of languages in the specified script + * @language_tags: The array of language tags + * @language_index: (out): The index of the requested language + * + * Fetches the index of a given language tag in the specified face's GSUB table + * or GPOS table, underneath the specified script index. + * + * Return value: true if the language tag is found, false otherwise * * Since: 2.0.0 **/ @@ -536,12 +727,27 @@ hb_ot_layout_script_select_language (hb_face_t *face, return false; } + +/** + * hb_ot_layout_language_get_required_feature_index: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_index: The index of the requested language tag + * @feature_index: (out): The index of the requested feature + * + * Fetches the index of a requested feature in the given face's GSUB or GPOS table, + * underneath the specified script and language. + * + * Return value: true if the feature is found, false otherwise + * + **/ hb_bool_t hb_ot_layout_language_get_required_feature_index (hb_face_t *face, hb_tag_t table_tag, unsigned int script_index, unsigned int language_index, - unsigned int *feature_index) + unsigned int *feature_index /* OUT */) { return hb_ot_layout_language_get_required_feature (face, table_tag, @@ -551,8 +757,20 @@ hb_ot_layout_language_get_required_feature_index (hb_face_t *face, nullptr); } + /** * hb_ot_layout_language_get_required_feature: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_index: The index of the requested language tag + * @feature_index: (out): The index of the requested feature + * @feature_tag: (out): The #hb_tag_t of the requested feature + * + * Fetches the tag of a requested feature index in the given face's GSUB or GPOS table, + * underneath the specified script and language. + * + * Return value: true if the feature is found, false otherwise * * Since: 0.9.30 **/ @@ -561,8 +779,8 @@ hb_ot_layout_language_get_required_feature (hb_face_t *face, hb_tag_t table_tag, unsigned int script_index, unsigned int language_index, - unsigned int *feature_index, - hb_tag_t *feature_tag) + unsigned int *feature_index /* OUT */, + hb_tag_t *feature_tag /* OUT */) { const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); @@ -574,6 +792,22 @@ hb_ot_layout_language_get_required_feature (hb_face_t *face, return l.has_required_feature (); } + +/** + * hb_ot_layout_language_get_feature_indexes: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_index: The index of the requested language tag + * @start_offset: offset of the first feature tag to retrieve + * @feature_count: (inout) (allow-none): Input = the maximum number of feature tags to return; + * Output: the actual number of feature tags returned (may be zero) + * @feature_indexes: (out) (array length=feature_count): The array of feature indexes found for the query + * + * Fetches a list of all features in the specified face's GSUB table + * or GPOS table, underneath the specified script and language. The list + * returned will begin at the offset provided. + **/ unsigned int hb_ot_layout_language_get_feature_indexes (hb_face_t *face, hb_tag_t table_tag, @@ -589,6 +823,23 @@ hb_ot_layout_language_get_feature_indexes (hb_face_t *face, return l.get_feature_indexes (start_offset, feature_count, feature_indexes); } + +/** + * hb_ot_layout_language_get_feature_tags: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_index: The index of the requested language tag + * @start_offset: offset of the first feature tag to retrieve + * @feature_count: (inout) (allow-none): Input = the maximum number of feature tags to return; + * Output = the actual number of feature tags returned (may be zero) + * @feature_tags: (out) (array length=feature_count): The array of #hb_tag_t feature tags found for the query + * + * Fetches a list of all features in the specified face's GSUB table + * or GPOS table, underneath the specified script and language. The list + * returned will begin at the offset provided. + * + **/ unsigned int hb_ot_layout_language_get_feature_tags (hb_face_t *face, hb_tag_t table_tag, @@ -614,13 +865,28 @@ hb_ot_layout_language_get_feature_tags (hb_face_t *face, } +/** + * hb_ot_layout_language_find_feature: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_index: The index of the requested language tag + * @feature_tag: #hb_tag_t of the feature tag requested + * @feature_index: (out): The index of the requested feature + * + * Fetches the index of a given feature tag in the specified face's GSUB table + * or GPOS table, underneath the specified script and language. + * + * Return value: true if the feature is found, false otherwise + * + **/ hb_bool_t hb_ot_layout_language_find_feature (hb_face_t *face, hb_tag_t table_tag, unsigned int script_index, unsigned int language_index, hb_tag_t feature_tag, - unsigned int *feature_index) + unsigned int *feature_index /* OUT */) { static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), ""); const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); @@ -640,8 +906,20 @@ hb_ot_layout_language_find_feature (hb_face_t *face, return false; } + /** * hb_ot_layout_feature_get_lookups: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @feature_index: The index of the requested feature + * @start_offset: offset of the first lookup to retrieve + * @lookup_count: (inout) (allow-none): Input = the maximum number of lookups to return; + * Output = the actual number of lookups returned (may be zero) + * @lookup_indexes: (out) (array length=lookup_count): The array of lookup indexes found for the query + * + * Fetches a list of all lookups enumerated for the specified feature, in + * the specified face's GSUB table or GPOS table. The list returned will + * begin at the offset provided. * * Since: 0.9.7 **/ @@ -662,8 +940,14 @@ hb_ot_layout_feature_get_lookups (hb_face_t *face, lookup_indexes); } + /** * hb_ot_layout_table_get_lookup_count: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * + * Fetches the total number of lookups enumerated in the specified + * face's GSUB table or GPOS table. * * Since: 0.9.22 **/ @@ -677,12 +961,12 @@ hb_ot_layout_table_get_lookup_count (hb_face_t *face, struct hb_collect_features_context_t { - hb_collect_features_context_t (hb_face_t *face, - hb_tag_t table_tag, - hb_set_t *feature_indexes_) + hb_collect_features_context_t (hb_face_t *face, + hb_tag_t table_tag, + hb_set_t *feature_indexes_) : g (get_gsubgpos_table (face, table_tag)), feature_indexes (feature_indexes_), - script_count(0),langsys_count(0) {} + script_count (0),langsys_count (0), feature_index_count (0) {} bool visited (const OT::Script &s) { @@ -711,6 +995,12 @@ struct hb_collect_features_context_t return visited (l, visited_langsys); } + bool visited_feature_indices (unsigned count) + { + feature_index_count += count; + return feature_index_count > HB_MAX_FEATURE_INDICES; + } + private: template bool visited (const T &p, hb_set_t &visited_set) @@ -732,6 +1022,7 @@ struct hb_collect_features_context_t hb_set_t visited_langsys; unsigned int script_count; unsigned int langsys_count; + unsigned int feature_index_count; }; static void @@ -744,10 +1035,11 @@ langsys_collect_features (hb_collect_features_context_t *c, if (!features) { /* All features. */ - if (l.has_required_feature ()) + if (l.has_required_feature () && !c->visited_feature_indices (1)) c->feature_indexes->add (l.get_required_feature_index ()); - l.add_feature_indexes_to (c->feature_indexes); + if (!c->visited_feature_indices (l.featureIndex.len)) + l.add_feature_indexes_to (c->feature_indexes); } else { @@ -805,8 +1097,21 @@ script_collect_features (hb_collect_features_context_t *c, } } + /** * hb_ot_layout_collect_features: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @scripts: The array of scripts to collect features for + * @languages: The array of languages to collect features for + * @features: The array of features to collect + * @feature_indexes: (out): The array of feature indexes found for the query + * + * Fetches a list of all feature indexes in the specified face's GSUB table + * or GPOS table, underneath the specified scripts, languages, and features. + * If no list of scripts is provided, all scripts will be queried. If no list + * of languages is provided, all languages will be queried. If no list of + * features is provided, all features will be queried. * * Since: 1.8.5 **/ @@ -843,8 +1148,21 @@ hb_ot_layout_collect_features (hb_face_t *face, } } + /** * hb_ot_layout_collect_lookups: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @scripts: The array of scripts to collect lookups for + * @languages: The array of languages to collect lookups for + * @features: The array of features to collect lookups for + * @lookup_indexes: (out): The array of lookup indexes found for the query + * + * Fetches a list of all feature-lookup indexes in the specified face's GSUB + * table or GPOS table, underneath the specified scripts, languages, and + * features. If no list of scripts is provided, all scripts will be queried. + * If no list of languages is provided, all languages will be queried. If no + * list of features is provided, all features will be queried. * * Since: 0.9.8 **/ @@ -864,10 +1182,24 @@ hb_ot_layout_collect_lookups (hb_face_t *face, for (hb_codepoint_t feature_index = HB_SET_VALUE_INVALID; hb_set_next (&feature_indexes, &feature_index);) g.get_feature (feature_index).add_lookup_indexes_to (lookup_indexes); + + g.feature_variation_collect_lookups (&feature_indexes, lookup_indexes); } + +#ifndef HB_NO_LAYOUT_COLLECT_GLYPHS /** * hb_ot_layout_lookup_collect_glyphs: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @lookup_index: The index of the feature lookup to query + * @glyphs_before: (out): Array of glyphs preceding the substitution range + * @glyphs_input: (out): Array of input glyphs that would be substituted by the lookup + * @glyphs_after: (out): Array of glyphs following the substitution range + * @glyphs_output: (out): Array of glyphs that would be the substitued output of the lookup + * + * Fetches a list of all glyphs affected by the specified lookup in the + * specified face's GSUB table or GPOS table. * * Since: 0.9.7 **/ @@ -902,10 +1234,24 @@ hb_ot_layout_lookup_collect_glyphs (hb_face_t *face, } } } +#endif /* Variations support */ + +/** + * hb_ot_layout_table_find_feature_variations: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @coords: The variation coordinates to query + * @num_coords: The number of variation coorinates + * @variations_index: (out): The array of feature variations found for the query + * + * Fetches a list of feature variations in the specified face's GSUB table + * or GPOS table, at the specified variation coordinates. + * + **/ hb_bool_t hb_ot_layout_table_find_feature_variations (hb_face_t *face, hb_tag_t table_tag, @@ -918,6 +1264,23 @@ hb_ot_layout_table_find_feature_variations (hb_face_t *face, return g.find_variations_index (coords, num_coords, variations_index); } + +/** + * hb_ot_layout_feature_with_variations_get_lookups: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @feature_index: The index of the feature to query + * @variations_index: The index of the feature variation to query + * @start_offset: offset of the first lookup to retrieve + * @lookup_count: (inout) (allow-none): Input = the maximum number of lookups to return; + * Output = the actual number of lookups returned (may be zero) + * @lookup_indexes: (out) (array length=lookup_count): The array of lookups found for the query + * + * Fetches a list of all lookups enumerated for the specified feature, in + * the specified face's GSUB table or GPOS table, enabled at the specified + * variations index. The list returned will begin at the offset provided. + * + **/ unsigned int hb_ot_layout_feature_with_variations_get_lookups (hb_face_t *face, hb_tag_t table_tag, @@ -940,14 +1303,35 @@ hb_ot_layout_feature_with_variations_get_lookups (hb_face_t *face, * OT::GSUB */ + +/** + * hb_ot_layout_has_substitution: + * @face: #hb_face_t to work upon + * + * Tests whether the specified face includes any GSUB substitutions. + * + * Return value: true if data found, false otherwise + * + **/ hb_bool_t hb_ot_layout_has_substitution (hb_face_t *face) { return face->table.GSUB->table->has_data (); } + /** * hb_ot_layout_lookup_would_substitute: + * @face: #hb_face_t to work upon + * @lookup_index: The index of the lookup to query + * @glyphs: The sequence of glyphs to query for substitution + * @glyphs_length: The length of the glyph sequence + * @zero_context: #hb_bool_t indicating whether substitutions should be context-free + * + * Tests whether a specified lookup in the specified face would + * trigger a substitution on the given glyph sequence. + * + * Return value: true if a substitution would be triggered, false otherwise * * Since: 0.9.7 **/ @@ -957,33 +1341,29 @@ hb_ot_layout_lookup_would_substitute (hb_face_t *face, const hb_codepoint_t *glyphs, unsigned int glyphs_length, hb_bool_t zero_context) -{ - return hb_ot_layout_lookup_would_substitute_fast (face, - lookup_index, - glyphs, glyphs_length, - zero_context); -} - -bool -hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face, - unsigned int lookup_index, - const hb_codepoint_t *glyphs, - unsigned int glyphs_length, - bool zero_context) { if (unlikely (lookup_index >= face->table.GSUB->lookup_count)) return false; OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context); const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index); - return l.would_apply (&c, &face->table.GSUB->accels[lookup_index]); } + +/** + * hb_ot_layout_substitute_start: + * @font: #hb_font_t to use + * @buffer: #hb_buffer_t buffer to work upon + * + * Called before substitution lookups are performed, to ensure that glyph + * class and other properties are set on the glyphs in the buffer. + * + **/ void hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer) { -_hb_ot_layout_set_glyph_props (font, buffer); + _hb_ot_layout_set_glyph_props (font, buffer); } void @@ -1038,13 +1418,19 @@ hb_ot_layout_delete_glyphs_inplace (hb_buffer_t *buffer, /** * hb_ot_layout_lookup_substitute_closure: + * @face: #hb_face_t to work upon + * @lookup_index: index of the feature lookup to query + * @glyphs: (out): Array of glyphs comprising the transitive closure of the lookup + * + * Compute the transitive closure of glyphs needed for a + * specified lookup. * * Since: 0.9.7 **/ void hb_ot_layout_lookup_substitute_closure (hb_face_t *face, unsigned int lookup_index, - hb_set_t *glyphs) + hb_set_t *glyphs /* OUT */) { hb_map_t done_lookups; OT::hb_closure_context_t c (face, glyphs, &done_lookups); @@ -1056,6 +1442,9 @@ hb_ot_layout_lookup_substitute_closure (hb_face_t *face, /** * hb_ot_layout_lookups_substitute_closure: + * @face: #hb_face_t to work upon + * @lookups: The set of lookups to query + * @glyphs: (out): Array of glyphs comprising the transitive closure of the lookups * * Compute the transitive closure of glyphs needed for all of the * provided lookups. @@ -1065,7 +1454,7 @@ hb_ot_layout_lookup_substitute_closure (hb_face_t *face, void hb_ot_layout_lookups_substitute_closure (hb_face_t *face, const hb_set_t *lookups, - hb_set_t *glyphs) + hb_set_t *glyphs /* OUT */) { hb_map_t done_lookups; OT::hb_closure_context_t c (face, glyphs, &done_lookups); @@ -1076,7 +1465,7 @@ hb_ot_layout_lookups_substitute_closure (hb_face_t *face, do { glyphs_length = glyphs->get_population (); - if (lookups != nullptr) + if (lookups) { for (hb_codepoint_t lookup_index = HB_SET_VALUE_INVALID; hb_set_next (lookups, &lookup_index);) gsub.get_lookup (lookup_index).closure (&c, lookup_index); @@ -1094,32 +1483,85 @@ hb_ot_layout_lookups_substitute_closure (hb_face_t *face, * OT::GPOS */ + +/** + * hb_ot_layout_has_positioning: + * @face: #hb_face_t to work upon + * + * Return value: true if the face has GPOS data, false otherwise + * + **/ hb_bool_t hb_ot_layout_has_positioning (hb_face_t *face) { return face->table.GPOS->table->has_data (); } +/** + * hb_ot_layout_position_start: + * @font: #hb_font_t to use + * @buffer: #hb_buffer_t buffer to work upon + * + * Called before positioning lookups are performed, to ensure that glyph + * attachment types and glyph-attachment chains are set for the glyphs in the buffer. + * + **/ void hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer) { OT::GPOS::position_start (font, buffer); } + +/** + * hb_ot_layout_position_finish_advances: + * @font: #hb_font_t to use + * @buffer: #hb_buffer_t buffer to work upon + * + * Called after positioning lookups are performed, to finish glyph advances. + * + **/ void hb_ot_layout_position_finish_advances (hb_font_t *font, hb_buffer_t *buffer) { OT::GPOS::position_finish_advances (font, buffer); } +/** + * hb_ot_layout_position_finish_offsets: + * @font: #hb_font_t to use + * @buffer: #hb_buffer_t buffer to work upon + * + * Called after positioning lookups are performed, to finish glyph offsets. + * + **/ void hb_ot_layout_position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer) { OT::GPOS::position_finish_offsets (font, buffer); } + +#ifndef HB_NO_LAYOUT_FEATURE_PARAMS /** * hb_ot_layout_get_size_params: + * @face: #hb_face_t to work upon + * @design_size: (out): The design size of the face + * @subfamily_id: (out): The identifier of the face within the font subfamily + * @subfamily_name_id: (out): The ‘name’ table name ID of the face within the font subfamily + * @range_start: (out): The minimum size of the recommended size range for the face + * @range_end: (out): The maximum size of the recommended size range for the face + * + * Fetches optical-size feature data (i.e., the `size` feature from GPOS). Note that + * the subfamily_id and the subfamily name string (accessible via the subfamily_name_id) + * as used here are defined as pertaining only to fonts within a font family that differ + * specifically in their respective size ranges; other ways to differentiate fonts within + * a subfamily are not covered by the `size` feature. + * + * For more information on this distinction, see the [`size` feature documentation]( + * https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#tag-size). + * + * Return value: true if data found, false otherwise * * Since: 0.9.10 **/ @@ -1163,7 +1605,6 @@ hb_ot_layout_get_size_params (hb_face_t *face, return false; } - /** * hb_ot_layout_feature_get_name_ids: * @face: #hb_face_t to work upon @@ -1238,24 +1679,20 @@ hb_ot_layout_feature_get_name_ids (hb_face_t *face, if (first_param_id) *first_param_id = HB_OT_NAME_ID_INVALID; return false; } - /** * hb_ot_layout_feature_get_characters: * @face: #hb_face_t to work upon * @table_tag: table tag to query, "GSUB" or "GPOS". * @feature_index: index of feature to query. - * @start_offset: In case the resulting char_count was equal to its input value, there - * is a chance there were more characters on the tag so this API can be - * called with an offset till resulting char_count gets to a number - * lower than input buffer (or consider using just a bigger buffer for - * one shot copying). - * @char_count: (inout) (allow-none): The count of characters for which this feature - * provides glyph variants. (May be zero.) - * @characters: (out caller-allocates) (array length=char_count): A buffer pointer. The Unicode codepoints - * of the characters for which this feature provides glyph variants. - * - * Fetches characters listed by designer under feature parameters for "Character - * Variant" ("cvXX") features. + * @start_offset: offset of the first character to retrieve + * @char_count: (inout) (allow-none): Input = the maximum number of characters to return; + * Output = the actual number of characters returned (may be zero) + * @characters: (out caller-allocates) (array length=char_count): A buffer pointer. + * The Unicode codepoints of the characters for which this feature provides + * glyph variants. + * + * Fetches a list of the characters defined as having a variant under the specified + * "Character Variant" ("cvXX") feature tag. * * Return value: Number of total sample characters in the cvXX feature. * @@ -1270,25 +1707,12 @@ hb_ot_layout_feature_get_characters (hb_face_t *face, hb_codepoint_t *characters /* OUT. May be NULL */) { const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); - - hb_tag_t feature_tag = g.get_feature_tag (feature_index); - const OT::Feature &f = g.get_feature (feature_index); - - const OT::FeatureParams &feature_params = f.get_feature_params (); - - const OT::FeatureParamsCharacterVariants& cv_params = - feature_params.get_character_variants_params(feature_tag); - - unsigned int len = 0; - if (char_count && characters && start_offset < cv_params.characters.len) - { - len = MIN (cv_params.characters.len - start_offset, *char_count); - for (unsigned int i = 0; i < len; ++i) - characters[i] = cv_params.characters[start_offset + i]; - } - if (char_count) *char_count = len; - return cv_params.characters.len; + return g.get_feature (feature_index) + .get_feature_params () + .get_character_variants_params(g.get_feature_tag (feature_index)) + .get_characters (start_offset, char_count, characters); } +#endif /* @@ -1455,13 +1879,17 @@ inline void hb_ot_map_t::apply (const Proxy &proxy, void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const { GSUBProxy proxy (font->face); + if (!buffer->message (font, "start table GSUB")) return; apply (proxy, plan, font, buffer); + (void)buffer->message (font, "end table GSUB"); } void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const { GPOSProxy proxy (font->face); + if (!buffer->message (font, "start table GPOS")) return; apply (proxy, plan, font, buffer); + (void)buffer->message (font, "end table GPOS"); } void @@ -1472,60 +1900,94 @@ hb_ot_layout_substitute_lookup (OT::hb_ot_apply_context_t *c, apply_string (c, lookup, accel); } -#if 0 -static const OT::BASE& _get_base (hb_face_t *face) -{ - return *face->table.BASE; -} - +#ifndef HB_NO_BASE +/** + * hb_ot_layout_get_baseline: + * @font: a font + * @baseline_tag: a baseline tag + * @direction: text direction. + * @script_tag: script tag. + * @language_tag: language tag. + * @coord: (out): baseline value if found. + * + * Fetches a baseline value from the face. + * + * Return value: if found baseline value in the font. + * + * Since: 2.6.0 + **/ hb_bool_t -hb_ot_layout_get_baseline (hb_font_t *font, - hb_ot_layout_baseline_t baseline, - hb_direction_t direction, - hb_tag_t script_tag, - hb_tag_t language_tag, - hb_position_t *coord /* OUT. May be NULL. */) +hb_ot_layout_get_baseline (hb_font_t *font, + hb_ot_layout_baseline_tag_t baseline_tag, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_position_t *coord /* OUT. May be NULL. */) { - const OT::BASE &base = _get_base (font->face); - bool result = base.get_baseline (font, baseline, direction, script_tag, - language_tag, coord); + bool result = font->face->table.BASE->get_baseline (font, baseline_tag, direction, script_tag, language_tag, coord); - /* TODO: Simulate https://docs.microsoft.com/en-us/typography/opentype/spec/baselinetags#ideographic-em-box */ - if (!result && coord) *coord = 0; - - if (coord) *coord = font->em_scale_dir (*coord, direction); + if (result && coord) + *coord = HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (*coord) : font->em_scale_x (*coord); return result; } +#endif -/* To be moved to public header */ -/* - * BASE - */ + +struct hb_get_glyph_alternates_dispatch_t : + hb_dispatch_context_t +{ + static return_t default_return_value () { return 0; } + bool stop_sublookup_iteration (return_t r) const { return r; } + + hb_face_t *face; + + hb_get_glyph_alternates_dispatch_t (hb_face_t *face) : + face (face) {} + + private: + template auto + _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN + ( obj.get_glyph_alternates (hb_forward (ds)...) ) + template auto + _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN + ( default_return_value () ) + public: + template auto + dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN + ( _dispatch (obj, hb_prioritize, hb_forward (ds)...) ) +}; /** - * hb_ot_layout_baseline_t: + * hb_ot_layout_lookup_get_glyph_alternates: + * @face: a face. + * @lookup_index: index of the feature lookup to query. + * @glyph: a glyph id. + * @start_offset: starting offset. + * @alternate_count: (inout) (allow-none): Input = the maximum number of alternate glyphs to return; + * Output = the actual number of alternate glyphs returned (may be zero). + * @alternate_glyphs: (out caller-allocates) (array length=alternate_count): A glyphs buffer. + * Alternate glyphs associated with the glyph id. * - * https://docs.microsoft.com/en-us/typography/opentype/spec/baselinetags + * Fetches alternates of a glyph from a given GSUB lookup index. * - * Since: DONTREPLACEME - */ -typedef enum { - HB_OT_LAYOUT_BASELINE_HANG = HB_TAG('h','a','n','g'), - HB_OT_LAYOUT_BASELINE_ICFB = HB_TAG('i','c','f','b'), - HB_OT_LAYOUT_BASELINE_ICFT = HB_TAG('i','c','f','t'), - HB_OT_LAYOUT_BASELINE_IDEO = HB_TAG('i','d','e','o'), - HB_OT_LAYOUT_BASELINE_IDTB = HB_TAG('i','d','t','b'), - HB_OT_LAYOUT_BASELINE_MATH = HB_TAG('m','a','t','h'), - HB_OT_LAYOUT_BASELINE_ROMN = HB_TAG('r','o','m','n') -} hb_ot_layout_baseline_t; - -HB_EXTERN hb_bool_t -hb_ot_layout_get_baseline (hb_font_t *font, - hb_ot_layout_baseline_t baseline, - hb_direction_t direction, - hb_tag_t script_tag, - hb_tag_t language_tag, - hb_position_t *coord /* OUT. May be NULL. */); + * Return value: total number of alternates found in the specific lookup index for the given glyph id. + * + * Since: 2.6.8 + **/ +HB_EXTERN unsigned +hb_ot_layout_lookup_get_glyph_alternates (hb_face_t *face, + unsigned lookup_index, + hb_codepoint_t glyph, + unsigned start_offset, + unsigned *alternate_count /* IN/OUT. May be NULL. */, + hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) +{ + hb_get_glyph_alternates_dispatch_t c (face); + const OT::SubstLookup &lookup = face->table.GSUB->table->get_lookup (lookup_index); + auto ret = lookup.dispatch (&c, glyph, start_offset, alternate_count, alternate_glyphs); + if (!ret && alternate_count) *alternate_count = 0; + return ret; +} #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.h index 0419f603916..cdb1955317c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.h @@ -93,6 +93,17 @@ hb_ot_tags_to_script_and_language (hb_tag_t script_tag, HB_EXTERN hb_bool_t hb_ot_layout_has_glyph_classes (hb_face_t *face); +/** + * hb_ot_layout_glyph_class_t: + * @HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED: Glyphs not matching the other classifications + * @HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH: Spacing, single characters, capable of accepting marks + * @HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE: Glyphs that represent ligation of multiple characters + * @HB_OT_LAYOUT_GLYPH_CLASS_MARK: Non-spacing, combining glyphs that represent marks + * @HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT: Spacing glyphs that represent part of a single character + * + * The GDEF classes defined for glyphs. + * + **/ typedef enum { HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED = 0, HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH = 1, @@ -110,7 +121,6 @@ hb_ot_layout_get_glyphs_in_class (hb_face_t *face, hb_ot_layout_glyph_class_t klass, hb_set_t *glyphs /* OUT */); - /* Not that useful. Provides list of attach points for a glyph that a * client may want to cache */ HB_EXTERN unsigned int @@ -150,7 +160,7 @@ HB_EXTERN hb_bool_t hb_ot_layout_table_find_script (hb_face_t *face, hb_tag_t table_tag, hb_tag_t script_tag, - unsigned int *script_index); + unsigned int *script_index /* OUT */); HB_EXTERN hb_bool_t hb_ot_layout_table_select_script (hb_face_t *face, @@ -188,15 +198,15 @@ hb_ot_layout_language_get_required_feature_index (hb_face_t *face, hb_tag_t table_tag, unsigned int script_index, unsigned int language_index, - unsigned int *feature_index); + unsigned int *feature_index /* OUT */); HB_EXTERN hb_bool_t hb_ot_layout_language_get_required_feature (hb_face_t *face, hb_tag_t table_tag, unsigned int script_index, unsigned int language_index, - unsigned int *feature_index, - hb_tag_t *feature_tag); + unsigned int *feature_index /* OUT */, + hb_tag_t *feature_tag /* OUT */); HB_EXTERN unsigned int hb_ot_layout_language_get_feature_indexes (hb_face_t *face, @@ -222,7 +232,7 @@ hb_ot_layout_language_find_feature (hb_face_t *face, unsigned int script_index, unsigned int language_index, hb_tag_t feature_tag, - unsigned int *feature_index); + unsigned int *feature_index /* OUT */); HB_EXTERN unsigned int hb_ot_layout_feature_get_lookups (hb_face_t *face, @@ -313,6 +323,14 @@ hb_ot_layout_feature_with_variations_get_lookups (hb_face_t *face, HB_EXTERN hb_bool_t hb_ot_layout_has_substitution (hb_face_t *face); +HB_EXTERN unsigned +hb_ot_layout_lookup_get_glyph_alternates (hb_face_t *face, + unsigned lookup_index, + hb_codepoint_t glyph, + unsigned start_offset, + unsigned *alternate_count /* IN/OUT */, + hb_codepoint_t *alternate_glyphs /* OUT */); + HB_EXTERN hb_bool_t hb_ot_layout_lookup_would_substitute (hb_face_t *face, unsigned int lookup_index, @@ -391,6 +409,54 @@ hb_ot_layout_feature_get_characters (hb_face_t *face, unsigned int *char_count /* IN/OUT. May be NULL */, hb_codepoint_t *characters /* OUT. May be NULL */); +/* + * BASE + */ + +/** + * hb_ot_layout_baseline_tag_t: + * @HB_OT_LAYOUT_BASELINE_TAG_ROMAN: The baseline used by alphabetic scripts such as Latin, Cyrillic and Greek. + * In vertical writing mode, the alphabetic baseline for characters rotated 90 degrees clockwise. + * (This would not apply to alphabetic characters that remain upright in vertical writing mode, since these + * characters are not rotated.) + * @HB_OT_LAYOUT_BASELINE_TAG_HANGING: The hanging baseline. In horizontal direction, this is the horizontal + * line from which syllables seem, to hang in Tibetan and other similar scripts. In vertical writing mode, + * for Tibetan (or some other similar script) characters rotated 90 degrees clockwise. + * @HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT: Ideographic character face bottom or left edge, + * if the direction is horizontal or vertical, respectively. + * @HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT: Ideographic character face top or right edge, + * if the direction is horizontal or vertical, respectively. + * @HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT: Ideographic em-box bottom or left edge, + * if the direction is horizontal or vertical, respectively. + * @HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT: Ideographic em-box top or right edge baseline, + * if the direction is horizontal or vertical, respectively. + * @HB_OT_LAYOUT_BASELINE_TAG_MATH: The baseline about which mathematical characters are centered. + * In vertical writing mode when mathematical characters rotated 90 degrees clockwise, are centered. + * + * Baseline tags from https://docs.microsoft.com/en-us/typography/opentype/spec/baselinetags + * + * Since: 2.6.0 + */ +typedef enum { + HB_OT_LAYOUT_BASELINE_TAG_ROMAN = HB_TAG ('r','o','m','n'), + HB_OT_LAYOUT_BASELINE_TAG_HANGING = HB_TAG ('h','a','n','g'), + HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT = HB_TAG ('i','c','f','b'), + HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT = HB_TAG ('i','c','f','t'), + HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT = HB_TAG ('i','d','e','o'), + HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT = HB_TAG ('i','d','t','p'), + HB_OT_LAYOUT_BASELINE_TAG_MATH = HB_TAG ('m','a','t','h'), + + _HB_OT_LAYOUT_BASELINE_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_ot_layout_baseline_tag_t; + +HB_EXTERN hb_bool_t +hb_ot_layout_get_baseline (hb_font_t *font, + hb_ot_layout_baseline_tag_t baseline_tag, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_position_t *coord /* OUT. May be NULL. */); + HB_END_DECLS #endif /* HB_OT_LAYOUT_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh index 9025bc5a8a0..07447f913c8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh @@ -96,13 +96,6 @@ HB_MARK_AS_FLAG_T (hb_ot_layout_glyph_props_flags_t); * GSUB/GPOS */ -HB_INTERNAL bool -hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face, - unsigned int lookup_index, - const hb_codepoint_t *glyphs, - unsigned int glyphs_length, - bool zero_context); - /* Should be called before all the substitute_lookup's are done. */ HB_INTERNAL void @@ -175,6 +168,17 @@ _hb_next_syllable (hb_buffer_t *buffer, unsigned int start) return start; } +static inline void +_hb_clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + info[i].syllable() = 0; +} + /* unicode_props */ @@ -215,7 +219,7 @@ _hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_buffer_t *buffer) unsigned int gen_cat = (unsigned int) unicode->general_category (u); unsigned int props = gen_cat; - if (u >= 0x80) + if (u >= 0x80u) { buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII; @@ -232,10 +236,10 @@ _hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_buffer_t *buffer) * FVSes are GC=Mn, we have use a separate bit to remember them. * Fixes: * https://github.com/harfbuzz/harfbuzz/issues/234 */ - else if (unlikely (hb_in_range (u, 0x180Bu, 0x180Du))) props |= UPROPS_MASK_HIDDEN; + else if (unlikely (hb_in_range (u, 0x180Bu, 0x180Du))) props |= UPROPS_MASK_HIDDEN; /* TAG characters need similar treatment. Fixes: * https://github.com/harfbuzz/harfbuzz/issues/463 */ - else if (unlikely (hb_in_range (u, 0xE0020u, 0xE007Fu))) props |= UPROPS_MASK_HIDDEN; + else if (unlikely (hb_in_range (u, 0xE0020u, 0xE007Fu))) props |= UPROPS_MASK_HIDDEN; /* COMBINING GRAPHEME JOINER should not be skipped; at least some times. * https://github.com/harfbuzz/harfbuzz/issues/554 */ else if (unlikely (u == 0x034Fu)) @@ -558,6 +562,17 @@ _hb_glyph_info_clear_substituted (hb_glyph_info_t *info) info->glyph_props() &= ~(HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED); } +static inline void +_hb_clear_substitution_flags (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + _hb_glyph_info_clear_substituted (&info[i]); +} + /* Allocation / deallocation. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-map.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-map.cc index a428b4bdbe3..d256a04ca5a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-map.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-map.cc @@ -26,6 +26,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-map.hh" #include "hb-ot-shape.hh" #include "hb-ot-layout.hh" @@ -34,7 +38,7 @@ void hb_ot_map_t::collect_lookups (unsigned int table_index, hb_set_t *lookups_out) const { for (unsigned int i = 0; i < lookups[table_index].length; i++) - hb_set_add (lookups_out, lookups[table_index][i].index); + lookups_out->add (lookups[table_index][i].index); } @@ -187,13 +191,14 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m, feature_infos[j].max_value = feature_infos[i].max_value; feature_infos[j].default_value = feature_infos[i].default_value; } else { - feature_infos[j].flags &= ~F_GLOBAL; - feature_infos[j].max_value = MAX (feature_infos[j].max_value, feature_infos[i].max_value); + if (feature_infos[j].flags & F_GLOBAL) + feature_infos[j].flags ^= F_GLOBAL; + feature_infos[j].max_value = hb_max (feature_infos[j].max_value, feature_infos[i].max_value); /* Inherit default_value from j */ } feature_infos[j].flags |= (feature_infos[i].flags & F_HAS_FALLBACK); - feature_infos[j].stage[0] = MIN (feature_infos[j].stage[0], feature_infos[i].stage[0]); - feature_infos[j].stage[1] = MIN (feature_infos[j].stage[1], feature_infos[i].stage[1]); + feature_infos[j].stage[0] = hb_min (feature_infos[j].stage[0], feature_infos[i].stage[0]); + feature_infos[j].stage[1] = hb_min (feature_infos[j].stage[1], feature_infos[i].stage[1]); } feature_infos.shrink (j + 1); } @@ -213,34 +218,34 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m, bits_needed = 0; else /* Limit bits per feature. */ - bits_needed = MIN(HB_OT_MAP_MAX_BITS, hb_bit_storage (info->max_value)); + bits_needed = hb_min (HB_OT_MAP_MAX_BITS, hb_bit_storage (info->max_value)); if (!info->max_value || next_bit + bits_needed > 8 * sizeof (hb_mask_t)) continue; /* Feature disabled, or not enough bits. */ - hb_bool_t found = false; + bool found = false; unsigned int feature_index[2]; for (unsigned int table_index = 0; table_index < 2; table_index++) { if (required_feature_tag[table_index] == info->tag) required_feature_stage[table_index] = info->stage[table_index]; - found |= hb_ot_layout_language_find_feature (face, - table_tags[table_index], - script_index[table_index], - language_index[table_index], - info->tag, - &feature_index[table_index]); + found |= (bool) hb_ot_layout_language_find_feature (face, + table_tags[table_index], + script_index[table_index], + language_index[table_index], + info->tag, + &feature_index[table_index]); } if (!found && (info->flags & F_GLOBAL_SEARCH)) { for (unsigned int table_index = 0; table_index < 2; table_index++) { - found |= hb_ot_layout_table_find_feature (face, - table_tags[table_index], - info->tag, - &feature_index[table_index]); + found |= (bool) hb_ot_layout_table_find_feature (face, + table_tags[table_index], + info->tag, + &feature_index[table_index]); } } if (!found && !(info->flags & F_HAS_FALLBACK)) @@ -332,3 +337,6 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m, } } } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-map.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-map.hh index 7505adfe127..5c23eb57814 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-map.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-map.hh @@ -68,7 +68,7 @@ struct hb_ot_map_t unsigned short random : 1; hb_mask_t mask; - static int cmp (const void *pa, const void *pb) + HB_INTERNAL static int cmp (const void *pa, const void *pb) { const lookup_map_t *a = (const lookup_map_t *) pa; const lookup_map_t *b = (const lookup_map_t *) pb; @@ -134,13 +134,13 @@ struct hb_ot_map_t unsigned int get_feature_stage (unsigned int table_index, hb_tag_t feature_tag) const { const feature_map_t *map = features.bsearch (feature_tag); - return map ? map->stage[table_index] : (unsigned int) -1; + return map ? map->stage[table_index] : UINT_MAX; } void get_stage_lookups (unsigned int table_index, unsigned int stage, const struct lookup_map_t **plookups, unsigned int *lookup_count) const { - if (unlikely (stage == (unsigned int) -1)) { + if (unlikely (stage == UINT_MAX)) { *plookups = nullptr; *lookup_count = 0; return; @@ -154,8 +154,8 @@ struct hb_ot_map_t HB_INTERNAL void collect_lookups (unsigned int table_index, hb_set_t *lookups) const; template - HB_INTERNAL inline void apply (const Proxy &proxy, - const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; + HB_INTERNAL void apply (const Proxy &proxy, + const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; HB_INTERNAL void substitute (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; HB_INTERNAL void position (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; @@ -167,7 +167,7 @@ struct hb_ot_map_t hb_mask_t global_mask; - hb_vector_t features; + hb_sorted_vector_t features; hb_vector_t lookups[2]; /* GSUB/GPOS */ hb_vector_t stages[2]; /* GSUB/GPOS */ }; @@ -213,8 +213,8 @@ struct hb_ot_map_builder_t { add_feature (feat.tag, feat.flags); } void enable_feature (hb_tag_t tag, - hb_ot_map_feature_flags_t flags=F_NONE, - unsigned int value=1) + hb_ot_map_feature_flags_t flags=F_NONE, + unsigned int value=1) { add_feature (tag, F_GLOBAL | flags, value); } void disable_feature (hb_tag_t tag) @@ -247,7 +247,7 @@ struct hb_ot_map_builder_t unsigned int default_value; /* for non-global features, what should the unset glyphs take */ unsigned int stage[2]; /* GSUB/GPOS */ - static int cmp (const void *pa, const void *pb) + HB_INTERNAL static int cmp (const void *pa, const void *pb) { const feature_info_t *a = (const feature_info_t *) pa; const feature_info_t *b = (const feature_info_t *) pb; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh index 07112e0f420..6cacf8eadb7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh @@ -48,7 +48,7 @@ struct MathValueRecord } protected: - HBINT16 value; /* The X or Y value in design units */ + HBINT16 value; /* The X or Y value in design units */ OffsetTo deviceTable; /* Offset to the device table - from the * beginning of parent table. May be NULL. * Suggested format for device table is 1. */ @@ -78,7 +78,7 @@ struct MathConstants } hb_position_t get_value (hb_ot_math_constant_t constant, - hb_font_t *font) const + hb_font_t *font) const { switch (constant) { @@ -279,14 +279,15 @@ struct MathKern protected: HBUINT16 heightCount; UnsizedArrayOf - mathValueRecordsZ; /* Array of correction heights at - * which the kern value changes. - * Sorted by the height value in - * design units (heightCount entries), - * Followed by: - * Array of kern values corresponding - * to heights. (heightCount+1 entries). - */ + mathValueRecordsZ; + /* Array of correction heights at + * which the kern value changes. + * Sorted by the height value in + * design units (heightCount entries), + * Followed by: + * Array of kern values corresponding + * to heights. (heightCount+1 entries). + */ public: DEFINE_SIZE_ARRAY (2, mathValueRecordsZ); @@ -345,15 +346,18 @@ struct MathKernInfo } protected: - OffsetTo mathKernCoverage; /* Offset to Coverage table - - * from the beginning of the - * MathKernInfo table. */ - ArrayOf mathKernInfoRecords; /* Array of - * MathKernInfoRecords, - * per-glyph information for - * mathematical positioning - * of subscripts and - * superscripts. */ + OffsetTo + mathKernCoverage; + /* Offset to Coverage table - + * from the beginning of the + * MathKernInfo table. */ + ArrayOf + mathKernInfoRecords; + /* Array of MathKernInfoRecords, + * per-glyph information for + * mathematical positioning + * of subscripts and + * superscripts. */ public: DEFINE_SIZE_ARRAY (4, mathKernInfoRecords); @@ -423,7 +427,7 @@ struct MathGlyphVariantRecord } protected: - GlyphID variantGlyph; /* Glyph ID for the variant. */ + HBGlyphID variantGlyph; /* Glyph ID for the variant. */ HBUINT16 advanceMeasurement; /* Advance width/height, in design units, of the * variant, in the direction of requested * glyph extension. */ @@ -453,16 +457,16 @@ struct MathGlyphPartRecord } void extract (hb_ot_math_glyph_part_t &out, - int scale, + int64_t mult, hb_font_t *font) const { out.glyph = glyph; - out.start_connector_length = font->em_scale (startConnectorLength, scale); - out.end_connector_length = font->em_scale (endConnectorLength, scale); - out.full_advance = font->em_scale (fullAdvance, scale); + out.start_connector_length = font->em_mult (startConnectorLength, mult); + out.end_connector_length = font->em_mult (endConnectorLength, mult); + out.full_advance = font->em_mult (fullAdvance, mult); - static_assert ((unsigned int) HB_MATH_GLYPH_PART_FLAG_EXTENDER == + static_assert ((unsigned int) HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER == (unsigned int) PartFlags::Extender, ""); out.flags = (hb_ot_math_glyph_part_flags_t) @@ -471,19 +475,21 @@ struct MathGlyphPartRecord } protected: - GlyphID glyph; /* Glyph ID for the part. */ - HBUINT16 startConnectorLength; /* Advance width/ height of the straight bar - * connector material, in design units, is at - * the beginning of the glyph, in the - * direction of the extension. */ - HBUINT16 endConnectorLength; /* Advance width/ height of the straight bar - * connector material, in design units, is at - * the end of the glyph, in the direction of - * the extension. */ - HBUINT16 fullAdvance; /* Full advance width/height for this part, - * in the direction of the extension. - * In design units. */ - PartFlags partFlags; /* Part qualifiers. */ + HBGlyphID glyph; /* Glyph ID for the part. */ + HBUINT16 startConnectorLength; + /* Advance width/ height of the straight bar + * connector material, in design units, is at + * the beginning of the glyph, in the + * direction of the extension. */ + HBUINT16 endConnectorLength; + /* Advance width/ height of the straight bar + * connector material, in design units, is at + * the end of the glyph, in the direction of + * the extension. */ + HBUINT16 fullAdvance; /* Full advance width/height for this part, + * in the direction of the extension. + * In design units. */ + PartFlags partFlags; /* Part qualifiers. */ public: DEFINE_SIZE_STATIC (10); @@ -508,11 +514,10 @@ struct MathGlyphAssembly { if (parts_count) { - int scale = font->dir_scale (direction); - hb_array_t arr = partRecords.sub_array (start_offset, parts_count); - unsigned int count = arr.length; - for (unsigned int i = 0; i < count; i++) - arr[i].extract (parts[i], scale, font); + int64_t mult = font->dir_mult (direction); + for (auto _ : hb_zip (partRecords.sub_array (start_offset, parts_count), + hb_array (parts, *parts_count))) + _.first.extract (_.second, mult, font); } if (italics_correction) @@ -522,12 +527,15 @@ struct MathGlyphAssembly } protected: - MathValueRecord italicsCorrection; /* Italics correction of this - * MathGlyphAssembly. Should not - * depend on the assembly size. */ - ArrayOf partRecords; /* Array of part records, from - * left to right and bottom to - * top. */ + MathValueRecord + italicsCorrection; + /* Italics correction of this + * MathGlyphAssembly. Should not + * depend on the assembly size. */ + ArrayOf + partRecords; /* Array of part records, from + * left to right and bottom to + * top. */ public: DEFINE_SIZE_ARRAY (6, partRecords); @@ -553,14 +561,10 @@ struct MathGlyphConstruction { if (variants_count) { - int scale = font->dir_scale (direction); - hb_array_t arr = mathGlyphVariantRecord.sub_array (start_offset, variants_count); - unsigned int count = arr.length; - for (unsigned int i = 0; i < count; i++) - { - variants[i].glyph = arr[i].variantGlyph; - variants[i].advance = font->em_scale (arr[i].advanceMeasurement, scale); - } + int64_t mult = font->dir_mult (direction); + for (auto _ : hb_zip (mathGlyphVariantRecord.sub_array (start_offset, variants_count), + hb_array (variants, *variants_count))) + _.second = {_.first.variantGlyph, font->em_mult (_.first.advanceMeasurement, mult)}; } return mathGlyphVariantRecord.len; } @@ -612,12 +616,12 @@ struct MathVariants .get_variants (direction, font, start_offset, variants_count, variants); } unsigned int get_glyph_parts (hb_codepoint_t glyph, - hb_direction_t direction, - hb_font_t *font, - unsigned int start_offset, - unsigned int *parts_count, /* IN/OUT */ - hb_ot_math_glyph_part_t *parts /* OUT */, - hb_position_t *italics_correction /* OUT */) const + hb_direction_t direction, + hb_font_t *font, + unsigned int start_offset, + unsigned int *parts_count, /* IN/OUT */ + hb_ot_math_glyph_part_t *parts /* OUT */, + hb_position_t *italics_correction /* OUT */) const { return get_glyph_construction (glyph, direction, font) .get_assembly () .get_parts (direction, font, @@ -645,26 +649,29 @@ struct MathVariants } protected: - HBUINT16 minConnectorOverlap; /* Minimum overlap of connecting - * glyphs during glyph construction, - * in design units. */ - OffsetTo vertGlyphCoverage; /* Offset to Coverage table - - * from the beginning of MathVariants - * table. */ - OffsetTo horizGlyphCoverage; /* Offset to Coverage table - - * from the beginning of MathVariants - * table. */ - HBUINT16 vertGlyphCount; /* Number of glyphs for which - * information is provided for - * vertically growing variants. */ - HBUINT16 horizGlyphCount; /* Number of glyphs for which - * information is provided for - * horizontally growing variants. */ + HBUINT16 minConnectorOverlap; + /* Minimum overlap of connecting + * glyphs during glyph construction, + * in design units. */ + OffsetTo vertGlyphCoverage; + /* Offset to Coverage table - + * from the beginning of MathVariants + * table. */ + OffsetTo horizGlyphCoverage; + /* Offset to Coverage table - + * from the beginning of MathVariants + * table. */ + HBUINT16 vertGlyphCount; /* Number of glyphs for which + * information is provided for + * vertically growing variants. */ + HBUINT16 horizGlyphCount;/* Number of glyphs for which + * information is provided for + * horizontally growing variants. */ /* Array of offsets to MathGlyphConstruction tables - from the beginning of the MathVariants table, for shapes growing in vertical/horizontal direction. */ - UnsizedArrayOf > + UnsizedArrayOf> glyphConstruction; public: @@ -694,7 +701,7 @@ struct MATH } hb_position_t get_constant (hb_ot_math_constant_t constant, - hb_font_t *font) const + hb_font_t *font) const { return (this+mathConstants).get_value (constant, font); } const MathGlyphInfo &get_glyph_info () const { return this+mathGlyphInfo; } @@ -702,11 +709,14 @@ struct MATH const MathVariants &get_variants () const { return this+mathVariants; } protected: - FixedVersion<>version; /* Version of the MATH table - * initially set to 0x00010000u */ - OffsetTo mathConstants;/* MathConstants table */ - OffsetTo mathGlyphInfo;/* MathGlyphInfo table */ - OffsetTo mathVariants; /* MathVariants table */ + FixedVersion<>version; /* Version of the MATH table + * initially set to 0x00010000u */ + OffsetTo + mathConstants; /* MathConstants table */ + OffsetTo + mathGlyphInfo; /* MathGlyphInfo table */ + OffsetTo + mathVariants; /* MathVariants table */ public: DEFINE_SIZE_STATIC (10); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-math.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-math.cc index e10cf3844ba..94a57201871 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-math.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-math.cc @@ -24,9 +24,10 @@ * Igalia Author(s): Frédéric Wang */ -#include "hb-open-type.hh" +#include "hb.hh" + +#ifndef HB_NO_MATH -#include "hb-ot-face.hh" #include "hb-ot-math-table.hh" @@ -37,6 +38,11 @@ * @include: hb-ot.h * * Functions for fetching mathematics layout data from OpenType fonts. + * + * HarfBuzz itself does not implement a math layout solution. The + * functions and types provided can be used by client programs to access + * the font data necessary for typesetting OpenType Math layout. + * **/ @@ -48,10 +54,9 @@ * hb_ot_math_has_data: * @face: #hb_face_t to test * - * This function allows to verify the presence of an OpenType MATH table on the - * face. + * Tests whether a face has a `MATH` table. * - * Return value: true if face has a MATH table, false otherwise + * Return value: true if the table is found, false otherwise * * Since: 1.3.3 **/ @@ -63,16 +68,18 @@ hb_ot_math_has_data (hb_face_t *face) /** * hb_ot_math_get_constant: - * @font: #hb_font_t from which to retrieve the value + * @font: #hb_font_t to work upon * @constant: #hb_ot_math_constant_t the constant to retrieve * - * This function returns the requested math constants as a #hb_position_t. - * If the request constant is HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN, - * HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN or - * HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN then the return value is - * actually an integer between 0 and 100 representing that percentage. + * Fetches the specified math constant. For most constants, the value returned + * is an #hb_position_t. + * + * However, if the requested constant is #HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN, + * #HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN or + * #HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN, then the return value is + * an integer between 0 and 100 representing that percentage. * - * Return value: the requested constant or 0 + * Return value: the requested constant or zero * * Since: 1.3.3 **/ @@ -85,10 +92,13 @@ hb_ot_math_get_constant (hb_font_t *font, /** * hb_ot_math_get_glyph_italics_correction: - * @font: #hb_font_t from which to retrieve the value - * @glyph: glyph index from which to retrieve the value + * @font: #hb_font_t to work upon + * @glyph: The glyph index from which to retrieve the value * - * Return value: the italics correction of the glyph or 0 + * Fetches an italics-correction value (if one exists) for the specified + * glyph index. + * + * Return value: the italics correction of the glyph or zero * * Since: 1.3.3 **/ @@ -101,10 +111,20 @@ hb_ot_math_get_glyph_italics_correction (hb_font_t *font, /** * hb_ot_math_get_glyph_top_accent_attachment: - * @font: #hb_font_t from which to retrieve the value - * @glyph: glyph index from which to retrieve the value + * @font: #hb_font_t to work upon + * @glyph: The glyph index from which to retrieve the value + * + * Fetches a top-accent-attachment value (if one exists) for the specified + * glyph index. + * + * For any glyph that does not have a top-accent-attachment value - that is, + * a glyph not covered by the `MathTopAccentAttachment` table (or, when + * @font has no `MathTopAccentAttachment` table or no `MATH` table, any + * glyph) - the function synthesizes a value, returning the position at + * one-half the glyph's advance width. * - * Return value: the top accent attachment of the glyph or 0 + * Return value: the top accent attachment of the glyph or 0.5 * the advance + * width of @glyph * * Since: 1.3.3 **/ @@ -117,8 +137,10 @@ hb_ot_math_get_glyph_top_accent_attachment (hb_font_t *font, /** * hb_ot_math_is_glyph_extended_shape: - * @face: a #hb_face_t to test - * @glyph: a glyph index to test + * @face: #hb_face_t to work upon + * @glyph: The glyph index to test + * + * Tests whether the given glyph index is an extended shape in the face. * * Return value: true if the glyph is an extended shape, false otherwise * @@ -133,18 +155,20 @@ hb_ot_math_is_glyph_extended_shape (hb_face_t *face, /** * hb_ot_math_get_glyph_kerning: - * @font: #hb_font_t from which to retrieve the value - * @glyph: glyph index from which to retrieve the value - * @kern: the #hb_ot_math_kern_t from which to retrieve the value + * @font: #hb_font_t to work upon + * @glyph: The glyph index from which to retrieve the value + * @kern: The #hb_ot_math_kern_t from which to retrieve the value * @correction_height: the correction height to use to determine the kerning. * - * This function tries to retrieve the MathKern table for the specified font, - * glyph and #hb_ot_math_kern_t. Then it browses the list of heights from the - * MathKern table to find one value that is greater or equal to specified - * correction_height. If one is found the corresponding value from the list of - * kerns is returned and otherwise the last kern value is returned. + * Fetches the math kerning (cut-ins) value for the specified font, glyph index, and + * @kern. + * + * If the MathKern table is found, the function examines it to find a height + * value that is greater or equal to @correction_height. If such a height + * value is found, corresponding kerning value from the table is returned. If + * no such height value is found, the last kerning value is returned. * - * Return value: requested kerning or 0 + * Return value: requested kerning value or zero * * Since: 1.3.3 **/ @@ -162,20 +186,24 @@ hb_ot_math_get_glyph_kerning (hb_font_t *font, /** * hb_ot_math_get_glyph_variants: - * @font: #hb_font_t from which to retrieve the values - * @glyph: index of the glyph to stretch - * @direction: direction of the stretching + * @font: #hb_font_t to work upon + * @glyph: The index of the glyph to stretch + * @direction: The direction of the stretching (horizontal or vertical) * @start_offset: offset of the first variant to retrieve - * @variants_count: maximum number of variants to retrieve after start_offset - * (IN) and actual number of variants retrieved (OUT) - * @variants: array of size at least @variants_count to store the result + * @variants_count: (inout): Input = the maximum number of variants to return; + * Output = the actual number of variants returned + * @variants: (out) (array length=variants_count): array of variants returned * - * This function tries to retrieve the MathGlyphConstruction for the specified - * font, glyph and direction. Note that only the value of - * #HB_DIRECTION_IS_HORIZONTAL is considered. It provides the corresponding list - * of size variants as an array of hb_ot_math_glyph_variant_t structs. + * Fetches the MathGlyphConstruction for the specified font, glyph index, and + * direction. The corresponding list of size variants is returned as a list of + * #hb_ot_math_glyph_variant_t structs. * - * Return value: the total number of size variants available or 0 + * The @direction parameter is only used to select between horizontal + * or vertical directions for the construction. Even though all #hb_direction_t + * values are accepted, only the result of #HB_DIRECTION_IS_HORIZONTAL is + * considered. + * + * Return value: the total number of size variants available or zero * * Since: 1.3.3 **/ @@ -195,15 +223,19 @@ hb_ot_math_get_glyph_variants (hb_font_t *font, /** * hb_ot_math_get_min_connector_overlap: - * @font: #hb_font_t from which to retrieve the value - * @direction: direction of the stretching + * @font: #hb_font_t to work upon + * @direction: direction of the stretching (horizontal or vertical) + * + * Fetches the MathVariants table for the specified font and returns the + * minimum overlap of connecting glyphs that are required to draw a glyph + * assembly in the specified direction. * - * This function tries to retrieve the MathVariants table for the specified - * font and returns the minimum overlap of connecting glyphs to draw a glyph - * assembly in the specified direction. Note that only the value of - * #HB_DIRECTION_IS_HORIZONTAL is considered. + * The @direction parameter is only used to select between horizontal + * or vertical directions for the construction. Even though all #hb_direction_t + * values are accepted, only the result of #HB_DIRECTION_IS_HORIZONTAL is + * considered. * - * Return value: requested min connector overlap or 0 + * Return value: requested minimum connector overlap or zero * * Since: 1.3.3 **/ @@ -216,19 +248,24 @@ hb_ot_math_get_min_connector_overlap (hb_font_t *font, /** * hb_ot_math_get_glyph_assembly: - * @font: #hb_font_t from which to retrieve the values - * @glyph: index of the glyph to stretch - * @direction: direction of the stretching + * @font: #hb_font_t to work upon + * @glyph: The index of the glyph to stretch + * @direction: direction of the stretching (horizontal or vertical) * @start_offset: offset of the first glyph part to retrieve - * @parts_count: maximum number of glyph parts to retrieve after start_offset - * (IN) and actual number of parts retrieved (OUT) - * @parts: array of size at least @parts_count to store the result - * @italics_correction: italic correction of the glyph assembly + * @parts_count: (inout): Input = maximum number of glyph parts to return; + * Output = actual number of parts returned + * @parts: (out) (array length=parts_count): the glyph parts returned + * @italics_correction: (out): italics correction of the glyph assembly * - * This function tries to retrieve the GlyphAssembly for the specified font, - * glyph and direction. Note that only the value of #HB_DIRECTION_IS_HORIZONTAL - * is considered. It provides the information necessary to draw the glyph - * assembly as an array of #hb_ot_math_glyph_part_t. + * Fetches the GlyphAssembly for the specified font, glyph index, and direction. + * Returned are a list of #hb_ot_math_glyph_part_t glyph parts that can be + * used to draw the glyph and an italics-correction value (if one is defined + * in the font). + * + * The @direction parameter is only used to select between horizontal + * or vertical directions for the construction. Even though all #hb_direction_t + * values are accepted, only the result of #HB_DIRECTION_IS_HORIZONTAL is + * considered. * * Return value: the total number of parts in the glyph assembly * @@ -251,3 +288,6 @@ hb_ot_math_get_glyph_assembly (hb_font_t *font, parts, italics_correction); } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-math.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-math.h index 3cbdb8b3e5d..0d92ffb4147 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-math.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-math.h @@ -50,6 +50,9 @@ HB_BEGIN_DECLS /** * hb_ot_math_constant_t: * + * The 'MATH' table constants specified at + * https://docs.microsoft.com/en-us/typography/opentype/spec/math + * * Since: 1.3.3 */ typedef enum { @@ -114,6 +117,9 @@ typedef enum { /** * hb_ot_math_kern_t: * + * The math kerning-table types defined for the four corners + * of a glyph. + * * Since: 1.3.3 */ typedef enum { @@ -125,6 +131,10 @@ typedef enum { /** * hb_ot_math_glyph_variant_t: + * @glyph: The glyph index of the variant + * @advance: The advance width of the variant + * + * Data type to hold math-variant information for a glyph. * * Since: 1.3.3 */ @@ -136,14 +146,25 @@ typedef struct hb_ot_math_glyph_variant_t { /** * hb_ot_math_glyph_part_flags_t: * + * Flags for math glyph parts. + * * Since: 1.3.3 */ typedef enum { /*< flags >*/ - HB_MATH_GLYPH_PART_FLAG_EXTENDER = 0x00000001u /* Extender glyph */ + HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER = 0x00000001u /* Extender glyph */ } hb_ot_math_glyph_part_flags_t; /** * hb_ot_math_glyph_part_t: + * @glyph: The glyph index of the variant part + * @start_connector_length: The length of the connector on the starting side of the variant part + * @end_connector_length: The length of the connector on the ending side of the variant part + * @full_advance: The total advance of the part + * @flags: #hb_ot_math_glyph_part_flags_t flags for the part + * + * Data type to hold information for a "part" component of a math-variant glyph. + * Large variants for stretchable math glyphs (such as parentheses) can be constructed + * on the fly from parts. * * Since: 1.3.3 */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-maxp-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-maxp-table.hh index 2940617b1b8..0afa53a882f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-maxp-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-maxp-table.hh @@ -77,7 +77,7 @@ struct maxp void set_num_glyphs (unsigned int count) { - numGlyphs.set (count); + numGlyphs = count; } bool sanitize (hb_sanitize_context_t *c) const @@ -94,46 +94,43 @@ struct maxp return_trace (likely (version.major == 0 && version.minor == 0x5000u)); } - bool subset (hb_subset_plan_t *plan) const + bool subset (hb_subset_context_t *c) const { - hb_blob_t *maxp_blob = hb_sanitize_context_t().reference_table (plan->source); - hb_blob_t *maxp_prime_blob = hb_blob_copy_writable_or_fail (maxp_blob); - hb_blob_destroy (maxp_blob); + TRACE_SUBSET (this); + maxp *maxp_prime = c->serializer->embed (this); + if (unlikely (!maxp_prime)) return_trace (false); - if (unlikely (!maxp_prime_blob)) { - return false; - } - maxp *maxp_prime = (maxp *) hb_blob_get_data (maxp_prime_blob, nullptr); + maxp_prime->numGlyphs = c->plan->num_output_glyphs (); + if (maxp_prime->version.major == 1) + { + const maxpV1Tail *src_v1 = &StructAfter (*this); + maxpV1Tail *dest_v1 = c->serializer->embed (src_v1); + if (unlikely (!dest_v1)) return_trace (false); - maxp_prime->set_num_glyphs (plan->glyphs.length); - if (plan->drop_hints) - drop_hint_fields (plan, maxp_prime); + if (c->plan->drop_hints) + drop_hint_fields (dest_v1); + } - bool result = plan->add_table (HB_OT_TAG_maxp, maxp_prime_blob); - hb_blob_destroy (maxp_prime_blob); - return result; + return_trace (true); } - static void drop_hint_fields (hb_subset_plan_t *plan HB_UNUSED, maxp *maxp_prime) + static void drop_hint_fields (maxpV1Tail* dest_v1) { - if (maxp_prime->version.major == 1) - { - maxpV1Tail &v1 = StructAfter (*maxp_prime); - v1.maxZones.set (1); - v1.maxTwilightPoints.set (0); - v1.maxStorage.set (0); - v1.maxFunctionDefs.set (0); - v1.maxInstructionDefs.set (0); - v1.maxStackElements.set (0); - v1.maxSizeOfInstructions.set (0); - } + dest_v1->maxZones = 1; + dest_v1->maxTwilightPoints = 0; + dest_v1->maxStorage = 0; + dest_v1->maxFunctionDefs = 0; + dest_v1->maxInstructionDefs = 0; + dest_v1->maxStackElements = 0; + dest_v1->maxSizeOfInstructions = 0; } protected: - FixedVersion<>version; /* Version of the maxp table (0.5 or 1.0), - * 0x00005000u or 0x00010000u. */ - HBUINT16 numGlyphs; /* The number of glyphs in the font. */ -/*maxpV1Tail v1Tail[VAR]; */ + FixedVersion<>version;/* Version of the maxp table (0.5 or 1.0), + * 0x00005000u or 0x00010000u. */ + HBUINT16 numGlyphs; + /* The number of glyphs in the font. */ +/*maxpV1Tail v1Tail[HB_VAR_ARRAY]; */ public: DEFINE_SIZE_STATIC (6); }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-meta-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-meta-table.hh new file mode 100644 index 00000000000..2b45f42572f --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-meta-table.hh @@ -0,0 +1,127 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_OT_META_TABLE_HH +#define HB_OT_META_TABLE_HH + +#include "hb-open-type.hh" + +/* + * meta -- Metadata Table + * https://docs.microsoft.com/en-us/typography/opentype/spec/meta + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6meta.html + */ +#define HB_OT_TAG_meta HB_TAG ('m','e','t','a') + + +namespace OT { + + +struct DataMap +{ + int cmp (hb_tag_t a) const { return tag.cmp (a); } + + hb_tag_t get_tag () const { return tag; } + + hb_blob_t *reference_entry (hb_blob_t *meta_blob) const + { return hb_blob_create_sub_blob (meta_blob, dataZ, dataLength); } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + dataZ.sanitize (c, base, dataLength))); + } + + protected: + Tag tag; /* A tag indicating the type of metadata. */ + LNNOffsetTo> + dataZ; /* Offset in bytes from the beginning of the + * metadata table to the data for this tag. */ + HBUINT32 dataLength; /* Length of the data. The data is not required to + * be padded to any byte boundary. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct meta +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_meta; + + struct accelerator_t + { + void init (hb_face_t *face) + { table = hb_sanitize_context_t ().reference_table (face); } + void fini () { table.destroy (); } + + hb_blob_t *reference_entry (hb_tag_t tag) const + { return table->dataMaps.lsearch (tag).reference_entry (table.get_blob ()); } + + unsigned int get_entries (unsigned int start_offset, + unsigned int *count, + hb_ot_meta_tag_t *entries) const + { + if (count) + { + + table->dataMaps.sub_array (start_offset, count) + | hb_map (&DataMap::get_tag) + | hb_map ([](hb_tag_t tag) { return (hb_ot_meta_tag_t) tag; }) + | hb_sink (hb_array (entries, *count)) + ; + } + return table->dataMaps.len; + } + + private: + hb_blob_ptr_t table; + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version == 1 && + dataMaps.sanitize (c, this))); + } + + protected: + HBUINT32 version; /* Version number of the metadata table — set to 1. */ + HBUINT32 flags; /* Flags — currently unused; set to 0. */ + HBUINT32 dataOffset; + /* Per Apple specification: + * Offset from the beginning of the table to the data. + * Per OT specification: + * Reserved. Not used; should be set to 0. */ + LArrayOf + dataMaps;/* Array of data map records. */ + public: + DEFINE_SIZE_ARRAY (16, dataMaps); +}; + +struct meta_accelerator_t : meta::accelerator_t {}; + +} /* namespace OT */ + + +#endif /* HB_OT_META_TABLE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-meta.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-meta.cc new file mode 100644 index 00000000000..f2d7d337cf3 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-meta.cc @@ -0,0 +1,77 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.hh" + +#ifndef HB_NO_META + +#include "hb-ot-meta-table.hh" + +/** + * SECTION:hb-ot-meta + * @title: hb-ot-meta + * @short_description: OpenType Metadata + * @include: hb-ot.h + * + * Functions for fetching metadata from fonts. + **/ + +/** + * hb_ot_meta_get_entry_tags: + * @face: a face object + * @start_offset: iteration's start offset + * @entries_count:(inout) (allow-none): buffer size as input, filled size as output + * @entries: (out caller-allocates) (array length=entries_count): entries tags buffer + * + * Return value: Number of all available feature types. + * + * Since: 2.6.0 + **/ +unsigned int +hb_ot_meta_get_entry_tags (hb_face_t *face, + unsigned int start_offset, + unsigned int *entries_count, /* IN/OUT. May be NULL. */ + hb_ot_meta_tag_t *entries /* OUT. May be NULL. */) +{ + return face->table.meta->get_entries (start_offset, entries_count, entries); +} + +/** + * hb_ot_meta_reference_entry: + * @face: a #hb_face_t object. + * @meta_tag: tag of metadata you like to have. + * + * It fetches metadata entry of a given tag from a font. + * + * Returns: (transfer full): A blob containing the blob. + * + * Since: 2.6.0 + **/ +hb_blob_t * +hb_ot_meta_reference_entry (hb_face_t *face, hb_ot_meta_tag_t meta_tag) +{ + return face->table.meta->reference_entry (meta_tag); +} + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-meta.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-meta.h new file mode 100644 index 00000000000..71e1b781970 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-meta.h @@ -0,0 +1,71 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_OT_H_IN +#error "Include instead." +#endif + +#ifndef HB_OT_META_H +#define HB_OT_META_H + +#include "hb.h" + +HB_BEGIN_DECLS + +/** + * hb_ot_meta_tag_t: + * @HB_OT_META_TAG_DESIGN_LANGUAGES: Design languages. Text, using only + * Basic Latin (ASCII) characters. Indicates languages and/or scripts + * for the user audiences that the font was primarily designed for. + * @HB_OT_META_TAG_SUPPORTED_LANGUAGES: Supported languages. Text, using + * only Basic Latin (ASCII) characters. Indicates languages and/or scripts + * that the font is declared to be capable of supporting. + * + * Known metadata tags from https://docs.microsoft.com/en-us/typography/opentype/spec/meta + * + * Since: 2.6.0 + **/ +typedef enum { +/* + HB_OT_META_TAG_APPL = HB_TAG ('a','p','p','l'), + HB_OT_META_TAG_BILD = HB_TAG ('b','i','l','d'), +*/ + HB_OT_META_TAG_DESIGN_LANGUAGES = HB_TAG ('d','l','n','g'), + HB_OT_META_TAG_SUPPORTED_LANGUAGES = HB_TAG ('s','l','n','g'), + + _HB_OT_META_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_ot_meta_tag_t; + +HB_EXTERN unsigned int +hb_ot_meta_get_entry_tags (hb_face_t *face, + unsigned int start_offset, + unsigned int *entries_count, /* IN/OUT. May be NULL. */ + hb_ot_meta_tag_t *entries /* OUT. May be NULL. */); + +HB_EXTERN hb_blob_t * +hb_ot_meta_reference_entry (hb_face_t *face, hb_ot_meta_tag_t meta_tag); + +HB_END_DECLS + +#endif /* HB_OT_META_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.cc new file mode 100644 index 00000000000..9c0920a8085 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.cc @@ -0,0 +1,231 @@ +/* + * Copyright © 2018-2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.hh" + +#include "hb-ot-var-mvar-table.hh" +#include "hb-ot-gasp-table.hh" // Just so we compile it; unused otherwise. +#include "hb-ot-os2-table.hh" +#include "hb-ot-post-table.hh" +#include "hb-ot-hhea-table.hh" +#include "hb-ot-metrics.hh" +#include "hb-ot-face.hh" + + +static float +_fix_ascender_descender (float value, hb_ot_metrics_tag_t metrics_tag) +{ + if (metrics_tag == HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER || + metrics_tag == HB_OT_METRICS_TAG_VERTICAL_ASCENDER) + return fabs ((double) value); + if (metrics_tag == HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER || + metrics_tag == HB_OT_METRICS_TAG_VERTICAL_DESCENDER) + return -fabs ((double) value); + return value; +} + +/* The common part of _get_position logic needed on hb-ot-font and here + to be able to have slim builds without the not always needed parts */ +bool +_hb_ot_metrics_get_position_common (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT. May be NULL. */) +{ + hb_face_t *face = font->face; + switch ((unsigned) metrics_tag) + { +#ifndef HB_NO_VAR +#define GET_VAR face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords) +#else +#define GET_VAR .0f +#endif +#define GET_METRIC_X(TABLE, ATTR) \ + (face->table.TABLE->has_data () && \ + (position && (*position = font->em_scalef_x (_fix_ascender_descender ( \ + face->table.TABLE->ATTR + GET_VAR, metrics_tag))), true)) +#define GET_METRIC_Y(TABLE, ATTR) \ + (face->table.TABLE->has_data () && \ + (position && (*position = font->em_scalef_y (_fix_ascender_descender ( \ + face->table.TABLE->ATTR + GET_VAR, metrics_tag))), true)) + case HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER: + return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoAscender)) || + GET_METRIC_Y (hhea, ascender); + case HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER: + return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoDescender)) || + GET_METRIC_Y (hhea, descender); + case HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP: + return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoLineGap)) || + GET_METRIC_Y (hhea, lineGap); + case HB_OT_METRICS_TAG_VERTICAL_ASCENDER: return GET_METRIC_X (vhea, ascender); + case HB_OT_METRICS_TAG_VERTICAL_DESCENDER: return GET_METRIC_X (vhea, descender); + case HB_OT_METRICS_TAG_VERTICAL_LINE_GAP: return GET_METRIC_X (vhea, lineGap); +#undef GET_METRIC_Y +#undef GET_METRIC_X +#undef GET_VAR + default: assert (0); return false; + } +} + +#ifndef HB_NO_METRICS + +#if 0 +static bool +_get_gasp (hb_face_t *face, float *result, hb_ot_metrics_tag_t metrics_tag) +{ + const OT::GaspRange& range = face->table.gasp->get_gasp_range (metrics_tag - HB_TAG ('g','s','p','0')); + if (&range == &Null (OT::GaspRange)) return false; + if (result) *result = range.rangeMaxPPEM + font->face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords); + return true; +} +#endif + +/* Private tags for https://github.com/harfbuzz/harfbuzz/issues/1866 */ +#define _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_OS2 HB_TAG ('O','a','s','c') +#define _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_HHEA HB_TAG ('H','a','s','c') +#define _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_OS2 HB_TAG ('O','d','s','c') +#define _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_HHEA HB_TAG ('H','d','s','c') +#define _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_OS2 HB_TAG ('O','l','g','p') +#define _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_HHEA HB_TAG ('H','l','g','p') + +/** + * hb_ot_metrics_get_position: + * @font: a #hb_font_t object. + * @metrics_tag: tag of metrics value you like to fetch. + * @position: (out) (optional): result of metrics value from the font. + * + * It fetches metrics value corresponding to a given tag from a font. + * + * Returns: Whether found the requested metrics in the font. + * Since: 2.6.0 + **/ +hb_bool_t +hb_ot_metrics_get_position (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT. May be NULL. */) +{ + hb_face_t *face = font->face; + switch ((unsigned) metrics_tag) + { + case HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER: + case HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER: + case HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP: + case HB_OT_METRICS_TAG_VERTICAL_ASCENDER: + case HB_OT_METRICS_TAG_VERTICAL_DESCENDER: + case HB_OT_METRICS_TAG_VERTICAL_LINE_GAP: return _hb_ot_metrics_get_position_common (font, metrics_tag, position); +#ifndef HB_NO_VAR +#define GET_VAR hb_ot_metrics_get_variation (font, metrics_tag) +#else +#define GET_VAR 0 +#endif +#define GET_METRIC_X(TABLE, ATTR) \ + (face->table.TABLE->has_data () && \ + (position && (*position = font->em_scalef_x (face->table.TABLE->ATTR + GET_VAR)), true)) +#define GET_METRIC_Y(TABLE, ATTR) \ + (face->table.TABLE->has_data () && \ + (position && (*position = font->em_scalef_y (face->table.TABLE->ATTR + GET_VAR)), true)) + case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT: return GET_METRIC_Y (OS2, usWinAscent); + case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT: return GET_METRIC_Y (OS2, usWinDescent); + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE: return GET_METRIC_Y (hhea, caretSlopeRise); + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN: return GET_METRIC_X (hhea, caretSlopeRun); + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET: return GET_METRIC_X (hhea, caretOffset); + case HB_OT_METRICS_TAG_VERTICAL_CARET_RISE: return GET_METRIC_X (vhea, caretSlopeRise); + case HB_OT_METRICS_TAG_VERTICAL_CARET_RUN: return GET_METRIC_Y (vhea, caretSlopeRun); + case HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET: return GET_METRIC_Y (vhea, caretOffset); + case HB_OT_METRICS_TAG_X_HEIGHT: return GET_METRIC_Y (OS2->v2 (), sxHeight); + case HB_OT_METRICS_TAG_CAP_HEIGHT: return GET_METRIC_Y (OS2->v2 (), sCapHeight); + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE: return GET_METRIC_X (OS2, ySubscriptXSize); + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE: return GET_METRIC_Y (OS2, ySubscriptYSize); + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET: return GET_METRIC_X (OS2, ySubscriptXOffset); + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET: return GET_METRIC_Y (OS2, ySubscriptYOffset); + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE: return GET_METRIC_X (OS2, ySuperscriptXSize); + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE: return GET_METRIC_Y (OS2, ySuperscriptYSize); + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET: return GET_METRIC_X (OS2, ySuperscriptXOffset); + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET: return GET_METRIC_Y (OS2, ySuperscriptYOffset); + case HB_OT_METRICS_TAG_STRIKEOUT_SIZE: return GET_METRIC_Y (OS2, yStrikeoutSize); + case HB_OT_METRICS_TAG_STRIKEOUT_OFFSET: return GET_METRIC_Y (OS2, yStrikeoutPosition); + case HB_OT_METRICS_TAG_UNDERLINE_SIZE: return GET_METRIC_Y (post->table, underlineThickness); + case HB_OT_METRICS_TAG_UNDERLINE_OFFSET: return GET_METRIC_Y (post->table, underlinePosition); + + /* Private tags */ + case _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_OS2: return GET_METRIC_Y (OS2, sTypoAscender); + case _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_HHEA: return GET_METRIC_Y (hhea, ascender); + case _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_OS2: return GET_METRIC_Y (OS2, sTypoDescender); + case _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_HHEA: return GET_METRIC_Y (hhea, descender); + case _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_OS2: return GET_METRIC_Y (OS2, sTypoLineGap); + case _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_HHEA: return GET_METRIC_Y (hhea, lineGap); +#undef GET_METRIC_Y +#undef GET_METRIC_X +#undef GET_VAR + default: return false; + } +} + +#ifndef HB_NO_VAR +/** + * hb_ot_metrics_get_variation: + * @font: + * @metrics_tag: + * + * Returns: + * + * Since: 2.6.0 + **/ +float +hb_ot_metrics_get_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag) +{ + return font->face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords); +} + +/** + * hb_ot_metrics_get_x_variation: + * @font: + * @metrics_tag: + * + * Returns: + * + * Since: 2.6.0 + **/ +hb_position_t +hb_ot_metrics_get_x_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag) +{ + return font->em_scalef_x (hb_ot_metrics_get_variation (font, metrics_tag)); +} + +/** + * hb_ot_metrics_get_y_variation: + * @font: + * @metrics_tag: + * + * Returns: + * + * Since: 2.6.0 + **/ +hb_position_t +hb_ot_metrics_get_y_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag) +{ + return font->em_scalef_y (hb_ot_metrics_get_variation (font, metrics_tag)); +} +#endif + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.h new file mode 100644 index 00000000000..c1be1d85b3c --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.h @@ -0,0 +1,122 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_OT_H_IN +#error "Include instead." +#endif + +#ifndef HB_OT_METRICS_H +#define HB_OT_METRICS_H + +#include "hb.h" +#include "hb-ot-name.h" + +HB_BEGIN_DECLS + + +/** + * hb_ot_metrics_tag_t: + * @HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER: horizontal ascender. + * @HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER: horizontal descender. + * @HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP: horizontal line gap. + * @HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT: horizontal clipping ascent. + * @HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT: horizontal clipping descent. + * @HB_OT_METRICS_TAG_VERTICAL_ASCENDER: vertical ascender. + * @HB_OT_METRICS_TAG_VERTICAL_DESCENDER: vertical descender. + * @HB_OT_METRICS_TAG_VERTICAL_LINE_GAP: vertical line gap. + * @HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE: horizontal caret rise. + * @HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN: horizontal caret run. + * @HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET: horizontal caret offset. + * @HB_OT_METRICS_TAG_VERTICAL_CARET_RISE: vertical caret rise. + * @HB_OT_METRICS_TAG_VERTICAL_CARET_RUN: vertical caret run. + * @HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET: vertical caret offset. + * @HB_OT_METRICS_TAG_X_HEIGHT: x height. + * @HB_OT_METRICS_TAG_CAP_HEIGHT: cap height. + * @HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE: subscript em x size. + * @HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE: subscript em y size. + * @HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET: subscript em x offset. + * @HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET: subscript em y offset. + * @HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE: superscript em x size. + * @HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE: superscript em y size. + * @HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET: superscript em x offset. + * @HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET: superscript em y offset. + * @HB_OT_METRICS_TAG_STRIKEOUT_SIZE: strikeout size. + * @HB_OT_METRICS_TAG_STRIKEOUT_OFFSET: strikeout offset. + * @HB_OT_METRICS_TAG_UNDERLINE_SIZE: underline size. + * @HB_OT_METRICS_TAG_UNDERLINE_OFFSET: underline offset. + * + * From https://docs.microsoft.com/en-us/typography/opentype/spec/mvar#value-tags + * + * Since: 2.6.0 + **/ +typedef enum { + HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER = HB_TAG ('h','a','s','c'), + HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER = HB_TAG ('h','d','s','c'), + HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP = HB_TAG ('h','l','g','p'), + HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT = HB_TAG ('h','c','l','a'), + HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT = HB_TAG ('h','c','l','d'), + HB_OT_METRICS_TAG_VERTICAL_ASCENDER = HB_TAG ('v','a','s','c'), + HB_OT_METRICS_TAG_VERTICAL_DESCENDER = HB_TAG ('v','d','s','c'), + HB_OT_METRICS_TAG_VERTICAL_LINE_GAP = HB_TAG ('v','l','g','p'), + HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE = HB_TAG ('h','c','r','s'), + HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN = HB_TAG ('h','c','r','n'), + HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET = HB_TAG ('h','c','o','f'), + HB_OT_METRICS_TAG_VERTICAL_CARET_RISE = HB_TAG ('v','c','r','s'), + HB_OT_METRICS_TAG_VERTICAL_CARET_RUN = HB_TAG ('v','c','r','n'), + HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET = HB_TAG ('v','c','o','f'), + HB_OT_METRICS_TAG_X_HEIGHT = HB_TAG ('x','h','g','t'), + HB_OT_METRICS_TAG_CAP_HEIGHT = HB_TAG ('c','p','h','t'), + HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE = HB_TAG ('s','b','x','s'), + HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE = HB_TAG ('s','b','y','s'), + HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET = HB_TAG ('s','b','x','o'), + HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET = HB_TAG ('s','b','y','o'), + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE = HB_TAG ('s','p','x','s'), + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE = HB_TAG ('s','p','y','s'), + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET = HB_TAG ('s','p','x','o'), + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET = HB_TAG ('s','p','y','o'), + HB_OT_METRICS_TAG_STRIKEOUT_SIZE = HB_TAG ('s','t','r','s'), + HB_OT_METRICS_TAG_STRIKEOUT_OFFSET = HB_TAG ('s','t','r','o'), + HB_OT_METRICS_TAG_UNDERLINE_SIZE = HB_TAG ('u','n','d','s'), + HB_OT_METRICS_TAG_UNDERLINE_OFFSET = HB_TAG ('u','n','d','o'), + + _HB_OT_METRICS_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_ot_metrics_tag_t; + +HB_EXTERN hb_bool_t +hb_ot_metrics_get_position (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT. May be NULL. */); + +HB_EXTERN float +hb_ot_metrics_get_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag); + +HB_EXTERN hb_position_t +hb_ot_metrics_get_x_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag); + +HB_EXTERN hb_position_t +hb_ot_metrics_get_y_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag); + +HB_END_DECLS + +#endif /* HB_OT_METRICS_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-glyf.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.hh similarity index 70% rename from src/java.desktop/share/native/libharfbuzz/hb-subset-glyf.hh rename to src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.hh index 385a8f9d564..2c75f32d999 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-glyf.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.hh @@ -1,5 +1,5 @@ /* - * Copyright © 2018 Google, Inc. + * Copyright © 2018 Ebrahim Byagowi * * This is part of HarfBuzz, a text shaping library. * @@ -20,21 +20,16 @@ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Garret Rieger */ -#ifndef HB_SUBSET_GLYF_HH -#define HB_SUBSET_GLYF_HH +#ifndef HB_OT_METRICS_HH +#define HB_OT_METRICS_HH #include "hb.hh" -#include "hb-subset.hh" - HB_INTERNAL bool -hb_subset_glyf_and_loca (hb_subset_plan_t *plan, - bool *use_short_loca, /* OUT */ - hb_blob_t **glyf_prime /* OUT */, - hb_blob_t **loca_prime /* OUT */); +_hb_ot_metrics_get_position_common (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT. May be NULL. */); -#endif /* HB_SUBSET_GLYF_HH */ +#endif /* HB_OT_METRICS_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-name-language.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-name-language-static.hh similarity index 96% rename from src/java.desktop/share/native/libharfbuzz/hb-ot-name-language.cc rename to src/java.desktop/share/native/libharfbuzz/hb-ot-name-language-static.hh index 1606a13cec9..5312f7b3a6e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-name-language.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-name-language-static.hh @@ -24,6 +24,9 @@ * Google Author(s): Behdad Esfahbod */ +#ifndef HB_OT_NAME_LANGUAGE_STATIC_HH +#define HB_OT_NAME_LANGUAGE_STATIC_HH + #include "hb-ot-name-language.hh" /* Following two tables were generated by joining FreeType, FontConfig, @@ -34,12 +37,8 @@ struct hb_ot_language_map_t { - static int cmp (const void *key, const void *item) - { - unsigned int a = * (unsigned int *) key; - unsigned int b = ((const hb_ot_language_map_t *) item)->code; - return a < b ? -1 : a > b ? +1 : 0; - } + int cmp (unsigned int key) const + { return key < code ? -1 : key > code ? +1 : 0; } uint16_t code; char lang[6]; @@ -427,12 +426,10 @@ _hb_ot_name_language_for (unsigned int code, const hb_ot_language_map_t *array, unsigned int len) { - const hb_ot_language_map_t *entry = (const hb_ot_language_map_t *) - hb_bsearch (&code, - array, - len, - sizeof (array[0]), - hb_ot_language_map_t::cmp); +#ifdef HB_NO_OT_NAME_LANGUAGE + return HB_LANGUAGE_INVALID; +#endif + auto *entry = hb_bsearch (code, array, len); if (entry) return hb_language_from_string (entry->lang, -1); @@ -455,3 +452,5 @@ _hb_ot_name_language_for_mac_code (unsigned int code) hb_mac_language_map, ARRAY_LENGTH (hb_mac_language_map)); } + +#endif /* HB_OT_NAME_LANGUAGE_STATIC_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-name-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-name-table.hh index 96600cb4006..fb2300d0688 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-name-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-name-table.hh @@ -51,6 +51,7 @@ struct NameRecord { hb_language_t language (hb_face_t *face) const { +#ifndef HB_NO_OT_NAME_LANGUAGE unsigned int p = platformID; unsigned int l = languageID; @@ -60,9 +61,12 @@ struct NameRecord if (p == 1) return _hb_ot_name_language_for_mac_code (l); +#ifndef HB_NO_OT_NAME_LANGUAGE_AAT if (p == 0) - return _hb_aat_language_get (face, l); + return face->table.ltag->get_language (l); +#endif +#endif return HB_LANGUAGE_INVALID; } @@ -93,11 +97,51 @@ struct NameRecord return UNSUPPORTED; } + NameRecord* copy (hb_serialize_context_t *c, const void *base) const + { + TRACE_SERIALIZE (this); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + out->offset.serialize_copy (c, offset, base, 0, hb_serialize_context_t::Tail, length); + return_trace (out); + } + + bool isUnicode () const + { + unsigned int p = platformID; + unsigned int e = encodingID; + + return (p == 0 || + (p == 3 && (e == 0 || e == 1 || e == 10))); + } + + static int cmp (const void *pa, const void *pb) + { + const NameRecord *a = (const NameRecord *)pa; + const NameRecord *b = (const NameRecord *)pb; + + if (a->platformID != b->platformID) + return a->platformID - b->platformID; + + if (a->encodingID != b->encodingID) + return a->encodingID - b->encodingID; + + if (a->languageID != b->languageID) + return a->languageID - b->languageID; + + if (a->nameID != b->nameID) + return a->nameID - b->nameID; + + if (a->length != b->length) + return a->length - b->length; + + return 0; + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); - /* We can check from base all the way up to the end of string... */ - return_trace (c->check_struct (this) && c->check_range ((char *) base, (unsigned int) length + offset)); + return_trace (c->check_struct (this) && offset.sanitize (c, base, length)); } HBUINT16 platformID; /* Platform ID. */ @@ -105,7 +149,8 @@ struct NameRecord HBUINT16 languageID; /* Language ID. */ HBUINT16 nameID; /* Name ID. */ HBUINT16 length; /* String length (in bytes). */ - HBUINT16 offset; /* String offset from start of storage area (in bytes). */ + NNOffsetTo> + offset; /* String offset from start of storage area (in bytes). */ public: DEFINE_SIZE_STATIC (12); }; @@ -119,7 +164,7 @@ _hb_ot_name_entry_cmp_key (const void *pa, const void *pb) /* Compare by name_id, then language. */ if (a->name_id != b->name_id) - return a->name_id < b->name_id ? -1 : +1; + return a->name_id - b->name_id; if (a->language == b->language) return 0; if (!a->language) return -1; @@ -141,10 +186,10 @@ _hb_ot_name_entry_cmp (const void *pa, const void *pb) const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb; if (a->entry_score != b->entry_score) - return a->entry_score < b->entry_score ? -1 : +1; + return a->entry_score - b->entry_score; if (a->entry_index != b->entry_index) - return a->entry_index < b->entry_index ? -1 : +1; + return a->entry_index - b->entry_index; return 0; } @@ -156,15 +201,65 @@ struct name unsigned int get_size () const { return min_size + count * nameRecordZ.item_size; } + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + const void *src_string_pool) + { + TRACE_SERIALIZE (this); + + if (unlikely (!c->extend_min ((*this)))) return_trace (false); + + this->format = 0; + this->count = it.len (); + + NameRecord *name_records = (NameRecord *) calloc (it.len (), NameRecord::static_size); + if (unlikely (!name_records)) return_trace (false); + + hb_array_t records (name_records, it.len ()); + + for (const NameRecord& record : it) + { + memcpy (name_records, &record, NameRecord::static_size); + name_records++; + } + + records.qsort (); + + c->copy_all (records, src_string_pool); + free (records.arrayZ); + + if (unlikely (c->ran_out_of_room)) return_trace (false); + + this->stringOffset = c->length (); + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + name *name_prime = c->serializer->start_embed (); + if (unlikely (!name_prime)) return_trace (false); + + auto it = + + nameRecordZ.as_array (count) + | hb_filter (c->plan->name_ids, &NameRecord::nameID) + | hb_filter (c->plan->name_languages, &NameRecord::languageID) + | hb_filter ([&] (const NameRecord& namerecord) { return c->plan->name_legacy || namerecord.isUnicode (); }) + ; + + name_prime->serialize (c->serializer, it, hb_addressof (this + stringOffset)); + return_trace (name_prime->count); + } + bool sanitize_records (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); const void *string_pool = (this+stringOffset).arrayZ; - unsigned int _count = count; - /* Move to run-time?! */ - for (unsigned int i = 0; i < _count; i++) - if (!nameRecordZ[i].sanitize (c, string_pool)) return_trace (false); - return_trace (true); + return_trace (nameRecordZ.sanitize (c, count, string_pool)); } bool sanitize (hb_sanitize_context_t *c) const @@ -173,14 +268,15 @@ struct name return_trace (c->check_struct (this) && likely (format == 0 || format == 1) && c->check_array (nameRecordZ.arrayZ, count) && - c->check_range (this, stringOffset)); + c->check_range (this, stringOffset) && + sanitize_records (c)); } struct accelerator_t { void init (hb_face_t *face) { - this->table = hb_sanitize_context_t().reference_table (face); + this->table = hb_sanitize_context_t ().reference_table (face); assert (this->table.get_length () >= this->table->stringOffset); this->pool = (const char *) (const void *) (this->table+this->table->stringOffset); this->pool_len = this->table.get_length () - this->table->stringOffset; @@ -224,16 +320,14 @@ struct name this->table.destroy (); } - int get_index (hb_ot_name_id_t name_id, - hb_language_t language, - unsigned int *width=nullptr) const + int get_index (hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *width=nullptr) const { const hb_ot_name_entry_t key = {name_id, {0}, language}; - const hb_ot_name_entry_t *entry = (const hb_ot_name_entry_t *) - hb_bsearch (&key, - (const hb_ot_name_entry_t *) this->names, + const hb_ot_name_entry_t *entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names, this->names.length, - sizeof (key), + sizeof (hb_ot_name_entry_t), _hb_ot_name_entry_cmp_key); if (!entry) return -1; @@ -261,16 +355,19 @@ struct name }; /* We only implement format 0 for now. */ - HBUINT16 format; /* Format selector (=0/1). */ - HBUINT16 count; /* Number of name records. */ - NNOffsetTo > - stringOffset; /* Offset to start of string storage (from start of table). */ + HBUINT16 format; /* Format selector (=0/1). */ + HBUINT16 count; /* Number of name records. */ + NNOffsetTo> + stringOffset; /* Offset to start of string storage (from start of table). */ UnsizedArrayOf - nameRecordZ; /* The name records where count is the number of records. */ + nameRecordZ; /* The name records where count is the number of records. */ public: DEFINE_SIZE_ARRAY (6, nameRecordZ); }; +#undef entry_index +#undef entry_score + struct name_accelerator_t : name::accelerator_t {}; } /* namespace OT */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-name.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-name.cc index 83a332e77d2..56bfd39168b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-name.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-name.cc @@ -26,9 +26,10 @@ #include "hb.hh" +#ifndef HB_NO_NAME + #include "hb-ot-name-table.hh" -#include "hb-ot-face.hh" #include "hb-utf.hh" @@ -93,7 +94,7 @@ hb_ot_name_convert_utf (hb_bytes_t bytes, dst = dst_next; src = src_next; - }; + } *text_size = dst - text; *dst = 0; /* NUL-terminate. */ @@ -105,7 +106,7 @@ hb_ot_name_convert_utf (hb_bytes_t bytes, { src = in_utf_t::next (src, src_end, &unicode, replacement); dst_len += out_utf_t::encode_len (unicode); - }; + } return dst_len; } @@ -222,3 +223,6 @@ hb_ot_name_get_utf32 (hb_face_t *face, { return hb_ot_name_get_utf (face, name_id, language, text_size, text); } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-table.hh index 6fbffb14512..3639a06e9be 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-table.hh @@ -30,6 +30,7 @@ #include "hb-open-type.hh" #include "hb-ot-os2-unicode-ranges.hh" +#include "hb-ot-cmap-table.hh" #include "hb-set.hh" @@ -59,6 +60,10 @@ struct OS2V1Tail struct OS2V2Tail { + bool has_data () const { return sxHeight || sCapHeight; } + + const OS2V2Tail * operator -> () const { return this; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -77,6 +82,23 @@ struct OS2V2Tail struct OS2V5Tail { + inline bool get_optical_size (unsigned int *lower, unsigned int *upper) const + { + unsigned int lower_optical_size = usLowerOpticalPointSize; + unsigned int upper_optical_size = usUpperOpticalPointSize; + + /* Per https://docs.microsoft.com/en-us/typography/opentype/spec/os2#lps */ + if (lower_optical_size < upper_optical_size && + lower_optical_size >= 1 && lower_optical_size <= 0xFFFE && + upper_optical_size >= 2 && upper_optical_size <= 0xFFFF) + { + *lower = lower_optical_size; + *upper = upper_optical_size; + return true; + } + return false; + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -94,7 +116,7 @@ struct OS2 { static constexpr hb_tag_t tableTag = HB_OT_TAG_OS2; - bool has_data () const { return this != &Null (OS2); } + bool has_data () const { return usWeightClass || usWidthClass || usFirstCharIndex || usLastCharIndex; } const OS2V1Tail &v1 () const { return version >= 1 ? v1X : Null (OS2V1Tail); } const OS2V2Tail &v2 () const { return version >= 2 ? v2X : Null (OS2V2Tail); } @@ -113,9 +135,9 @@ struct OS2 OBLIQUE = 1u<<9 }; - bool is_italic () const { return fsSelection & ITALIC; } - bool is_oblique () const { return fsSelection & OBLIQUE; } - bool is_typo_metrics () const { return fsSelection & USE_TYPO_METRICS; } + bool is_italic () const { return fsSelection & ITALIC; } + bool is_oblique () const { return fsSelection & OBLIQUE; } + bool use_typo_metrics () const { return fsSelection & USE_TYPO_METRICS; } enum width_class_t { FWIDTH_ULTRA_CONDENSED = 1, /* 50% */ @@ -145,36 +167,49 @@ struct OS2 } } - bool subset (hb_subset_plan_t *plan) const + bool subset (hb_subset_context_t *c) const { - hb_blob_t *os2_blob = hb_sanitize_context_t ().reference_table (plan->source); - hb_blob_t *os2_prime_blob = hb_blob_create_sub_blob (os2_blob, 0, -1); - // TODO(grieger): move to hb_blob_copy_writable_or_fail - hb_blob_destroy (os2_blob); - - OS2 *os2_prime = (OS2 *) hb_blob_get_data_writable (os2_prime_blob, nullptr); - if (unlikely (!os2_prime)) { - hb_blob_destroy (os2_prime_blob); - return false; + TRACE_SUBSET (this); + OS2 *os2_prime = c->serializer->embed (this); + if (unlikely (!os2_prime)) return_trace (false); + + hb_set_t unicodes; + if (!c->plan->glyphs_requested->is_empty ()) + { + hb_map_t unicode_glyphid_map; + + OT::cmap::accelerator_t cmap; + cmap.init (c->plan->source); + cmap.collect_mapping (&unicodes, &unicode_glyphid_map); + cmap.fini (); + + if (c->plan->unicodes->is_empty ()) unicodes.clear (); + else hb_set_set (&unicodes, c->plan->unicodes); + + + unicode_glyphid_map.iter () + | hb_filter (c->plan->glyphs_requested, hb_second) + | hb_map (hb_first) + | hb_sink (unicodes) + ; } - + /* when --gids option is not used, no need to do collect_mapping that is + * iterating all codepoints in each subtable, which is not efficient */ uint16_t min_cp, max_cp; - find_min_and_max_codepoint (plan->unicodes, &min_cp, &max_cp); - os2_prime->usFirstCharIndex.set (min_cp); - os2_prime->usLastCharIndex.set (max_cp); + find_min_and_max_codepoint (unicodes.is_empty () ? c->plan->unicodes : &unicodes, &min_cp, &max_cp); + os2_prime->usFirstCharIndex = min_cp; + os2_prime->usLastCharIndex = max_cp; - _update_unicode_ranges (plan->unicodes, os2_prime->ulUnicodeRange); - bool result = plan->add_table (HB_OT_TAG_OS2, os2_prime_blob); + _update_unicode_ranges (unicodes.is_empty () ? c->plan->unicodes : &unicodes, os2_prime->ulUnicodeRange); - hb_blob_destroy (os2_prime_blob); - return result; + return_trace (true); } void _update_unicode_ranges (const hb_set_t *codepoints, HBUINT32 ulUnicodeRange[4]) const { + HBUINT32 newBits[4]; for (unsigned int i = 0; i < 4; i++) - ulUnicodeRange[i].set (0); + newBits[i] = 0; hb_codepoint_t cp = HB_SET_VALUE_INVALID; while (codepoints->next (&cp)) { @@ -184,40 +219,52 @@ struct OS2 unsigned int block = bit / 32; unsigned int bit_in_block = bit % 32; unsigned int mask = 1 << bit_in_block; - ulUnicodeRange[block].set (ulUnicodeRange[block] | mask); + newBits[block] = newBits[block] | mask; } if (cp >= 0x10000 && cp <= 0x110000) { /* the spec says that bit 57 ("Non Plane 0") implies that there's at least one codepoint beyond the BMP; so I also include all the non-BMP codepoints here */ - ulUnicodeRange[1].set (ulUnicodeRange[1] | (1 << 25)); + newBits[1] = newBits[1] | (1 << 25); } } + + for (unsigned int i = 0; i < 4; i++) + ulUnicodeRange[i] = ulUnicodeRange[i] & newBits[i]; // set bits only if set in the original } static void find_min_and_max_codepoint (const hb_set_t *codepoints, - uint16_t *min_cp, /* OUT */ - uint16_t *max_cp /* OUT */) + uint16_t *min_cp, /* OUT */ + uint16_t *max_cp /* OUT */) { - *min_cp = codepoints->get_min (); - *max_cp = codepoints->get_max (); + *min_cp = hb_min (0xFFFFu, codepoints->get_min ()); + *max_cp = hb_min (0xFFFFu, codepoints->get_max ()); } - enum font_page_t { - HEBREW_FONT_PAGE = 0xB100, // Hebrew Windows 3.1 font page - SIMP_ARABIC_FONT_PAGE = 0xB200, // Simplified Arabic Windows 3.1 font page - TRAD_ARABIC_FONT_PAGE = 0xB300, // Traditional Arabic Windows 3.1 font page - OEM_ARABIC_FONT_PAGE = 0xB400, // OEM Arabic Windows 3.1 font page - SIMP_FARSI_FONT_PAGE = 0xBA00, // Simplified Farsi Windows 3.1 font page - TRAD_FARSI_FONT_PAGE = 0xBB00, // Traditional Farsi Windows 3.1 font page - THAI_FONT_PAGE = 0xDE00 // Thai Windows 3.1 font page + /* https://github.com/Microsoft/Font-Validator/blob/520aaae/OTFontFileVal/val_OS2.cs#L644-L681 */ + enum font_page_t + { + FONT_PAGE_HEBREW = 0xB100, /* Hebrew Windows 3.1 font page */ + FONT_PAGE_SIMP_ARABIC = 0xB200, /* Simplified Arabic Windows 3.1 font page */ + FONT_PAGE_TRAD_ARABIC = 0xB300, /* Traditional Arabic Windows 3.1 font page */ + FONT_PAGE_OEM_ARABIC = 0xB400, /* OEM Arabic Windows 3.1 font page */ + FONT_PAGE_SIMP_FARSI = 0xBA00, /* Simplified Farsi Windows 3.1 font page */ + FONT_PAGE_TRAD_FARSI = 0xBB00, /* Traditional Farsi Windows 3.1 font page */ + FONT_PAGE_THAI = 0xDE00 /* Thai Windows 3.1 font page */ }; - - // https://github.com/Microsoft/Font-Validator/blob/520aaae/OTFontFileVal/val_OS2.cs#L644-L681 font_page_t get_font_page () const { return (font_page_t) (version == 0 ? fsSelection & 0xFF00 : 0); } + unsigned get_size () const + { + unsigned result = min_size; + if (version >= 1) result += v1X.get_size (); + if (version >= 2) result += v2X.get_size (); + if (version >= 5) result += v5X.get_size (); + return result; + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-unicode-ranges.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-unicode-ranges.hh index 7e573b33206..9613d2d186d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-unicode-ranges.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-unicode-ranges.hh @@ -33,19 +33,8 @@ namespace OT { struct OS2Range { - static int - cmp (const void *_key, const void *_item) - { - hb_codepoint_t cp = *((hb_codepoint_t *) _key); - const OS2Range *range = (OS2Range *) _item; - - if (cp < range->start) - return -1; - else if (cp <= range->end) - return 0; - else - return +1; - } + int cmp (hb_codepoint_t key) const + { return (key < start) ? -1 : key <= end ? 0 : +1; } hb_codepoint_t start; hb_codepoint_t end; @@ -233,13 +222,8 @@ static const OS2Range _hb_os2_unicode_ranges[] = static unsigned int _hb_ot_os2_get_unicode_range_bit (hb_codepoint_t cp) { - OS2Range *range = (OS2Range*) hb_bsearch (&cp, _hb_os2_unicode_ranges, - ARRAY_LENGTH (_hb_os2_unicode_ranges), - sizeof (OS2Range), - OS2Range::cmp); - if (range != nullptr) - return range->bit; - return -1; + auto *range = hb_sorted_array (_hb_os2_unicode_ranges).bsearch (cp); + return range ? range->bit : -1; } } /* namespace OT */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table.hh index 40134b40f13..08449ac2488 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table.hh @@ -35,8 +35,6 @@ #undef HB_STRING_ARRAY_LIST #undef HB_STRING_ARRAY_NAME -#define NUM_FORMAT1_NAMES 258 - /* * post -- PostScript * https://docs.microsoft.com/en-us/typography/opentype/spec/post @@ -73,26 +71,25 @@ struct post { static constexpr hb_tag_t tableTag = HB_OT_TAG_post; - bool subset (hb_subset_plan_t *plan) const + void serialize (hb_serialize_context_t *c) const { - unsigned int post_prime_length; - hb_blob_t *post_blob = hb_sanitize_context_t ().reference_table(plan->source); - hb_blob_t *post_prime_blob = hb_blob_create_sub_blob (post_blob, 0, post::min_size); - post *post_prime = (post *) hb_blob_get_data_writable (post_prime_blob, &post_prime_length); - hb_blob_destroy (post_blob); + post *post_prime = c->allocate_min (); + if (unlikely (!post_prime)) return; - if (unlikely (!post_prime || post_prime_length != post::min_size)) - { - hb_blob_destroy (post_prime_blob); - DEBUG_MSG(SUBSET, nullptr, "Invalid source post table with length %d.", post_prime_length); - return false; - } + memcpy (post_prime, this, post::min_size); + post_prime->version.major = 3; // Version 3 does not have any glyph names. + } - post_prime->version.major.set (3); // Version 3 does not have any glyph names. - bool result = plan->add_table (HB_OT_TAG_post, post_prime_blob); - hb_blob_destroy (post_prime_blob); + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + post *post_prime = c->serializer->start_embed (); + if (unlikely (!post_prime)) return_trace (false); - return result; + serialize (c->serializer); + if (c->serializer->in_error () || c->serializer->ran_out_of_room) return_trace (false); + + return_trace (true); } struct accelerator_t @@ -131,7 +128,7 @@ struct post hb_bytes_t s = find_glyph_name (glyph); if (!s.length) return false; if (!buf_len) return true; - unsigned int len = MIN (buf_len - 1, s.length); + unsigned int len = hb_min (buf_len - 1, s.length); strncpy (buf, s.arrayZ, len); buf[len] = '\0'; return true; @@ -158,7 +155,7 @@ struct post for (unsigned int i = 0; i < count; i++) gids[i] = i; - hb_sort_r (gids, count, sizeof (gids[0]), cmp_gids, (void *) this); + hb_qsort (gids, count, sizeof (gids[0]), cmp_gids, (void *) this); if (unlikely (!gids_sorted_by_name.cmpexch (nullptr, gids))) { @@ -168,8 +165,7 @@ struct post } hb_bytes_t st (name, len); - const uint16_t *gid = (const uint16_t *) hb_bsearch_r (hb_addressof (st), gids, count, - sizeof (gids[0]), cmp_key, (void *) this); + auto* gid = hb_bsearch (st, gids, count, sizeof (gids[0]), cmp_key, (void *) this); if (gid) { *glyph = *gid; @@ -179,12 +175,14 @@ struct post return false; } + hb_blob_ptr_t table; + protected: unsigned int get_glyph_count () const { if (version == 0x00010000) - return NUM_FORMAT1_NAMES; + return format1_names_length; if (version == 0x00020000) return glyphNameIndex->len; @@ -212,7 +210,7 @@ struct post { if (version == 0x00010000) { - if (glyph >= NUM_FORMAT1_NAMES) + if (glyph >= format1_names_length) return hb_bytes_t (); return format1_names (glyph); @@ -222,9 +220,9 @@ struct post return hb_bytes_t (); unsigned int index = glyphNameIndex->arrayZ[glyph]; - if (index < NUM_FORMAT1_NAMES) + if (index < format1_names_length) return format1_names (index); - index -= NUM_FORMAT1_NAMES; + index -= format1_names_length; if (index >= index_to_offset.length) return hb_bytes_t (); @@ -238,7 +236,6 @@ struct post } private: - hb_blob_ptr_t table; uint32_t version; const ArrayOf *glyphNameIndex; hb_vector_t index_to_offset; @@ -246,6 +243,8 @@ struct post hb_atomic_ptr_t gids_sorted_by_name; }; + bool has_data () const { return version.to_int (); } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -260,7 +259,7 @@ struct post * 0x00020000 for version 2.0 * 0x00025000 for version 2.5 (deprecated) * 0x00030000 for version 3.0 */ - Fixed italicAngle; /* Italic angle in counter-clockwise degrees + HBFixed italicAngle; /* Italic angle in counter-clockwise degrees * from the vertical. Zero for upright text, * negative for text that leans to the right * (forward). */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-fallback.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-fallback.hh index f0d104c63d8..8a29d54d961 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-fallback.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-fallback.hh @@ -49,8 +49,8 @@ arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUS hb_font_t *font, unsigned int feature_index) { - OT::GlyphID glyphs[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; - OT::GlyphID substitutes[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; + OT::HBGlyphID glyphs[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; + OT::HBGlyphID substitutes[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; unsigned int num_glyphs = 0; /* Populate arrays */ @@ -66,8 +66,8 @@ arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUS u_glyph > 0xFFFFu || s_glyph > 0xFFFFu) continue; - glyphs[num_glyphs].set (u_glyph); - substitutes[num_glyphs].set (s_glyph); + glyphs[num_glyphs] = u_glyph; + substitutes[num_glyphs] = s_glyph; num_glyphs++; } @@ -77,7 +77,9 @@ arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUS /* Bubble-sort or something equally good! * May not be good-enough for presidential candidate interviews, but good-enough for us... */ - hb_stable_sort (&glyphs[0], num_glyphs, (int(*)(const OT::GlyphID*, const OT::GlyphID *)) OT::GlyphID::cmp, &substitutes[0]); + hb_stable_sort (&glyphs[0], num_glyphs, + (int(*)(const OT::HBUINT16*, const OT::HBUINT16 *)) OT::HBGlyphID::cmp, + &substitutes[0]); /* Each glyph takes four bytes max, and there's some overhead. */ @@ -86,27 +88,26 @@ arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUS OT::SubstLookup *lookup = c.start_serialize (); bool ret = lookup->serialize_single (&c, OT::LookupFlag::IgnoreMarks, - hb_array (glyphs, num_glyphs), + hb_sorted_array (glyphs, num_glyphs), hb_array (substitutes, num_glyphs)); c.end_serialize (); - /* TODO sanitize the results? */ - return ret ? c.copy () : nullptr; + return ret && !c.in_error () ? c.copy () : nullptr; } static OT::SubstLookup * arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_font_t *font) { - OT::GlyphID first_glyphs[ARRAY_LENGTH_CONST (ligature_table)]; + OT::HBGlyphID first_glyphs[ARRAY_LENGTH_CONST (ligature_table)]; unsigned int first_glyphs_indirection[ARRAY_LENGTH_CONST (ligature_table)]; unsigned int ligature_per_first_glyph_count_list[ARRAY_LENGTH_CONST (first_glyphs)]; unsigned int num_first_glyphs = 0; /* We know that all our ligatures are 2-component */ - OT::GlyphID ligature_list[ARRAY_LENGTH_CONST (first_glyphs) * ARRAY_LENGTH_CONST(ligature_table[0].ligatures)]; + OT::HBGlyphID ligature_list[ARRAY_LENGTH_CONST (first_glyphs) * ARRAY_LENGTH_CONST(ligature_table[0].ligatures)]; unsigned int component_count_list[ARRAY_LENGTH_CONST (ligature_list)]; - OT::GlyphID component_list[ARRAY_LENGTH_CONST (ligature_list) * 1/* One extra component per ligature */]; + OT::HBGlyphID component_list[ARRAY_LENGTH_CONST (ligature_list) * 1/* One extra component per ligature */]; unsigned int num_ligatures = 0; /* Populate arrays */ @@ -118,12 +119,14 @@ arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UN hb_codepoint_t first_glyph; if (!hb_font_get_glyph (font, first_u, 0, &first_glyph)) continue; - first_glyphs[num_first_glyphs].set (first_glyph); + first_glyphs[num_first_glyphs] = first_glyph; ligature_per_first_glyph_count_list[num_first_glyphs] = 0; first_glyphs_indirection[num_first_glyphs] = first_glyph_idx; num_first_glyphs++; } - hb_stable_sort (&first_glyphs[0], num_first_glyphs, (int(*)(const OT::GlyphID*, const OT::GlyphID *)) OT::GlyphID::cmp, &first_glyphs_indirection[0]); + hb_stable_sort (&first_glyphs[0], num_first_glyphs, + (int(*)(const OT::HBUINT16*, const OT::HBUINT16 *)) OT::HBGlyphID::cmp, + &first_glyphs_indirection[0]); /* Now that the first-glyphs are sorted, walk again, populate ligatures. */ for (unsigned int i = 0; i < num_first_glyphs; i++) @@ -142,9 +145,9 @@ arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UN ligature_per_first_glyph_count_list[i]++; - ligature_list[num_ligatures].set (ligature_glyph); + ligature_list[num_ligatures] = ligature_glyph; component_count_list[num_ligatures] = 2; - component_list[num_ligatures].set (second_glyph); + component_list[num_ligatures] = second_glyph; num_ligatures++; } } @@ -159,7 +162,7 @@ arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UN OT::SubstLookup *lookup = c.start_serialize (); bool ret = lookup->serialize_ligature (&c, OT::LookupFlag::IgnoreMarks, - hb_array (first_glyphs, num_first_glyphs), + hb_sorted_array (first_glyphs, num_first_glyphs), hb_array (ligature_per_first_glyph_count_list, num_first_glyphs), hb_array (ligature_list, num_ligatures), hb_array (component_count_list, num_ligatures), @@ -167,7 +170,7 @@ arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UN c.end_serialize (); /* TODO sanitize the results? */ - return ret ? c.copy () : nullptr; + return ret && !c.in_error () ? c.copy () : nullptr; } static OT::SubstLookup * @@ -227,8 +230,8 @@ arabic_fallback_plan_init_win1256 (arabic_fallback_plan_t *fallback_plan HB_UNUS return false; const Manifest &manifest = reinterpret_cast (arabic_win1256_gsub_lookups.manifest); - static_assert (sizeof (arabic_win1256_gsub_lookups.manifestData) / sizeof (ManifestLookup) - <= ARABIC_FALLBACK_MAX_LOOKUPS, ""); + static_assert (sizeof (arabic_win1256_gsub_lookups.manifestData) == + ARABIC_FALLBACK_MAX_LOOKUPS * sizeof (ManifestLookup), ""); /* TODO sanitize the table? */ unsigned j = 0; @@ -289,7 +292,7 @@ arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan, { arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) calloc (1, sizeof (arabic_fallback_plan_t)); if (unlikely (!fallback_plan)) - return const_cast (&Null(arabic_fallback_plan_t)); + return const_cast (&Null (arabic_fallback_plan_t)); fallback_plan->num_lookups = 0; fallback_plan->free_lookups = false; @@ -306,7 +309,7 @@ arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan, assert (fallback_plan->num_lookups == 0); free (fallback_plan); - return const_cast (&Null(arabic_fallback_plan_t)); + return const_cast (&Null (arabic_fallback_plan_t)); } static void diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-joining-list.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-joining-list.hh new file mode 100644 index 00000000000..c022d4bb06b --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-joining-list.hh @@ -0,0 +1,46 @@ +/* == Start of generated function == */ +/* + * The following function is generated by running: + * + * ./gen-arabic-joining-list.py ArabicShaping.txt Scripts.txt + * + * on files with these headers: + * + * # ArabicShaping-13.0.0.txt + * # Date: 2020-01-31, 23:55:00 GMT [KW, RP] + * # Scripts-13.0.0.txt + * # Date: 2020-01-22, 00:07:43 GMT + */ + +#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_JOINING_LIST_HH +#define HB_OT_SHAPE_COMPLEX_ARABIC_JOINING_LIST_HH + +static bool +has_arabic_joining (hb_script_t script) +{ + /* List of scripts that have data in arabic-table. */ + switch ((int) script) + { + case HB_SCRIPT_ADLAM: + case HB_SCRIPT_ARABIC: + case HB_SCRIPT_CHORASMIAN: + case HB_SCRIPT_HANIFI_ROHINGYA: + case HB_SCRIPT_MANDAIC: + case HB_SCRIPT_MANICHAEAN: + case HB_SCRIPT_MONGOLIAN: + case HB_SCRIPT_NKO: + case HB_SCRIPT_PHAGS_PA: + case HB_SCRIPT_PSALTER_PAHLAVI: + case HB_SCRIPT_SOGDIAN: + case HB_SCRIPT_SYRIAC: + return true; + + default: + return false; + } +} + + +#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_JOINING_LIST_HH */ + +/* == End of generated function == */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-table.hh index 7bb0aeb5220..d47d0367700 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-table.hh @@ -6,10 +6,10 @@ * * on files with these headers: * - * # ArabicShaping-11.0.0.txt - * # Date: 2018-02-21, 14:50:00 GMT [KW, RP] - * # Blocks-11.0.0.txt - * # Date: 2017-10-16, 24:39:00 GMT [KW] + * # ArabicShaping-13.0.0.txt + * # Date: 2020-01-31, 23:55:00 GMT [KW, RP] + * # Blocks-13.0.0.txt + * # Date: 2019-07-10, 19:06:00 GMT [KW] * UnicodeData.txt does not have a header. */ @@ -17,15 +17,15 @@ #define HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH -#define X JOINING_TYPE_X -#define R JOINING_TYPE_R -#define T JOINING_TYPE_T -#define U JOINING_TYPE_U #define A JOINING_GROUP_ALAPH #define DR JOINING_GROUP_DALATH_RISH -#define L JOINING_TYPE_L #define C JOINING_TYPE_C #define D JOINING_TYPE_D +#define L JOINING_TYPE_L +#define R JOINING_TYPE_R +#define T JOINING_TYPE_T +#define U JOINING_TYPE_U +#define X JOINING_TYPE_X static const uint8_t joining_table[] = { @@ -71,7 +71,7 @@ static const uint8_t joining_table[] = /* Mandaic */ - /* 0840 */ R,D,D,D,D,D,R,R,D,R,D,D,D,D,D,D,D,D,D,D,R,D,U,U,U,X,X,X,X,X,X,X, + /* 0840 */ R,D,D,D,D,D,R,R,D,R,D,D,D,D,D,D,D,D,D,D,R,D,R,R,R,X,X,X,X,X,X,X, /* Syriac Supplement */ @@ -80,8 +80,8 @@ static const uint8_t joining_table[] = /* Arabic Extended-A */ - /* 08A0 */ D,D,D,D,D,D,D,D,D,D,R,R,R,U,R,D,D,R,R,D,D,X,D,D,D,R,D,D,D,D,X,X, - /* 08C0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 08A0 */ D,D,D,D,D,D,D,D,D,D,R,R,R,U,R,D,D,R,R,D,D,X,D,D,D,R,D,D,D,D,D,D, + /* 08C0 */ D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, /* 08E0 */ X,X,U, #define joining_offset_0x1806u 739 @@ -139,22 +139,29 @@ static const uint8_t joining_table[] = /* 10F20 */ D,D,D,R,D,D,D,D,D,D,D,D,D,D,D,D, /* 10F40 */ D,D,D,D,D,U,X,X,X,X,X,X,X,X,X,X,X,D,D,D,R, -#define joining_offset_0x110bdu 1219 +#define joining_offset_0x10fb0u 1219 + + /* Chorasmian */ + + /* 10FA0 */ D,U,D,D,R,R,R,U,D,R,R,D,D,R,D,D, + /* 10FC0 */ U,D,R,R,D,U,U,U,U,R,D,L, + +#define joining_offset_0x110bdu 1247 /* Kaithi */ /* 110A0 */ U,X,X, /* 110C0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,U, -#define joining_offset_0x1e900u 1236 +#define joining_offset_0x1e900u 1264 /* Adlam */ /* 1E900 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, /* 1E920 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, - /* 1E940 */ D,D,D,D, + /* 1E940 */ D,D,D,D,X,X,X,X,X,X,X,T, -}; /* Table items: 1304; occupancy: 56% */ +}; /* Table items: 1340; occupancy: 57% */ static unsigned int @@ -183,6 +190,7 @@ joining_type (hb_codepoint_t u) if (hb_in_range (u, 0x10B80u, 0x10BAFu)) return joining_table[u - 0x10B80u + joining_offset_0x10b80u]; if (hb_in_range (u, 0x10D00u, 0x10D23u)) return joining_table[u - 0x10D00u + joining_offset_0x10d00u]; if (hb_in_range (u, 0x10F30u, 0x10F54u)) return joining_table[u - 0x10F30u + joining_offset_0x10f30u]; + if (hb_in_range (u, 0x10FB0u, 0x10FCBu)) return joining_table[u - 0x10FB0u + joining_offset_0x10fb0u]; break; case 0x11u: @@ -190,7 +198,7 @@ joining_type (hb_codepoint_t u) break; case 0x1Eu: - if (hb_in_range (u, 0x1E900u, 0x1E943u)) return joining_table[u - 0x1E900u + joining_offset_0x1e900u]; + if (hb_in_range (u, 0x1E900u, 0x1E94Bu)) return joining_table[u - 0x1E900u + joining_offset_0x1e900u]; break; default: @@ -199,15 +207,15 @@ joining_type (hb_codepoint_t u) return X; } -#undef X -#undef R -#undef T -#undef U #undef A #undef DR -#undef L #undef C #undef D +#undef L +#undef R +#undef T +#undef U +#undef X static const uint16_t shaping_table[][4] = @@ -406,16 +414,16 @@ static const struct ligature_set_t { } ligature_table[] = { { 0xFEDFu, { - { 0xFE88u, 0xFEF9u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM */ { 0xFE82u, 0xFEF5u }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM */ - { 0xFE8Eu, 0xFEFBu }, /* ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM */ { 0xFE84u, 0xFEF7u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM */ + { 0xFE88u, 0xFEF9u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM */ + { 0xFE8Eu, 0xFEFBu }, /* ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM */ }}, { 0xFEE0u, { - { 0xFE88u, 0xFEFAu }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM */ { 0xFE82u, 0xFEF6u }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM */ - { 0xFE8Eu, 0xFEFCu }, /* ARABIC LIGATURE LAM WITH ALEF FINAL FORM */ { 0xFE84u, 0xFEF8u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM */ + { 0xFE88u, 0xFEFAu }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM */ + { 0xFE8Eu, 0xFEFCu }, /* ARABIC LIGATURE LAM WITH ALEF FINAL FORM */ }}, }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic.cc index a25ced192bd..c8ad501bca6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic.cc @@ -25,6 +25,9 @@ */ #include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex-arabic.hh" #include "hb-ot-shape.hh" @@ -225,8 +228,6 @@ collect_features_arabic (hb_ot_shape_planner_t *plan) map->enable_feature (HB_TAG('c','a','l','t'), F_MANUAL_ZWJ); map->add_gsub_pause (nullptr); - /* And undo here. */ - /* The spec includes 'cswh'. Earlier versions of Windows * used to enable this by default, but testing suggests * that Windows 8 and later do not enable it by default, @@ -289,7 +290,7 @@ arabic_joining (hb_buffer_t *buffer) { unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; - unsigned int prev = (unsigned int) -1, state = 0; + unsigned int prev = UINT_MAX, state = 0; /* Check pre-context */ for (unsigned int i = 0; i < buffer->context_len[0]; i++) @@ -315,7 +316,7 @@ arabic_joining (hb_buffer_t *buffer) const arabic_state_table_entry *entry = &arabic_state_table[state][this_type]; - if (entry->prev_action != NONE && prev != (unsigned int) -1) + if (entry->prev_action != NONE && prev != UINT_MAX) { info[prev].arabic_shaping_action() = entry->prev_action; buffer->unsafe_to_break (prev, i + 1); @@ -335,7 +336,7 @@ arabic_joining (hb_buffer_t *buffer) continue; const arabic_state_table_entry *entry = &arabic_state_table[state][this_type]; - if (entry->prev_action != NONE && prev != (unsigned int) -1) + if (entry->prev_action != NONE && prev != UINT_MAX) info[prev].arabic_shaping_action() = entry->prev_action; break; } @@ -383,6 +384,10 @@ arabic_fallback_shape (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) { +#ifdef HB_NO_OT_SHAPE_COMPLEX_ARABIC_FALLBACK + return; +#endif + const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; if (!arabic_plan->do_fallback) @@ -467,7 +472,7 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED, unsigned int j = new_len; for (unsigned int i = count; i; i--) { - if (!hb_in_range (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING)) + if (!hb_in_range (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING)) { if (step == CUT) { @@ -488,7 +493,7 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED, unsigned int end = i; while (i && - hb_in_range (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING)) + hb_in_range (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING)) { i--; hb_position_t width = font->get_glyph_h_advance (info[i].codepoint); @@ -506,7 +511,7 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED, unsigned int start = i; unsigned int context = i; while (context && - !hb_in_range (info[context - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING) && + !hb_in_range (info[context - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING) && (_hb_glyph_info_is_default_ignorable (&info[context - 1]) || HB_ARABIC_GENERAL_CATEGORY_IS_WORD (_hb_glyph_info_get_general_category (&info[context - 1])))) { @@ -597,7 +602,7 @@ postprocess_glyphs_arabic (const hb_ot_shape_plan_t *plan, HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); } -/* http://www.unicode.org/reports/tr53/ */ +/* https://www.unicode.org/reports/tr53/ */ static hb_codepoint_t modifier_combining_marks[] = @@ -706,3 +711,6 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic = HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, true, /* fallback_position */ }; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-default.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-default.cc index 97923ecf692..a755aea098b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-default.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-default.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex.hh" @@ -44,3 +48,26 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_default = HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, true, /* fallback_position */ }; + +/* Same as default but no mark advance zeroing / fallback positioning. + * Dumbest shaper ever, basically. */ +const hb_ot_complex_shaper_t _hb_ot_complex_shaper_dumber = +{ + nullptr, /* collect_features */ + nullptr, /* override_features */ + nullptr, /* data_create */ + nullptr, /* data_destroy */ + nullptr, /* preprocess_text */ + nullptr, /* postprocess_glyphs */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + nullptr, /* decompose */ + nullptr, /* compose */ + nullptr, /* setup_masks */ + HB_TAG_NONE, /* gpos_tag */ + nullptr, /* reorder_marks */ + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + false, /* fallback_position */ +}; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-hangul.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-hangul.cc index c77cac163e3..d7db43755ec 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-hangul.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-hangul.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex.hh" @@ -214,7 +218,8 @@ preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED, else { /* No valid syllable as base for tone mark; try to insert dotted circle. */ - if (font->has_glyph (0x25CCu)) + if (!(buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE) && + font->has_glyph (0x25CCu)) { hb_codepoint_t chars[2]; if (!is_zero_width_char (font, u)) { @@ -429,3 +434,6 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hangul = HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, false, /* fallback_position */ }; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-hebrew.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-hebrew.cc index 1d80a46bb23..57540805868 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-hebrew.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-hebrew.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex.hh" @@ -70,6 +74,10 @@ compose_hebrew (const hb_ot_shape_normalize_context_t *c, bool found = (bool) c->unicode->compose (a, b, ab); +#ifdef HB_NO_OT_SHAPE_COMPLEX_HEBREW_FALLBACK + return found; +#endif + if (!found && !c->plan->has_gpos_mark) { /* Special-case Hebrew presentation forms that are excluded from @@ -172,3 +180,6 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hebrew = HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, true, /* fallback_position */ }; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic-machine.hh index 9fc63157ed9..4c04e0ca6ec 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic-machine.hh @@ -34,711 +34,284 @@ #line 36 "hb-ot-shape-complex-indic-machine.hh" static const unsigned char _indic_syllable_machine_trans_keys[] = { - 8u, 8u, 4u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, - 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 4u, 8u, 6u, 6u, 16u, 16u, - 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, - 16u, 16u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, - 4u, 8u, 4u, 13u, 8u, 8u, 4u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, - 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 4u, 8u, - 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, - 4u, 8u, 6u, 6u, 16u, 16u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, - 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 8u, 8u, 4u, 8u, 5u, 7u, 7u, 7u, - 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, - 7u, 7u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, - 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 13u, 4u, 8u, 4u, 13u, - 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 8u, 8u, 4u, 8u, 5u, 7u, - 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, - 5u, 7u, 7u, 7u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, - 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 13u, 4u, 8u, - 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 13u, - 5u, 8u, 8u, 8u, 1u, 19u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, - 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, - 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 5u, 10u, 5u, 10u, - 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 3u, 10u, - 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, - 3u, 10u, 4u, 10u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, - 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, - 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, - 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, - 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, - 3u, 13u, 3u, 10u, 4u, 10u, 5u, 10u, 5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, - 10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, - 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 3u, 10u, - 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, - 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, - 1u, 16u, 1u, 16u, 1u, 16u, 4u, 8u, 3u, 10u, 3u, 10u, 4u, 10u, 1u, 16u, - 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, - 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 5u, 10u, - 5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, - 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, - 5u, 10u, 3u, 10u, 4u, 10u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, - 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, - 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 4u, 13u, - 3u, 10u, 4u, 8u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, - 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, - 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 5u, 10u, 5u, 10u, 5u, 10u, - 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, - 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, - 4u, 10u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, - 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, - 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 1u, 16u, 3u, 13u, - 1u, 16u, 4u, 13u, 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, - 3u, 10u, 5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, - 0 + 8u, 8u, 4u, 8u, 5u, 7u, 5u, 8u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, + 4u, 13u, 4u, 8u, 8u, 8u, 5u, 7u, 5u, 8u, 4u, 8u, 6u, 6u, 16u, 16u, + 4u, 8u, 4u, 13u, 4u, 13u, 4u, 13u, 8u, 8u, 5u, 7u, 5u, 8u, 4u, 8u, + 6u, 6u, 16u, 16u, 4u, 8u, 4u, 8u, 4u, 13u, 8u, 8u, 5u, 7u, 5u, 8u, + 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 4u, 8u, 5u, 8u, 8u, 8u, 1u, 19u, + 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 5u, 10u, 5u, 10u, 10u, 10u, 5u, 10u, + 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 4u, 10u, 5u, 10u, 4u, 10u, 5u, 10u, + 3u, 10u, 5u, 10u, 3u, 17u, 3u, 17u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, + 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, + 1u, 16u, 3u, 10u, 4u, 10u, 5u, 10u, 4u, 10u, 5u, 10u, 5u, 10u, 3u, 10u, + 5u, 10u, 3u, 17u, 3u, 17u, 4u, 8u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, + 3u, 17u, 1u, 16u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 1u, 16u, 3u, 10u, + 4u, 10u, 5u, 10u, 3u, 17u, 4u, 10u, 5u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, + 3u, 17u, 4u, 13u, 4u, 8u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, + 1u, 16u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 1u, 16u, 3u, 10u, 4u, 10u, + 5u, 10u, 3u, 17u, 4u, 10u, 5u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 1u, 17u, + 3u, 17u, 1u, 17u, 4u, 13u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 3u, 10u, + 5u, 10u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 0 }; static const char _indic_syllable_machine_key_spans[] = { - 1, 5, 3, 1, 4, 3, 1, 4, - 3, 1, 4, 3, 1, 5, 1, 1, - 5, 1, 1, 5, 1, 1, 5, 1, - 1, 10, 5, 10, 5, 10, 5, 10, - 5, 10, 1, 5, 3, 1, 4, 3, - 1, 4, 3, 1, 4, 3, 1, 5, - 1, 1, 5, 1, 1, 5, 1, 1, - 5, 1, 1, 10, 5, 10, 5, 10, - 5, 10, 5, 10, 1, 5, 3, 1, - 4, 3, 1, 4, 3, 1, 4, 3, - 1, 5, 1, 1, 5, 1, 1, 5, - 1, 1, 5, 1, 1, 10, 5, 10, - 5, 10, 5, 10, 5, 1, 5, 3, - 1, 4, 3, 1, 4, 3, 1, 4, - 3, 1, 5, 1, 1, 5, 1, 1, - 5, 1, 1, 5, 1, 1, 10, 5, - 10, 5, 10, 5, 10, 5, 10, 10, - 4, 1, 19, 11, 8, 7, 16, 11, - 8, 7, 16, 11, 8, 7, 16, 11, - 8, 7, 16, 11, 8, 7, 6, 6, - 6, 1, 1, 1, 6, 8, 6, 8, - 7, 6, 8, 7, 6, 8, 7, 6, - 8, 7, 8, 11, 16, 16, 16, 8, - 11, 16, 16, 16, 8, 11, 16, 16, - 16, 8, 11, 16, 16, 16, 8, 11, - 11, 8, 7, 16, 11, 8, 7, 16, - 11, 8, 7, 16, 11, 8, 7, 16, - 11, 8, 7, 6, 6, 6, 1, 1, - 1, 6, 8, 6, 8, 7, 6, 8, - 7, 6, 8, 7, 6, 8, 7, 8, - 11, 16, 16, 16, 8, 11, 16, 16, - 16, 8, 11, 16, 16, 16, 8, 11, - 16, 16, 16, 5, 8, 8, 7, 16, - 11, 8, 7, 16, 11, 8, 7, 16, - 11, 8, 7, 16, 11, 8, 7, 6, - 6, 6, 1, 1, 1, 6, 8, 6, - 8, 7, 6, 8, 7, 6, 8, 7, - 6, 8, 7, 8, 11, 16, 16, 16, - 8, 11, 16, 16, 16, 8, 11, 16, - 16, 16, 8, 11, 16, 16, 16, 10, - 8, 5, 11, 8, 7, 16, 11, 8, - 7, 16, 11, 8, 7, 16, 11, 8, - 7, 16, 11, 8, 7, 6, 6, 6, - 1, 1, 1, 6, 8, 6, 8, 7, - 6, 8, 7, 6, 8, 7, 6, 8, - 7, 8, 11, 16, 16, 16, 8, 11, - 16, 16, 16, 8, 11, 16, 16, 16, - 8, 11, 16, 16, 16, 8, 16, 11, - 16, 10, 6, 1, 1, 1, 6, 16, - 8, 6, 6, 1, 1, 1, 6, 16 + 1, 5, 3, 4, 5, 1, 1, 5, + 10, 5, 1, 3, 4, 5, 1, 1, + 5, 10, 10, 10, 1, 3, 4, 5, + 1, 1, 5, 5, 10, 1, 3, 4, + 5, 1, 1, 5, 5, 4, 1, 19, + 15, 15, 14, 16, 6, 6, 1, 6, + 16, 16, 16, 8, 7, 6, 7, 6, + 8, 6, 15, 15, 15, 15, 14, 16, + 15, 15, 14, 16, 6, 1, 6, 16, + 16, 8, 7, 6, 7, 6, 6, 8, + 6, 15, 15, 5, 15, 15, 14, 16, + 15, 16, 6, 1, 6, 16, 16, 8, + 7, 6, 15, 7, 6, 6, 8, 6, + 15, 10, 5, 15, 15, 14, 16, 15, + 16, 6, 1, 6, 16, 16, 8, 7, + 6, 15, 7, 6, 6, 8, 6, 17, + 15, 17, 10, 6, 1, 6, 16, 8, + 6, 6, 1, 6, 16 }; static const short _indic_syllable_machine_index_offsets[] = { - 0, 2, 8, 12, 14, 19, 23, 25, - 30, 34, 36, 41, 45, 47, 53, 55, - 57, 63, 65, 67, 73, 75, 77, 83, - 85, 87, 98, 104, 115, 121, 132, 138, - 149, 155, 166, 168, 174, 178, 180, 185, - 189, 191, 196, 200, 202, 207, 211, 213, - 219, 221, 223, 229, 231, 233, 239, 241, - 243, 249, 251, 253, 264, 270, 281, 287, - 298, 304, 315, 321, 332, 334, 340, 344, - 346, 351, 355, 357, 362, 366, 368, 373, - 377, 379, 385, 387, 389, 395, 397, 399, - 405, 407, 409, 415, 417, 419, 430, 436, - 447, 453, 464, 470, 481, 487, 489, 495, - 499, 501, 506, 510, 512, 517, 521, 523, - 528, 532, 534, 540, 542, 544, 550, 552, - 554, 560, 562, 564, 570, 572, 574, 585, - 591, 602, 608, 619, 625, 636, 642, 653, - 664, 669, 671, 691, 703, 712, 720, 737, - 749, 758, 766, 783, 795, 804, 812, 829, - 841, 850, 858, 875, 887, 896, 904, 911, - 918, 925, 927, 929, 931, 938, 947, 954, - 963, 971, 978, 987, 995, 1002, 1011, 1019, - 1026, 1035, 1043, 1052, 1064, 1081, 1098, 1115, - 1124, 1136, 1153, 1170, 1187, 1196, 1208, 1225, - 1242, 1259, 1268, 1280, 1297, 1314, 1331, 1340, - 1352, 1364, 1373, 1381, 1398, 1410, 1419, 1427, - 1444, 1456, 1465, 1473, 1490, 1502, 1511, 1519, - 1536, 1548, 1557, 1565, 1572, 1579, 1586, 1588, - 1590, 1592, 1599, 1608, 1615, 1624, 1632, 1639, - 1648, 1656, 1663, 1672, 1680, 1687, 1696, 1704, - 1713, 1725, 1742, 1759, 1776, 1785, 1797, 1814, - 1831, 1848, 1857, 1869, 1886, 1903, 1920, 1929, - 1941, 1958, 1975, 1992, 1998, 2007, 2016, 2024, - 2041, 2053, 2062, 2070, 2087, 2099, 2108, 2116, - 2133, 2145, 2154, 2162, 2179, 2191, 2200, 2208, - 2215, 2222, 2229, 2231, 2233, 2235, 2242, 2251, - 2258, 2267, 2275, 2282, 2291, 2299, 2306, 2315, - 2323, 2330, 2339, 2347, 2356, 2368, 2385, 2402, - 2419, 2428, 2440, 2457, 2474, 2491, 2500, 2512, - 2529, 2546, 2563, 2572, 2584, 2601, 2618, 2635, - 2646, 2655, 2661, 2673, 2682, 2690, 2707, 2719, - 2728, 2736, 2753, 2765, 2774, 2782, 2799, 2811, - 2820, 2828, 2845, 2857, 2866, 2874, 2881, 2888, - 2895, 2897, 2899, 2901, 2908, 2917, 2924, 2933, - 2941, 2948, 2957, 2965, 2972, 2981, 2989, 2996, - 3005, 3013, 3022, 3034, 3051, 3068, 3085, 3094, - 3106, 3123, 3140, 3157, 3166, 3178, 3195, 3212, - 3229, 3238, 3250, 3267, 3284, 3301, 3310, 3327, - 3339, 3356, 3367, 3374, 3376, 3378, 3380, 3387, - 3404, 3413, 3420, 3427, 3429, 3431, 3433, 3440 + 0, 2, 8, 12, 17, 23, 25, 27, + 33, 44, 50, 52, 56, 61, 67, 69, + 71, 77, 88, 99, 110, 112, 116, 121, + 127, 129, 131, 137, 143, 154, 156, 160, + 165, 171, 173, 175, 181, 187, 192, 194, + 214, 230, 246, 261, 278, 285, 292, 294, + 301, 318, 335, 352, 361, 369, 376, 384, + 391, 400, 407, 423, 439, 455, 471, 486, + 503, 519, 535, 550, 567, 574, 576, 583, + 600, 617, 626, 634, 641, 649, 656, 663, + 672, 679, 695, 711, 717, 733, 749, 764, + 781, 797, 814, 821, 823, 830, 847, 864, + 873, 881, 888, 904, 912, 919, 926, 935, + 942, 958, 969, 975, 991, 1007, 1022, 1039, + 1055, 1072, 1079, 1081, 1088, 1105, 1122, 1131, + 1139, 1146, 1162, 1170, 1177, 1184, 1193, 1200, + 1218, 1234, 1252, 1263, 1270, 1272, 1279, 1296, + 1305, 1312, 1319, 1321, 1328 }; -static const short _indic_syllable_machine_indicies[] = { +static const unsigned char _indic_syllable_machine_indicies[] = { 1, 0, 2, 3, 3, 4, 1, 0, - 5, 5, 4, 0, 4, 0, 6, 6, - 7, 1, 0, 8, 8, 7, 0, 7, - 0, 9, 9, 10, 1, 0, 11, 11, - 10, 0, 10, 0, 12, 12, 13, 1, - 0, 14, 14, 13, 0, 13, 0, 15, - 0, 0, 0, 1, 0, 16, 0, 17, - 0, 18, 12, 12, 13, 1, 0, 19, - 0, 20, 0, 21, 9, 9, 10, 1, - 0, 22, 0, 23, 0, 24, 6, 6, - 7, 1, 0, 25, 0, 26, 0, 2, - 3, 3, 4, 1, 0, 0, 0, 0, - 27, 0, 28, 3, 3, 4, 1, 0, - 28, 3, 3, 4, 1, 0, 0, 0, - 0, 29, 0, 30, 3, 3, 4, 1, - 0, 30, 3, 3, 4, 1, 0, 0, - 0, 0, 31, 0, 32, 3, 3, 4, - 1, 0, 32, 3, 3, 4, 1, 0, - 0, 0, 0, 33, 0, 34, 3, 3, - 4, 1, 0, 34, 3, 3, 4, 1, - 0, 0, 0, 0, 35, 0, 37, 36, - 38, 39, 39, 40, 37, 36, 41, 41, - 40, 36, 40, 36, 42, 42, 43, 37, - 36, 44, 44, 43, 36, 43, 36, 45, - 45, 46, 37, 36, 47, 47, 46, 36, - 46, 36, 48, 48, 49, 37, 36, 50, - 50, 49, 36, 49, 36, 51, 36, 36, - 36, 37, 36, 52, 36, 53, 36, 54, - 48, 48, 49, 37, 36, 55, 36, 56, - 36, 57, 45, 45, 46, 37, 36, 58, - 36, 59, 36, 60, 42, 42, 43, 37, - 36, 61, 36, 62, 36, 38, 39, 39, - 40, 37, 36, 36, 36, 36, 63, 36, - 64, 39, 39, 40, 37, 36, 64, 39, - 39, 40, 37, 36, 36, 36, 36, 65, - 36, 66, 39, 39, 40, 37, 36, 66, - 39, 39, 40, 37, 36, 36, 36, 36, - 67, 36, 68, 39, 39, 40, 37, 36, - 68, 39, 39, 40, 37, 36, 36, 36, - 36, 69, 36, 70, 39, 39, 40, 37, - 36, 70, 39, 39, 40, 37, 36, 36, - 36, 36, 71, 36, 73, 72, 74, 75, - 75, 76, 73, 72, 78, 78, 76, 77, - 76, 77, 79, 79, 80, 73, 72, 81, - 81, 80, 72, 80, 72, 82, 82, 83, - 73, 72, 84, 84, 83, 72, 83, 72, - 85, 85, 86, 73, 72, 87, 87, 86, - 72, 86, 72, 88, 72, 72, 72, 73, - 72, 89, 72, 90, 72, 91, 85, 85, - 86, 73, 72, 92, 72, 93, 72, 94, - 82, 82, 83, 73, 72, 95, 72, 96, - 72, 97, 79, 79, 80, 73, 72, 98, - 72, 99, 72, 74, 75, 75, 76, 73, - 72, 72, 72, 72, 100, 72, 101, 75, - 75, 76, 73, 72, 101, 75, 75, 76, - 73, 72, 72, 72, 72, 102, 72, 103, - 75, 75, 76, 73, 72, 103, 75, 75, - 76, 73, 72, 72, 72, 72, 104, 72, - 105, 75, 75, 76, 73, 72, 105, 75, - 75, 76, 73, 72, 72, 72, 72, 106, - 72, 107, 75, 75, 76, 73, 72, 109, - 108, 110, 111, 111, 112, 109, 108, 113, - 113, 112, 108, 112, 108, 114, 114, 115, - 109, 108, 116, 116, 115, 108, 115, 108, - 117, 117, 118, 109, 108, 119, 119, 118, - 108, 118, 108, 120, 120, 121, 109, 108, - 122, 122, 121, 108, 121, 108, 123, 108, - 108, 108, 109, 108, 124, 108, 125, 108, - 126, 120, 120, 121, 109, 108, 127, 108, - 128, 108, 129, 117, 117, 118, 109, 108, - 130, 108, 131, 108, 132, 114, 114, 115, - 109, 108, 133, 108, 134, 108, 110, 111, - 111, 112, 109, 108, 108, 108, 108, 135, - 108, 136, 111, 111, 112, 109, 108, 136, - 111, 111, 112, 109, 108, 108, 108, 108, - 137, 108, 138, 111, 111, 112, 109, 108, - 138, 111, 111, 112, 109, 108, 108, 108, - 108, 139, 108, 140, 111, 111, 112, 109, - 108, 140, 111, 111, 112, 109, 108, 108, - 108, 108, 141, 108, 142, 111, 111, 112, - 109, 108, 142, 111, 111, 112, 109, 108, - 108, 108, 108, 143, 108, 107, 75, 75, - 76, 73, 72, 72, 72, 72, 144, 72, - 78, 78, 76, 1, 0, 146, 145, 148, - 149, 150, 151, 152, 153, 76, 73, 147, - 154, 155, 155, 144, 147, 156, 157, 147, - 158, 159, 147, 161, 162, 163, 164, 4, - 1, 160, 165, 160, 160, 35, 160, 166, - 162, 167, 167, 4, 1, 160, 165, 160, - 162, 167, 167, 4, 1, 160, 165, 160, - 168, 160, 160, 160, 17, 169, 160, 1, - 160, 165, 160, 160, 160, 160, 160, 168, - 160, 170, 171, 172, 173, 4, 1, 160, - 165, 160, 160, 33, 160, 174, 171, 175, - 175, 4, 1, 160, 165, 160, 171, 175, - 175, 4, 1, 160, 165, 160, 176, 160, - 160, 160, 17, 177, 160, 1, 160, 165, - 160, 160, 160, 160, 160, 176, 160, 178, - 179, 180, 181, 4, 1, 160, 165, 160, - 160, 31, 160, 182, 179, 183, 183, 4, - 1, 160, 165, 160, 179, 183, 183, 4, - 1, 160, 165, 160, 184, 160, 160, 160, - 17, 185, 160, 1, 160, 165, 160, 160, - 160, 160, 160, 184, 160, 186, 187, 188, - 189, 4, 1, 160, 165, 160, 160, 29, - 160, 190, 187, 191, 191, 4, 1, 160, - 165, 160, 187, 191, 191, 4, 1, 160, - 165, 160, 192, 160, 160, 160, 17, 193, - 160, 1, 160, 165, 160, 160, 160, 160, - 160, 192, 160, 194, 195, 196, 197, 4, - 1, 160, 165, 160, 160, 27, 160, 198, - 195, 199, 199, 4, 1, 160, 165, 160, - 195, 199, 199, 4, 1, 160, 165, 160, - 17, 200, 160, 1, 160, 165, 160, 201, - 201, 160, 1, 160, 165, 160, 202, 160, - 160, 203, 160, 165, 160, 165, 160, 204, - 160, 205, 160, 202, 160, 160, 160, 160, - 165, 160, 17, 160, 201, 201, 160, 1, - 160, 165, 160, 201, 200, 160, 1, 160, - 165, 160, 206, 26, 207, 208, 7, 1, - 160, 165, 160, 26, 207, 208, 7, 1, - 160, 165, 160, 207, 207, 7, 1, 160, - 165, 160, 209, 23, 210, 211, 10, 1, - 160, 165, 160, 23, 210, 211, 10, 1, - 160, 165, 160, 210, 210, 10, 1, 160, - 165, 160, 212, 20, 213, 214, 13, 1, - 160, 165, 160, 20, 213, 214, 13, 1, - 160, 165, 160, 213, 213, 13, 1, 160, - 165, 160, 215, 17, 201, 216, 160, 1, - 160, 165, 160, 17, 201, 216, 160, 1, - 160, 165, 160, 194, 195, 199, 199, 4, - 1, 160, 165, 160, 194, 195, 196, 199, - 4, 1, 160, 165, 160, 160, 27, 160, - 192, 160, 217, 160, 201, 201, 160, 1, - 160, 165, 160, 160, 160, 160, 160, 192, - 160, 192, 160, 160, 160, 201, 201, 160, - 1, 160, 165, 160, 160, 160, 160, 160, - 192, 160, 192, 160, 160, 160, 201, 193, - 160, 1, 160, 165, 160, 160, 160, 160, - 160, 192, 160, 186, 187, 191, 191, 4, - 1, 160, 165, 160, 186, 187, 188, 191, - 4, 1, 160, 165, 160, 160, 29, 160, - 184, 160, 218, 160, 201, 201, 160, 1, - 160, 165, 160, 160, 160, 160, 160, 184, - 160, 184, 160, 160, 160, 201, 201, 160, - 1, 160, 165, 160, 160, 160, 160, 160, - 184, 160, 184, 160, 160, 160, 201, 185, - 160, 1, 160, 165, 160, 160, 160, 160, - 160, 184, 160, 178, 179, 183, 183, 4, - 1, 160, 165, 160, 178, 179, 180, 183, - 4, 1, 160, 165, 160, 160, 31, 160, - 176, 160, 219, 160, 201, 201, 160, 1, - 160, 165, 160, 160, 160, 160, 160, 176, - 160, 176, 160, 160, 160, 201, 201, 160, - 1, 160, 165, 160, 160, 160, 160, 160, - 176, 160, 176, 160, 160, 160, 201, 177, - 160, 1, 160, 165, 160, 160, 160, 160, - 160, 176, 160, 170, 171, 175, 175, 4, - 1, 160, 165, 160, 170, 171, 172, 175, - 4, 1, 160, 165, 160, 160, 33, 160, - 168, 160, 220, 160, 201, 201, 160, 1, - 160, 165, 160, 160, 160, 160, 160, 168, - 160, 168, 160, 160, 160, 201, 201, 160, - 1, 160, 165, 160, 160, 160, 160, 160, - 168, 160, 168, 160, 160, 160, 201, 169, - 160, 1, 160, 165, 160, 160, 160, 160, - 160, 168, 160, 161, 162, 167, 167, 4, - 1, 160, 165, 160, 161, 162, 163, 167, - 4, 1, 160, 165, 160, 160, 35, 160, - 222, 223, 224, 225, 40, 37, 221, 226, - 221, 221, 71, 221, 227, 223, 228, 225, - 40, 37, 221, 226, 221, 223, 228, 225, - 40, 37, 221, 226, 221, 229, 221, 221, - 221, 53, 230, 221, 37, 221, 226, 221, - 221, 221, 221, 221, 229, 221, 231, 232, - 233, 234, 40, 37, 221, 226, 221, 221, - 69, 221, 235, 232, 236, 236, 40, 37, - 221, 226, 221, 232, 236, 236, 40, 37, - 221, 226, 221, 237, 221, 221, 221, 53, - 238, 221, 37, 221, 226, 221, 221, 221, - 221, 221, 237, 221, 239, 240, 241, 242, - 40, 37, 221, 226, 221, 221, 67, 221, - 243, 240, 244, 244, 40, 37, 221, 226, - 221, 240, 244, 244, 40, 37, 221, 226, - 221, 245, 221, 221, 221, 53, 246, 221, - 37, 221, 226, 221, 221, 221, 221, 221, - 245, 221, 247, 248, 249, 250, 40, 37, - 221, 226, 221, 221, 65, 221, 251, 248, - 252, 252, 40, 37, 221, 226, 221, 248, - 252, 252, 40, 37, 221, 226, 221, 253, - 221, 221, 221, 53, 254, 221, 37, 221, - 226, 221, 221, 221, 221, 221, 253, 221, - 255, 256, 257, 258, 40, 37, 221, 226, - 221, 221, 63, 221, 259, 256, 260, 260, - 40, 37, 221, 226, 221, 256, 260, 260, - 40, 37, 221, 226, 221, 53, 261, 221, - 37, 221, 226, 221, 262, 262, 221, 37, - 221, 226, 221, 263, 221, 221, 264, 221, - 226, 221, 226, 221, 265, 221, 266, 221, - 263, 221, 221, 221, 221, 226, 221, 53, - 221, 262, 262, 221, 37, 221, 226, 221, - 262, 261, 221, 37, 221, 226, 221, 267, - 62, 268, 269, 43, 37, 221, 226, 221, - 62, 268, 269, 43, 37, 221, 226, 221, - 268, 268, 43, 37, 221, 226, 221, 270, - 59, 271, 272, 46, 37, 221, 226, 221, - 59, 271, 272, 46, 37, 221, 226, 221, - 271, 271, 46, 37, 221, 226, 221, 273, - 56, 274, 275, 49, 37, 221, 226, 221, - 56, 274, 275, 49, 37, 221, 226, 221, - 274, 274, 49, 37, 221, 226, 221, 276, - 53, 262, 277, 221, 37, 221, 226, 221, - 53, 262, 277, 221, 37, 221, 226, 221, - 255, 256, 260, 260, 40, 37, 221, 226, - 221, 255, 256, 257, 260, 40, 37, 221, - 226, 221, 221, 63, 221, 253, 221, 278, - 221, 262, 262, 221, 37, 221, 226, 221, - 221, 221, 221, 221, 253, 221, 253, 221, - 221, 221, 262, 262, 221, 37, 221, 226, - 221, 221, 221, 221, 221, 253, 221, 253, - 221, 221, 221, 262, 254, 221, 37, 221, - 226, 221, 221, 221, 221, 221, 253, 221, - 247, 248, 252, 252, 40, 37, 221, 226, - 221, 247, 248, 249, 252, 40, 37, 221, - 226, 221, 221, 65, 221, 245, 221, 279, - 221, 262, 262, 221, 37, 221, 226, 221, - 221, 221, 221, 221, 245, 221, 245, 221, - 221, 221, 262, 262, 221, 37, 221, 226, - 221, 221, 221, 221, 221, 245, 221, 245, - 221, 221, 221, 262, 246, 221, 37, 221, - 226, 221, 221, 221, 221, 221, 245, 221, - 239, 240, 244, 244, 40, 37, 221, 226, - 221, 239, 240, 241, 244, 40, 37, 221, - 226, 221, 221, 67, 221, 237, 221, 280, - 221, 262, 262, 221, 37, 221, 226, 221, - 221, 221, 221, 221, 237, 221, 237, 221, - 221, 221, 262, 262, 221, 37, 221, 226, - 221, 221, 221, 221, 221, 237, 221, 237, - 221, 221, 221, 262, 238, 221, 37, 221, - 226, 221, 221, 221, 221, 221, 237, 221, - 231, 232, 236, 236, 40, 37, 221, 226, - 221, 231, 232, 233, 236, 40, 37, 221, - 226, 221, 221, 69, 221, 229, 221, 281, - 221, 262, 262, 221, 37, 221, 226, 221, - 221, 221, 221, 221, 229, 221, 229, 221, - 221, 221, 262, 262, 221, 37, 221, 226, - 221, 221, 221, 221, 221, 229, 221, 229, - 221, 221, 221, 262, 230, 221, 37, 221, - 226, 221, 221, 221, 221, 221, 229, 221, - 70, 39, 39, 40, 37, 221, 222, 223, - 228, 225, 40, 37, 221, 226, 221, 283, - 151, 284, 284, 76, 73, 282, 154, 282, - 151, 284, 284, 76, 73, 282, 154, 282, - 285, 282, 282, 282, 90, 286, 282, 73, - 282, 154, 282, 282, 282, 282, 282, 285, - 282, 287, 288, 289, 290, 76, 73, 282, - 154, 282, 282, 106, 282, 291, 288, 292, - 292, 76, 73, 282, 154, 282, 288, 292, - 292, 76, 73, 282, 154, 282, 293, 282, - 282, 282, 90, 294, 282, 73, 282, 154, - 282, 282, 282, 282, 282, 293, 282, 295, - 296, 297, 298, 76, 73, 282, 154, 282, - 282, 104, 282, 299, 296, 300, 300, 76, - 73, 282, 154, 282, 296, 300, 300, 76, - 73, 282, 154, 282, 301, 282, 282, 282, - 90, 302, 282, 73, 282, 154, 282, 282, - 282, 282, 282, 301, 282, 303, 304, 305, - 306, 76, 73, 282, 154, 282, 282, 102, - 282, 307, 304, 308, 308, 76, 73, 282, - 154, 282, 304, 308, 308, 76, 73, 282, - 154, 282, 309, 282, 282, 282, 90, 310, - 282, 73, 282, 154, 282, 282, 282, 282, - 282, 309, 282, 311, 312, 313, 314, 76, - 73, 282, 154, 282, 282, 100, 282, 315, - 312, 316, 316, 76, 73, 282, 154, 282, - 312, 316, 316, 76, 73, 282, 154, 282, - 90, 317, 282, 73, 282, 154, 282, 318, - 318, 282, 73, 282, 154, 282, 319, 282, - 282, 320, 282, 154, 282, 154, 282, 321, - 282, 322, 282, 319, 282, 282, 282, 282, - 154, 282, 90, 282, 318, 318, 282, 73, - 282, 154, 282, 318, 317, 282, 73, 282, - 154, 282, 323, 99, 324, 325, 80, 73, - 282, 154, 282, 99, 324, 325, 80, 73, - 282, 154, 282, 324, 324, 80, 73, 282, - 154, 282, 326, 96, 327, 328, 83, 73, - 282, 154, 282, 96, 327, 328, 83, 73, - 282, 154, 282, 327, 327, 83, 73, 282, - 154, 282, 329, 93, 330, 331, 86, 73, - 282, 154, 282, 93, 330, 331, 86, 73, - 282, 154, 282, 330, 330, 86, 73, 282, - 154, 282, 332, 90, 318, 333, 282, 73, - 282, 154, 282, 90, 318, 333, 282, 73, - 282, 154, 282, 311, 312, 316, 316, 76, - 73, 282, 154, 282, 311, 312, 313, 316, - 76, 73, 282, 154, 282, 282, 100, 282, - 309, 282, 334, 282, 318, 318, 282, 73, - 282, 154, 282, 282, 282, 282, 282, 309, - 282, 309, 282, 282, 282, 318, 318, 282, - 73, 282, 154, 282, 282, 282, 282, 282, - 309, 282, 309, 282, 282, 282, 318, 310, - 282, 73, 282, 154, 282, 282, 282, 282, - 282, 309, 282, 303, 304, 308, 308, 76, - 73, 282, 154, 282, 303, 304, 305, 308, - 76, 73, 282, 154, 282, 282, 102, 282, - 301, 282, 335, 282, 318, 318, 282, 73, - 282, 154, 282, 282, 282, 282, 282, 301, - 282, 301, 282, 282, 282, 318, 318, 282, - 73, 282, 154, 282, 282, 282, 282, 282, - 301, 282, 301, 282, 282, 282, 318, 302, - 282, 73, 282, 154, 282, 282, 282, 282, - 282, 301, 282, 295, 296, 300, 300, 76, - 73, 282, 154, 282, 295, 296, 297, 300, - 76, 73, 282, 154, 282, 282, 104, 282, - 293, 282, 336, 282, 318, 318, 282, 73, - 282, 154, 282, 282, 282, 282, 282, 293, - 282, 293, 282, 282, 282, 318, 318, 282, - 73, 282, 154, 282, 282, 282, 282, 282, - 293, 282, 293, 282, 282, 282, 318, 294, - 282, 73, 282, 154, 282, 282, 282, 282, - 282, 293, 282, 287, 288, 292, 292, 76, - 73, 282, 154, 282, 287, 288, 289, 292, - 76, 73, 282, 154, 282, 282, 106, 282, - 285, 282, 337, 282, 318, 318, 282, 73, - 282, 154, 282, 282, 282, 282, 282, 285, - 282, 285, 282, 282, 282, 318, 318, 282, - 73, 282, 154, 282, 282, 282, 282, 282, - 285, 282, 285, 282, 282, 282, 318, 286, - 282, 73, 282, 154, 282, 282, 282, 282, - 282, 285, 282, 107, 75, 75, 76, 73, - 338, 338, 338, 338, 144, 338, 150, 151, - 284, 284, 76, 73, 282, 154, 282, 107, - 75, 75, 76, 73, 338, 340, 341, 342, - 343, 112, 109, 339, 344, 339, 339, 143, - 339, 345, 341, 343, 343, 112, 109, 339, - 344, 339, 341, 343, 343, 112, 109, 339, - 344, 339, 346, 339, 339, 339, 125, 347, - 339, 109, 339, 344, 339, 339, 339, 339, - 339, 346, 339, 348, 349, 350, 351, 112, - 109, 339, 344, 339, 339, 141, 339, 352, - 349, 353, 353, 112, 109, 339, 344, 339, - 349, 353, 353, 112, 109, 339, 344, 339, - 354, 339, 339, 339, 125, 355, 339, 109, - 339, 344, 339, 339, 339, 339, 339, 354, - 339, 356, 357, 358, 359, 112, 109, 339, - 344, 339, 339, 139, 339, 360, 357, 361, - 361, 112, 109, 339, 344, 339, 357, 361, - 361, 112, 109, 339, 344, 339, 362, 339, - 339, 339, 125, 363, 339, 109, 339, 344, - 339, 339, 339, 339, 339, 362, 339, 364, - 365, 366, 367, 112, 109, 339, 344, 339, - 339, 137, 339, 368, 365, 369, 369, 112, - 109, 339, 344, 339, 365, 369, 369, 112, - 109, 339, 344, 339, 370, 339, 339, 339, - 125, 371, 339, 109, 339, 344, 339, 339, - 339, 339, 339, 370, 339, 372, 373, 374, - 375, 112, 109, 339, 344, 339, 339, 135, - 339, 376, 373, 377, 377, 112, 109, 339, - 344, 339, 373, 377, 377, 112, 109, 339, - 344, 339, 125, 378, 339, 109, 339, 344, - 339, 379, 379, 339, 109, 339, 344, 339, - 380, 339, 339, 381, 339, 344, 339, 344, - 339, 382, 339, 383, 339, 380, 339, 339, - 339, 339, 344, 339, 125, 339, 379, 379, - 339, 109, 339, 344, 339, 379, 378, 339, - 109, 339, 344, 339, 384, 134, 385, 386, - 115, 109, 339, 344, 339, 134, 385, 386, - 115, 109, 339, 344, 339, 385, 385, 115, - 109, 339, 344, 339, 387, 131, 388, 389, - 118, 109, 339, 344, 339, 131, 388, 389, - 118, 109, 339, 344, 339, 388, 388, 118, - 109, 339, 344, 339, 390, 128, 391, 392, - 121, 109, 339, 344, 339, 128, 391, 392, - 121, 109, 339, 344, 339, 391, 391, 121, - 109, 339, 344, 339, 393, 125, 379, 394, - 339, 109, 339, 344, 339, 125, 379, 394, - 339, 109, 339, 344, 339, 372, 373, 377, - 377, 112, 109, 339, 344, 339, 372, 373, - 374, 377, 112, 109, 339, 344, 339, 339, - 135, 339, 370, 339, 395, 339, 379, 379, - 339, 109, 339, 344, 339, 339, 339, 339, - 339, 370, 339, 370, 339, 339, 339, 379, - 379, 339, 109, 339, 344, 339, 339, 339, - 339, 339, 370, 339, 370, 339, 339, 339, - 379, 371, 339, 109, 339, 344, 339, 339, - 339, 339, 339, 370, 339, 364, 365, 369, - 369, 112, 109, 339, 344, 339, 364, 365, - 366, 369, 112, 109, 339, 344, 339, 339, - 137, 339, 362, 339, 396, 339, 379, 379, - 339, 109, 339, 344, 339, 339, 339, 339, - 339, 362, 339, 362, 339, 339, 339, 379, - 379, 339, 109, 339, 344, 339, 339, 339, - 339, 339, 362, 339, 362, 339, 339, 339, - 379, 363, 339, 109, 339, 344, 339, 339, - 339, 339, 339, 362, 339, 356, 357, 361, - 361, 112, 109, 339, 344, 339, 356, 357, - 358, 361, 112, 109, 339, 344, 339, 339, - 139, 339, 354, 339, 397, 339, 379, 379, - 339, 109, 339, 344, 339, 339, 339, 339, - 339, 354, 339, 354, 339, 339, 339, 379, - 379, 339, 109, 339, 344, 339, 339, 339, - 339, 339, 354, 339, 354, 339, 339, 339, - 379, 355, 339, 109, 339, 344, 339, 339, - 339, 339, 339, 354, 339, 348, 349, 353, - 353, 112, 109, 339, 344, 339, 348, 349, - 350, 353, 112, 109, 339, 344, 339, 339, - 141, 339, 346, 339, 398, 339, 379, 379, - 339, 109, 339, 344, 339, 339, 339, 339, - 339, 346, 339, 346, 339, 339, 339, 379, - 379, 339, 109, 339, 344, 339, 339, 339, - 339, 339, 346, 339, 346, 339, 339, 339, - 379, 347, 339, 109, 339, 344, 339, 339, - 339, 339, 339, 346, 339, 340, 341, 343, - 343, 112, 109, 339, 344, 339, 148, 149, - 150, 151, 399, 284, 76, 73, 282, 154, - 155, 155, 144, 282, 282, 148, 282, 161, - 400, 163, 164, 4, 1, 160, 165, 160, - 160, 35, 160, 168, 149, 150, 151, 401, - 402, 76, 403, 160, 404, 160, 155, 144, - 160, 160, 168, 160, 107, 405, 405, 76, - 403, 160, 165, 160, 160, 144, 160, 406, - 160, 160, 407, 160, 404, 160, 404, 160, - 408, 160, 205, 160, 406, 160, 160, 160, - 160, 404, 160, 168, 160, 220, 107, 405, - 405, 76, 403, 160, 165, 160, 160, 160, - 160, 160, 168, 160, 410, 409, 411, 411, - 409, 146, 409, 412, 409, 411, 411, 409, - 146, 409, 412, 409, 413, 409, 409, 414, - 409, 412, 409, 412, 409, 415, 409, 416, - 409, 413, 409, 409, 409, 409, 412, 409, - 148, 338, 338, 338, 338, 338, 338, 338, - 338, 338, 155, 338, 338, 338, 338, 148, - 338, 0 + 3, 3, 4, 0, 3, 3, 4, 1, + 0, 5, 3, 3, 4, 1, 0, 6, + 0, 7, 0, 8, 3, 3, 4, 1, + 0, 2, 3, 3, 4, 1, 0, 0, + 0, 0, 9, 0, 11, 12, 12, 13, + 14, 10, 14, 10, 12, 12, 13, 10, + 12, 12, 13, 14, 10, 15, 12, 12, + 13, 14, 10, 16, 10, 17, 10, 18, + 12, 12, 13, 14, 10, 11, 12, 12, + 13, 14, 10, 10, 10, 10, 19, 10, + 11, 12, 12, 13, 14, 10, 10, 10, + 10, 20, 10, 22, 23, 23, 24, 25, + 21, 21, 21, 21, 26, 21, 25, 21, + 23, 23, 24, 27, 23, 23, 24, 25, + 21, 28, 23, 23, 24, 25, 21, 29, + 21, 30, 21, 22, 23, 23, 24, 25, + 21, 31, 23, 23, 24, 25, 21, 33, + 34, 34, 35, 36, 32, 32, 32, 32, + 37, 32, 36, 32, 34, 34, 35, 32, + 34, 34, 35, 36, 32, 38, 34, 34, + 35, 36, 32, 39, 32, 40, 32, 33, + 34, 34, 35, 36, 32, 41, 34, 34, + 35, 36, 32, 23, 23, 24, 1, 0, + 43, 42, 45, 46, 47, 48, 49, 50, + 24, 25, 44, 51, 52, 52, 26, 44, + 53, 54, 55, 56, 57, 44, 59, 60, + 61, 62, 4, 1, 58, 63, 58, 58, + 9, 58, 58, 58, 64, 58, 65, 60, + 66, 66, 4, 1, 58, 63, 58, 58, + 58, 58, 58, 58, 64, 58, 60, 66, + 66, 4, 1, 58, 63, 58, 58, 58, + 58, 58, 58, 64, 58, 45, 58, 58, + 58, 67, 68, 58, 1, 58, 63, 58, + 58, 58, 58, 58, 45, 58, 69, 69, + 58, 1, 58, 63, 58, 63, 58, 58, + 70, 58, 63, 58, 63, 58, 63, 58, + 58, 58, 58, 63, 58, 45, 58, 71, + 58, 69, 69, 58, 1, 58, 63, 58, + 58, 58, 58, 58, 45, 58, 45, 58, + 58, 58, 69, 69, 58, 1, 58, 63, + 58, 58, 58, 58, 58, 45, 58, 45, + 58, 58, 58, 69, 68, 58, 1, 58, + 63, 58, 58, 58, 58, 58, 45, 58, + 72, 7, 73, 74, 4, 1, 58, 63, + 58, 7, 73, 74, 4, 1, 58, 63, + 58, 73, 73, 4, 1, 58, 63, 58, + 75, 76, 76, 4, 1, 58, 63, 58, + 67, 77, 58, 1, 58, 63, 58, 67, + 58, 69, 69, 58, 1, 58, 63, 58, + 69, 77, 58, 1, 58, 63, 58, 59, + 60, 66, 66, 4, 1, 58, 63, 58, + 58, 58, 58, 58, 58, 64, 58, 59, + 60, 61, 66, 4, 1, 58, 63, 58, + 58, 9, 58, 58, 58, 64, 58, 79, + 80, 81, 82, 13, 14, 78, 83, 78, + 78, 20, 78, 78, 78, 84, 78, 85, + 80, 86, 82, 13, 14, 78, 83, 78, + 78, 78, 78, 78, 78, 84, 78, 80, + 86, 82, 13, 14, 78, 83, 78, 78, + 78, 78, 78, 78, 84, 78, 87, 78, + 78, 78, 88, 89, 78, 14, 78, 83, + 78, 78, 78, 78, 78, 87, 78, 90, + 80, 91, 92, 13, 14, 78, 83, 78, + 78, 19, 78, 78, 78, 84, 78, 93, + 80, 86, 86, 13, 14, 78, 83, 78, + 78, 78, 78, 78, 78, 84, 78, 80, + 86, 86, 13, 14, 78, 83, 78, 78, + 78, 78, 78, 78, 84, 78, 87, 78, + 78, 78, 94, 89, 78, 14, 78, 83, + 78, 78, 78, 78, 78, 87, 78, 83, + 78, 78, 95, 78, 83, 78, 83, 78, + 83, 78, 78, 78, 78, 83, 78, 87, + 78, 96, 78, 94, 94, 78, 14, 78, + 83, 78, 78, 78, 78, 78, 87, 78, + 87, 78, 78, 78, 94, 94, 78, 14, + 78, 83, 78, 78, 78, 78, 78, 87, + 78, 97, 17, 98, 99, 13, 14, 78, + 83, 78, 17, 98, 99, 13, 14, 78, + 83, 78, 98, 98, 13, 14, 78, 83, + 78, 100, 101, 101, 13, 14, 78, 83, + 78, 88, 102, 78, 14, 78, 83, 78, + 94, 94, 78, 14, 78, 83, 78, 88, + 78, 94, 94, 78, 14, 78, 83, 78, + 94, 102, 78, 14, 78, 83, 78, 90, + 80, 86, 86, 13, 14, 78, 83, 78, + 78, 78, 78, 78, 78, 84, 78, 90, + 80, 91, 86, 13, 14, 78, 83, 78, + 78, 19, 78, 78, 78, 84, 78, 11, + 12, 12, 13, 14, 78, 79, 80, 86, + 82, 13, 14, 78, 83, 78, 78, 78, + 78, 78, 78, 84, 78, 104, 48, 105, + 105, 24, 25, 103, 51, 103, 103, 103, + 103, 103, 103, 55, 103, 48, 105, 105, + 24, 25, 103, 51, 103, 103, 103, 103, + 103, 103, 55, 103, 106, 103, 103, 103, + 107, 108, 103, 25, 103, 51, 103, 103, + 103, 103, 103, 106, 103, 47, 48, 109, + 110, 24, 25, 103, 51, 103, 103, 26, + 103, 103, 103, 55, 103, 106, 103, 103, + 103, 111, 108, 103, 25, 103, 51, 103, + 103, 103, 103, 103, 106, 103, 51, 103, + 103, 112, 103, 51, 103, 51, 103, 51, + 103, 103, 103, 103, 51, 103, 106, 103, + 113, 103, 111, 111, 103, 25, 103, 51, + 103, 103, 103, 103, 103, 106, 103, 106, + 103, 103, 103, 111, 111, 103, 25, 103, + 51, 103, 103, 103, 103, 103, 106, 103, + 114, 30, 115, 116, 24, 25, 103, 51, + 103, 30, 115, 116, 24, 25, 103, 51, + 103, 115, 115, 24, 25, 103, 51, 103, + 47, 48, 105, 105, 24, 25, 103, 51, + 103, 103, 103, 103, 103, 103, 55, 103, + 117, 118, 118, 24, 25, 103, 51, 103, + 107, 119, 103, 25, 103, 51, 103, 111, + 111, 103, 25, 103, 51, 103, 107, 103, + 111, 111, 103, 25, 103, 51, 103, 111, + 119, 103, 25, 103, 51, 103, 47, 48, + 109, 105, 24, 25, 103, 51, 103, 103, + 26, 103, 103, 103, 55, 103, 22, 23, + 23, 24, 25, 120, 120, 120, 120, 26, + 120, 22, 23, 23, 24, 25, 120, 122, + 123, 124, 125, 35, 36, 121, 126, 121, + 121, 37, 121, 121, 121, 127, 121, 128, + 123, 125, 125, 35, 36, 121, 126, 121, + 121, 121, 121, 121, 121, 127, 121, 123, + 125, 125, 35, 36, 121, 126, 121, 121, + 121, 121, 121, 121, 127, 121, 129, 121, + 121, 121, 130, 131, 121, 36, 121, 126, + 121, 121, 121, 121, 121, 129, 121, 122, + 123, 124, 52, 35, 36, 121, 126, 121, + 121, 37, 121, 121, 121, 127, 121, 129, + 121, 121, 121, 132, 131, 121, 36, 121, + 126, 121, 121, 121, 121, 121, 129, 121, + 126, 121, 121, 133, 121, 126, 121, 126, + 121, 126, 121, 121, 121, 121, 126, 121, + 129, 121, 134, 121, 132, 132, 121, 36, + 121, 126, 121, 121, 121, 121, 121, 129, + 121, 129, 121, 121, 121, 132, 132, 121, + 36, 121, 126, 121, 121, 121, 121, 121, + 129, 121, 135, 40, 136, 137, 35, 36, + 121, 126, 121, 40, 136, 137, 35, 36, + 121, 126, 121, 136, 136, 35, 36, 121, + 126, 121, 122, 123, 125, 125, 35, 36, + 121, 126, 121, 121, 121, 121, 121, 121, + 127, 121, 138, 139, 139, 35, 36, 121, + 126, 121, 130, 140, 121, 36, 121, 126, + 121, 132, 132, 121, 36, 121, 126, 121, + 130, 121, 132, 132, 121, 36, 121, 126, + 121, 132, 140, 121, 36, 121, 126, 121, + 45, 46, 47, 48, 109, 105, 24, 25, + 103, 51, 52, 52, 26, 103, 103, 45, + 55, 103, 59, 141, 61, 62, 4, 1, + 58, 63, 58, 58, 9, 58, 58, 58, + 64, 58, 45, 46, 47, 48, 142, 143, + 24, 144, 58, 145, 58, 52, 26, 58, + 58, 45, 55, 58, 22, 146, 146, 24, + 144, 58, 63, 58, 58, 26, 58, 145, + 58, 58, 147, 58, 145, 58, 145, 58, + 145, 58, 58, 58, 58, 145, 58, 45, + 58, 71, 22, 146, 146, 24, 144, 58, + 63, 58, 58, 58, 58, 58, 45, 58, + 149, 148, 150, 150, 148, 43, 148, 151, + 148, 150, 150, 148, 43, 148, 151, 148, + 151, 148, 148, 152, 148, 151, 148, 151, + 148, 151, 148, 148, 148, 148, 151, 148, + 45, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 52, 120, 120, 120, 120, 45, + 120, 0 }; -static const short _indic_syllable_machine_trans_targs[] = { - 138, 160, 166, 2, 167, 3, 5, 170, - 6, 8, 173, 9, 11, 176, 12, 14, - 15, 159, 17, 18, 175, 20, 21, 172, - 23, 24, 169, 178, 182, 183, 187, 188, - 192, 193, 197, 198, 138, 221, 227, 36, - 228, 37, 39, 231, 40, 42, 234, 43, - 45, 237, 46, 48, 49, 220, 51, 52, - 236, 54, 55, 233, 57, 58, 230, 239, - 243, 244, 248, 249, 253, 254, 258, 260, - 138, 281, 287, 70, 288, 138, 71, 73, - 291, 74, 76, 294, 77, 79, 297, 80, - 82, 83, 280, 85, 86, 296, 88, 89, - 293, 91, 92, 290, 299, 303, 304, 308, - 309, 313, 314, 318, 138, 343, 349, 103, - 350, 104, 106, 353, 107, 109, 356, 110, - 112, 359, 113, 115, 116, 342, 118, 119, - 358, 121, 122, 355, 124, 125, 352, 361, - 365, 366, 370, 371, 375, 376, 380, 381, - 320, 138, 394, 138, 139, 200, 261, 263, - 319, 321, 283, 322, 382, 383, 392, 399, - 138, 140, 142, 33, 199, 162, 141, 32, - 143, 195, 144, 146, 31, 194, 145, 30, - 147, 190, 148, 150, 29, 189, 149, 28, - 151, 185, 152, 154, 27, 184, 153, 26, - 155, 180, 156, 158, 25, 179, 157, 1, - 165, 0, 161, 164, 163, 138, 168, 4, - 22, 171, 7, 19, 174, 10, 16, 177, - 13, 181, 186, 191, 196, 138, 201, 203, - 67, 259, 223, 202, 66, 204, 256, 205, - 207, 65, 255, 206, 64, 208, 251, 209, - 211, 63, 250, 210, 62, 212, 246, 213, - 215, 61, 245, 214, 60, 216, 241, 217, - 219, 59, 240, 218, 35, 226, 34, 222, - 225, 224, 138, 229, 38, 56, 232, 41, - 53, 235, 44, 50, 238, 47, 242, 247, - 252, 257, 138, 262, 100, 264, 316, 265, - 267, 99, 315, 266, 98, 268, 311, 269, - 271, 97, 310, 270, 96, 272, 306, 273, - 275, 95, 305, 274, 94, 276, 301, 277, - 279, 93, 300, 278, 69, 286, 68, 282, - 285, 284, 138, 289, 72, 90, 292, 75, - 87, 295, 78, 84, 298, 81, 302, 307, - 312, 317, 138, 138, 323, 325, 134, 133, - 345, 324, 326, 378, 327, 329, 132, 377, - 328, 131, 330, 373, 331, 333, 130, 372, - 332, 129, 334, 368, 335, 337, 128, 367, - 336, 127, 338, 363, 339, 341, 126, 362, - 340, 102, 348, 101, 344, 347, 346, 138, - 351, 105, 123, 354, 108, 120, 357, 111, - 117, 360, 114, 364, 369, 374, 379, 135, - 384, 385, 391, 386, 388, 136, 387, 390, - 389, 138, 393, 137, 396, 395, 398, 397, - 138 +static const unsigned char _indic_syllable_machine_trans_targs[] = { + 39, 45, 50, 2, 51, 5, 6, 53, + 57, 58, 39, 67, 11, 73, 68, 14, + 15, 75, 80, 81, 84, 39, 89, 21, + 95, 90, 98, 39, 24, 25, 97, 103, + 39, 112, 30, 118, 113, 121, 33, 34, + 120, 126, 39, 137, 39, 40, 60, 85, + 87, 105, 106, 91, 107, 127, 128, 99, + 135, 140, 39, 41, 43, 8, 59, 46, + 54, 42, 1, 44, 48, 0, 47, 49, + 52, 3, 4, 55, 7, 56, 39, 61, + 63, 18, 83, 69, 76, 62, 9, 64, + 78, 71, 65, 17, 82, 66, 10, 70, + 72, 74, 12, 13, 77, 16, 79, 39, + 86, 26, 88, 101, 93, 19, 104, 20, + 92, 94, 96, 22, 23, 100, 27, 102, + 39, 39, 108, 110, 28, 35, 114, 122, + 109, 111, 124, 116, 29, 115, 117, 119, + 31, 32, 123, 36, 125, 129, 130, 134, + 131, 132, 37, 133, 39, 136, 38, 138, + 139 }; static const char _indic_syllable_machine_trans_actions[] = { 1, 0, 2, 0, 2, 0, 0, 2, - 0, 0, 2, 0, 0, 2, 0, 0, - 0, 2, 0, 0, 2, 0, 0, 2, - 0, 0, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 3, 0, 2, 0, - 2, 0, 0, 2, 0, 0, 2, 0, - 0, 2, 0, 0, 0, 2, 0, 0, - 2, 0, 0, 2, 0, 0, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 4, 0, 2, 0, 2, 5, 0, 0, - 2, 0, 0, 2, 0, 0, 2, 0, - 0, 0, 2, 0, 0, 2, 0, 0, - 2, 0, 0, 2, 6, 2, 6, 2, - 6, 2, 6, 2, 7, 0, 2, 0, - 2, 0, 0, 2, 0, 0, 2, 0, - 0, 2, 0, 0, 0, 2, 0, 0, - 2, 0, 0, 2, 0, 0, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 6, 8, 0, 11, 2, 2, 6, 0, - 12, 12, 0, 2, 6, 2, 2, 0, - 13, 2, 0, 0, 2, 0, 2, 0, - 2, 2, 2, 0, 0, 2, 2, 0, - 2, 2, 2, 0, 0, 2, 2, 0, - 2, 2, 2, 0, 0, 2, 2, 0, - 2, 2, 2, 0, 0, 2, 2, 0, - 2, 0, 0, 0, 0, 14, 2, 0, - 0, 2, 0, 0, 2, 0, 0, 2, - 0, 2, 2, 2, 2, 15, 2, 0, - 0, 2, 0, 2, 0, 2, 2, 2, - 0, 0, 2, 2, 0, 2, 2, 2, - 0, 0, 2, 2, 0, 2, 2, 2, - 0, 0, 2, 2, 0, 2, 2, 2, - 0, 0, 2, 2, 0, 2, 0, 0, - 0, 0, 16, 2, 0, 0, 2, 0, - 0, 2, 0, 0, 2, 0, 2, 2, - 2, 2, 17, 6, 0, 6, 2, 6, - 0, 0, 6, 6, 0, 6, 2, 6, - 0, 0, 6, 6, 0, 6, 2, 6, - 0, 0, 6, 6, 0, 6, 2, 6, - 0, 0, 6, 6, 0, 2, 0, 0, - 0, 0, 18, 2, 0, 0, 2, 0, - 0, 2, 0, 0, 2, 0, 2, 2, - 2, 2, 19, 20, 2, 0, 0, 0, - 0, 2, 2, 2, 2, 0, 0, 2, - 2, 0, 2, 2, 2, 0, 0, 2, - 2, 0, 2, 2, 2, 0, 0, 2, - 2, 0, 2, 2, 2, 0, 0, 2, - 2, 0, 2, 0, 0, 0, 0, 21, - 2, 0, 0, 2, 0, 0, 2, 0, - 0, 2, 0, 2, 2, 2, 2, 0, - 0, 22, 22, 0, 0, 0, 0, 0, - 0, 23, 2, 0, 0, 0, 0, 0, - 24 + 2, 2, 3, 2, 0, 2, 0, 0, + 0, 2, 2, 2, 2, 4, 2, 0, + 5, 0, 5, 6, 0, 0, 5, 2, + 7, 2, 0, 2, 0, 2, 0, 0, + 2, 2, 8, 0, 11, 2, 2, 5, + 0, 12, 12, 0, 2, 5, 2, 5, + 2, 0, 13, 2, 0, 0, 2, 0, + 2, 2, 0, 2, 2, 0, 0, 2, + 2, 0, 0, 0, 0, 2, 14, 2, + 0, 0, 2, 0, 2, 2, 0, 2, + 2, 2, 2, 0, 2, 2, 0, 0, + 2, 2, 0, 0, 0, 0, 2, 15, + 5, 0, 5, 2, 2, 0, 5, 0, + 0, 2, 5, 0, 0, 0, 0, 2, + 16, 17, 2, 0, 0, 0, 0, 2, + 2, 2, 2, 2, 0, 0, 2, 2, + 0, 0, 0, 0, 2, 0, 18, 18, + 0, 0, 0, 0, 19, 2, 0, 0, + 0 }; static const char _indic_syllable_machine_to_state_actions[] = { @@ -746,6 +319,7 @@ static const char _indic_syllable_machine_to_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -758,40 +332,7 @@ static const char _indic_syllable_machine_to_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0 }; static const char _indic_syllable_machine_from_state_actions[] = { @@ -799,6 +340,7 @@ static const char _indic_syllable_machine_from_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -811,126 +353,61 @@ static const char _indic_syllable_machine_from_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 10, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0 }; static const short _indic_syllable_machine_eof_trans[] = { 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 73, 73, 78, 78, - 73, 73, 73, 73, 73, 73, 73, 73, - 73, 73, 73, 73, 73, 73, 73, 73, - 73, 73, 73, 73, 73, 73, 73, 73, - 73, 73, 73, 73, 73, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 73, - 1, 146, 0, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 339, - 283, 339, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 283, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 410, 410, 410, 410, 410, 410, 410, 339 + 1, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 22, 22, 28, 22, 22, + 22, 22, 22, 22, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 1, 43, 0, + 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 104, 104, 104, + 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 104, 104, 104, 104, 104, 104, + 104, 121, 121, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 122, 122, 104, + 59, 59, 59, 59, 59, 59, 59, 149, + 149, 149, 149, 149, 121 }; -static const int indic_syllable_machine_start = 138; -static const int indic_syllable_machine_first_final = 138; +static const int indic_syllable_machine_start = 39; +static const int indic_syllable_machine_first_final = 39; static const int indic_syllable_machine_error = -1; -static const int indic_syllable_machine_en_main = 138; +static const int indic_syllable_machine_en_main = 39; #line 36 "hb-ot-shape-complex-indic-machine.rl" -#line 92 "hb-ot-shape-complex-indic-machine.rl" +#line 93 "hb-ot-shape-complex-indic-machine.rl" #define found_syllable(syllable_type) \ HB_STMT_START { \ if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \ for (unsigned int i = ts; i < te; i++) \ - info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + info[i].syllable() = (syllable_serial << 4) | indic_##syllable_type; \ syllable_serial++; \ if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ } HB_STMT_END static void -find_syllables (hb_buffer_t *buffer) +find_syllables_indic (hb_buffer_t *buffer) { unsigned int p, pe, eof, ts, te, act; int cs; hb_glyph_info_t *info = buffer->info; -#line 934 "hb-ot-shape-complex-indic-machine.hh" +#line 411 "hb-ot-shape-complex-indic-machine.hh" { cs = indic_syllable_machine_start; ts = 0; @@ -938,7 +415,7 @@ find_syllables (hb_buffer_t *buffer) act = 0; } -#line 112 "hb-ot-shape-complex-indic-machine.rl" +#line 113 "hb-ot-shape-complex-indic-machine.rl" p = 0; @@ -946,12 +423,12 @@ find_syllables (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 950 "hb-ot-shape-complex-indic-machine.hh" +#line 427 "hb-ot-shape-complex-indic-machine.hh" { int _slen; int _trans; const unsigned char *_keys; - const short *_inds; + const unsigned char *_inds; if ( p == pe ) goto _test_eof; _resume: @@ -960,7 +437,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 964 "hb-ot-shape-complex-indic-machine.hh" +#line 441 "hb-ot-shape-complex-indic-machine.hh" } _keys = _indic_syllable_machine_trans_keys + (cs<<1); @@ -982,75 +459,55 @@ _eof_trans: #line 1 "NONE" {te = p+1;} break; - case 14: -#line 83 "hb-ot-shape-complex-indic-machine.rl" - {te = p+1;{ found_syllable (consonant_syllable); }} - break; - case 16: -#line 84 "hb-ot-shape-complex-indic-machine.rl" - {te = p+1;{ found_syllable (vowel_syllable); }} - break; - case 21: -#line 85 "hb-ot-shape-complex-indic-machine.rl" - {te = p+1;{ found_syllable (standalone_cluster); }} - break; - case 24: -#line 86 "hb-ot-shape-complex-indic-machine.rl" - {te = p+1;{ found_syllable (symbol_cluster); }} - break; - case 18: -#line 87 "hb-ot-shape-complex-indic-machine.rl" - {te = p+1;{ found_syllable (broken_cluster); }} - break; case 11: -#line 88 "hb-ot-shape-complex-indic-machine.rl" +#line 89 "hb-ot-shape-complex-indic-machine.rl" {te = p+1;{ found_syllable (non_indic_cluster); }} break; case 13: -#line 83 "hb-ot-shape-complex-indic-machine.rl" +#line 84 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (consonant_syllable); }} break; - case 15: -#line 84 "hb-ot-shape-complex-indic-machine.rl" + case 14: +#line 85 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (vowel_syllable); }} break; - case 20: -#line 85 "hb-ot-shape-complex-indic-machine.rl" + case 17: +#line 86 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (standalone_cluster); }} break; - case 23: -#line 86 "hb-ot-shape-complex-indic-machine.rl" + case 19: +#line 87 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (symbol_cluster); }} break; - case 17: -#line 87 "hb-ot-shape-complex-indic-machine.rl" + case 15: +#line 88 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (broken_cluster); }} break; - case 19: -#line 88 "hb-ot-shape-complex-indic-machine.rl" + case 16: +#line 89 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (non_indic_cluster); }} break; case 1: -#line 83 "hb-ot-shape-complex-indic-machine.rl" +#line 84 "hb-ot-shape-complex-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (consonant_syllable); }} break; case 3: -#line 84 "hb-ot-shape-complex-indic-machine.rl" +#line 85 "hb-ot-shape-complex-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (vowel_syllable); }} break; case 7: -#line 85 "hb-ot-shape-complex-indic-machine.rl" +#line 86 "hb-ot-shape-complex-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (standalone_cluster); }} break; case 8: -#line 86 "hb-ot-shape-complex-indic-machine.rl" +#line 87 "hb-ot-shape-complex-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (symbol_cluster); }} break; case 4: -#line 87 "hb-ot-shape-complex-indic-machine.rl" +#line 88 "hb-ot-shape-complex-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (broken_cluster); }} break; - case 5: + case 6: #line 1 "NONE" { switch( act ) { case 1: @@ -1065,25 +522,25 @@ _eof_trans: } } break; - case 22: + case 18: #line 1 "NONE" {te = p+1;} -#line 83 "hb-ot-shape-complex-indic-machine.rl" +#line 84 "hb-ot-shape-complex-indic-machine.rl" {act = 1;} break; - case 6: + case 5: #line 1 "NONE" {te = p+1;} -#line 87 "hb-ot-shape-complex-indic-machine.rl" +#line 88 "hb-ot-shape-complex-indic-machine.rl" {act = 5;} break; case 12: #line 1 "NONE" {te = p+1;} -#line 88 "hb-ot-shape-complex-indic-machine.rl" +#line 89 "hb-ot-shape-complex-indic-machine.rl" {act = 6;} break; -#line 1087 "hb-ot-shape-complex-indic-machine.hh" +#line 544 "hb-ot-shape-complex-indic-machine.hh" } _again: @@ -1092,7 +549,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 1096 "hb-ot-shape-complex-indic-machine.hh" +#line 553 "hb-ot-shape-complex-indic-machine.hh" } if ( ++p != pe ) @@ -1108,8 +565,10 @@ _again: } -#line 120 "hb-ot-shape-complex-indic-machine.rl" +#line 121 "hb-ot-shape-complex-indic-machine.rl" } +#undef found_syllable + #endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic-table.cc index c061ed1a78c..a150fd24866 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic-table.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic-table.cc @@ -6,71 +6,77 @@ * * on files with these headers: * - * # IndicSyllabicCategory-11.0.0.txt - * # Date: 2018-05-21, 18:33:00 GMT [KW, RP] - * # IndicPositionalCategory-11.0.0.txt - * # Date: 2018-02-05, 16:21:00 GMT [KW, RP] - * # Blocks-11.0.0.txt - * # Date: 2017-10-16, 24:39:00 GMT [KW] + * # IndicSyllabicCategory-13.0.0.txt + * # Date: 2019-07-22, 19:55:00 GMT [KW, RP] + * # IndicPositionalCategory-13.0.0.txt + * # Date: 2019-07-23, 00:01:00 GMT [KW, RP] + * # Blocks-13.0.0.txt + * # Date: 2019-07-10, 19:06:00 GMT [KW] */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex-indic.hh" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-macros" -#define ISC_A INDIC_SYLLABIC_CATEGORY_AVAGRAHA /* 16 chars; Avagraha */ -#define ISC_Bi INDIC_SYLLABIC_CATEGORY_BINDU /* 83 chars; Bindu */ -#define ISC_BJN INDIC_SYLLABIC_CATEGORY_BRAHMI_JOINING_NUMBER /* 20 chars; Brahmi_Joining_Number */ -#define ISC_Ca INDIC_SYLLABIC_CATEGORY_CANTILLATION_MARK /* 58 chars; Cantillation_Mark */ -#define ISC_C INDIC_SYLLABIC_CATEGORY_CONSONANT /* 2110 chars; Consonant */ -#define ISC_CD INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD /* 10 chars; Consonant_Dead */ -#define ISC_CF INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL /* 67 chars; Consonant_Final */ -#define ISC_CHL INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER /* 5 chars; Consonant_Head_Letter */ -#define ISC_CIP INDIC_SYLLABIC_CATEGORY_CONSONANT_INITIAL_POSTFIXED /* 1 chars; Consonant_Initial_Postfixed */ -#define ISC_CK INDIC_SYLLABIC_CATEGORY_CONSONANT_KILLER /* 2 chars; Consonant_Killer */ -#define ISC_CM INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL /* 28 chars; Consonant_Medial */ -#define ISC_CP INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER /* 21 chars; Consonant_Placeholder */ -#define ISC_CPR INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA /* 2 chars; Consonant_Preceding_Repha */ -#define ISC_CPrf INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED /* 7 chars; Consonant_Prefixed */ -#define ISC_CS INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED /* 95 chars; Consonant_Subjoined */ -#define ISC_CSR INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA /* 4 chars; Consonant_Succeeding_Repha */ -#define ISC_CWS INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER /* 6 chars; Consonant_With_Stacker */ -#define ISC_GM INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK /* 3 chars; Gemination_Mark */ -#define ISC_IS INDIC_SYLLABIC_CATEGORY_INVISIBLE_STACKER /* 11 chars; Invisible_Stacker */ -#define ISC_ZWJ INDIC_SYLLABIC_CATEGORY_JOINER /* 1 chars; Joiner */ -#define ISC_ML INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER /* 1 chars; Modifying_Letter */ -#define ISC_ZWNJ INDIC_SYLLABIC_CATEGORY_NON_JOINER /* 1 chars; Non_Joiner */ -#define ISC_N INDIC_SYLLABIC_CATEGORY_NUKTA /* 30 chars; Nukta */ -#define ISC_Nd INDIC_SYLLABIC_CATEGORY_NUMBER /* 480 chars; Number */ -#define ISC_NJ INDIC_SYLLABIC_CATEGORY_NUMBER_JOINER /* 1 chars; Number_Joiner */ -#define ISC_x INDIC_SYLLABIC_CATEGORY_OTHER /* 1 chars; Other */ -#define ISC_PK INDIC_SYLLABIC_CATEGORY_PURE_KILLER /* 21 chars; Pure_Killer */ -#define ISC_RS INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER /* 2 chars; Register_Shifter */ -#define ISC_SM INDIC_SYLLABIC_CATEGORY_SYLLABLE_MODIFIER /* 25 chars; Syllable_Modifier */ -#define ISC_TL INDIC_SYLLABIC_CATEGORY_TONE_LETTER /* 7 chars; Tone_Letter */ -#define ISC_TM INDIC_SYLLABIC_CATEGORY_TONE_MARK /* 42 chars; Tone_Mark */ -#define ISC_V INDIC_SYLLABIC_CATEGORY_VIRAMA /* 25 chars; Virama */ -#define ISC_Vs INDIC_SYLLABIC_CATEGORY_VISARGA /* 36 chars; Visarga */ -#define ISC_Vo INDIC_SYLLABIC_CATEGORY_VOWEL /* 30 chars; Vowel */ -#define ISC_M INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT /* 660 chars; Vowel_Dependent */ -#define ISC_VI INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT /* 464 chars; Vowel_Independent */ - -#define IMC_B INDIC_MATRA_CATEGORY_BOTTOM /* 340 chars; Bottom */ -#define IMC_BL INDIC_MATRA_CATEGORY_BOTTOM_AND_LEFT /* 1 chars; Bottom_And_Left */ -#define IMC_BR INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT /* 2 chars; Bottom_And_Right */ -#define IMC_L INDIC_MATRA_CATEGORY_LEFT /* 59 chars; Left */ -#define IMC_LR INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT /* 21 chars; Left_And_Right */ -#define IMC_x INDIC_MATRA_CATEGORY_NOT_APPLICABLE /* 1 chars; Not_Applicable */ -#define IMC_O INDIC_MATRA_CATEGORY_OVERSTRUCK /* 10 chars; Overstruck */ -#define IMC_R INDIC_MATRA_CATEGORY_RIGHT /* 276 chars; Right */ -#define IMC_T INDIC_MATRA_CATEGORY_TOP /* 393 chars; Top */ -#define IMC_TB INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM /* 10 chars; Top_And_Bottom */ -#define IMC_TBR INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT /* 1 chars; Top_And_Bottom_And_Right */ -#define IMC_TL INDIC_MATRA_CATEGORY_TOP_AND_LEFT /* 6 chars; Top_And_Left */ -#define IMC_TLR INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT /* 4 chars; Top_And_Left_And_Right */ -#define IMC_TR INDIC_MATRA_CATEGORY_TOP_AND_RIGHT /* 13 chars; Top_And_Right */ -#define IMC_VOL INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT /* 19 chars; Visual_Order_Left */ +#define ISC_A INDIC_SYLLABIC_CATEGORY_AVAGRAHA /* 17 chars; Avagraha */ +#define ISC_Bi INDIC_SYLLABIC_CATEGORY_BINDU /* 91 chars; Bindu */ +#define ISC_BJN INDIC_SYLLABIC_CATEGORY_BRAHMI_JOINING_NUMBER /* 20 chars; Brahmi_Joining_Number */ +#define ISC_Ca INDIC_SYLLABIC_CATEGORY_CANTILLATION_MARK /* 59 chars; Cantillation_Mark */ +#define ISC_C INDIC_SYLLABIC_CATEGORY_CONSONANT /* 2195 chars; Consonant */ +#define ISC_CD INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD /* 12 chars; Consonant_Dead */ +#define ISC_CF INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL /* 67 chars; Consonant_Final */ +#define ISC_CHL INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER /* 5 chars; Consonant_Head_Letter */ +#define ISC_CIP INDIC_SYLLABIC_CATEGORY_CONSONANT_INITIAL_POSTFIXED /* 1 chars; Consonant_Initial_Postfixed */ +#define ISC_CK INDIC_SYLLABIC_CATEGORY_CONSONANT_KILLER /* 2 chars; Consonant_Killer */ +#define ISC_CM INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL /* 31 chars; Consonant_Medial */ +#define ISC_CP INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER /* 22 chars; Consonant_Placeholder */ +#define ISC_CPR INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA /* 3 chars; Consonant_Preceding_Repha */ +#define ISC_CPrf INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED /* 10 chars; Consonant_Prefixed */ +#define ISC_CS INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED /* 94 chars; Consonant_Subjoined */ +#define ISC_CSR INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA /* 4 chars; Consonant_Succeeding_Repha */ +#define ISC_CWS INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER /* 8 chars; Consonant_With_Stacker */ +#define ISC_GM INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK /* 3 chars; Gemination_Mark */ +#define ISC_IS INDIC_SYLLABIC_CATEGORY_INVISIBLE_STACKER /* 12 chars; Invisible_Stacker */ +#define ISC_ZWJ INDIC_SYLLABIC_CATEGORY_JOINER /* 1 chars; Joiner */ +#define ISC_ML INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER /* 1 chars; Modifying_Letter */ +#define ISC_ZWNJ INDIC_SYLLABIC_CATEGORY_NON_JOINER /* 1 chars; Non_Joiner */ +#define ISC_N INDIC_SYLLABIC_CATEGORY_NUKTA /* 31 chars; Nukta */ +#define ISC_Nd INDIC_SYLLABIC_CATEGORY_NUMBER /* 491 chars; Number */ +#define ISC_NJ INDIC_SYLLABIC_CATEGORY_NUMBER_JOINER /* 1 chars; Number_Joiner */ +#define ISC_x INDIC_SYLLABIC_CATEGORY_OTHER /* 1 chars; Other */ +#define ISC_PK INDIC_SYLLABIC_CATEGORY_PURE_KILLER /* 23 chars; Pure_Killer */ +#define ISC_RS INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER /* 2 chars; Register_Shifter */ +#define ISC_SM INDIC_SYLLABIC_CATEGORY_SYLLABLE_MODIFIER /* 25 chars; Syllable_Modifier */ +#define ISC_TL INDIC_SYLLABIC_CATEGORY_TONE_LETTER /* 7 chars; Tone_Letter */ +#define ISC_TM INDIC_SYLLABIC_CATEGORY_TONE_MARK /* 42 chars; Tone_Mark */ +#define ISC_V INDIC_SYLLABIC_CATEGORY_VIRAMA /* 27 chars; Virama */ +#define ISC_Vs INDIC_SYLLABIC_CATEGORY_VISARGA /* 35 chars; Visarga */ +#define ISC_Vo INDIC_SYLLABIC_CATEGORY_VOWEL /* 30 chars; Vowel */ +#define ISC_M INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT /* 683 chars; Vowel_Dependent */ +#define ISC_VI INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT /* 484 chars; Vowel_Independent */ + +#define IMC_B INDIC_MATRA_CATEGORY_BOTTOM /* 351 chars; Bottom */ +#define IMC_BL INDIC_MATRA_CATEGORY_BOTTOM_AND_LEFT /* 1 chars; Bottom_And_Left */ +#define IMC_BR INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT /* 4 chars; Bottom_And_Right */ +#define IMC_L INDIC_MATRA_CATEGORY_LEFT /* 64 chars; Left */ +#define IMC_LR INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT /* 22 chars; Left_And_Right */ +#define IMC_x INDIC_MATRA_CATEGORY_NOT_APPLICABLE /* 1 chars; Not_Applicable */ +#define IMC_O INDIC_MATRA_CATEGORY_OVERSTRUCK /* 10 chars; Overstruck */ +#define IMC_R INDIC_MATRA_CATEGORY_RIGHT /* 288 chars; Right */ +#define IMC_T INDIC_MATRA_CATEGORY_TOP /* 415 chars; Top */ +#define IMC_TB INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM /* 10 chars; Top_And_Bottom */ +#define IMC_TBL INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_LEFT /* 2 chars; Top_And_Bottom_And_Left */ +#define IMC_TBR INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT /* 1 chars; Top_And_Bottom_And_Right */ +#define IMC_TL INDIC_MATRA_CATEGORY_TOP_AND_LEFT /* 6 chars; Top_And_Left */ +#define IMC_TLR INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT /* 4 chars; Top_And_Left_And_Right */ +#define IMC_TR INDIC_MATRA_CATEGORY_TOP_AND_RIGHT /* 13 chars; Top_And_Right */ +#define IMC_VOL INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT /* 19 chars; Visual_Order_Left */ + #pragma GCC diagnostic pop #define _(S,M) INDIC_COMBINE_CATEGORIES (ISC_##S, IMC_##M) @@ -152,7 +158,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 0A38 */ _(C,x), _(C,x), _(x,x), _(x,x), _(N,B), _(x,x), _(M,R), _(M,L), /* 0A40 */ _(M,R), _(M,B), _(M,B), _(x,x), _(x,x), _(x,x), _(x,x), _(M,T), /* 0A48 */ _(M,T), _(x,x), _(x,x), _(M,T), _(M,T), _(V,B), _(x,x), _(x,x), - /* 0A50 */ _(x,x), _(Ca,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 0A50 */ _(x,x), _(Ca,B), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 0A58 */ _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), _(C,x), _(x,x), /* 0A60 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(Nd,x), _(Nd,x), /* 0A68 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), @@ -190,7 +196,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 0B38 */ _(C,x), _(C,x), _(x,x), _(x,x), _(N,B), _(A,x), _(M,R), _(M,T), /* 0B40 */ _(M,R), _(M,B), _(M,B), _(M,B), _(M,B), _(x,x), _(x,x), _(M,L), /* 0B48 */ _(M,TL), _(x,x), _(x,x), _(M,LR),_(M,TLR), _(V,B), _(x,x), _(x,x), - /* 0B50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(M,T), _(M,TR), + /* 0B50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(M,T), _(M,T), _(M,TR), /* 0B58 */ _(x,x), _(x,x), _(x,x), _(x,x), _(C,x), _(C,x), _(x,x), _(C,x), /* 0B60 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x), /* 0B68 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), @@ -237,7 +243,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* Kannada */ - /* 0C80 */ _(x,x), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0C80 */ _(Bi,x), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), /* 0C88 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(VI,x), _(VI,x), /* 0C90 */ _(VI,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), /* 0C98 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -256,7 +262,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* Malayalam */ - /* 0D00 */ _(Bi,T), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0D00 */ _(Bi,T), _(Bi,T), _(Bi,R), _(Vs,R), _(Bi,x), _(VI,x), _(VI,x), _(VI,x), /* 0D08 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(VI,x), _(VI,x), /* 0D10 */ _(VI,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), /* 0D18 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -265,7 +271,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 0D30 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 0D38 */ _(C,x), _(C,x), _(C,x), _(PK,T), _(PK,T), _(A,x), _(M,R), _(M,R), /* 0D40 */ _(M,R), _(M,R), _(M,R), _(M,B), _(M,B), _(x,x), _(M,L), _(M,L), - /* 0D48 */ _(M,L), _(x,x), _(M,LR), _(M,LR), _(M,LR), _(V,T),_(CPR,x), _(x,x), + /* 0D48 */ _(M,L), _(x,x), _(M,LR), _(M,LR), _(M,LR), _(V,T),_(CPR,T), _(x,x), /* 0D50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(CD,x), _(CD,x), _(CD,x), _(M,R), /* 0D58 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(VI,x), /* 0D60 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x), @@ -275,7 +281,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* Sinhala */ - /* 0D80 */ _(x,x), _(x,x), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0D80 */ _(x,x), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), /* 0D88 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), /* 0D90 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), /* 0D98 */ _(x,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -303,7 +309,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 1020 */ _(C,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), /* 1028 */ _(VI,x), _(VI,x), _(VI,x), _(M,R), _(M,R), _(M,T), _(M,T), _(M,B), /* 1030 */ _(M,B), _(M,L), _(M,T), _(M,T), _(M,T), _(M,T), _(Bi,T), _(TM,B), - /* 1038 */ _(Vs,R), _(IS,x), _(PK,T), _(CM,R), _(CM,x), _(CM,B), _(CM,B), _(C,x), + /* 1038 */ _(Vs,R), _(IS,x), _(PK,T), _(CM,R),_(CM,TBL), _(CM,B), _(CM,B), _(C,x), /* 1040 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), /* 1048 */ _(Nd,x), _(Nd,x), _(x,x), _(CP,x), _(x,x), _(x,x), _(CP,x), _(x,x), /* 1050 */ _(C,x), _(C,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(M,R), _(M,R), @@ -346,8 +352,8 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 1CD8 */ _(Ca,B), _(Ca,B), _(Ca,T), _(Ca,T), _(Ca,B), _(Ca,B), _(Ca,B), _(Ca,B), /* 1CE0 */ _(Ca,T), _(Ca,R), _(x,O), _(x,O), _(x,O), _(x,O), _(x,O), _(x,O), /* 1CE8 */ _(x,O), _(x,x), _(x,x), _(x,x), _(x,x), _(x,B), _(x,x), _(x,x), - /* 1CF0 */ _(x,x), _(x,x), _(Vs,x), _(Vs,x), _(Ca,T),_(CWS,x),_(CWS,x), _(Ca,R), - /* 1CF8 */ _(Ca,x), _(Ca,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1CF0 */ _(x,x), _(x,x), _(CD,x), _(CD,x), _(Ca,T),_(CWS,x),_(CWS,x), _(Ca,R), + /* 1CF8 */ _(Ca,x), _(Ca,x), _(CP,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), #define indic_offset_0x2008u 1656 @@ -435,6 +441,7 @@ hb_indic_get_categories (hb_codepoint_t u) } #undef _ + #undef ISC_A #undef ISC_Bi #undef ISC_BJN @@ -471,6 +478,7 @@ hb_indic_get_categories (hb_codepoint_t u) #undef ISC_Vo #undef ISC_M #undef ISC_VI + #undef IMC_B #undef IMC_BL #undef IMC_BR @@ -481,10 +489,13 @@ hb_indic_get_categories (hb_codepoint_t u) #undef IMC_R #undef IMC_T #undef IMC_TB +#undef IMC_TBL #undef IMC_TBR #undef IMC_TL #undef IMC_TLR #undef IMC_TR #undef IMC_VOL +#endif + /* == End of generated table == */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic.cc index 38b47fa63e4..e011f510d32 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex-indic.hh" #include "hb-ot-shape-complex-vowel-constraints.hh" #include "hb-ot-layout.hh" @@ -127,62 +131,47 @@ indic_features[] = {HB_TAG('b','l','w','s'), F_GLOBAL_MANUAL_JOINERS}, {HB_TAG('p','s','t','s'), F_GLOBAL_MANUAL_JOINERS}, {HB_TAG('h','a','l','n'), F_GLOBAL_MANUAL_JOINERS}, - /* - * Positioning features. - * We don't care about the types. - */ - {HB_TAG('d','i','s','t'), F_GLOBAL}, - {HB_TAG('a','b','v','m'), F_GLOBAL}, - {HB_TAG('b','l','w','m'), F_GLOBAL}, }; /* * Must be in the same order as the indic_features array. */ enum { - _NUKT, - _AKHN, - RPHF, - _RKRF, - PREF, - BLWF, - ABVF, - HALF, - PSTF, - _VATU, - _CJCT, - - INIT, - _PRES, - _ABVS, - _BLWS, - _PSTS, - _HALN, - - _DIST, - _ABVM, - _BLWM, + _INDIC_NUKT, + _INDIC_AKHN, + INDIC_RPHF, + _INDIC_RKRF, + INDIC_PREF, + INDIC_BLWF, + INDIC_ABVF, + INDIC_HALF, + INDIC_PSTF, + _INDIC_VATU, + _INDIC_CJCT, + + INDIC_INIT, + _INDIC_PRES, + _INDIC_ABVS, + _INDIC_BLWS, + _INDIC_PSTS, + _INDIC_HALN, INDIC_NUM_FEATURES, - INDIC_BASIC_FEATURES = INIT, /* Don't forget to update this! */ + INDIC_BASIC_FEATURES = INDIC_INIT, /* Don't forget to update this! */ }; static void -setup_syllables (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); -static void -initial_reordering (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); +setup_syllables_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); static void -final_reordering (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); +initial_reordering_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); static void -clear_syllables (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); +final_reordering_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); static void collect_features_indic (hb_ot_shape_planner_t *plan) @@ -190,7 +179,7 @@ collect_features_indic (hb_ot_shape_planner_t *plan) hb_ot_map_builder_t *map = &plan->map; /* Do this before any lookups have been applied. */ - map->add_gsub_pause (setup_syllables); + map->add_gsub_pause (setup_syllables_indic); map->enable_feature (HB_TAG('l','o','c','l')); /* The Indic specs do not require ccmp, but we apply it here since if @@ -199,14 +188,14 @@ collect_features_indic (hb_ot_shape_planner_t *plan) unsigned int i = 0; - map->add_gsub_pause (initial_reordering); + map->add_gsub_pause (initial_reordering_indic); for (; i < INDIC_BASIC_FEATURES; i++) { map->add_feature (indic_features[i]); map->add_gsub_pause (nullptr); } - map->add_gsub_pause (final_reordering); + map->add_gsub_pause (final_reordering_indic); for (; i < INDIC_NUM_FEATURES; i++) map->add_feature (indic_features[i]); @@ -214,7 +203,7 @@ collect_features_indic (hb_ot_shape_planner_t *plan) map->enable_feature (HB_TAG('c','a','l','t')); map->enable_feature (HB_TAG('c','l','i','g')); - map->add_gsub_pause (clear_syllables); + map->add_gsub_pause (_hb_clear_syllables); } static void @@ -224,32 +213,6 @@ override_features_indic (hb_ot_shape_planner_t *plan) } -struct would_substitute_feature_t -{ - void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_) - { - zero_context = zero_context_; - map->get_stage_lookups (0/*GSUB*/, - map->get_feature_stage (0/*GSUB*/, feature_tag), - &lookups, &count); - } - - bool would_substitute (const hb_codepoint_t *glyphs, - unsigned int glyphs_count, - hb_face_t *face) const - { - for (unsigned int i = 0; i < count; i++) - if (hb_ot_layout_lookup_would_substitute_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context)) - return true; - return false; - } - - private: - const hb_ot_map_t::lookup_map_t *lookups; - unsigned int count; - bool zero_context; -}; - struct indic_shape_plan_t { bool load_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const @@ -274,13 +237,18 @@ struct indic_shape_plan_t const indic_config_t *config; bool is_old_spec; +#ifndef HB_NO_UNISCRIBE_BUG_COMPATIBLE bool uniscribe_bug_compatible; +#else + static constexpr bool uniscribe_bug_compatible = false; +#endif mutable hb_atomic_int_t virama_glyph; - would_substitute_feature_t rphf; - would_substitute_feature_t pref; - would_substitute_feature_t blwf; - would_substitute_feature_t pstf; + hb_indic_would_substitute_feature_t rphf; + hb_indic_would_substitute_feature_t pref; + hb_indic_would_substitute_feature_t blwf; + hb_indic_would_substitute_feature_t pstf; + hb_indic_would_substitute_feature_t vatu; hb_mask_t mask_array[INDIC_NUM_FEATURES]; }; @@ -300,7 +268,9 @@ data_create_indic (const hb_ot_shape_plan_t *plan) } indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chosen_script[0] & 0x000000FFu) != '2'); +#ifndef HB_NO_UNISCRIBE_BUG_COMPATIBLE indic_plan->uniscribe_bug_compatible = hb_options ().uniscribe_bug_compatible; +#endif indic_plan->virama_glyph.set_relaxed (-1); /* Use zero-context would_substitute() matching for new-spec of the main @@ -317,6 +287,7 @@ data_create_indic (const hb_ot_shape_plan_t *plan) indic_plan->pref.init (&plan->map, HB_TAG('p','r','e','f'), zero_context); indic_plan->blwf.init (&plan->map, HB_TAG('b','l','w','f'), zero_context); indic_plan->pstf.init (&plan->map, HB_TAG('p','s','t','f'), zero_context); + indic_plan->vatu.init (&plan->map, HB_TAG('v','a','t','u'), zero_context); for (unsigned int i = 0; i < ARRAY_LENGTH (indic_plan->mask_array); i++) indic_plan->mask_array[i] = (indic_features[i].flags & F_GLOBAL) ? @@ -346,10 +317,16 @@ consonant_position_from_face (const indic_shape_plan_t *indic_plan, * base at 0. The font however, only has lookups matching * 930,94D in 'blwf', not the expected 94D,930 (with new-spec * table). As such, we simply match both sequences. Seems - * to work. */ + * to work. + * + * Vatu is done as well, for: + * https://github.com/harfbuzz/harfbuzz/issues/1587 + */ hb_codepoint_t glyphs[3] = {virama, consonant, virama}; if (indic_plan->blwf.would_substitute (glyphs , 2, face) || - indic_plan->blwf.would_substitute (glyphs+1, 2, face)) + indic_plan->blwf.would_substitute (glyphs+1, 2, face) || + indic_plan->vatu.would_substitute (glyphs , 2, face) || + indic_plan->vatu.would_substitute (glyphs+1, 2, face)) return POS_BELOW_C; if (indic_plan->pstf.would_substitute (glyphs , 2, face) || indic_plan->pstf.would_substitute (glyphs+1, 2, face)) @@ -361,13 +338,13 @@ consonant_position_from_face (const indic_shape_plan_t *indic_plan, } -enum syllable_type_t { - consonant_syllable, - vowel_syllable, - standalone_cluster, - symbol_cluster, - broken_cluster, - non_indic_cluster, +enum indic_syllable_type_t { + indic_consonant_syllable, + indic_vowel_syllable, + indic_standalone_cluster, + indic_symbol_cluster, + indic_broken_cluster, + indic_non_indic_cluster, }; #include "hb-ot-shape-complex-indic-machine.hh" @@ -391,11 +368,11 @@ setup_masks_indic (const hb_ot_shape_plan_t *plan HB_UNUSED, } static void -setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) +setup_syllables_indic (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) { - find_syllables (buffer); + find_syllables_indic (buffer); foreach_syllable (buffer, start, end) buffer->unsafe_to_break (start, end); } @@ -412,9 +389,9 @@ compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) static void -update_consonant_positions (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer) +update_consonant_positions_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) { const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; @@ -487,7 +464,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, * and has more than one consonant, Ra is excluded from candidates for * base consonants. */ unsigned int limit = start; - if (indic_plan->mask_array[RPHF] && + if (indic_plan->mask_array[INDIC_RPHF] && start + 3 <= end && ( (indic_plan->config->reph_mode == REPH_MODE_IMPLICIT && !is_joiner (info[start + 2])) || @@ -645,7 +622,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, /* Reorder characters */ for (unsigned int i = start; i < base; i++) - info[i].indic_position() = MIN (POS_PRE_C, (indic_position_t) info[i].indic_position()); + info[i].indic_position() = hb_min (POS_PRE_C, (indic_position_t) info[i].indic_position()); if (base < end) info[base].indic_position() = POS_BASE_C; @@ -673,7 +650,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, * is *not* a Halant after last consonant already. We know that is the * case for Kannada, while it reorders unconditionally in other scripts, * eg. Malayalam, Bengali, and Devanagari. We don't currently know about - * other scripts, so we blacklist Kannada. + * other scripts, so we block Kannada. * * Kannada test case: * U+0C9A,U+0CCD,U+0C9A,U+0CCD @@ -720,7 +697,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, indic_position_t last_pos = POS_START; for (unsigned int i = start; i < end; i++) { - if ((FLAG_UNSAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | FLAG (OT_H)))) + if ((FLAG_UNSAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | FLAG (OT_H)))) { info[i].indic_position() = last_pos; if (unlikely (info[i].indic_category() == OT_H && @@ -801,7 +778,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, unsigned int j = start + info[i].syllable(); while (j != i) { - max = MAX (max, j); + max = hb_max (max, j); unsigned int next = start + info[j].syllable(); info[j].syllable() = 255; /* So we don't process j later again. */ j = next; @@ -823,13 +800,13 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, /* Reph */ for (unsigned int i = start; i < end && info[i].indic_position() == POS_RA_TO_BECOME_REPH; i++) - info[i].mask |= indic_plan->mask_array[RPHF]; + info[i].mask |= indic_plan->mask_array[INDIC_RPHF]; /* Pre-base */ - mask = indic_plan->mask_array[HALF]; + mask = indic_plan->mask_array[INDIC_HALF]; if (!indic_plan->is_old_spec && indic_plan->config->blwf_mode == BLWF_MODE_PRE_AND_POST) - mask |= indic_plan->mask_array[BLWF]; + mask |= indic_plan->mask_array[INDIC_BLWF]; for (unsigned int i = start; i < base; i++) info[i].mask |= mask; /* Base */ @@ -837,7 +814,9 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, if (base < end) info[base].mask |= mask; /* Post-base */ - mask = indic_plan->mask_array[BLWF] | indic_plan->mask_array[ABVF] | indic_plan->mask_array[PSTF]; + mask = indic_plan->mask_array[INDIC_BLWF] | + indic_plan->mask_array[INDIC_ABVF] | + indic_plan->mask_array[INDIC_PSTF]; for (unsigned int i = base + 1; i < end; i++) info[i].mask |= mask; } @@ -869,13 +848,13 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, (i + 2 == base || info[i+2].indic_category() != OT_ZWJ)) { - info[i ].mask |= indic_plan->mask_array[BLWF]; - info[i+1].mask |= indic_plan->mask_array[BLWF]; + info[i ].mask |= indic_plan->mask_array[INDIC_BLWF]; + info[i+1].mask |= indic_plan->mask_array[INDIC_BLWF]; } } unsigned int pref_len = 2; - if (indic_plan->mask_array[PREF] && base + pref_len < end) + if (indic_plan->mask_array[INDIC_PREF] && base + pref_len < end) { /* Find a Halant,Ra sequence and mark it for pre-base-reordering processing. */ for (unsigned int i = base + 1; i + pref_len - 1 < end; i++) { @@ -885,7 +864,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, if (indic_plan->pref.would_substitute (glyphs, pref_len, face)) { for (unsigned int j = 0; j < pref_len; j++) - info[i++].mask |= indic_plan->mask_array[PREF]; + info[i++].mask |= indic_plan->mask_array[INDIC_PREF]; break; } } @@ -906,7 +885,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, /* A ZWNJ disables HALF. */ if (non_joiner) - info[j].mask &= ~indic_plan->mask_array[HALF]; + info[j].mask &= ~indic_plan->mask_array[INDIC_HALF]; } while (j > start && !is_consonant (info[j])); } @@ -918,11 +897,10 @@ initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan, hb_buffer_t *buffer, unsigned int start, unsigned int end) { - const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; - /* We treat placeholder/dotted-circle as if they are consonants, so we * should just chain. Only if not in compatibility mode that is... */ + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; if (indic_plan->uniscribe_bug_compatible) { /* For dotted-circle, this is what Uniscribe does: @@ -936,42 +914,45 @@ initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan, } static void -initial_reordering_syllable (const hb_ot_shape_plan_t *plan, - hb_face_t *face, - hb_buffer_t *buffer, - unsigned int start, unsigned int end) +initial_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) { - syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F); + indic_syllable_type_t syllable_type = (indic_syllable_type_t) (buffer->info[start].syllable() & 0x0F); switch (syllable_type) { - case vowel_syllable: /* We made the vowels look like consonants. So let's call the consonant logic! */ - case consonant_syllable: + case indic_vowel_syllable: /* We made the vowels look like consonants. So let's call the consonant logic! */ + case indic_consonant_syllable: initial_reordering_consonant_syllable (plan, face, buffer, start, end); break; - case broken_cluster: /* We already inserted dotted-circles, so just call the standalone_cluster. */ - case standalone_cluster: + case indic_broken_cluster: /* We already inserted dotted-circles, so just call the standalone_cluster. */ + case indic_standalone_cluster: initial_reordering_standalone_cluster (plan, face, buffer, start, end); break; - case symbol_cluster: - case non_indic_cluster: + case indic_symbol_cluster: + case indic_non_indic_cluster: break; } } static inline void -insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font, - hb_buffer_t *buffer) +insert_dotted_circles_indic (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer) { + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + /* Note: This loop is extra overhead, but should not be measurable. * TODO Use a buffer scratch flag to remove the loop. */ bool has_broken_syllables = false; unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 0; i < count; i++) - if ((info[i].syllable() & 0x0F) == broken_cluster) + if ((info[i].syllable() & 0x0F) == indic_broken_cluster) { has_broken_syllables = true; break; @@ -996,8 +977,8 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, while (buffer->idx < buffer->len && buffer->successful) { unsigned int syllable = buffer->cur().syllable(); - syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F); - if (unlikely (last_syllable != syllable && syllable_type == broken_cluster)) + indic_syllable_type_t syllable_type = (indic_syllable_type_t) (syllable & 0x0F); + if (unlikely (last_syllable != syllable && syllable_type == indic_broken_cluster)) { last_syllable = syllable; @@ -1005,7 +986,6 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, ginfo.cluster = buffer->cur().cluster; ginfo.mask = buffer->cur().mask; ginfo.syllable() = buffer->cur().syllable(); - /* TODO Set glyph_props? */ /* Insert dottedcircle after possible Repha. */ while (buffer->idx < buffer->len && buffer->successful && @@ -1022,21 +1002,21 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, } static void -initial_reordering (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer) +initial_reordering_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) { - update_consonant_positions (plan, font, buffer); - insert_dotted_circles (plan, font, buffer); + update_consonant_positions_indic (plan, font, buffer); + insert_dotted_circles_indic (plan, font, buffer); foreach_syllable (buffer, start, end) - initial_reordering_syllable (plan, font->face, buffer, start, end); + initial_reordering_syllable_indic (plan, font->face, buffer, start, end); } static void -final_reordering_syllable (const hb_ot_shape_plan_t *plan, - hb_buffer_t *buffer, - unsigned int start, unsigned int end) +final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) { const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; hb_glyph_info_t *info = buffer->info; @@ -1072,7 +1052,7 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, * syllable. */ - bool try_pref = !!indic_plan->mask_array[PREF]; + bool try_pref = !!indic_plan->mask_array[INDIC_PREF]; /* Find base again */ unsigned int base; @@ -1082,7 +1062,7 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, if (try_pref && base + 1 < end) { for (unsigned int i = base + 1; i < end; i++) - if ((info[i].mask & indic_plan->mask_array[PREF]) != 0) + if ((info[i].mask & indic_plan->mask_array[INDIC_PREF]) != 0) { if (!(_hb_glyph_info_substituted (&info[i]) && _hb_glyph_info_ligated_and_didnt_multiply (&info[i]))) @@ -1199,9 +1179,14 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, goto search; } } - /* -> If ZWNJ follows this halant, position is moved after it. */ - if (info[new_pos + 1].indic_category() == OT_ZWNJ) - new_pos++; + /* -> If ZWNJ follows this halant, position is moved after it. + * + * IMPLEMENTATION NOTES: + * + * This is taken care of by the state-machine. A Halant,ZWNJ is a terminating + * sequence for a consonant syllable; any pre-base matras occurring after it + * will belong to the subsequent syllable. + */ } } else @@ -1224,14 +1209,14 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, /* Note: this merge_clusters() is intentionally *after* the reordering. * Indic matra reordering is special and tricky... */ - buffer->merge_clusters (new_pos, MIN (end, base + 1)); + buffer->merge_clusters (new_pos, hb_min (end, base + 1)); new_pos--; } } else { for (unsigned int i = start; i < base; i++) if (info[i].indic_position () == POS_PRE_M) { - buffer->merge_clusters (i, MIN (end, base + 1)); + buffer->merge_clusters (i, hb_min (end, base + 1)); break; } } @@ -1348,6 +1333,7 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, goto reph_move; } } + /* See https://github.com/harfbuzz/harfbuzz/issues/2298#issuecomment-615318654 */ /* 6. Otherwise, reorder reph to the end of the syllable. */ @@ -1364,13 +1350,15 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, * TEST: U+0930,U+094D,U+0915,U+094B,U+094D */ if (!indic_plan->uniscribe_bug_compatible && - unlikely (is_halant (info[new_reph_pos]))) { + unlikely (is_halant (info[new_reph_pos]))) + { for (unsigned int i = base + 1; i < new_reph_pos; i++) if (info[i].indic_category() == OT_M) { /* Ok, got it. */ new_reph_pos--; } } + goto reph_move; } @@ -1397,7 +1385,7 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, if (try_pref && base + 1 < end) /* Otherwise there can't be any pre-base-reordering Ra. */ { for (unsigned int i = base + 1; i < end; i++) - if ((info[i].mask & indic_plan->mask_array[PREF]) != 0) + if ((info[i].mask & indic_plan->mask_array[INDIC_PREF]) != 0) { /* 1. Only reorder a glyph produced by substitution during application * of the feature. (Note that a font may shape a Ra consonant with @@ -1460,7 +1448,7 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, if (!start || !(FLAG_UNSAFE (_hb_glyph_info_get_general_category (&info[start - 1])) & FLAG_RANGE (HB_UNICODE_GENERAL_CATEGORY_FORMAT, HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))) - info[start].mask |= indic_plan->mask_array[INIT]; + info[start].mask |= indic_plan->mask_array[INDIC_INIT]; else buffer->unsafe_to_break (start - 1, start + 1); } @@ -1490,33 +1478,21 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, static void -final_reordering (const hb_ot_shape_plan_t *plan, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) +final_reordering_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) { unsigned int count = buffer->len; if (unlikely (!count)) return; foreach_syllable (buffer, start, end) - final_reordering_syllable (plan, buffer, start, end); + final_reordering_syllable_indic (plan, buffer, start, end); HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category); HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position); } -static void -clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) -{ - hb_glyph_info_t *info = buffer->info; - unsigned int count = buffer->len; - for (unsigned int i = 0; i < count; i++) - info[i].syllable() = 0; -} - - static void preprocess_text_indic (const hb_ot_shape_plan_t *plan, hb_buffer_t *buffer, @@ -1583,11 +1559,10 @@ decompose_indic (const hb_ot_shape_normalize_context_t *c, * https://docs.microsoft.com/en-us/typography/script-development/sinhala#shaping */ - const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) c->plan->data; + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) c->plan->data; hb_codepoint_t glyph; - - if (hb_options ().uniscribe_bug_compatible || + if (indic_plan->uniscribe_bug_compatible || (c->font->get_nominal_glyph (ab, &glyph) && indic_plan->pstf.would_substitute (&glyph, 1, c->font->face))) { @@ -1635,3 +1610,6 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic = HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, false, /* fallback_position */ }; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic.hh index 0aae0d3953a..5b3347b9f52 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic.hh @@ -62,17 +62,26 @@ enum indic_category_t { OT_Coeng = 14, /* Khmer-style Virama. */ OT_Repha = 15, /* Atomically-encoded logical or visual repha. */ OT_Ra = 16, - OT_CM = 17, /* Consonant-Medial; Unused by Indic shaper. */ + OT_CM = 17, /* Consonant-Medial. */ OT_Symbol = 18, /* Avagraha, etc that take marks (SM,A,VD). */ - OT_CS = 19 + OT_CS = 19, + + /* The following are used by Khmer & Myanmar shapers. Defined + * here for them to share. */ + OT_VAbv = 26, + OT_VBlw = 27, + OT_VPre = 28, + OT_VPst = 29, }; +#define MEDIAL_FLAGS (FLAG (OT_CM)) + /* Note: * * We treat Vowels and placeholders as if they were consonants. This is safe because Vowels * cannot happen in a consonant syllable. The plus side however is, we can call the * consonant syllable logic from the vowel syllable function and get it all right! */ -#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_CS) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_PLACEHOLDER) | FLAG (OT_DOTTEDCIRCLE)) +#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_CS) | FLAG (OT_Ra) | MEDIAL_FLAGS | FLAG (OT_V) | FLAG (OT_PLACEHOLDER) | FLAG (OT_DOTTEDCIRCLE)) #define JOINER_FLAGS (FLAG (OT_ZWJ) | FLAG (OT_ZWNJ)) @@ -156,6 +165,7 @@ enum indic_matra_category_t { INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT, INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT, INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM = INDIC_MATRA_CATEGORY_BOTTOM, + INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_LEFT = INDIC_MATRA_CATEGORY_BOTTOM, INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT, INDIC_MATRA_CATEGORY_TOP_AND_LEFT = INDIC_MATRA_CATEGORY_TOP, INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT, @@ -276,7 +286,7 @@ matra_position_indic (hb_codepoint_t u, indic_position_t side) case POS_POST_C: return MATRA_POS_RIGHT (u); case POS_ABOVE_C: return MATRA_POS_TOP (u); case POS_BELOW_C: return MATRA_POS_BOTTOM (u); - }; + } return side; } @@ -357,11 +367,12 @@ set_indic_properties (hb_glyph_info_t &info) /* According to ScriptExtensions.txt, these Grantha marks may also be used in Tamil, * so the Indic shaper needs to know their categories. */ else if (unlikely (u == 0x11301u || u == 0x11303u)) cat = OT_SM; - else if (unlikely (u == 0x1133cu)) cat = OT_N; + else if (unlikely (u == 0x1133Bu || u == 0x1133Cu)) cat = OT_N; else if (unlikely (u == 0x0AFBu)) cat = OT_N; /* https://github.com/harfbuzz/harfbuzz/issues/552 */ else if (unlikely (u == 0x0980u)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/issues/538 */ + else if (unlikely (u == 0x09FCu)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/pull/1613 */ else if (unlikely (u == 0x0C80u)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/pull/623 */ else if (unlikely (hb_in_range (u, 0x2010u, 0x2011u))) cat = OT_PLACEHOLDER; @@ -395,5 +406,31 @@ set_indic_properties (hb_glyph_info_t &info) info.indic_position() = pos; } +struct hb_indic_would_substitute_feature_t +{ + void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_) + { + zero_context = zero_context_; + map->get_stage_lookups (0/*GSUB*/, + map->get_feature_stage (0/*GSUB*/, feature_tag), + &lookups, &count); + } + + bool would_substitute (const hb_codepoint_t *glyphs, + unsigned int glyphs_count, + hb_face_t *face) const + { + for (unsigned int i = 0; i < count; i++) + if (hb_ot_layout_lookup_would_substitute (face, lookups[i].index, glyphs, glyphs_count, zero_context)) + return true; + return false; + } + + private: + const hb_ot_map_t::lookup_map_t *lookups; + unsigned int count; + bool zero_context; +}; + #endif /* HB_OT_SHAPE_COMPLEX_INDIC_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer-machine.hh index 2b59a50e724..4d737dad05c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer-machine.hh @@ -35,29 +35,27 @@ #line 36 "hb-ot-shape-complex-khmer-machine.hh" static const unsigned char _khmer_syllable_machine_trans_keys[] = { 5u, 26u, 5u, 21u, 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, 5u, 26u, 5u, 21u, - 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, - 5u, 26u, 5u, 21u, 5u, 26u, 5u, 21u, 5u, 26u, 1u, 16u, 1u, 29u, 5u, 29u, - 5u, 29u, 5u, 29u, 22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 5u, 29u, 5u, 26u, - 5u, 29u, 5u, 29u, 22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 1u, 16u, 5u, 29u, - 5u, 29u, 0 + 5u, 26u, 5u, 21u, 5u, 21u, 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, 5u, 26u, + 5u, 21u, 5u, 26u, 5u, 21u, 5u, 26u, 1u, 29u, 5u, 29u, 5u, 29u, 5u, 29u, + 22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 5u, 29u, 1u, 16u, 5u, 26u, 5u, 29u, + 5u, 29u, 22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 1u, 16u, 5u, 29u, 5u, 29u, + 0 }; static const char _khmer_syllable_machine_key_spans[] = { 22, 17, 22, 17, 16, 17, 22, 17, - 22, 17, 16, 17, 22, 17, 16, 17, - 22, 17, 22, 17, 22, 16, 29, 25, - 25, 25, 1, 18, 25, 25, 25, 22, - 25, 25, 1, 18, 25, 25, 16, 25, - 25 + 22, 17, 17, 22, 17, 16, 17, 22, + 17, 22, 17, 22, 29, 25, 25, 25, + 1, 18, 25, 25, 25, 16, 22, 25, + 25, 1, 18, 25, 25, 16, 25, 25 }; static const short _khmer_syllable_machine_index_offsets[] = { 0, 23, 41, 64, 82, 99, 117, 140, - 158, 181, 199, 216, 234, 257, 275, 292, - 310, 333, 351, 374, 392, 415, 432, 462, - 488, 514, 540, 542, 561, 587, 613, 639, - 662, 688, 714, 716, 735, 761, 787, 804, - 830 + 158, 181, 199, 217, 240, 258, 275, 293, + 316, 334, 357, 375, 398, 428, 454, 480, + 506, 508, 527, 553, 579, 605, 622, 645, + 671, 697, 699, 718, 744, 770, 787, 813 }; static const char _khmer_syllable_machine_indicies[] = { @@ -85,142 +83,136 @@ static const char _khmer_syllable_machine_indicies[] = { 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 4, 0, 11, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 12, 0, 13, - 13, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 13, 0, - 15, 15, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, - 16, 14, 15, 15, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 16, 17, 17, 17, 17, 18, - 17, 19, 19, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 18, 17, 20, 20, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 20, 17, 21, 21, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 22, 17, 23, 23, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 24, 17, - 17, 17, 17, 18, 17, 23, 23, 17, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 24, 17, 25, - 25, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 26, - 17, 17, 17, 17, 18, 17, 25, 25, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 26, 17, - 15, 15, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 27, - 16, 17, 17, 17, 17, 18, 17, 28, - 28, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 28, 17, - 13, 13, 29, 29, 30, 30, 29, 29, - 29, 29, 2, 2, 29, 31, 29, 13, - 29, 29, 29, 29, 16, 20, 29, 29, - 29, 18, 24, 26, 22, 29, 33, 33, - 32, 32, 32, 32, 32, 32, 32, 34, - 32, 32, 32, 32, 32, 2, 3, 6, - 32, 32, 32, 4, 10, 12, 8, 32, - 35, 35, 32, 32, 32, 32, 32, 32, - 32, 36, 32, 32, 32, 32, 32, 32, - 3, 6, 32, 32, 32, 4, 10, 12, - 8, 32, 5, 5, 32, 32, 32, 32, - 32, 32, 32, 36, 32, 32, 32, 32, - 32, 32, 4, 6, 32, 32, 32, 32, - 32, 32, 8, 32, 6, 32, 7, 7, - 32, 32, 32, 32, 32, 32, 32, 36, - 32, 32, 32, 32, 32, 32, 8, 6, - 32, 37, 37, 32, 32, 32, 32, 32, - 32, 32, 36, 32, 32, 32, 32, 32, - 32, 10, 6, 32, 32, 32, 4, 32, - 32, 8, 32, 38, 38, 32, 32, 32, - 32, 32, 32, 32, 36, 32, 32, 32, - 32, 32, 32, 12, 6, 32, 32, 32, - 4, 10, 32, 8, 32, 35, 35, 32, - 32, 32, 32, 32, 32, 32, 34, 32, - 32, 32, 32, 32, 32, 3, 6, 32, - 32, 32, 4, 10, 12, 8, 32, 15, - 15, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 16, - 39, 39, 39, 39, 18, 39, 41, 41, - 40, 40, 40, 40, 40, 40, 40, 42, - 40, 40, 40, 40, 40, 40, 16, 20, - 40, 40, 40, 18, 24, 26, 22, 40, - 19, 19, 40, 40, 40, 40, 40, 40, - 40, 42, 40, 40, 40, 40, 40, 40, - 18, 20, 40, 40, 40, 40, 40, 40, - 22, 40, 20, 40, 21, 21, 40, 40, - 40, 40, 40, 40, 40, 42, 40, 40, - 40, 40, 40, 40, 22, 20, 40, 43, - 43, 40, 40, 40, 40, 40, 40, 40, - 42, 40, 40, 40, 40, 40, 40, 24, - 20, 40, 40, 40, 18, 40, 40, 22, - 40, 44, 44, 40, 40, 40, 40, 40, - 40, 40, 42, 40, 40, 40, 40, 40, - 40, 26, 20, 40, 40, 40, 18, 24, - 40, 22, 40, 28, 28, 39, 39, 39, + 0, 0, 0, 0, 0, 12, 0, 14, + 14, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 15, + 13, 14, 14, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 15, 16, 16, 16, 16, 17, 16, + 18, 18, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 17, 16, 19, 19, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 19, 16, 20, 20, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 21, 16, 22, 22, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 23, 16, 16, + 16, 16, 17, 16, 22, 22, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 23, 16, 24, 24, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 25, 16, + 16, 16, 16, 17, 16, 24, 24, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 25, 16, 14, + 14, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 26, 15, + 16, 16, 16, 16, 17, 16, 28, 28, + 27, 27, 29, 29, 27, 27, 27, 27, + 2, 2, 27, 30, 27, 28, 27, 27, + 27, 27, 15, 19, 27, 27, 27, 17, + 23, 25, 21, 27, 32, 32, 31, 31, + 31, 31, 31, 31, 31, 33, 31, 31, + 31, 31, 31, 2, 3, 6, 31, 31, + 31, 4, 10, 12, 8, 31, 34, 34, + 31, 31, 31, 31, 31, 31, 31, 35, + 31, 31, 31, 31, 31, 31, 3, 6, + 31, 31, 31, 4, 10, 12, 8, 31, + 5, 5, 31, 31, 31, 31, 31, 31, + 31, 35, 31, 31, 31, 31, 31, 31, + 4, 6, 31, 31, 31, 31, 31, 31, + 8, 31, 6, 31, 7, 7, 31, 31, + 31, 31, 31, 31, 31, 35, 31, 31, + 31, 31, 31, 31, 8, 6, 31, 36, + 36, 31, 31, 31, 31, 31, 31, 31, + 35, 31, 31, 31, 31, 31, 31, 10, + 6, 31, 31, 31, 4, 31, 31, 8, + 31, 37, 37, 31, 31, 31, 31, 31, + 31, 31, 35, 31, 31, 31, 31, 31, + 31, 12, 6, 31, 31, 31, 4, 10, + 31, 8, 31, 34, 34, 31, 31, 31, + 31, 31, 31, 31, 33, 31, 31, 31, + 31, 31, 31, 3, 6, 31, 31, 31, + 4, 10, 12, 8, 31, 28, 28, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 28, 31, 14, 14, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 15, 38, + 38, 38, 38, 17, 38, 40, 40, 39, + 39, 39, 39, 39, 39, 39, 41, 39, + 39, 39, 39, 39, 39, 15, 19, 39, + 39, 39, 17, 23, 25, 21, 39, 18, + 18, 39, 39, 39, 39, 39, 39, 39, + 41, 39, 39, 39, 39, 39, 39, 17, + 19, 39, 39, 39, 39, 39, 39, 21, + 39, 19, 39, 20, 20, 39, 39, 39, + 39, 39, 39, 39, 41, 39, 39, 39, + 39, 39, 39, 21, 19, 39, 42, 42, + 39, 39, 39, 39, 39, 39, 39, 41, + 39, 39, 39, 39, 39, 39, 23, 19, + 39, 39, 39, 17, 39, 39, 21, 39, + 43, 43, 39, 39, 39, 39, 39, 39, + 39, 41, 39, 39, 39, 39, 39, 39, + 25, 19, 39, 39, 39, 17, 23, 39, + 21, 39, 44, 44, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 28, 39, 45, 45, 40, 40, - 40, 40, 40, 40, 40, 46, 40, 40, - 40, 40, 40, 27, 16, 20, 40, 40, - 40, 18, 24, 26, 22, 40, 41, 41, - 40, 40, 40, 40, 40, 40, 40, 46, - 40, 40, 40, 40, 40, 40, 16, 20, - 40, 40, 40, 18, 24, 26, 22, 40, - 0 + 39, 44, 39, 45, 45, 39, 39, 39, + 39, 39, 39, 39, 30, 39, 39, 39, + 39, 39, 26, 15, 19, 39, 39, 39, + 17, 23, 25, 21, 39, 40, 40, 39, + 39, 39, 39, 39, 39, 39, 30, 39, + 39, 39, 39, 39, 39, 15, 19, 39, + 39, 39, 17, 23, 25, 21, 39, 0 }; static const char _khmer_syllable_machine_trans_targs[] = { - 22, 1, 30, 24, 25, 3, 26, 5, - 27, 7, 28, 9, 29, 23, 22, 11, - 32, 22, 33, 13, 34, 15, 35, 17, - 36, 19, 37, 40, 39, 22, 31, 38, - 22, 0, 10, 2, 4, 6, 8, 22, - 22, 12, 14, 16, 18, 20, 21 + 20, 1, 28, 22, 23, 3, 24, 5, + 25, 7, 26, 9, 27, 20, 10, 31, + 20, 32, 12, 33, 14, 34, 16, 35, + 18, 36, 39, 20, 21, 30, 37, 20, + 0, 29, 2, 4, 6, 8, 20, 20, + 11, 13, 15, 17, 38, 19 }; static const char _khmer_syllable_machine_trans_actions[] = { 1, 0, 2, 2, 2, 0, 0, 0, - 2, 0, 2, 0, 2, 2, 3, 0, - 4, 5, 2, 0, 0, 0, 2, 0, - 2, 0, 2, 4, 4, 8, 9, 0, - 10, 0, 0, 0, 0, 0, 0, 11, - 12, 0, 0, 0, 0, 0, 0 + 2, 0, 2, 0, 2, 3, 0, 4, + 5, 2, 0, 0, 0, 2, 0, 2, + 0, 2, 4, 8, 2, 9, 0, 10, + 0, 0, 0, 0, 0, 0, 11, 12, + 0, 0, 0, 0, 4, 0 }; static const char _khmer_syllable_machine_to_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 6, 0, + 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0 + 0, 0, 0, 0, 0, 0, 0, 0 }; static const char _khmer_syllable_machine_from_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 7, 0, + 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0 + 0, 0, 0, 0, 0, 0, 0, 0 }; static const unsigned char _khmer_syllable_machine_eof_trans[] = { 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 15, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 0, 33, - 33, 33, 33, 33, 33, 33, 33, 40, - 41, 41, 41, 41, 41, 41, 40, 41, - 41 + 1, 1, 14, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 0, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 39, 40, + 40, 40, 40, 40, 40, 40, 40, 40 }; -static const int khmer_syllable_machine_start = 22; -static const int khmer_syllable_machine_first_final = 22; +static const int khmer_syllable_machine_start = 20; +static const int khmer_syllable_machine_first_final = 20; static const int khmer_syllable_machine_error = -1; -static const int khmer_syllable_machine_en_main = 22; +static const int khmer_syllable_machine_en_main = 20; #line 36 "hb-ot-shape-complex-khmer-machine.rl" @@ -234,19 +226,19 @@ static const int khmer_syllable_machine_en_main = 22; HB_STMT_START { \ if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \ for (unsigned int i = ts; i < te; i++) \ - info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + info[i].syllable() = (syllable_serial << 4) | khmer_##syllable_type; \ syllable_serial++; \ if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ } HB_STMT_END static void -find_syllables (hb_buffer_t *buffer) +find_syllables_khmer (hb_buffer_t *buffer) { unsigned int p, pe, eof, ts, te, act HB_UNUSED; int cs; hb_glyph_info_t *info = buffer->info; -#line 250 "hb-ot-shape-complex-khmer-machine.hh" +#line 242 "hb-ot-shape-complex-khmer-machine.hh" { cs = khmer_syllable_machine_start; ts = 0; @@ -262,7 +254,7 @@ find_syllables (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 266 "hb-ot-shape-complex-khmer-machine.hh" +#line 258 "hb-ot-shape-complex-khmer-machine.hh" { int _slen; int _trans; @@ -276,7 +268,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 280 "hb-ot-shape-complex-khmer-machine.hh" +#line 272 "hb-ot-shape-complex-khmer-machine.hh" } _keys = _khmer_syllable_machine_trans_keys + (cs<<1); @@ -346,7 +338,7 @@ _eof_trans: #line 76 "hb-ot-shape-complex-khmer-machine.rl" {act = 3;} break; -#line 350 "hb-ot-shape-complex-khmer-machine.hh" +#line 342 "hb-ot-shape-complex-khmer-machine.hh" } _again: @@ -355,7 +347,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 359 "hb-ot-shape-complex-khmer-machine.hh" +#line 351 "hb-ot-shape-complex-khmer-machine.hh" } if ( ++p != pe ) @@ -375,4 +367,6 @@ _again: } +#undef found_syllable + #endif /* HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer.cc index f5437b39713..6be5d3731bf 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex-khmer.hh" #include "hb-ot-layout.hh" @@ -52,50 +56,35 @@ khmer_features[] = {HB_TAG('a','b','v','s'), F_GLOBAL_MANUAL_JOINERS}, {HB_TAG('b','l','w','s'), F_GLOBAL_MANUAL_JOINERS}, {HB_TAG('p','s','t','s'), F_GLOBAL_MANUAL_JOINERS}, - /* - * Positioning features. - * We don't care about the types. - */ - {HB_TAG('d','i','s','t'), F_GLOBAL}, - {HB_TAG('a','b','v','m'), F_GLOBAL}, - {HB_TAG('b','l','w','m'), F_GLOBAL}, }; /* * Must be in the same order as the khmer_features array. */ enum { - PREF, - BLWF, - ABVF, - PSTF, - CFAR, - - _PRES, - _ABVS, - _BLWS, - _PSTS, + KHMER_PREF, + KHMER_BLWF, + KHMER_ABVF, + KHMER_PSTF, + KHMER_CFAR, - _DIST, - _ABVM, - _BLWM, + _KHMER_PRES, + _KHMER_ABVS, + _KHMER_BLWS, + _KHMER_PSTS, KHMER_NUM_FEATURES, - KHMER_BASIC_FEATURES = _PRES, /* Don't forget to update this! */ + KHMER_BASIC_FEATURES = _KHMER_PRES, /* Don't forget to update this! */ }; static void -setup_syllables (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); -static void -reorder (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); +setup_syllables_khmer (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); static void -clear_syllables (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); +reorder_khmer (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); static void collect_features_khmer (hb_ot_shape_planner_t *plan) @@ -103,8 +92,8 @@ collect_features_khmer (hb_ot_shape_planner_t *plan) hb_ot_map_builder_t *map = &plan->map; /* Do this before any lookups have been applied. */ - map->add_gsub_pause (setup_syllables); - map->add_gsub_pause (reorder); + map->add_gsub_pause (setup_syllables_khmer); + map->add_gsub_pause (reorder_khmer); /* Testing suggests that Uniscribe does NOT pause between basic * features. Test with KhmerUI.ttf and the following three @@ -123,7 +112,7 @@ collect_features_khmer (hb_ot_shape_planner_t *plan) for (; i < KHMER_BASIC_FEATURES; i++) map->add_feature (khmer_features[i]); - map->add_gsub_pause (clear_syllables); + map->add_gsub_pause (_hb_clear_syllables); for (; i < KHMER_NUM_FEATURES; i++) map->add_feature (khmer_features[i]); @@ -149,32 +138,6 @@ override_features_khmer (hb_ot_shape_planner_t *plan) } -struct would_substitute_feature_t -{ - void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_) - { - zero_context = zero_context_; - map->get_stage_lookups (0/*GSUB*/, - map->get_feature_stage (0/*GSUB*/, feature_tag), - &lookups, &count); - } - - bool would_substitute (const hb_codepoint_t *glyphs, - unsigned int glyphs_count, - hb_face_t *face) const - { - for (unsigned int i = 0; i < count; i++) - if (hb_ot_layout_lookup_would_substitute_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context)) - return true; - return false; - } - - private: - const hb_ot_map_t::lookup_map_t *lookups; - unsigned int count; - bool zero_context; -}; - struct khmer_shape_plan_t { bool get_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const @@ -198,8 +161,6 @@ struct khmer_shape_plan_t mutable hb_codepoint_t virama_glyph; - would_substitute_feature_t pref; - hb_mask_t mask_array[KHMER_NUM_FEATURES]; }; @@ -212,8 +173,6 @@ data_create_khmer (const hb_ot_shape_plan_t *plan) khmer_plan->virama_glyph = (hb_codepoint_t) -1; - khmer_plan->pref.init (&plan->map, HB_TAG('p','r','e','f'), true); - for (unsigned int i = 0; i < ARRAY_LENGTH (khmer_plan->mask_array); i++) khmer_plan->mask_array[i] = (khmer_features[i].flags & F_GLOBAL) ? 0 : plan->map.get_1_mask (khmer_features[i].tag); @@ -228,10 +187,10 @@ data_destroy_khmer (void *data) } -enum syllable_type_t { - consonant_syllable, - broken_cluster, - non_khmer_cluster, +enum khmer_syllable_type_t { + khmer_consonant_syllable, + khmer_broken_cluster, + khmer_non_khmer_cluster, }; #include "hb-ot-shape-complex-khmer-machine.hh" @@ -253,11 +212,11 @@ setup_masks_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED, } static void -setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) +setup_syllables_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) { - find_syllables (buffer); + find_syllables_khmer (buffer); foreach_syllable (buffer, start, end) buffer->unsafe_to_break (start, end); } @@ -278,7 +237,9 @@ reorder_consonant_syllable (const hb_ot_shape_plan_t *plan, /* Setup masks. */ { /* Post-base */ - hb_mask_t mask = khmer_plan->mask_array[BLWF] | khmer_plan->mask_array[ABVF] | khmer_plan->mask_array[PSTF]; + hb_mask_t mask = khmer_plan->mask_array[KHMER_BLWF] | + khmer_plan->mask_array[KHMER_ABVF] | + khmer_plan->mask_array[KHMER_PSTF]; for (unsigned int i = start + 1; i < end; i++) info[i].mask |= mask; } @@ -305,7 +266,7 @@ reorder_consonant_syllable (const hb_ot_shape_plan_t *plan, if (info[i + 1].khmer_category() == OT_Ra) { for (unsigned int j = 0; j < 2; j++) - info[i + j].mask |= khmer_plan->mask_array[PREF]; + info[i + j].mask |= khmer_plan->mask_array[KHMER_PREF]; /* Move the Coeng,Ro sequence to the start. */ buffer->merge_clusters (start, i + 2); @@ -321,9 +282,9 @@ reorder_consonant_syllable (const hb_ot_shape_plan_t *plan, * U+1784,U+17D2,U+179A,U+17D2,U+1782 * U+1784,U+17D2,U+1782,U+17D2,U+179A */ - if (khmer_plan->mask_array[CFAR]) + if (khmer_plan->mask_array[KHMER_CFAR]) for (unsigned int j = i + 2; j < end; j++) - info[j].mask |= khmer_plan->mask_array[CFAR]; + info[j].mask |= khmer_plan->mask_array[KHMER_CFAR]; num_coengs = 2; /* Done. */ } @@ -342,35 +303,39 @@ reorder_consonant_syllable (const hb_ot_shape_plan_t *plan, } static void -initial_reordering_syllable (const hb_ot_shape_plan_t *plan, - hb_face_t *face, - hb_buffer_t *buffer, - unsigned int start, unsigned int end) +reorder_syllable_khmer (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) { - syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F); + khmer_syllable_type_t syllable_type = (khmer_syllable_type_t) (buffer->info[start].syllable() & 0x0F); switch (syllable_type) { - case broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */ - case consonant_syllable: + case khmer_broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */ + case khmer_consonant_syllable: reorder_consonant_syllable (plan, face, buffer, start, end); break; - case non_khmer_cluster: + case khmer_non_khmer_cluster: break; } } static inline void -insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font, - hb_buffer_t *buffer) +insert_dotted_circles_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer) { - /* Note: This loop is extra overhead, but should not be measurable. */ + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + + /* Note: This loop is extra overhead, but should not be measurable. + * TODO Use a buffer scratch flag to remove the loop. */ bool has_broken_syllables = false; unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 0; i < count; i++) - if ((info[i].syllable() & 0x0F) == broken_cluster) + if ((info[i].syllable() & 0x0F) == khmer_broken_cluster) { has_broken_syllables = true; break; @@ -395,8 +360,8 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, while (buffer->idx < buffer->len && buffer->successful) { unsigned int syllable = buffer->cur().syllable(); - syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F); - if (unlikely (last_syllable != syllable && syllable_type == broken_cluster)) + khmer_syllable_type_t syllable_type = (khmer_syllable_type_t) (syllable & 0x0F); + if (unlikely (last_syllable != syllable && syllable_type == khmer_broken_cluster)) { last_syllable = syllable; @@ -404,7 +369,6 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, ginfo.cluster = buffer->cur().cluster; ginfo.mask = buffer->cur().mask; ginfo.syllable() = buffer->cur().syllable(); - /* TODO Set glyph_props? */ /* Insert dottedcircle after possible Repha. */ while (buffer->idx < buffer->len && buffer->successful && @@ -421,29 +385,18 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, } static void -reorder (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer) +reorder_khmer (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) { - insert_dotted_circles (plan, font, buffer); + insert_dotted_circles_khmer (plan, font, buffer); foreach_syllable (buffer, start, end) - initial_reordering_syllable (plan, font->face, buffer, start, end); + reorder_syllable_khmer (plan, font->face, buffer, start, end); HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_category); } -static void -clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) -{ - hb_glyph_info_t *info = buffer->info; - unsigned int count = buffer->len; - for (unsigned int i = 0; i < count; i++) - info[i].syllable() = 0; -} - static bool decompose_khmer (const hb_ot_shape_normalize_context_t *c, @@ -499,3 +452,6 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_khmer = HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, false, /* fallback_position */ }; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer.hh index 4b94cae1f13..b809d8e4f66 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer.hh @@ -43,11 +43,10 @@ enum khmer_category_t OT_Robatic = 20, OT_Xgroup = 21, OT_Ygroup = 22, - - OT_VAbv = 26, - OT_VBlw = 27, - OT_VPre = 28, - OT_VPst = 29, + //OT_VAbv = 26, + //OT_VBlw = 27, + //OT_VPre = 28, + //OT_VPst = 29, }; static inline void @@ -100,12 +99,12 @@ set_khmer_properties (hb_glyph_info_t &info) if (cat == (khmer_category_t) OT_M) switch ((int) pos) { - case POS_PRE_C: cat = OT_VPre; break; - case POS_BELOW_C: cat = OT_VBlw; break; - case POS_ABOVE_C: cat = OT_VAbv; break; - case POS_POST_C: cat = OT_VPst; break; + case POS_PRE_C: cat = (khmer_category_t) OT_VPre; break; + case POS_BELOW_C: cat = (khmer_category_t) OT_VBlw; break; + case POS_ABOVE_C: cat = (khmer_category_t) OT_VAbv; break; + case POS_POST_C: cat = (khmer_category_t) OT_VPst; break; default: assert (0); - }; + } info.khmer_category() = cat; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar-machine.hh index 7364d6b400d..c011c9f6a38 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar-machine.hh @@ -36,29 +36,31 @@ static const unsigned char _myanmar_syllable_machine_trans_keys[] = { 1u, 32u, 3u, 30u, 5u, 29u, 5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 1u, 16u, 3u, 29u, 3u, 29u, 3u, 29u, - 3u, 29u, 3u, 29u, 3u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 5u, 29u, - 5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 3u, 29u, 3u, 29u, 3u, 29u, - 3u, 29u, 3u, 30u, 3u, 29u, 1u, 32u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, - 3u, 29u, 3u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 1u, 32u, 8u, 8u, - 0 + 3u, 29u, 3u, 29u, 3u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, + 5u, 29u, 5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 3u, 29u, 3u, 29u, + 3u, 29u, 3u, 29u, 1u, 16u, 3u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, + 3u, 29u, 3u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 30u, + 3u, 29u, 1u, 32u, 1u, 32u, 8u, 8u, 0 }; static const char _myanmar_syllable_machine_key_spans[] = { 32, 28, 25, 4, 25, 23, 21, 21, 27, 27, 27, 27, 16, 27, 27, 27, - 27, 27, 28, 27, 27, 27, 27, 25, - 4, 25, 23, 21, 21, 27, 27, 27, - 27, 28, 27, 32, 27, 27, 27, 27, - 27, 28, 27, 27, 27, 27, 32, 1 + 27, 27, 28, 27, 27, 27, 27, 27, + 25, 4, 25, 23, 21, 21, 27, 27, + 27, 27, 16, 28, 27, 27, 27, 27, + 27, 28, 27, 27, 27, 27, 27, 28, + 27, 32, 32, 1 }; static const short _myanmar_syllable_machine_index_offsets[] = { 0, 33, 62, 88, 93, 119, 143, 165, 187, 215, 243, 271, 299, 316, 344, 372, 400, 428, 456, 485, 513, 541, 569, 597, - 623, 628, 654, 678, 700, 722, 750, 778, - 806, 834, 863, 891, 924, 952, 980, 1008, - 1036, 1064, 1093, 1121, 1149, 1177, 1205, 1238 + 625, 651, 656, 682, 706, 728, 750, 778, + 806, 834, 862, 879, 908, 936, 964, 992, + 1020, 1048, 1077, 1105, 1133, 1161, 1189, 1217, + 1246, 1274, 1307, 1340 }; static const char _myanmar_syllable_machine_indicies[] = { @@ -124,120 +126,134 @@ static const char _myanmar_syllable_machine_indicies[] = { 21, 21, 21, 21, 21, 21, 32, 33, 34, 35, 36, 43, 21, 22, 21, 24, 24, 21, 25, 21, 26, 21, 21, 21, - 21, 21, 21, 21, 43, 21, 21, 28, + 21, 21, 21, 21, 21, 21, 21, 28, 21, 30, 21, 32, 33, 34, 35, 36, 21, 22, 21, 24, 24, 21, 25, 21, 26, 21, 21, 21, 21, 21, 21, 21, 43, 21, 21, 28, 21, 21, 21, 32, 33, 34, 35, 36, 21, 22, 21, 24, 24, 21, 25, 21, 26, 21, 21, 21, - 21, 21, 21, 21, 43, 21, 21, 28, + 21, 21, 21, 21, 44, 21, 21, 28, 29, 30, 21, 32, 33, 34, 35, 36, - 21, 22, 23, 24, 24, 21, 25, 21, + 21, 22, 21, 24, 24, 21, 25, 21, 26, 21, 21, 21, 21, 21, 21, 21, - 27, 21, 21, 28, 29, 30, 31, 32, - 33, 34, 35, 36, 21, 3, 3, 44, - 5, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 45, 44, 44, 44, 44, 44, - 44, 14, 44, 44, 44, 18, 44, 3, - 3, 44, 5, 44, 3, 3, 44, 5, - 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, - 14, 44, 44, 44, 18, 44, 46, 44, - 3, 3, 44, 5, 44, 14, 44, 44, - 44, 44, 44, 44, 44, 47, 44, 44, - 44, 44, 44, 44, 14, 44, 3, 3, - 44, 5, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 47, 44, 44, 44, 44, - 44, 44, 14, 44, 3, 3, 44, 5, - 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, - 14, 44, 2, 44, 3, 3, 44, 5, - 44, 6, 44, 44, 44, 44, 44, 44, - 44, 48, 44, 44, 48, 44, 44, 44, - 14, 49, 44, 44, 18, 44, 2, 44, - 3, 3, 44, 5, 44, 6, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 14, 44, 44, 44, - 18, 44, 2, 44, 3, 3, 44, 5, - 44, 6, 44, 44, 44, 44, 44, 44, - 44, 48, 44, 44, 44, 44, 44, 44, - 14, 49, 44, 44, 18, 44, 2, 44, - 3, 3, 44, 5, 44, 6, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 14, 49, 44, 44, - 18, 44, 22, 23, 24, 24, 21, 25, - 21, 26, 21, 21, 21, 21, 21, 21, - 21, 50, 21, 21, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 21, 22, - 51, 24, 24, 21, 25, 21, 26, 21, - 21, 21, 21, 21, 21, 21, 27, 21, - 21, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 21, 1, 1, 2, 3, 3, - 3, 44, 5, 44, 6, 1, 44, 44, - 44, 44, 1, 44, 8, 44, 44, 10, + 21, 21, 21, 28, 29, 30, 21, 32, + 33, 34, 35, 36, 21, 22, 23, 24, + 24, 21, 25, 21, 26, 21, 21, 21, + 21, 21, 21, 21, 27, 21, 21, 28, + 29, 30, 31, 32, 33, 34, 35, 36, + 21, 46, 46, 45, 5, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 47, 45, + 45, 45, 45, 45, 45, 14, 45, 45, + 45, 18, 45, 46, 46, 45, 5, 45, + 46, 46, 45, 5, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 14, 45, 45, 45, + 18, 45, 48, 45, 46, 46, 45, 5, + 45, 14, 45, 45, 45, 45, 45, 45, + 45, 49, 45, 45, 45, 45, 45, 45, + 14, 45, 46, 46, 45, 5, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 49, + 45, 45, 45, 45, 45, 45, 14, 45, + 46, 46, 45, 5, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 14, 45, 2, 45, + 46, 46, 45, 5, 45, 6, 45, 45, + 45, 45, 45, 45, 45, 50, 45, 45, + 50, 45, 45, 45, 14, 51, 45, 45, + 18, 45, 2, 45, 46, 46, 45, 5, + 45, 6, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, + 14, 45, 45, 45, 18, 45, 2, 45, + 46, 46, 45, 5, 45, 6, 45, 45, + 45, 45, 45, 45, 45, 50, 45, 45, + 45, 45, 45, 45, 14, 51, 45, 45, + 18, 45, 2, 45, 46, 46, 45, 5, + 45, 6, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, + 14, 51, 45, 45, 18, 45, 52, 52, + 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 52, 45, 2, + 3, 46, 46, 45, 5, 45, 6, 45, + 45, 45, 45, 45, 45, 45, 8, 45, + 45, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 45, 2, 45, 46, 46, + 45, 5, 45, 6, 45, 45, 45, 45, + 45, 45, 45, 8, 45, 45, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 45, + 2, 45, 46, 46, 45, 5, 45, 6, + 45, 45, 45, 45, 45, 45, 45, 53, + 45, 45, 45, 45, 45, 45, 14, 15, + 16, 17, 18, 45, 2, 45, 46, 46, + 45, 5, 45, 6, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 14, 15, 16, 17, 18, 45, + 2, 45, 46, 46, 45, 5, 45, 6, + 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 14, 15, + 16, 45, 18, 45, 2, 45, 46, 46, + 45, 5, 45, 6, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 14, 45, 16, 45, 18, 45, + 2, 45, 46, 46, 45, 5, 45, 6, + 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 14, 15, + 16, 17, 18, 53, 45, 2, 45, 46, + 46, 45, 5, 45, 6, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 10, + 45, 12, 45, 14, 15, 16, 17, 18, + 45, 2, 45, 46, 46, 45, 5, 45, + 6, 45, 45, 45, 45, 45, 45, 45, + 53, 45, 45, 10, 45, 45, 45, 14, + 15, 16, 17, 18, 45, 2, 45, 46, + 46, 45, 5, 45, 6, 45, 45, 45, + 45, 45, 45, 45, 54, 45, 45, 10, + 11, 12, 45, 14, 15, 16, 17, 18, + 45, 2, 45, 46, 46, 45, 5, 45, + 6, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 10, 11, 12, 45, 14, + 15, 16, 17, 18, 45, 2, 3, 46, + 46, 45, 5, 45, 6, 45, 45, 45, + 45, 45, 45, 45, 8, 45, 45, 10, 11, 12, 13, 14, 15, 16, 17, 18, - 19, 44, 1, 44, 2, 44, 3, 3, - 44, 5, 44, 6, 44, 44, 44, 44, - 44, 44, 44, 8, 44, 44, 10, 11, - 12, 13, 14, 15, 16, 17, 18, 44, - 2, 44, 3, 3, 44, 5, 44, 6, - 44, 44, 44, 44, 44, 44, 44, 52, - 44, 44, 44, 44, 44, 44, 14, 15, - 16, 17, 18, 44, 2, 44, 3, 3, - 44, 5, 44, 6, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 14, 15, 16, 17, 18, 44, - 2, 44, 3, 3, 44, 5, 44, 6, - 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 14, 15, - 16, 44, 18, 44, 2, 44, 3, 3, - 44, 5, 44, 6, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 14, 44, 16, 44, 18, 44, - 2, 44, 3, 3, 44, 5, 44, 6, - 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 14, 15, - 16, 17, 18, 52, 44, 2, 44, 3, - 3, 44, 5, 44, 6, 44, 44, 44, - 44, 44, 44, 44, 52, 44, 44, 10, - 44, 12, 44, 14, 15, 16, 17, 18, - 44, 2, 44, 3, 3, 44, 5, 44, - 6, 44, 44, 44, 44, 44, 44, 44, - 52, 44, 44, 10, 44, 44, 44, 14, - 15, 16, 17, 18, 44, 2, 44, 3, - 3, 44, 5, 44, 6, 44, 44, 44, - 44, 44, 44, 44, 52, 44, 44, 10, - 11, 12, 44, 14, 15, 16, 17, 18, - 44, 2, 3, 3, 3, 44, 5, 44, - 6, 44, 44, 44, 44, 44, 44, 44, - 8, 44, 44, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 44, 1, 1, 53, - 53, 53, 53, 53, 53, 53, 53, 1, - 53, 53, 53, 53, 1, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 1, 53, 54, 53, - 0 + 45, 22, 23, 24, 24, 21, 25, 21, + 26, 21, 21, 21, 21, 21, 21, 21, + 55, 21, 21, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 21, 22, 56, + 24, 24, 21, 25, 21, 26, 21, 21, + 21, 21, 21, 21, 21, 27, 21, 21, + 28, 29, 30, 31, 32, 33, 34, 35, + 36, 21, 1, 1, 2, 3, 46, 46, + 45, 5, 45, 6, 1, 45, 45, 45, + 45, 1, 45, 8, 45, 45, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, + 45, 1, 45, 1, 1, 57, 57, 57, + 57, 57, 57, 57, 57, 1, 57, 57, + 57, 57, 1, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 1, 57, 58, 57, 0 }; static const char _myanmar_syllable_machine_trans_targs[] = { - 0, 1, 23, 0, 0, 24, 30, 33, - 36, 46, 37, 42, 43, 44, 26, 39, - 40, 41, 29, 45, 47, 0, 2, 12, + 0, 1, 24, 34, 0, 25, 31, 47, + 36, 50, 37, 42, 43, 44, 27, 39, + 40, 41, 30, 46, 51, 0, 2, 12, 0, 3, 9, 13, 14, 19, 20, 21, - 5, 16, 17, 18, 8, 22, 4, 6, - 7, 10, 11, 15, 0, 25, 27, 28, - 31, 32, 34, 35, 38, 0, 0 + 5, 16, 17, 18, 8, 23, 4, 6, + 7, 10, 11, 15, 22, 0, 0, 26, + 28, 29, 32, 33, 35, 38, 45, 48, + 49, 0, 0 }; static const char _myanmar_syllable_machine_trans_actions[] = { - 3, 0, 0, 4, 5, 0, 0, 0, + 3, 0, 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 0, 0, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 6, 0, 0, - 7, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 8, 0, 0, 0, - 0, 0, 0, 0, 0, 9, 10 + 0, 9, 10 }; static const char _myanmar_syllable_machine_to_state_actions[] = { @@ -246,7 +262,8 @@ static const char _myanmar_syllable_machine_to_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 }; static const char _myanmar_syllable_machine_from_state_actions[] = { @@ -255,16 +272,18 @@ static const char _myanmar_syllable_machine_from_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 }; static const short _myanmar_syllable_machine_eof_trans[] = { 0, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 45, - 45, 45, 45, 45, 45, 45, 45, 45, - 45, 22, 22, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 54, 54 + 22, 22, 22, 22, 22, 22, 22, 22, + 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 22, + 22, 46, 58, 58 }; static const int myanmar_syllable_machine_start = 0; @@ -285,19 +304,19 @@ static const int myanmar_syllable_machine_en_main = 0; HB_STMT_START { \ if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \ for (unsigned int i = ts; i < te; i++) \ - info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + info[i].syllable() = (syllable_serial << 4) | myanmar_##syllable_type; \ syllable_serial++; \ if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ } HB_STMT_END static void -find_syllables (hb_buffer_t *buffer) +find_syllables_myanmar (hb_buffer_t *buffer) { unsigned int p, pe, eof, ts, te, act HB_UNUSED; int cs; hb_glyph_info_t *info = buffer->info; -#line 301 "hb-ot-shape-complex-myanmar-machine.hh" +#line 320 "hb-ot-shape-complex-myanmar-machine.hh" { cs = myanmar_syllable_machine_start; ts = 0; @@ -313,7 +332,7 @@ find_syllables (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 317 "hb-ot-shape-complex-myanmar-machine.hh" +#line 336 "hb-ot-shape-complex-myanmar-machine.hh" { int _slen; int _trans; @@ -327,7 +346,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 331 "hb-ot-shape-complex-myanmar-machine.hh" +#line 350 "hb-ot-shape-complex-myanmar-machine.hh" } _keys = _myanmar_syllable_machine_trans_keys + (cs<<1); @@ -345,11 +364,11 @@ _eof_trans: goto _again; switch ( _myanmar_syllable_machine_trans_actions[_trans] ) { - case 7: + case 6: #line 86 "hb-ot-shape-complex-myanmar-machine.rl" {te = p+1;{ found_syllable (consonant_syllable); }} break; - case 5: + case 4: #line 87 "hb-ot-shape-complex-myanmar-machine.rl" {te = p+1;{ found_syllable (non_myanmar_cluster); }} break; @@ -357,7 +376,7 @@ _eof_trans: #line 88 "hb-ot-shape-complex-myanmar-machine.rl" {te = p+1;{ found_syllable (punctuation_cluster); }} break; - case 4: + case 8: #line 89 "hb-ot-shape-complex-myanmar-machine.rl" {te = p+1;{ found_syllable (broken_cluster); }} break; @@ -365,11 +384,11 @@ _eof_trans: #line 90 "hb-ot-shape-complex-myanmar-machine.rl" {te = p+1;{ found_syllable (non_myanmar_cluster); }} break; - case 6: + case 5: #line 86 "hb-ot-shape-complex-myanmar-machine.rl" {te = p;p--;{ found_syllable (consonant_syllable); }} break; - case 8: + case 7: #line 89 "hb-ot-shape-complex-myanmar-machine.rl" {te = p;p--;{ found_syllable (broken_cluster); }} break; @@ -377,7 +396,7 @@ _eof_trans: #line 90 "hb-ot-shape-complex-myanmar-machine.rl" {te = p;p--;{ found_syllable (non_myanmar_cluster); }} break; -#line 381 "hb-ot-shape-complex-myanmar-machine.hh" +#line 400 "hb-ot-shape-complex-myanmar-machine.hh" } _again: @@ -386,7 +405,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 390 "hb-ot-shape-complex-myanmar-machine.hh" +#line 409 "hb-ot-shape-complex-myanmar-machine.hh" } if ( ++p != pe ) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar.cc index e84c8f4adc6..03b93a63548 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex-myanmar.hh" @@ -32,7 +36,7 @@ */ static const hb_tag_t -basic_features[] = +myanmar_basic_features[] = { /* * Basic features. @@ -44,7 +48,7 @@ basic_features[] = HB_TAG('p','s','t','f'), }; static const hb_tag_t -other_features[] = +myanmar_other_features[] = { /* * Other features. @@ -55,36 +59,13 @@ other_features[] = HB_TAG('b','l','w','s'), HB_TAG('p','s','t','s'), }; -static const hb_tag_t -positioning_features[] = -{ - /* - * Positioning features. - * We don't care about the types. - */ - HB_TAG('d','i','s','t'), - /* Pre-release version of Windows 8 Myanmar font had abvm,blwm - * features. The released Windows 8 version of the font (as well - * as the released spec) used 'mark' instead. The Windows 8 - * shaper however didn't apply 'mark' but did apply 'mkmk'. - * Perhaps it applied abvm/blwm. This was fixed in a Windows 8 - * update, so now it applies mark/mkmk. We are guessing that - * it still applies abvm/blwm too. - */ - HB_TAG('a','b','v','m'), - HB_TAG('b','l','w','m'), -}; static void -setup_syllables (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); -static void -reorder (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); +setup_syllables_myanmar (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); static void -clear_syllables (const hb_ot_shape_plan_t *plan, +reorder_myanmar (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); @@ -94,7 +75,7 @@ collect_features_myanmar (hb_ot_shape_planner_t *plan) hb_ot_map_builder_t *map = &plan->map; /* Do this before any lookups have been applied. */ - map->add_gsub_pause (setup_syllables); + map->add_gsub_pause (setup_syllables_myanmar); map->enable_feature (HB_TAG('l','o','c','l')); /* The Indic specs do not require ccmp, but we apply it here since if @@ -102,21 +83,18 @@ collect_features_myanmar (hb_ot_shape_planner_t *plan) map->enable_feature (HB_TAG('c','c','m','p')); - map->add_gsub_pause (reorder); + map->add_gsub_pause (reorder_myanmar); - for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++) + for (unsigned int i = 0; i < ARRAY_LENGTH (myanmar_basic_features); i++) { - map->enable_feature (basic_features[i], F_MANUAL_ZWJ); + map->enable_feature (myanmar_basic_features[i], F_MANUAL_ZWJ); map->add_gsub_pause (nullptr); } - map->add_gsub_pause (clear_syllables); + map->add_gsub_pause (_hb_clear_syllables); - for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++) - map->enable_feature (other_features[i], F_MANUAL_ZWJ); - - for (unsigned int i = 0; i < ARRAY_LENGTH (positioning_features); i++) - map->enable_feature (positioning_features[i]); + for (unsigned int i = 0; i < ARRAY_LENGTH (myanmar_other_features); i++) + map->enable_feature (myanmar_other_features[i], F_MANUAL_ZWJ); } static void @@ -126,11 +104,11 @@ override_features_myanmar (hb_ot_shape_planner_t *plan) } -enum syllable_type_t { - consonant_syllable, - punctuation_cluster, - broken_cluster, - non_myanmar_cluster, +enum myanmar_syllable_type_t { + myanmar_consonant_syllable, + myanmar_punctuation_cluster, + myanmar_broken_cluster, + myanmar_non_myanmar_cluster, }; #include "hb-ot-shape-complex-myanmar-machine.hh" @@ -138,8 +116,8 @@ enum syllable_type_t { static void setup_masks_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_buffer_t *buffer, - hb_font_t *font HB_UNUSED) + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) { HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_category); HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_position); @@ -154,11 +132,11 @@ setup_masks_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, } static void -setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) +setup_syllables_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) { - find_syllables (buffer); + find_syllables_myanmar (buffer); foreach_syllable (buffer, start, end) buffer->unsafe_to_break (start, end); } @@ -274,36 +252,40 @@ initial_reordering_consonant_syllable (hb_buffer_t *buffer, } static void -initial_reordering_syllable (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_face_t *face HB_UNUSED, - hb_buffer_t *buffer, - unsigned int start, unsigned int end) +reorder_syllable_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_face_t *face HB_UNUSED, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) { - syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F); + myanmar_syllable_type_t syllable_type = (myanmar_syllable_type_t) (buffer->info[start].syllable() & 0x0F); switch (syllable_type) { - case broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */ - case consonant_syllable: + case myanmar_broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */ + case myanmar_consonant_syllable: initial_reordering_consonant_syllable (buffer, start, end); break; - case punctuation_cluster: - case non_myanmar_cluster: + case myanmar_punctuation_cluster: + case myanmar_non_myanmar_cluster: break; } } static inline void -insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font, - hb_buffer_t *buffer) +insert_dotted_circles_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer) { - /* Note: This loop is extra overhead, but should not be measurable. */ + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + + /* Note: This loop is extra overhead, but should not be measurable. + * TODO Use a buffer scratch flag to remove the loop. */ bool has_broken_syllables = false; unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 0; i < count; i++) - if ((info[i].syllable() & 0x0F) == broken_cluster) + if ((info[i].syllable() & 0x0F) == myanmar_broken_cluster) { has_broken_syllables = true; break; @@ -328,8 +310,8 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, while (buffer->idx < buffer->len && buffer->successful) { unsigned int syllable = buffer->cur().syllable(); - syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F); - if (unlikely (last_syllable != syllable && syllable_type == broken_cluster)) + myanmar_syllable_type_t syllable_type = (myanmar_syllable_type_t) (syllable & 0x0F); + if (unlikely (last_syllable != syllable && syllable_type == myanmar_broken_cluster)) { last_syllable = syllable; @@ -347,30 +329,19 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, } static void -reorder (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer) +reorder_myanmar (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) { - insert_dotted_circles (plan, font, buffer); + insert_dotted_circles_myanmar (plan, font, buffer); foreach_syllable (buffer, start, end) - initial_reordering_syllable (plan, font->face, buffer, start, end); + reorder_syllable_myanmar (plan, font->face, buffer, start, end); HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_category); HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_position); } -static void -clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) -{ - hb_glyph_info_t *info = buffer->info; - unsigned int count = buffer->len; - for (unsigned int i = 0; i < count; i++) - info[i].syllable() = 0; -} - const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar = { @@ -411,3 +382,6 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar_zawgyi = HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, false, /* fallback_position */ }; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar.hh index 86717a95004..4adbc5ae3d3 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar.hh @@ -49,10 +49,10 @@ enum myanmar_category_t { OT_MW = 23, /* Various consonant medial types */ OT_MY = 24, /* Various consonant medial types */ OT_PT = 25, /* Pwo and other tones */ - OT_VAbv = 26, - OT_VBlw = 27, - OT_VPre = 28, - OT_VPst = 29, + //OT_VAbv = 26, + //OT_VBlw = 27, + //OT_VPre = 28, + //OT_VPst = 29, OT_VS = 30, /* Variation selectors */ OT_P = 31, /* Punctuation */ OT_D = 32, /* Digits except zero */ @@ -146,7 +146,7 @@ set_myanmar_properties (hb_glyph_info_t &info) break; case 0xAA74u: case 0xAA75u: case 0xAA76u: - /* https://github.com/roozbehp/unicode-data/issues/3 */ + /* https://github.com/harfbuzz/harfbuzz/issues/218 */ cat = OT_C; break; } @@ -155,11 +155,11 @@ set_myanmar_properties (hb_glyph_info_t &info) { switch ((int) pos) { - case POS_PRE_C: cat = OT_VPre; + case POS_PRE_C: cat = (myanmar_category_t) OT_VPre; pos = POS_PRE_M; break; - case POS_ABOVE_C: cat = OT_VAbv; break; - case POS_BELOW_C: cat = OT_VBlw; break; - case POS_POST_C: cat = OT_VPst; break; + case POS_ABOVE_C: cat = (myanmar_category_t) OT_VAbv; break; + case POS_BELOW_C: cat = (myanmar_category_t) OT_VBlw; break; + case POS_POST_C: cat = (myanmar_category_t) OT_VPst; break; } } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-thai.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-thai.cc index b13697b9e18..e18a06baa12 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-thai.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-thai.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex.hh" @@ -218,6 +222,10 @@ do_thai_pua_shaping (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_buffer_t *buffer, hb_font_t *font) { +#ifdef HB_NO_OT_SHAPE_COMPLEX_THAI_FALLBACK + return; +#endif + thai_above_state_t above_state = thai_above_start_state[NOT_CONSONANT]; thai_below_state_t below_state = thai_below_start_state[NOT_CONSONANT]; unsigned int base = 0; @@ -381,3 +389,6 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_thai = HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, false,/* fallback_position */ }; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use-machine.hh index 3d0271b1ee4..796b611ba66 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use-machine.hh @@ -36,36 +36,42 @@ #line 38 "hb-ot-shape-complex-use-machine.hh" static const unsigned char _use_syllable_machine_trans_keys[] = { - 12u, 44u, 1u, 15u, 1u, 1u, 12u, 44u, 0u, 44u, 21u, 21u, 8u, 44u, 8u, 44u, - 1u, 15u, 1u, 1u, 8u, 44u, 8u, 44u, 8u, 39u, 8u, 26u, 8u, 26u, 8u, 26u, - 8u, 39u, 8u, 39u, 8u, 39u, 8u, 44u, 8u, 44u, 8u, 44u, 8u, 44u, 8u, 44u, - 8u, 44u, 8u, 44u, 8u, 44u, 1u, 39u, 8u, 44u, 13u, 21u, 4u, 4u, 13u, 13u, - 8u, 44u, 8u, 44u, 8u, 44u, 8u, 39u, 8u, 26u, 8u, 26u, 8u, 26u, 8u, 39u, - 8u, 39u, 8u, 39u, 8u, 44u, 8u, 44u, 8u, 44u, 8u, 44u, 8u, 44u, 8u, 44u, - 8u, 44u, 8u, 44u, 1u, 39u, 1u, 15u, 12u, 44u, 1u, 44u, 8u, 44u, 21u, 42u, - 41u, 42u, 42u, 42u, 1u, 5u, 0 + 12u, 48u, 1u, 15u, 1u, 1u, 12u, 48u, 1u, 1u, 0u, 48u, 21u, 21u, 11u, 48u, + 11u, 48u, 1u, 15u, 1u, 1u, 11u, 48u, 22u, 48u, 23u, 48u, 24u, 47u, 25u, 47u, + 26u, 47u, 45u, 46u, 46u, 46u, 24u, 48u, 24u, 48u, 24u, 48u, 1u, 1u, 24u, 48u, + 23u, 48u, 23u, 48u, 23u, 48u, 22u, 48u, 22u, 48u, 22u, 48u, 22u, 48u, 11u, 48u, + 1u, 48u, 11u, 48u, 13u, 21u, 4u, 4u, 13u, 13u, 11u, 48u, 11u, 48u, 41u, 42u, + 42u, 42u, 11u, 48u, 11u, 48u, 22u, 48u, 23u, 48u, 24u, 47u, 25u, 47u, 26u, 47u, + 45u, 46u, 46u, 46u, 24u, 48u, 24u, 48u, 24u, 48u, 24u, 48u, 23u, 48u, 23u, 48u, + 23u, 48u, 22u, 48u, 22u, 48u, 22u, 48u, 22u, 48u, 11u, 48u, 1u, 48u, 1u, 15u, + 4u, 4u, 13u, 21u, 13u, 13u, 12u, 48u, 1u, 48u, 11u, 48u, 41u, 42u, 42u, 42u, + 21u, 42u, 1u, 5u, 0 }; static const char _use_syllable_machine_key_spans[] = { - 33, 15, 1, 33, 45, 1, 37, 37, - 15, 1, 37, 37, 32, 19, 19, 19, - 32, 32, 32, 37, 37, 37, 37, 37, - 37, 37, 37, 39, 37, 9, 1, 1, - 37, 37, 37, 32, 19, 19, 19, 32, - 32, 32, 37, 37, 37, 37, 37, 37, - 37, 37, 39, 15, 33, 44, 37, 22, - 2, 1, 5 + 37, 15, 1, 37, 1, 49, 1, 38, + 38, 15, 1, 38, 27, 26, 24, 23, + 22, 2, 1, 25, 25, 25, 1, 25, + 26, 26, 26, 27, 27, 27, 27, 38, + 48, 38, 9, 1, 1, 38, 38, 2, + 1, 38, 38, 27, 26, 24, 23, 22, + 2, 1, 25, 25, 25, 25, 26, 26, + 26, 27, 27, 27, 27, 38, 48, 15, + 1, 9, 1, 37, 48, 38, 2, 1, + 22, 5 }; static const short _use_syllable_machine_index_offsets[] = { - 0, 34, 50, 52, 86, 132, 134, 172, - 210, 226, 228, 266, 304, 337, 357, 377, - 397, 430, 463, 496, 534, 572, 610, 648, - 686, 724, 762, 800, 840, 878, 888, 890, - 892, 930, 968, 1006, 1039, 1059, 1079, 1099, - 1132, 1165, 1198, 1236, 1274, 1312, 1350, 1388, - 1426, 1464, 1502, 1542, 1558, 1592, 1637, 1675, - 1698, 1701, 1703 + 0, 38, 54, 56, 94, 96, 146, 148, + 187, 226, 242, 244, 283, 311, 338, 363, + 387, 410, 413, 415, 441, 467, 493, 495, + 521, 548, 575, 602, 630, 658, 686, 714, + 753, 802, 841, 851, 853, 855, 894, 933, + 936, 938, 977, 1016, 1044, 1071, 1096, 1120, + 1143, 1146, 1148, 1174, 1200, 1226, 1252, 1279, + 1306, 1333, 1361, 1389, 1417, 1445, 1484, 1533, + 1549, 1551, 1561, 1563, 1601, 1650, 1689, 1692, + 1694, 1717 }; static const char _use_syllable_machine_indicies[] = { @@ -73,308 +79,320 @@ static const char _use_syllable_machine_indicies[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 3, 2, 2, 2, 2, 2, + 1, 0, 0, 0, 1, 0, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 4, 2, 3, 2, 6, 5, 5, 5, + 2, 2, 2, 2, 4, 2, 3, 2, + 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 6, 5, 7, 8, - 9, 7, 10, 8, 9, 9, 11, 9, - 9, 3, 12, 9, 9, 13, 7, 7, - 14, 15, 9, 9, 16, 17, 18, 19, - 20, 21, 22, 16, 23, 24, 25, 26, - 27, 28, 9, 29, 30, 31, 9, 9, - 9, 32, 33, 9, 35, 34, 37, 36, - 36, 38, 1, 36, 36, 39, 36, 36, - 36, 36, 36, 40, 41, 42, 43, 44, - 45, 46, 47, 41, 48, 40, 49, 50, - 51, 52, 36, 53, 54, 55, 36, 36, - 36, 36, 56, 36, 37, 36, 36, 38, - 1, 36, 36, 39, 36, 36, 36, 36, - 36, 57, 41, 42, 43, 44, 45, 46, - 47, 41, 48, 49, 49, 50, 51, 52, - 36, 53, 54, 55, 36, 36, 36, 36, - 56, 36, 38, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, - 59, 58, 38, 58, 37, 36, 36, 38, - 1, 36, 36, 39, 36, 36, 36, 36, - 36, 36, 41, 42, 43, 44, 45, 46, - 47, 41, 48, 49, 49, 50, 51, 52, - 36, 53, 54, 55, 36, 36, 36, 36, - 56, 36, 37, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 41, 42, 43, 44, 45, 36, 36, 36, - 36, 36, 36, 50, 51, 52, 36, 53, - 54, 55, 36, 36, 36, 36, 42, 36, - 37, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 42, - 43, 44, 45, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 53, 54, 55, - 36, 37, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 43, 44, 45, 36, 37, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 44, 45, - 36, 37, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 45, 36, 37, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 43, 44, 45, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 53, 54, 55, 36, 37, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 43, 44, - 45, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 54, 55, 36, 37, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 43, - 44, 45, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 55, 36, - 37, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 42, - 43, 44, 45, 36, 36, 36, 36, 36, - 36, 50, 51, 52, 36, 53, 54, 55, - 36, 36, 36, 36, 42, 36, 37, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 42, 43, 44, - 45, 36, 36, 36, 36, 36, 36, 36, - 51, 52, 36, 53, 54, 55, 36, 36, - 36, 36, 42, 36, 37, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 42, 43, 44, 45, 36, - 36, 36, 36, 36, 36, 36, 36, 52, - 36, 53, 54, 55, 36, 36, 36, 36, - 42, 36, 37, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 41, 42, 43, 44, 45, 36, 47, 41, - 36, 36, 36, 50, 51, 52, 36, 53, - 54, 55, 36, 36, 36, 36, 42, 36, - 37, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 41, 42, - 43, 44, 45, 36, 60, 41, 36, 36, - 36, 50, 51, 52, 36, 53, 54, 55, - 36, 36, 36, 36, 42, 36, 37, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 41, 42, 43, 44, - 45, 36, 36, 41, 36, 36, 36, 50, - 51, 52, 36, 53, 54, 55, 36, 36, - 36, 36, 42, 36, 37, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 41, 42, 43, 44, 45, 46, - 47, 41, 36, 36, 36, 50, 51, 52, - 36, 53, 54, 55, 36, 36, 36, 36, - 42, 36, 37, 36, 36, 38, 1, 36, - 36, 39, 36, 36, 36, 36, 36, 36, - 41, 42, 43, 44, 45, 46, 47, 41, - 48, 36, 49, 50, 51, 52, 36, 53, - 54, 55, 36, 36, 36, 36, 56, 36, - 38, 58, 58, 58, 58, 58, 58, 37, - 58, 58, 58, 58, 58, 58, 59, 58, - 58, 58, 58, 58, 58, 58, 42, 43, - 44, 45, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 53, 54, 55, 58, - 37, 36, 36, 38, 1, 36, 36, 39, - 36, 36, 36, 36, 36, 36, 41, 42, - 43, 44, 45, 46, 47, 41, 48, 40, - 49, 50, 51, 52, 36, 53, 54, 55, - 36, 36, 36, 36, 56, 36, 62, 61, - 61, 61, 61, 61, 61, 61, 63, 61, - 10, 64, 62, 61, 11, 65, 65, 3, - 6, 65, 65, 66, 65, 65, 65, 65, - 65, 67, 16, 17, 18, 19, 20, 21, - 22, 16, 23, 25, 25, 26, 27, 28, - 65, 29, 30, 31, 65, 65, 65, 65, - 33, 65, 11, 65, 65, 3, 6, 65, - 65, 66, 65, 65, 65, 65, 65, 65, - 16, 17, 18, 19, 20, 21, 22, 16, - 23, 25, 25, 26, 27, 28, 65, 29, - 30, 31, 65, 65, 65, 65, 33, 65, - 11, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 16, 17, - 18, 19, 20, 65, 65, 65, 65, 65, - 65, 26, 27, 28, 65, 29, 30, 31, - 65, 65, 65, 65, 17, 65, 11, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 17, 18, 19, - 20, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 29, 30, 31, 65, 11, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 18, - 19, 20, 65, 11, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 19, 20, 65, 11, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 20, 65, 11, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 18, 19, 20, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 29, 30, 31, 65, 11, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 18, 19, 20, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 30, 31, 65, 11, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 18, 19, 20, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 31, 65, 11, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 17, 18, 19, - 20, 65, 65, 65, 65, 65, 65, 26, - 27, 28, 65, 29, 30, 31, 65, 65, - 65, 65, 17, 65, 11, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 17, 18, 19, 20, 65, - 65, 65, 65, 65, 65, 65, 27, 28, - 65, 29, 30, 31, 65, 65, 65, 65, - 17, 65, 11, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 17, 18, 19, 20, 65, 65, 65, - 65, 65, 65, 65, 65, 28, 65, 29, - 30, 31, 65, 65, 65, 65, 17, 65, - 11, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 16, 17, - 18, 19, 20, 65, 22, 16, 65, 65, - 65, 26, 27, 28, 65, 29, 30, 31, - 65, 65, 65, 65, 17, 65, 11, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 16, 17, 18, 19, - 20, 65, 68, 16, 65, 65, 65, 26, - 27, 28, 65, 29, 30, 31, 65, 65, - 65, 65, 17, 65, 11, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 16, 17, 18, 19, 20, 65, - 65, 16, 65, 65, 65, 26, 27, 28, - 65, 29, 30, 31, 65, 65, 65, 65, - 17, 65, 11, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 16, 17, 18, 19, 20, 21, 22, 16, - 65, 65, 65, 26, 27, 28, 65, 29, - 30, 31, 65, 65, 65, 65, 17, 65, - 11, 65, 65, 3, 6, 65, 65, 66, - 65, 65, 65, 65, 65, 65, 16, 17, - 18, 19, 20, 21, 22, 16, 23, 65, - 25, 26, 27, 28, 65, 29, 30, 31, - 65, 65, 65, 65, 33, 65, 3, 65, - 65, 65, 65, 65, 65, 11, 65, 65, - 65, 65, 65, 65, 4, 65, 65, 65, - 65, 65, 65, 65, 17, 18, 19, 20, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 29, 30, 31, 65, 3, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 4, 69, 6, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 6, 69, - 8, 65, 65, 65, 8, 65, 65, 11, - 65, 65, 3, 6, 65, 65, 66, 65, - 65, 65, 65, 65, 65, 16, 17, 18, - 19, 20, 21, 22, 16, 23, 24, 25, - 26, 27, 28, 65, 29, 30, 31, 65, - 65, 65, 65, 33, 65, 11, 65, 65, - 3, 6, 65, 65, 66, 65, 65, 65, - 65, 65, 65, 16, 17, 18, 19, 20, - 21, 22, 16, 23, 24, 25, 26, 27, - 28, 65, 29, 30, 31, 65, 65, 65, - 65, 33, 65, 71, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 71, - 72, 70, 71, 72, 70, 72, 70, 8, - 69, 69, 69, 8, 69, 0 + 6, 5, 5, 5, 6, 5, 7, 5, + 8, 9, 10, 8, 11, 12, 10, 10, + 10, 10, 10, 3, 13, 14, 10, 15, + 8, 8, 16, 17, 10, 10, 18, 19, + 20, 21, 22, 23, 24, 18, 25, 26, + 27, 28, 29, 30, 10, 31, 32, 33, + 10, 34, 35, 36, 37, 38, 39, 40, + 13, 10, 42, 41, 44, 1, 43, 43, + 45, 43, 43, 43, 43, 43, 46, 47, + 48, 49, 50, 51, 52, 53, 47, 54, + 46, 55, 56, 57, 58, 43, 59, 60, + 61, 43, 43, 43, 43, 62, 63, 64, + 65, 1, 43, 44, 1, 43, 43, 45, + 43, 43, 43, 43, 43, 66, 47, 48, + 49, 50, 51, 52, 53, 47, 54, 55, + 55, 56, 57, 58, 43, 59, 60, 61, + 43, 43, 43, 43, 62, 63, 64, 65, + 1, 43, 44, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, + 68, 67, 44, 67, 44, 1, 43, 43, + 45, 43, 43, 43, 43, 43, 43, 47, + 48, 49, 50, 51, 52, 53, 47, 54, + 55, 55, 56, 57, 58, 43, 59, 60, + 61, 43, 43, 43, 43, 62, 63, 64, + 65, 1, 43, 47, 48, 49, 50, 51, + 43, 43, 43, 43, 43, 43, 56, 57, + 58, 43, 59, 60, 61, 43, 43, 43, + 43, 48, 63, 64, 65, 69, 43, 48, + 49, 50, 51, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 59, 60, 61, + 43, 43, 43, 43, 43, 63, 64, 65, + 69, 43, 49, 50, 51, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 63, + 64, 65, 43, 50, 51, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 63, + 64, 65, 43, 51, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 63, 64, + 65, 43, 63, 64, 43, 64, 43, 49, + 50, 51, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 59, 60, 61, 43, + 43, 43, 43, 43, 63, 64, 65, 69, + 43, 49, 50, 51, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 60, + 61, 43, 43, 43, 43, 43, 63, 64, + 65, 69, 43, 49, 50, 51, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 61, 43, 43, 43, 43, 43, + 63, 64, 65, 69, 43, 71, 70, 49, + 50, 51, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 63, 64, 65, 69, + 43, 48, 49, 50, 51, 43, 43, 43, + 43, 43, 43, 56, 57, 58, 43, 59, + 60, 61, 43, 43, 43, 43, 48, 63, + 64, 65, 69, 43, 48, 49, 50, 51, + 43, 43, 43, 43, 43, 43, 43, 57, + 58, 43, 59, 60, 61, 43, 43, 43, + 43, 48, 63, 64, 65, 69, 43, 48, + 49, 50, 51, 43, 43, 43, 43, 43, + 43, 43, 43, 58, 43, 59, 60, 61, + 43, 43, 43, 43, 48, 63, 64, 65, + 69, 43, 47, 48, 49, 50, 51, 43, + 53, 47, 43, 43, 43, 56, 57, 58, + 43, 59, 60, 61, 43, 43, 43, 43, + 48, 63, 64, 65, 69, 43, 47, 48, + 49, 50, 51, 43, 72, 47, 43, 43, + 43, 56, 57, 58, 43, 59, 60, 61, + 43, 43, 43, 43, 48, 63, 64, 65, + 69, 43, 47, 48, 49, 50, 51, 43, + 43, 47, 43, 43, 43, 56, 57, 58, + 43, 59, 60, 61, 43, 43, 43, 43, + 48, 63, 64, 65, 69, 43, 47, 48, + 49, 50, 51, 52, 53, 47, 43, 43, + 43, 56, 57, 58, 43, 59, 60, 61, + 43, 43, 43, 43, 48, 63, 64, 65, + 69, 43, 44, 1, 43, 43, 45, 43, + 43, 43, 43, 43, 43, 47, 48, 49, + 50, 51, 52, 53, 47, 54, 43, 55, + 56, 57, 58, 43, 59, 60, 61, 43, + 43, 43, 43, 62, 63, 64, 65, 1, + 43, 44, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 68, + 67, 67, 67, 67, 67, 67, 67, 48, + 49, 50, 51, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 59, 60, 61, + 67, 67, 67, 67, 67, 63, 64, 65, + 69, 67, 44, 1, 43, 43, 45, 43, + 43, 43, 43, 43, 43, 47, 48, 49, + 50, 51, 52, 53, 47, 54, 46, 55, + 56, 57, 58, 43, 59, 60, 61, 43, + 43, 43, 43, 62, 63, 64, 65, 1, + 43, 74, 73, 73, 73, 73, 73, 73, + 73, 75, 73, 11, 76, 74, 73, 44, + 1, 43, 43, 45, 43, 43, 43, 43, + 43, 77, 47, 48, 49, 50, 51, 52, + 53, 47, 54, 46, 55, 56, 57, 58, + 43, 59, 60, 61, 43, 78, 79, 43, + 62, 63, 64, 65, 1, 43, 44, 1, + 43, 43, 45, 43, 43, 43, 43, 43, + 43, 47, 48, 49, 50, 51, 52, 53, + 47, 54, 46, 55, 56, 57, 58, 43, + 59, 60, 61, 43, 78, 79, 43, 62, + 63, 64, 65, 1, 43, 78, 79, 80, + 79, 80, 3, 6, 81, 81, 82, 81, + 81, 81, 81, 81, 83, 18, 19, 20, + 21, 22, 23, 24, 18, 25, 27, 27, + 28, 29, 30, 81, 31, 32, 33, 81, + 81, 81, 81, 37, 38, 39, 40, 6, + 81, 3, 6, 81, 81, 82, 81, 81, + 81, 81, 81, 81, 18, 19, 20, 21, + 22, 23, 24, 18, 25, 27, 27, 28, + 29, 30, 81, 31, 32, 33, 81, 81, + 81, 81, 37, 38, 39, 40, 6, 81, + 18, 19, 20, 21, 22, 81, 81, 81, + 81, 81, 81, 28, 29, 30, 81, 31, + 32, 33, 81, 81, 81, 81, 19, 38, + 39, 40, 84, 81, 19, 20, 21, 22, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 31, 32, 33, 81, 81, 81, + 81, 81, 38, 39, 40, 84, 81, 20, + 21, 22, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 38, 39, 40, 81, + 21, 22, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 38, 39, 40, 81, + 22, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 38, 39, 40, 81, 38, + 39, 81, 39, 81, 20, 21, 22, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 31, 32, 33, 81, 81, 81, 81, + 81, 38, 39, 40, 84, 81, 20, 21, + 22, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 32, 33, 81, 81, + 81, 81, 81, 38, 39, 40, 84, 81, + 20, 21, 22, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 33, + 81, 81, 81, 81, 81, 38, 39, 40, + 84, 81, 20, 21, 22, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 38, + 39, 40, 84, 81, 19, 20, 21, 22, + 81, 81, 81, 81, 81, 81, 28, 29, + 30, 81, 31, 32, 33, 81, 81, 81, + 81, 19, 38, 39, 40, 84, 81, 19, + 20, 21, 22, 81, 81, 81, 81, 81, + 81, 81, 29, 30, 81, 31, 32, 33, + 81, 81, 81, 81, 19, 38, 39, 40, + 84, 81, 19, 20, 21, 22, 81, 81, + 81, 81, 81, 81, 81, 81, 30, 81, + 31, 32, 33, 81, 81, 81, 81, 19, + 38, 39, 40, 84, 81, 18, 19, 20, + 21, 22, 81, 24, 18, 81, 81, 81, + 28, 29, 30, 81, 31, 32, 33, 81, + 81, 81, 81, 19, 38, 39, 40, 84, + 81, 18, 19, 20, 21, 22, 81, 85, + 18, 81, 81, 81, 28, 29, 30, 81, + 31, 32, 33, 81, 81, 81, 81, 19, + 38, 39, 40, 84, 81, 18, 19, 20, + 21, 22, 81, 81, 18, 81, 81, 81, + 28, 29, 30, 81, 31, 32, 33, 81, + 81, 81, 81, 19, 38, 39, 40, 84, + 81, 18, 19, 20, 21, 22, 23, 24, + 18, 81, 81, 81, 28, 29, 30, 81, + 31, 32, 33, 81, 81, 81, 81, 19, + 38, 39, 40, 84, 81, 3, 6, 81, + 81, 82, 81, 81, 81, 81, 81, 81, + 18, 19, 20, 21, 22, 23, 24, 18, + 25, 81, 27, 28, 29, 30, 81, 31, + 32, 33, 81, 81, 81, 81, 37, 38, + 39, 40, 6, 81, 3, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 4, 81, 81, 81, 81, 81, + 81, 81, 19, 20, 21, 22, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 31, 32, 33, 81, 81, 81, 81, 81, + 38, 39, 40, 84, 81, 3, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 4, 86, 87, 81, 14, + 81, 81, 81, 81, 81, 81, 81, 88, + 81, 14, 81, 6, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 6, 86, 86, 86, 6, + 86, 9, 81, 81, 81, 9, 81, 81, + 81, 81, 81, 3, 6, 14, 81, 82, + 81, 81, 81, 81, 81, 81, 18, 19, + 20, 21, 22, 23, 24, 18, 25, 26, + 27, 28, 29, 30, 81, 31, 32, 33, + 81, 34, 35, 81, 37, 38, 39, 40, + 6, 81, 3, 6, 81, 81, 82, 81, + 81, 81, 81, 81, 81, 18, 19, 20, + 21, 22, 23, 24, 18, 25, 26, 27, + 28, 29, 30, 81, 31, 32, 33, 81, + 81, 81, 81, 37, 38, 39, 40, 6, + 81, 34, 35, 81, 35, 81, 78, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 78, 79, 80, 9, 86, 86, + 86, 9, 86, 0 }; static const char _use_syllable_machine_trans_targs[] = { - 4, 8, 4, 32, 2, 4, 1, 5, - 6, 4, 29, 4, 51, 52, 53, 55, - 34, 35, 36, 37, 38, 45, 46, 48, - 54, 49, 42, 43, 44, 39, 40, 41, - 58, 50, 4, 4, 4, 4, 7, 0, - 28, 11, 12, 13, 14, 15, 22, 23, - 25, 26, 19, 20, 21, 16, 17, 18, - 27, 10, 4, 9, 24, 4, 30, 31, - 4, 4, 3, 33, 47, 4, 4, 56, - 57 + 5, 9, 5, 41, 2, 5, 1, 53, + 6, 7, 5, 34, 37, 63, 64, 67, + 68, 72, 43, 44, 45, 46, 47, 57, + 58, 60, 69, 61, 54, 55, 56, 50, + 51, 52, 70, 71, 73, 62, 48, 49, + 5, 5, 5, 5, 8, 0, 33, 12, + 13, 14, 15, 16, 27, 28, 30, 31, + 24, 25, 26, 19, 20, 21, 32, 17, + 18, 5, 11, 5, 10, 22, 5, 23, + 29, 5, 35, 36, 5, 38, 39, 40, + 5, 5, 3, 42, 4, 59, 5, 65, + 66 }; static const char _use_syllable_machine_trans_actions[] = { - 1, 0, 2, 3, 0, 4, 0, 0, - 7, 8, 0, 9, 10, 10, 3, 0, + 1, 0, 2, 3, 0, 4, 0, 5, + 0, 5, 8, 0, 5, 9, 0, 9, + 3, 0, 5, 5, 0, 0, 0, 5, + 5, 5, 3, 3, 5, 5, 5, 5, + 5, 5, 0, 0, 0, 3, 0, 0, + 10, 11, 12, 13, 5, 0, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, - 3, 3, 0, 0, 0, 0, 0, 0, - 0, 3, 11, 12, 13, 14, 7, 0, - 7, 0, 0, 0, 0, 0, 0, 0, - 0, 7, 0, 0, 0, 0, 0, 0, - 0, 7, 15, 0, 0, 16, 0, 0, - 17, 18, 0, 3, 0, 19, 20, 0, + 0, 14, 5, 15, 0, 0, 16, 0, + 0, 17, 0, 0, 18, 5, 0, 0, + 19, 20, 0, 3, 0, 5, 21, 0, 0 }; static const char _use_syllable_machine_to_state_actions[] = { - 0, 0, 0, 0, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0 }; static const char _use_syllable_machine_from_state_actions[] = { - 0, 0, 0, 0, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 + 0, 0 }; static const short _use_syllable_machine_eof_trans[] = { - 1, 3, 3, 6, 0, 35, 37, 37, - 59, 59, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 59, 37, 62, 65, 62, - 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 70, 70, 66, 66, 71, - 71, 71, 70 + 1, 3, 3, 6, 6, 0, 42, 44, + 44, 68, 68, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 71, 44, + 44, 44, 44, 44, 44, 44, 44, 44, + 68, 44, 74, 77, 74, 44, 44, 81, + 81, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 87, + 82, 82, 82, 87, 82, 82, 82, 82, + 81, 87 }; -static const int use_syllable_machine_start = 4; -static const int use_syllable_machine_first_final = 4; +static const int use_syllable_machine_start = 5; +static const int use_syllable_machine_first_final = 5; static const int use_syllable_machine_error = -1; -static const int use_syllable_machine_en_main = 4; +static const int use_syllable_machine_en_main = 5; #line 38 "hb-ot-shape-complex-use-machine.rl" -#line 143 "hb-ot-shape-complex-use-machine.rl" +#line 162 "hb-ot-shape-complex-use-machine.rl" #define found_syllable(syllable_type) \ HB_STMT_START { \ if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \ for (unsigned int i = ts; i < te; i++) \ - info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + info[i].syllable() = (syllable_serial << 4) | use_##syllable_type; \ syllable_serial++; \ if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ } HB_STMT_END static void -find_syllables (hb_buffer_t *buffer) +find_syllables_use (hb_buffer_t *buffer) { unsigned int p, pe, eof, ts, te, act; int cs; hb_glyph_info_t *info = buffer->info; -#line 378 "hb-ot-shape-complex-use-machine.hh" +#line 396 "hb-ot-shape-complex-use-machine.hh" { cs = use_syllable_machine_start; ts = 0; @@ -382,7 +400,7 @@ find_syllables (hb_buffer_t *buffer) act = 0; } -#line 163 "hb-ot-shape-complex-use-machine.rl" +#line 182 "hb-ot-shape-complex-use-machine.rl" p = 0; @@ -390,7 +408,7 @@ find_syllables (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 394 "hb-ot-shape-complex-use-machine.hh" +#line 412 "hb-ot-shape-complex-use-machine.hh" { int _slen; int _trans; @@ -400,11 +418,11 @@ find_syllables (hb_buffer_t *buffer) goto _test_eof; _resume: switch ( _use_syllable_machine_from_state_actions[cs] ) { - case 6: + case 7: #line 1 "NONE" {ts = p;} break; -#line 408 "hb-ot-shape-complex-use-machine.hh" +#line 426 "hb-ot-shape-complex-use-machine.hh" } _keys = _use_syllable_machine_trans_keys + (cs<<1); @@ -422,73 +440,77 @@ _eof_trans: goto _again; switch ( _use_syllable_machine_trans_actions[_trans] ) { - case 7: + case 5: #line 1 "NONE" {te = p+1;} break; case 12: -#line 132 "hb-ot-shape-complex-use-machine.rl" +#line 150 "hb-ot-shape-complex-use-machine.rl" {te = p+1;{ found_syllable (independent_cluster); }} break; case 14: -#line 134 "hb-ot-shape-complex-use-machine.rl" +#line 153 "hb-ot-shape-complex-use-machine.rl" {te = p+1;{ found_syllable (standard_cluster); }} break; - case 9: -#line 138 "hb-ot-shape-complex-use-machine.rl" + case 10: +#line 157 "hb-ot-shape-complex-use-machine.rl" {te = p+1;{ found_syllable (broken_cluster); }} break; case 8: -#line 139 "hb-ot-shape-complex-use-machine.rl" +#line 158 "hb-ot-shape-complex-use-machine.rl" {te = p+1;{ found_syllable (non_cluster); }} break; case 11: -#line 132 "hb-ot-shape-complex-use-machine.rl" +#line 150 "hb-ot-shape-complex-use-machine.rl" {te = p;p--;{ found_syllable (independent_cluster); }} break; case 15: -#line 133 "hb-ot-shape-complex-use-machine.rl" +#line 151 "hb-ot-shape-complex-use-machine.rl" {te = p;p--;{ found_syllable (virama_terminated_cluster); }} break; + case 16: +#line 152 "hb-ot-shape-complex-use-machine.rl" + {te = p;p--;{ found_syllable (sakot_terminated_cluster); }} + break; case 13: -#line 134 "hb-ot-shape-complex-use-machine.rl" +#line 153 "hb-ot-shape-complex-use-machine.rl" {te = p;p--;{ found_syllable (standard_cluster); }} break; - case 17: -#line 135 "hb-ot-shape-complex-use-machine.rl" + case 18: +#line 154 "hb-ot-shape-complex-use-machine.rl" {te = p;p--;{ found_syllable (number_joiner_terminated_cluster); }} break; - case 16: -#line 136 "hb-ot-shape-complex-use-machine.rl" + case 17: +#line 155 "hb-ot-shape-complex-use-machine.rl" {te = p;p--;{ found_syllable (numeral_cluster); }} break; - case 20: -#line 137 "hb-ot-shape-complex-use-machine.rl" + case 19: +#line 156 "hb-ot-shape-complex-use-machine.rl" {te = p;p--;{ found_syllable (symbol_cluster); }} break; - case 18: -#line 138 "hb-ot-shape-complex-use-machine.rl" + case 20: +#line 157 "hb-ot-shape-complex-use-machine.rl" {te = p;p--;{ found_syllable (broken_cluster); }} break; - case 19: -#line 139 "hb-ot-shape-complex-use-machine.rl" + case 21: +#line 158 "hb-ot-shape-complex-use-machine.rl" {te = p;p--;{ found_syllable (non_cluster); }} break; case 1: -#line 134 "hb-ot-shape-complex-use-machine.rl" +#line 153 "hb-ot-shape-complex-use-machine.rl" {{p = ((te))-1;}{ found_syllable (standard_cluster); }} break; case 4: -#line 138 "hb-ot-shape-complex-use-machine.rl" +#line 157 "hb-ot-shape-complex-use-machine.rl" {{p = ((te))-1;}{ found_syllable (broken_cluster); }} break; case 2: #line 1 "NONE" { switch( act ) { - case 7: + case 8: {{p = ((te))-1;} found_syllable (broken_cluster); } break; - case 8: + case 9: {{p = ((te))-1;} found_syllable (non_cluster); } break; } @@ -497,25 +519,25 @@ _eof_trans: case 3: #line 1 "NONE" {te = p+1;} -#line 138 "hb-ot-shape-complex-use-machine.rl" - {act = 7;} +#line 157 "hb-ot-shape-complex-use-machine.rl" + {act = 8;} break; - case 10: + case 9: #line 1 "NONE" {te = p+1;} -#line 139 "hb-ot-shape-complex-use-machine.rl" - {act = 8;} +#line 158 "hb-ot-shape-complex-use-machine.rl" + {act = 9;} break; -#line 510 "hb-ot-shape-complex-use-machine.hh" +#line 532 "hb-ot-shape-complex-use-machine.hh" } _again: switch ( _use_syllable_machine_to_state_actions[cs] ) { - case 5: + case 6: #line 1 "NONE" {ts = 0;} break; -#line 519 "hb-ot-shape-complex-use-machine.hh" +#line 541 "hb-ot-shape-complex-use-machine.hh" } if ( ++p != pe ) @@ -531,7 +553,7 @@ _again: } -#line 171 "hb-ot-shape-complex-use-machine.rl" +#line 190 "hb-ot-shape-complex-use-machine.rl" } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use-table.cc index 9411e28e435..910b01a32af 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use-table.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use-table.cc @@ -6,15 +6,19 @@ * * on files with these headers: * - * # IndicSyllabicCategory-11.0.0.txt - * # Date: 2018-05-21, 18:33:00 GMT [KW, RP] - * # IndicPositionalCategory-11.0.0.txt - * # Date: 2018-02-05, 16:21:00 GMT [KW, RP] - * # Blocks-11.0.0.txt - * # Date: 2017-10-16, 24:39:00 GMT [KW] + * # IndicSyllabicCategory-13.0.0.txt + * # Date: 2019-07-22, 19:55:00 GMT [KW, RP] + * # IndicPositionalCategory-13.0.0.txt + * # Date: 2019-07-23, 00:01:00 GMT [KW, RP] + * # Blocks-13.0.0.txt + * # Date: 2019-07-10, 19:06:00 GMT [KW] * UnicodeData.txt does not have a header. */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex-use.hh" #pragma GCC diagnostic push @@ -22,7 +26,6 @@ #define B USE_B /* BASE */ #define CGJ USE_CGJ /* CGJ */ #define CS USE_CS /* CONS_WITH_STACKER */ -#define FM USE_FM /* CONS_FINAL_MOD */ #define GB USE_GB /* BASE_OTHER */ #define H USE_H /* HALANT */ #define HN USE_HN /* HALANT_NUM */ @@ -34,29 +37,33 @@ #define Rsv USE_Rsv /* Reserved */ #define S USE_S /* SYM */ #define SUB USE_SUB /* CONS_SUB */ +#define Sk USE_Sk /* SAKOT */ #define VS USE_VS /* VARIATION_SELECTOR */ #define WJ USE_WJ /* Word_Joiner */ #define ZWJ USE_ZWJ /* ZWJ */ #define ZWNJ USE_ZWNJ /* ZWNJ */ -#define CMBlw USE_CMBlw #define CMAbv USE_CMAbv +#define CMBlw USE_CMBlw +#define FAbv USE_FAbv #define FBlw USE_FBlw #define FPst USE_FPst -#define FAbv USE_FAbv -#define MPre USE_MPre +#define FMAbv USE_FMAbv +#define FMBlw USE_FMBlw +#define FMPst USE_FMPst +#define MAbv USE_MAbv #define MBlw USE_MBlw #define MPst USE_MPst -#define MAbv USE_MAbv -#define SMBlw USE_SMBlw +#define MPre USE_MPre #define SMAbv USE_SMAbv -#define VPre USE_VPre +#define SMBlw USE_SMBlw +#define VAbv USE_VAbv #define VBlw USE_VBlw #define VPst USE_VPst -#define VAbv USE_VAbv -#define VMPre USE_VMPre +#define VPre USE_VPre +#define VMAbv USE_VMAbv #define VMBlw USE_VMBlw #define VMPst USE_VMPst -#define VMAbv USE_VMAbv +#define VMPre USE_VMPre #pragma GCC diagnostic pop static const USE_TABLE_ELEMENT_TYPE use_table[] = { @@ -75,7 +82,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Latin-1 Supplement */ /* 00A0 */ GB, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, - /* 00B0 */ O, O, FM, FM, O, O, O, O, O, O, O, O, O, O, O, O, + /* 00B0 */ O, O, FMPst, FMPst, O, O, O, O, O, O, O, O, O, O, O, O, /* 00C0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* 00D0 */ O, O, O, O, O, O, O, GB, @@ -108,7 +115,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 09C0 */ VPst, VBlw, VBlw, VBlw, VBlw, O, O, VPre, VPre, O, O, VPst, VPst, H, IND, O, /* 09D0 */ O, O, O, O, O, O, O, VPst, O, O, O, O, B, B, O, B, /* 09E0 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B, - /* 09F0 */ B, B, O, O, O, O, O, O, O, O, O, O, B, O, FM, O, + /* 09F0 */ B, B, O, O, O, O, O, O, O, O, O, O, B, O, FMAbv, O, /* Gurmukhi */ @@ -139,7 +146,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 0B20 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, /* 0B30 */ B, O, B, B, O, B, B, B, B, B, O, O, CMBlw, B, VPst, VAbv, /* 0B40 */ VPst, VBlw, VBlw, VBlw, VBlw, O, O, VPre, VPst, O, O, VPst, VPst, H, O, O, - /* 0B50 */ O, O, O, O, O, O, VAbv, VAbv, O, O, O, O, B, B, O, B, + /* 0B50 */ O, O, O, O, O, VAbv, VAbv, VAbv, O, O, O, O, B, B, O, B, /* 0B60 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B, /* 0B70 */ O, B, O, O, O, O, O, O, O, O, O, O, O, O, O, O, @@ -167,7 +174,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Kannada */ - /* 0C80 */ O, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, O, B, B, + /* 0C80 */ B, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, O, B, B, /* 0C90 */ B, O, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 0CA0 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, /* 0CB0 */ B, B, B, B, O, B, B, B, B, B, O, O, CMBlw, B, VPst, VAbv, @@ -178,7 +185,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Malayalam */ - /* 0D00 */ VMAbv, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, O, B, B, + /* 0D00 */ VMAbv, VMAbv, VMPst, VMPst, B, B, B, B, B, B, B, B, B, O, B, B, /* 0D10 */ B, O, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 0D20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 0D30 */ B, B, B, B, B, B, B, B, B, B, B, VAbv, VAbv, B, VPst, VPst, @@ -189,7 +196,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Sinhala */ - /* 0D80 */ O, O, VMPst, VMPst, O, B, B, B, B, B, B, B, B, B, B, B, + /* 0D80 */ O, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, B, B, B, /* 0D90 */ B, B, B, B, B, B, B, O, O, O, B, B, B, B, B, B, /* 0DA0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 0DB0 */ B, B, O, B, B, B, B, B, B, B, B, B, O, B, O, O, @@ -204,7 +211,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Tibetan */ VBlw, VBlw, O, O, O, O, O, O, /* 0F20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, - /* 0F30 */ B, B, B, B, O, FM, O, FM, O, CMAbv, O, O, O, O, VPst, VPre, + /* 0F30 */ B, B, B, B, O, FMBlw, O, FMBlw, O, CMAbv, O, O, O, O, VPst, VPre, /* 0F40 */ B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, B, /* 0F50 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 0F60 */ B, B, B, B, B, B, B, B, B, B, B, B, B, O, O, O, @@ -213,7 +220,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 0F90 */ SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, O, SUB, SUB, SUB, SUB, SUB, SUB, SUB, /* 0FA0 */ SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, /* 0FB0 */ SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, O, O, O, - /* 0FC0 */ O, O, O, O, O, O, FM, O, + /* 0FC0 */ O, O, O, O, O, O, FMBlw, O, #define use_offset_0x1000u 1536 @@ -260,8 +267,8 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 1790 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 17A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 17B0 */ B, B, B, B, O, O, VPst, VAbv, VAbv, VAbv, VAbv, VBlw, VBlw, VBlw, VPst, VPst, - /* 17C0 */ VPst, VPre, VPre, VPre, VPst, VPst, VMAbv, VMPst, VPst, VMAbv, VMAbv, FM, FAbv, CMAbv, FM, FM, - /* 17D0 */ FM, VAbv, H, FM, O, O, O, O, O, O, O, O, B, VAbv, O, O, + /* 17C0 */ VPst, VPre, VPre, VPre, VPst, VPst, VMAbv, VMPst, VPst, VMAbv, VMAbv, FMAbv, FAbv, CMAbv, FMAbv, FMAbv, + /* 17D0 */ FMAbv, VAbv, H, FMAbv, O, O, O, O, O, O, O, O, B, FMAbv, O, O, /* 17E0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, #define use_offset_0x1900u 1936 @@ -272,7 +279,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 1900 */ GB, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 1910 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, O, /* 1920 */ VAbv, VAbv, VBlw, VPst, VPst, VAbv, VAbv, VAbv, VAbv, SUB, SUB, SUB, O, O, O, O, - /* 1930 */ FPst, FPst, VMBlw, FPst, FPst, FPst, FPst, FPst, FPst, FBlw, VAbv, FM, O, O, O, O, + /* 1930 */ FPst, FPst, VMBlw, FPst, FPst, FPst, FPst, FPst, FPst, FBlw, VAbv, FMBlw, O, O, O, O, /* 1940 */ O, O, O, O, O, O, B, B, B, B, B, B, B, B, B, B, /* Tai Le */ @@ -288,7 +295,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 19A0 */ B, B, B, B, B, B, B, B, B, B, B, B, O, O, O, O, /* 19B0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 19C0 */ B, B, B, B, B, B, B, B, VMPst, VMPst, O, O, O, O, O, O, - /* 19D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + /* 19D0 */ B, B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, /* 19E0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* 19F0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, @@ -302,9 +309,9 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 1A20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 1A30 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 1A40 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, - /* 1A50 */ B, B, B, B, B, MPre, MBlw, SUB, FAbv, FAbv, FAbv, SUB, SUB, SUB, SUB, O, - /* 1A60 */ H, VPst, VAbv, VPst, VPst, VAbv, VAbv, VAbv, VAbv, VBlw, VBlw, VAbv, VBlw, VPst, VPre, VPre, - /* 1A70 */ VPre, VPre, VPre, VAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VAbv, FM, FM, O, O, FBlw, + /* 1A50 */ B, B, B, B, B, MPre, MBlw, SUB, FAbv, FAbv, MAbv, SUB, SUB, SUB, SUB, O, + /* 1A60 */ Sk, VPst, VAbv, VPst, VPst, VAbv, VAbv, VAbv, VAbv, VBlw, VBlw, VAbv, VBlw, VPst, VPre, VPre, + /* 1A70 */ VPre, VPre, VPre, VAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VAbv, FMAbv, FMAbv, O, O, FMBlw, /* 1A80 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, /* 1A90 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, @@ -318,8 +325,8 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 1B20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 1B30 */ B, B, B, B, CMAbv, VPst, VAbv, VAbv, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VPre, VPre, /* 1B40 */ VPst, VPst, VAbv, VAbv, H, B, B, B, B, B, B, B, O, O, O, O, - /* 1B50 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, - /* 1B60 */ O, O, O, O, O, O, O, O, O, O, O, SMAbv, SMBlw, SMAbv, SMAbv, SMAbv, + /* 1B50 */ B, B, B, B, B, B, B, B, B, B, O, GB, GB, O, O, GB, + /* 1B60 */ O, S, GB, S, S, S, S, S, GB, S, S, SMAbv, SMBlw, SMAbv, SMAbv, SMAbv, /* 1B70 */ SMAbv, SMAbv, SMAbv, SMAbv, O, O, O, O, O, O, O, O, O, O, O, O, /* Sundanese */ @@ -340,8 +347,8 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 1C00 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 1C10 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, - /* 1C20 */ B, B, B, B, SUB, SUB, VPst, VPre, VPre, VPst, VPst, VPst, VBlw, FAbv, FAbv, FAbv, - /* 1C30 */ FAbv, FAbv, FAbv, FAbv, VMPre, VMPre, FM, CMBlw, O, O, O, O, O, O, O, O, + /* 1C20 */ B, B, B, B, SUB, SUB, VPst, VPre, VPre, VPre, VPst, VPst, VBlw, FAbv, FAbv, FAbv, + /* 1C30 */ FAbv, FAbv, FAbv, FAbv, VMPre, VMPre, FMAbv, CMBlw, O, O, O, O, O, O, O, O, /* 1C40 */ B, B, B, B, B, B, B, B, B, B, O, O, O, B, B, B, #define use_offset_0x1cd0u 2688 @@ -351,13 +358,13 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 1CD0 */ VMAbv, VMAbv, VMAbv, O, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMAbv, VMAbv, VMBlw, VMBlw, VMBlw, VMBlw, /* 1CE0 */ VMAbv, VMPst, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, O, O, O, O, VMBlw, O, O, - /* 1CF0 */ O, O, VMPst, VMPst, VMAbv, CS, CS, VMPst, VMAbv, VMAbv, O, O, O, O, O, O, + /* 1CF0 */ O, O, IND, IND, VMAbv, CS, CS, VMPst, VMAbv, VMAbv, GB, O, O, O, O, O, #define use_offset_0x1df8u 2736 /* Combining Diacritical Marks Supplement */ - O, O, O, FM, O, O, O, O, + O, O, O, FMAbv, O, O, O, O, #define use_offset_0x2008u 2744 @@ -372,8 +379,8 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Superscripts and Subscripts */ - /* 2070 */ O, O, O, O, FM, O, O, O, O, O, O, O, O, O, O, O, - /* 2080 */ O, O, FM, FM, FM, O, O, O, + /* 2070 */ O, O, O, O, FMPst, O, O, O, O, O, O, O, O, O, O, O, + /* 2080 */ O, O, FMPst, FMPst, FMPst, O, O, O, #define use_offset_0x20f0u 2800 @@ -393,9 +400,9 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Syloti Nagri */ - /* A800 */ B, B, O, B, B, B, VAbv, B, B, B, B, VMAbv, B, B, B, B, + /* A800 */ B, B, VAbv, B, B, B, H, B, B, B, B, VMAbv, B, B, B, B, /* A810 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, - /* A820 */ B, B, B, VPst, VPst, VBlw, VAbv, VPst, O, O, O, O, O, O, O, O, + /* A820 */ B, B, B, VPst, VPst, VBlw, VAbv, VPst, O, O, O, O, VBlw, O, O, O, /* A830 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* Phags-pa */ @@ -438,7 +445,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* A980 */ VMAbv, VMAbv, FAbv, VMPst, B, B, B, B, B, B, B, B, B, B, B, B, /* A990 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* A9A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, - /* A9B0 */ B, B, B, CMAbv, VPst, VPst, VAbv, VAbv, VBlw, VBlw, VPre, VPre, VAbv, SUB, MPst, MBlw, + /* A9B0 */ B, B, B, CMAbv, VPst, VPst, VAbv, VAbv, VBlw, VBlw, VPre, VPre, VAbv, MBlw, MBlw, MBlw, /* A9C0 */ H, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* A9D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, @@ -533,7 +540,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11110 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11120 */ B, B, B, B, B, B, B, VBlw, VBlw, VBlw, VAbv, VAbv, VPre, VBlw, VAbv, VAbv, /* 11130 */ VBlw, VAbv, VAbv, H, CMBlw, O, B, B, B, B, B, B, B, B, B, B, - /* 11140 */ O, O, O, O, B, VPst, VPst, O, O, O, O, O, O, O, O, O, + /* 11140 */ O, O, O, O, B, VPst, VPst, B, O, O, O, O, O, O, O, O, /* Mahajani */ @@ -547,7 +554,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11190 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 111A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 111B0 */ B, B, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, - /* 111C0 */ H, B, R, R, O, O, O, O, GB, FBlw, CMBlw, VAbv, VBlw, O, O, O, + /* 111C0 */ H, B, R, R, O, O, O, O, GB, FMBlw, CMBlw, VAbv, VBlw, O, VPre, VMAbv, /* 111D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, /* Sinhala Archaic Numbers */ @@ -581,7 +588,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Grantha */ - /* 11300 */ VMAbv, VMAbv, VMAbv, VMPst, O, B, B, B, B, B, B, B, B, O, O, B, + /* 11300 */ VMAbv, VMAbv, VMAbv, VMAbv, O, B, B, B, B, B, B, B, B, O, O, B, /* 11310 */ B, O, O, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11320 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, /* 11330 */ B, O, B, B, O, B, B, B, B, B, O, CMBlw, CMBlw, B, VPst, VPst, @@ -600,8 +607,8 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11420 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11430 */ B, B, B, B, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, /* 11440 */ VPst, VPst, H, VMAbv, VMAbv, VMPst, CMBlw, B, O, O, O, O, O, O, O, O, - /* 11450 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, FM, O, - /* 11460 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* 11450 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, FMAbv, B, + /* 11460 */ CS, CS, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* 11470 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* Tirhuta */ @@ -610,7 +617,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11490 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 114A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 114B0 */ VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VPre, VAbv, VPst, VPst, VPst, VPst, VMAbv, - /* 114C0 */ VMAbv, VMPst, H, CMBlw, B, O, O, O, O, O, O, O, O, O, O, O, + /* 114C0 */ VMAbv, VMAbv, H, CMBlw, B, O, O, O, O, O, O, O, O, O, O, O, /* 114D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, #define use_offset_0x11580u 4720 @@ -643,7 +650,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11680 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11690 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 116A0 */ B, B, B, B, B, B, B, B, B, B, B, VMAbv, VMPst, VAbv, VPre, VPst, - /* 116B0 */ VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, H, CMBlw, O, O, O, O, O, O, O, O, + /* 116B0 */ VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, H, CMBlw, B, O, O, O, O, O, O, O, /* 116C0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, /* 116D0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* 116E0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, @@ -666,15 +673,36 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11820 */ B, B, B, B, B, B, B, B, B, B, B, B, VPst, VPre, VPst, VBlw, /* 11830 */ VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, VMAbv, VMPst, H, CMBlw, O, O, O, O, O, -#define use_offset_0x11a00u 5232 +#define use_offset_0x11900u 5232 + + + /* Dives Akuru */ + + /* 11900 */ B, B, B, B, B, B, B, O, O, B, O, O, B, B, B, B, + /* 11910 */ B, B, B, B, O, B, B, O, B, B, B, B, B, B, B, B, + /* 11920 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11930 */ VPst, VPst, VPst, VPst, VPst, VPre, O, VPre, VPst, O, O, VMAbv, VMAbv, VPst, H, R, + /* 11940 */ MPst, R, MBlw, CMBlw, O, O, O, O, O, O, O, O, O, O, O, O, + /* 11950 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, +#define use_offset_0x119a0u 5328 + + + /* Nandinagari */ + + /* 119A0 */ B, B, B, B, B, B, B, B, O, O, B, B, B, B, B, B, + /* 119B0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 119C0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 119D0 */ B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, O, O, VAbv, VAbv, VPst, VPst, VMPst, VMPst, + /* 119E0 */ H, B, O, O, VPre, O, O, O, O, O, O, O, O, O, O, O, + /* 119F0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* Zanabazar Square */ /* 11A00 */ B, VAbv, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, VAbv, VAbv, VBlw, B, B, B, B, B, /* 11A10 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11A20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, - /* 11A30 */ B, B, B, FM, VBlw, VMAbv, VMAbv, VMAbv, VMAbv, VMPst, R, MBlw, MBlw, MBlw, MBlw, GB, + /* 11A30 */ B, B, B, FMBlw, VBlw, VMAbv, VMAbv, VMAbv, VMAbv, VMPst, R, MBlw, MBlw, MBlw, MBlw, GB, /* 11A40 */ O, O, O, O, O, GB, O, H, O, O, O, O, O, O, O, O, /* Soyombo */ @@ -682,10 +710,10 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11A50 */ B, VAbv, VBlw, VBlw, VAbv, VAbv, VAbv, VPst, VPst, VBlw, VBlw, VBlw, B, B, B, B, /* 11A60 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11A70 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, - /* 11A80 */ B, B, B, B, O, O, R, R, R, R, FBlw, FBlw, FBlw, FBlw, FBlw, FBlw, + /* 11A80 */ B, B, B, B, R, R, R, R, R, R, FBlw, FBlw, FBlw, FBlw, FBlw, FBlw, /* 11A90 */ FBlw, FBlw, FBlw, FBlw, FBlw, FBlw, VMAbv, VMPst, CMAbv, H, O, O, O, B, O, O, -#define use_offset_0x11c00u 5392 +#define use_offset_0x11c00u 5584 /* Bhaiksuki */ @@ -706,7 +734,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11CA0 */ SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, O, SUB, SUB, SUB, SUB, SUB, SUB, SUB, /* 11CB0 */ VBlw, VPre, VBlw, VAbv, VPst, VMAbv, VMAbv, O, -#define use_offset_0x11d00u 5576 +#define use_offset_0x11d00u 5768 /* Masaram Gondi */ @@ -726,7 +754,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11D90 */ VAbv, VAbv, O, VPst, VPst, VMAbv, VMPst, H, O, O, O, O, O, O, O, O, /* 11DA0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, -#define use_offset_0x11ee0u 5752 +#define use_offset_0x11ee0u 5944 /* Makasar */ @@ -734,7 +762,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11EE0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11EF0 */ B, B, GB, VAbv, VBlw, VPre, VPst, O, -}; /* Table items: 5776; occupancy: 74% */ +}; /* Table items: 5968; occupancy: 74% */ USE_TABLE_ELEMENT_TYPE hb_use_get_category (hb_codepoint_t u) @@ -785,7 +813,8 @@ hb_use_get_category (hb_codepoint_t u) if (hb_in_range (u, 0x11400u, 0x114DFu)) return use_table[u - 0x11400u + use_offset_0x11400u]; if (hb_in_range (u, 0x11580u, 0x1173Fu)) return use_table[u - 0x11580u + use_offset_0x11580u]; if (hb_in_range (u, 0x11800u, 0x1183Fu)) return use_table[u - 0x11800u + use_offset_0x11800u]; - if (hb_in_range (u, 0x11A00u, 0x11A9Fu)) return use_table[u - 0x11A00u + use_offset_0x11a00u]; + if (hb_in_range (u, 0x11900u, 0x1195Fu)) return use_table[u - 0x11900u + use_offset_0x11900u]; + if (hb_in_range (u, 0x119A0u, 0x11A9Fu)) return use_table[u - 0x119A0u + use_offset_0x119a0u]; if (hb_in_range (u, 0x11C00u, 0x11CB7u)) return use_table[u - 0x11C00u + use_offset_0x11c00u]; if (hb_in_range (u, 0x11D00u, 0x11DAFu)) return use_table[u - 0x11D00u + use_offset_0x11d00u]; if (hb_in_range (u, 0x11EE0u, 0x11EF7u)) return use_table[u - 0x11EE0u + use_offset_0x11ee0u]; @@ -800,7 +829,6 @@ hb_use_get_category (hb_codepoint_t u) #undef B #undef CGJ #undef CS -#undef FM #undef GB #undef H #undef HN @@ -812,28 +840,34 @@ hb_use_get_category (hb_codepoint_t u) #undef Rsv #undef S #undef SUB +#undef Sk #undef VS #undef WJ #undef ZWJ #undef ZWNJ -#undef CMBlw #undef CMAbv +#undef CMBlw +#undef FAbv #undef FBlw #undef FPst -#undef FAbv -#undef MPre +#undef FMAbv +#undef FMBlw +#undef FMPst +#undef MAbv #undef MBlw #undef MPst -#undef MAbv -#undef SMBlw +#undef MPre #undef SMAbv -#undef VPre +#undef SMBlw +#undef VAbv #undef VBlw #undef VPst -#undef VAbv -#undef VMPre +#undef VPre +#undef VMAbv #undef VMBlw #undef VMPst -#undef VMAbv +#undef VMPre + +#endif /* == End of generated table == */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use.cc index 25b135ab58c..b5cf48ac227 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use.cc @@ -26,12 +26,17 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex-use.hh" #include "hb-ot-shape-complex-arabic.hh" +#include "hb-ot-shape-complex-arabic-joining-list.hh" #include "hb-ot-shape-complex-vowel-constraints.hh" /* buffer var allocations */ -#define use_category() complex_var_u8_0() +#define use_category() complex_var_u8_1() /* @@ -40,7 +45,7 @@ */ static const hb_tag_t -basic_features[] = +use_basic_features[] = { /* * Basic features. @@ -55,28 +60,23 @@ basic_features[] = HB_TAG('c','j','c','t'), }; static const hb_tag_t -arabic_features[] = +use_topographical_features[] = { HB_TAG('i','s','o','l'), HB_TAG('i','n','i','t'), HB_TAG('m','e','d','i'), HB_TAG('f','i','n','a'), - /* The spec doesn't specify these but we apply anyway, since our Arabic shaper - * does. These are only used in Syriac spec. */ - HB_TAG('m','e','d','2'), - HB_TAG('f','i','n','2'), - HB_TAG('f','i','n','3'), }; -/* Same order as arabic_features. Don't need Syriac stuff.*/ +/* Same order as use_topographical_features. */ enum joining_form_t { - ISOL, - INIT, - MEDI, - FINA, - _NONE + USE_ISOL, + USE_INIT, + USE_MEDI, + USE_FINA, + _USE_NONE }; static const hb_tag_t -other_features[] = +use_other_features[] = { /* * Other features. @@ -89,42 +89,23 @@ other_features[] = HB_TAG('p','r','e','s'), HB_TAG('p','s','t','s'), }; -static const hb_tag_t -positioning_features[] = -{ - /* - * Positioning features. - * We don't care about the types. - */ - HB_TAG('d','i','s','t'), - HB_TAG('a','b','v','m'), - HB_TAG('b','l','w','m'), -}; static void -setup_syllables (const hb_ot_shape_plan_t *plan, +setup_syllables_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +record_rphf_use (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); static void -clear_substitution_flags (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); -static void -record_rphf (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); +record_pref_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); static void -record_pref (const hb_ot_shape_plan_t *plan, +reorder_use (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); -static void -reorder (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); -static void -clear_syllables (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); static void collect_features_use (hb_ot_shape_planner_t *plan) @@ -132,7 +113,7 @@ collect_features_use (hb_ot_shape_planner_t *plan) hb_ot_map_builder_t *map = &plan->map; /* Do this before any lookups have been applied. */ - map->add_gsub_pause (setup_syllables); + map->add_gsub_pause (setup_syllables_use); /* "Default glyph pre-processing group" */ map->enable_feature (HB_TAG('l','o','c','l')); @@ -141,32 +122,28 @@ collect_features_use (hb_ot_shape_planner_t *plan) map->enable_feature (HB_TAG('a','k','h','n'), F_MANUAL_ZWJ); /* "Reordering group" */ - map->add_gsub_pause (clear_substitution_flags); + map->add_gsub_pause (_hb_clear_substitution_flags); map->add_feature (HB_TAG('r','p','h','f'), F_MANUAL_ZWJ); - map->add_gsub_pause (record_rphf); - map->add_gsub_pause (clear_substitution_flags); + map->add_gsub_pause (record_rphf_use); + map->add_gsub_pause (_hb_clear_substitution_flags); map->enable_feature (HB_TAG('p','r','e','f'), F_MANUAL_ZWJ); - map->add_gsub_pause (record_pref); + map->add_gsub_pause (record_pref_use); /* "Orthographic unit shaping group" */ - for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++) - map->enable_feature (basic_features[i], F_MANUAL_ZWJ); + for (unsigned int i = 0; i < ARRAY_LENGTH (use_basic_features); i++) + map->enable_feature (use_basic_features[i], F_MANUAL_ZWJ); - map->add_gsub_pause (reorder); - map->add_gsub_pause (clear_syllables); + map->add_gsub_pause (reorder_use); + map->add_gsub_pause (_hb_clear_syllables); /* "Topographical features" */ - for (unsigned int i = 0; i < ARRAY_LENGTH (arabic_features); i++) - map->add_feature (arabic_features[i]); + for (unsigned int i = 0; i < ARRAY_LENGTH (use_topographical_features); i++) + map->add_feature (use_topographical_features[i]); map->add_gsub_pause (nullptr); /* "Standard typographic presentation" */ - for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++) - map->enable_feature (other_features[i], F_MANUAL_ZWJ); - - /* "Positional feature application" */ - for (unsigned int i = 0; i < ARRAY_LENGTH (positioning_features); i++) - map->enable_feature (positioning_features[i]); + for (unsigned int i = 0; i < ARRAY_LENGTH (use_other_features); i++) + map->enable_feature (use_other_features[i], F_MANUAL_ZWJ); } struct use_shape_plan_t @@ -176,40 +153,6 @@ struct use_shape_plan_t arabic_shape_plan_t *arabic_plan; }; -static bool -has_arabic_joining (hb_script_t script) -{ - /* List of scripts that have data in arabic-table. */ - switch ((int) script) - { - /* Unicode-1.1 additions */ - case HB_SCRIPT_ARABIC: - - /* Unicode-3.0 additions */ - case HB_SCRIPT_MONGOLIAN: - case HB_SCRIPT_SYRIAC: - - /* Unicode-5.0 additions */ - case HB_SCRIPT_NKO: - case HB_SCRIPT_PHAGS_PA: - - /* Unicode-6.0 additions */ - case HB_SCRIPT_MANDAIC: - - /* Unicode-7.0 additions */ - case HB_SCRIPT_MANICHAEAN: - case HB_SCRIPT_PSALTER_PAHLAVI: - - /* Unicode-9.0 additions */ - case HB_SCRIPT_ADLAM: - - return true; - - default: - return false; - } -} - static void * data_create_use (const hb_ot_shape_plan_t *plan) { @@ -243,15 +186,16 @@ data_destroy_use (void *data) free (data); } -enum syllable_type_t { - independent_cluster, - virama_terminated_cluster, - standard_cluster, - number_joiner_terminated_cluster, - numeral_cluster, - symbol_cluster, - broken_cluster, - non_cluster, +enum use_syllable_type_t { + use_independent_cluster, + use_virama_terminated_cluster, + use_sakot_terminated_cluster, + use_standard_cluster, + use_number_joiner_terminated_cluster, + use_numeral_cluster, + use_symbol_cluster, + use_broken_cluster, + use_non_cluster, }; #include "hb-ot-shape-complex-use-machine.hh" @@ -294,7 +238,7 @@ setup_rphf_mask (const hb_ot_shape_plan_t *plan, foreach_syllable (buffer, start, end) { - unsigned int limit = info[start].use_category() == USE_R ? 1 : MIN (3u, end - start); + unsigned int limit = info[start].use_category() == USE_R ? 1 : hb_min (3u, end - start); for (unsigned int i = start; i < start + limit; i++) info[i].mask |= mask; } @@ -308,11 +252,11 @@ setup_topographical_masks (const hb_ot_shape_plan_t *plan, if (use_plan->arabic_plan) return; - static_assert ((INIT < 4 && ISOL < 4 && MEDI < 4 && FINA < 4), ""); + static_assert ((USE_INIT < 4 && USE_ISOL < 4 && USE_MEDI < 4 && USE_FINA < 4), ""); hb_mask_t masks[4], all_masks = 0; for (unsigned int i = 0; i < 4; i++) { - masks[i] = plan->map.get_1_mask (arabic_features[i]); + masks[i] = plan->map.get_1_mask (use_topographical_features[i]); if (masks[i] == plan->map.get_global_mask ()) masks[i] = 0; all_masks |= masks[i]; @@ -322,38 +266,39 @@ setup_topographical_masks (const hb_ot_shape_plan_t *plan, hb_mask_t other_masks = ~all_masks; unsigned int last_start = 0; - joining_form_t last_form = _NONE; + joining_form_t last_form = _USE_NONE; hb_glyph_info_t *info = buffer->info; foreach_syllable (buffer, start, end) { - syllable_type_t syllable_type = (syllable_type_t) (info[start].syllable() & 0x0F); + use_syllable_type_t syllable_type = (use_syllable_type_t) (info[start].syllable() & 0x0F); switch (syllable_type) { - case independent_cluster: - case symbol_cluster: - case non_cluster: + case use_independent_cluster: + case use_symbol_cluster: + case use_non_cluster: /* These don't join. Nothing to do. */ - last_form = _NONE; + last_form = _USE_NONE; break; - case virama_terminated_cluster: - case standard_cluster: - case number_joiner_terminated_cluster: - case numeral_cluster: - case broken_cluster: + case use_virama_terminated_cluster: + case use_sakot_terminated_cluster: + case use_standard_cluster: + case use_number_joiner_terminated_cluster: + case use_numeral_cluster: + case use_broken_cluster: - bool join = last_form == FINA || last_form == ISOL; + bool join = last_form == USE_FINA || last_form == USE_ISOL; if (join) { /* Fixup previous syllable's form. */ - last_form = last_form == FINA ? MEDI : INIT; + last_form = last_form == USE_FINA ? USE_MEDI : USE_INIT; for (unsigned int i = last_start; i < start; i++) info[i].mask = (info[i].mask & other_masks) | masks[last_form]; } /* Form for this syllable. */ - last_form = join ? FINA : ISOL; + last_form = join ? USE_FINA : USE_ISOL; for (unsigned int i = start; i < end; i++) info[i].mask = (info[i].mask & other_masks) | masks[last_form]; @@ -365,11 +310,11 @@ setup_topographical_masks (const hb_ot_shape_plan_t *plan, } static void -setup_syllables (const hb_ot_shape_plan_t *plan, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) +setup_syllables_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) { - find_syllables (buffer); + find_syllables_use (buffer); foreach_syllable (buffer, start, end) buffer->unsafe_to_break (start, end); setup_rphf_mask (plan, buffer); @@ -377,20 +322,9 @@ setup_syllables (const hb_ot_shape_plan_t *plan, } static void -clear_substitution_flags (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) -{ - hb_glyph_info_t *info = buffer->info; - unsigned int count = buffer->len; - for (unsigned int i = 0; i < count; i++) - _hb_glyph_info_clear_substituted (&info[i]); -} - -static void -record_rphf (const hb_ot_shape_plan_t *plan, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) +record_rphf_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) { const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; @@ -411,9 +345,9 @@ record_rphf (const hb_ot_shape_plan_t *plan, } static void -record_pref (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) +record_pref_use (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) { hb_glyph_info_t *info = buffer->info; @@ -430,21 +364,22 @@ record_pref (const hb_ot_shape_plan_t *plan HB_UNUSED, } static inline bool -is_halant (const hb_glyph_info_t &info) +is_halant_use (const hb_glyph_info_t &info) { return (info.use_category() == USE_H || info.use_category() == USE_HVM) && !_hb_glyph_info_ligated (&info); } static void -reorder_syllable (hb_buffer_t *buffer, unsigned int start, unsigned int end) +reorder_syllable_use (hb_buffer_t *buffer, unsigned int start, unsigned int end) { - syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F); + use_syllable_type_t syllable_type = (use_syllable_type_t) (buffer->info[start].syllable() & 0x0F); /* Only a few syllable types need reordering. */ if (unlikely (!(FLAG_UNSAFE (syllable_type) & - (FLAG (virama_terminated_cluster) | - FLAG (standard_cluster) | - FLAG (broken_cluster) | + (FLAG (use_virama_terminated_cluster) | + FLAG (use_sakot_terminated_cluster) | + FLAG (use_standard_cluster) | + FLAG (use_broken_cluster) | 0)))) return; @@ -475,7 +410,7 @@ reorder_syllable (hb_buffer_t *buffer, unsigned int start, unsigned int end) for (unsigned int i = start + 1; i < end; i++) { bool is_post_base_glyph = (FLAG64_UNSAFE (info[i].use_category()) & POST_BASE_FLAGS64) || - is_halant (info[i]); + is_halant_use (info[i]); if (is_post_base_glyph || i == end - 1) { /* If we hit a post-base glyph, move before it; otherwise move to the @@ -499,7 +434,7 @@ reorder_syllable (hb_buffer_t *buffer, unsigned int start, unsigned int end) for (unsigned int i = start; i < end; i++) { uint32_t flag = FLAG_UNSAFE (info[i].use_category()); - if (is_halant (info[i])) + if (is_halant_use (info[i])) { /* If we hit a halant, move after it; otherwise move to the beginning, and * shift things in between forward. */ @@ -519,16 +454,20 @@ reorder_syllable (hb_buffer_t *buffer, unsigned int start, unsigned int end) } static inline void -insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font, - hb_buffer_t *buffer) +insert_dotted_circles_use (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer) { - /* Note: This loop is extra overhead, but should not be measurable. */ + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + + /* Note: This loop is extra overhead, but should not be measurable. + * TODO Use a buffer scratch flag to remove the loop. */ bool has_broken_syllables = false; unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 0; i < count; i++) - if ((info[i].syllable() & 0x0F) == broken_cluster) + if ((info[i].syllable() & 0x0F) == use_broken_cluster) { has_broken_syllables = true; break; @@ -548,8 +487,8 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, while (buffer->idx < buffer->len && buffer->successful) { unsigned int syllable = buffer->cur().syllable(); - syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F); - if (unlikely (last_syllable != syllable && syllable_type == broken_cluster)) + use_syllable_type_t syllable_type = (use_syllable_type_t) (syllable & 0x0F); + if (unlikely (last_syllable != syllable && syllable_type == use_broken_cluster)) { last_syllable = syllable; @@ -557,7 +496,6 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, ginfo.cluster = buffer->cur().cluster; ginfo.mask = buffer->cur().mask; ginfo.syllable() = buffer->cur().syllable(); - /* TODO Set glyph_props? */ /* Insert dottedcircle after possible Repha. */ while (buffer->idx < buffer->len && buffer->successful && @@ -574,29 +512,18 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, } static void -reorder (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer) +reorder_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) { - insert_dotted_circles (plan, font, buffer); + insert_dotted_circles_use (plan, font, buffer); foreach_syllable (buffer, start, end) - reorder_syllable (buffer, start, end); + reorder_syllable_use (buffer, start, end); HB_BUFFER_DEALLOCATE_VAR (buffer, use_category); } -static void -clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) -{ - hb_glyph_info_t *info = buffer->info; - unsigned int count = buffer->len; - for (unsigned int i = 0; i < count; i++) - info[i].syllable() = 0; -} - static void preprocess_text_use (const hb_ot_shape_plan_t *plan, @@ -637,3 +564,6 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_use = HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, false, /* fallback_position */ }; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use.hh index d7edef40ff2..6a42d58c5a8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use.hh @@ -68,6 +68,12 @@ enum use_category_t { USE_VS = 21, /* VARIATION_SELECTOR */ // USE_V = 36, /* VOWEL */ // USE_VM = 40, /* VOWEL_MOD */ + USE_CS = 43, /* CONS_WITH_STACKER */ + + /* https://github.com/harfbuzz/harfbuzz/issues/1102 */ + USE_HVM = 44, /* HALANT_OR_VOWEL_MODIFIER */ + + USE_Sk = 48, /* SAKOT */ USE_FAbv = 24, /* CONS_FINAL_ABOVE */ USE_FBlw = 25, /* CONS_FINAL_BELOW */ @@ -88,10 +94,9 @@ enum use_category_t { USE_VMPre = 23, /* VOWEL_MOD_PRE */ USE_SMAbv = 41, /* SYM_MOD_ABOVE */ USE_SMBlw = 42, /* SYM_MOD_BELOW */ - USE_CS = 43, /* CONS_WITH_STACKER */ - - /* https://github.com/harfbuzz/harfbuzz/issues/1102 */ - USE_HVM = 44, /* HALANT_OR_VOWEL_MODIFIER */ + USE_FMAbv = 45, /* CONS_FINAL_MOD UIPC = Top */ + USE_FMBlw = 46, /* CONS_FINAL_MOD UIPC = Bottom */ + USE_FMPst = 47, /* CONS_FINAL_MOD UIPC = Not_Applicable */ }; HB_INTERNAL USE_TABLE_ELEMENT_TYPE diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-vowel-constraints.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-vowel-constraints.cc index 366751efee2..adfaa301049 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-vowel-constraints.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-vowel-constraints.cc @@ -2,17 +2,22 @@ /* * The following functions are generated by running: * - * ./gen-vowel-constraints.py use Scripts.txt + * ./gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt * * on files with these headers: * - * # Copied from https://docs.microsoft.com/en-us/typography/script-development/use - * # On October 23, 2018; with documentd dated 02/07/2018. + * # IndicShapingInvalidCluster.txt + * # Date: 2015-03-12, 21:17:00 GMT [AG] + * # Date: 2019-11-08, 23:22:00 GMT [AG] * - * # Scripts-11.0.0.txt - * # Date: 2018-02-21, 05:34:31 GMT + * # Scripts-13.0.0.txt + * # Date: 2020-01-22, 00:07:43 GMT */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex-vowel-constraints.hh" static void @@ -34,6 +39,12 @@ _hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_buffer_t *buffer, hb_font_t *font HB_UNUSED) { +#ifdef HB_NO_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS + return; +#endif + if (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE) + return; + /* UGLY UGLY UGLY business of adding dotted-circle in the middle of * vowel-sequences that look like another vowel. Data for each script * collected from the USE script development spec. @@ -87,8 +98,7 @@ _hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED, 0x0907u == buffer->cur (2).codepoint) { buffer->next_glyph (); - buffer->next_glyph (); - _output_dotted_circle (buffer); + matched = true; } break; } @@ -201,6 +211,21 @@ _hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED, processed = true; break; + case HB_SCRIPT_TAMIL: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + if (0x0B85u == buffer->cur ().codepoint && + 0x0BC2u == buffer->cur (1).codepoint) + { + matched = true; + } + buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + processed = true; + break; + case HB_SCRIPT_TELUGU: for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) { @@ -434,4 +459,6 @@ _hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED, } } + +#endif /* == End of generated functions == */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex.hh index 2756ae1aeeb..59165c46c70 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex.hh @@ -50,8 +50,9 @@ enum hb_ot_shape_zero_width_marks_type_t { /* Master OT shaper list */ #define HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \ - HB_COMPLEX_SHAPER_IMPLEMENT (default) /* should be first */ \ HB_COMPLEX_SHAPER_IMPLEMENT (arabic) \ + HB_COMPLEX_SHAPER_IMPLEMENT (default) \ + HB_COMPLEX_SHAPER_IMPLEMENT (dumber) \ HB_COMPLEX_SHAPER_IMPLEMENT (hangul) \ HB_COMPLEX_SHAPER_IMPLEMENT (hebrew) \ HB_COMPLEX_SHAPER_IMPLEMENT (indic) \ @@ -60,7 +61,7 @@ enum hb_ot_shape_zero_width_marks_type_t { HB_COMPLEX_SHAPER_IMPLEMENT (myanmar_zawgyi) \ HB_COMPLEX_SHAPER_IMPLEMENT (thai) \ HB_COMPLEX_SHAPER_IMPLEMENT (use) \ - /* ^--- Add new shapers here */ + /* ^--- Add new shapers here; keep sorted. */ struct hb_ot_complex_shaper_t @@ -377,6 +378,13 @@ hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner) case HB_SCRIPT_MAKASAR: //case HB_SCRIPT_SOGDIAN: + /* Unicode-12.0 additions */ + case HB_SCRIPT_NANDINAGARI: + + /* Unicode-13.0 additions */ + case HB_SCRIPT_CHORASMIAN: + case HB_SCRIPT_DIVES_AKURU: + /* If the designer designed the font for the 'DFLT' script, * (or we ended up arbitrarily pick 'latn'), use the default shaper. * Otherwise, use the specific shaper. diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc index d29bc9b9ef5..b6b6f0b1535 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-fallback.hh" #include "hb-kern.hh" @@ -166,6 +170,10 @@ _hb_ot_shape_fallback_mark_position_recategorize_marks (const hb_ot_shape_plan_t hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) { +#ifdef HB_NO_OT_SHAPE_FALLBACK + return; +#endif + unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 0; i < count; i++) @@ -414,12 +422,12 @@ position_cluster (const hb_ot_shape_plan_t *plan, /* Find the base glyph */ hb_glyph_info_t *info = buffer->info; for (unsigned int i = start; i < end; i++) - if (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i]))) + if (!_hb_glyph_info_is_unicode_mark (&info[i])) { /* Find mark glyphs */ unsigned int j; for (j = i + 1; j < end; j++) - if (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[j]))) + if (!_hb_glyph_info_is_unicode_mark (&info[j])) break; position_around_base (plan, font, buffer, i, j, adjust_offsets_when_zeroing); @@ -434,13 +442,17 @@ _hb_ot_shape_fallback_mark_position (const hb_ot_shape_plan_t *plan, hb_buffer_t *buffer, bool adjust_offsets_when_zeroing) { +#ifdef HB_NO_OT_SHAPE_FALLBACK + return; +#endif + _hb_buffer_assert_gsubgpos_vars (buffer); unsigned int start = 0; unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 1; i < count; i++) - if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])))) { + if (likely (!_hb_glyph_info_is_unicode_mark (&info[i]))) { position_cluster (plan, font, buffer, start, i, adjust_offsets_when_zeroing); start = i; } @@ -448,6 +460,7 @@ _hb_ot_shape_fallback_mark_position (const hb_ot_shape_plan_t *plan, } +#ifndef HB_DISABLE_DEPRECATED struct hb_ot_shape_fallback_kern_driver_t { hb_ot_shape_fallback_kern_driver_t (hb_font_t *font_, @@ -466,6 +479,7 @@ struct hb_ot_shape_fallback_kern_driver_t hb_font_t *font; hb_direction_t direction; }; +#endif /* Performs font-assisted kerning. */ void @@ -473,6 +487,11 @@ _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) { +#ifdef HB_NO_OT_SHAPE_FALLBACK + return; +#endif + +#ifndef HB_DISABLE_DEPRECATED if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction) ? !font->has_glyph_h_kerning_func () : !font->has_glyph_v_kerning_func ()) @@ -489,6 +508,7 @@ _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan, if (reverse) buffer->reverse (); +#endif } @@ -571,3 +591,6 @@ _hb_ot_shape_fallback_spaces (const hb_ot_shape_plan_t *plan HB_UNUSED, } } } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc index ed73abb1530..aa88b2681bc 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-normalize.hh" #include "hb-ot-shape-complex.hh" #include "hb-ot-shape.hh" @@ -330,7 +334,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, { unsigned int end; for (end = buffer->idx + 1; end < count; end++) - if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end])))) + if (unlikely (_hb_glyph_info_is_unicode_mark (&buffer->info[end]))) break; if (end < count) @@ -356,7 +360,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, /* Find all the marks now. */ for (end = buffer->idx + 1; end < count; end++) - if (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end]))) + if (!_hb_glyph_info_is_unicode_mark(&buffer->info[end])) break; /* idx to end is one non-simple cluster. */ @@ -431,7 +435,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, * This is both an optimization to avoid trying to compose every two neighboring * glyphs in most scripts AND a desired feature for Hangul. Apparently Hangul * fonts are not designed to mix-and-match pre-composed syllables and Jamo. */ - HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->cur()))) + _hb_glyph_info_is_unicode_mark(&buffer->cur())) { if (/* If there's anything between the starter and this char, they should have CCC * smaller than this character's. */ @@ -469,3 +473,6 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, buffer->swap_buffers (); } } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc index e9c8f882162..154725598ba 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc @@ -26,6 +26,14 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#ifdef HB_NO_OT_LAYOUT +#error "Cannot compile 'ot' shaper with HB_NO_OT_LAYOUT." +#endif + #include "hb-shaper-impl.hh" #include "hb-ot-shape.hh" @@ -40,6 +48,16 @@ #include "hb-aat-layout.hh" +#ifndef HB_NO_AAT_SHAPE +static inline bool +_hb_apply_morx (hb_face_t *face, const hb_segment_properties_t *props) +{ + /* https://github.com/harfbuzz/harfbuzz/issues/2124 */ + return hb_aat_layout_has_substitution (face) && + (HB_DIRECTION_IS_HORIZONTAL (props->direction) || !hb_ot_layout_has_substitution (face)); +} +#endif + /** * SECTION:hb-ot-shape * @title: hb-ot-shape @@ -55,36 +73,24 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, const hb_feature_t *user_features, unsigned int num_user_features); -static bool -_hb_apply_morx (hb_face_t *face) -{ - if (hb_options ().aat && - hb_aat_layout_has_substitution (face)) - return true; - - /* Ignore empty GSUB tables. */ - return (!hb_ot_layout_has_substitution (face) || - !hb_ot_layout_table_get_script_tags (face, - HB_OT_TAG_GSUB, - 0, nullptr, nullptr)) && - hb_aat_layout_has_substitution (face); -} - hb_ot_shape_planner_t::hb_ot_shape_planner_t (hb_face_t *face, const hb_segment_properties_t *props) : face (face), props (*props), map (face, props), - aat_map (face, props), - apply_morx (_hb_apply_morx (face)) + aat_map (face, props) +#ifndef HB_NO_AAT_SHAPE + , apply_morx (_hb_apply_morx (face, props)) +#endif { shaper = hb_ot_shape_complex_categorize (this); script_zero_marks = shaper->zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE; script_fallback_mark_positioning = shaper->fallback_position; - if (apply_morx) - shaper = &_hb_ot_complex_shaper_default; + /* https://github.com/harfbuzz/harfbuzz/issues/1528 */ + if (apply_morx && shaper != &_hb_ot_complex_shaper_default) + shaper = &_hb_ot_complex_shaper_dumber; } void @@ -94,21 +100,32 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, plan.props = props; plan.shaper = shaper; map.compile (plan.map, key); +#ifndef HB_NO_AAT_SHAPE if (apply_morx) aat_map.compile (plan.aat_map); +#endif +#ifndef HB_NO_OT_SHAPE_FRACTIONS plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c')); plan.numr_mask = plan.map.get_1_mask (HB_TAG ('n','u','m','r')); plan.dnom_mask = plan.map.get_1_mask (HB_TAG ('d','n','o','m')); plan.has_frac = plan.frac_mask || (plan.numr_mask && plan.dnom_mask); +#endif + plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m')); + plan.has_vert = !!plan.map.get_1_mask (HB_TAG ('v','e','r','t')); + hb_tag_t kern_tag = HB_DIRECTION_IS_HORIZONTAL (props.direction) ? HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n'); +#ifndef HB_NO_OT_KERN plan.kern_mask = plan.map.get_mask (kern_tag); - plan.trak_mask = plan.map.get_mask (HB_TAG ('t','r','a','k')); - plan.requested_kerning = !!plan.kern_mask; +#endif +#ifndef HB_NO_AAT_SHAPE + plan.trak_mask = plan.map.get_mask (HB_TAG ('t','r','a','k')); plan.requested_tracking = !!plan.trak_mask; +#endif + bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX; bool disable_gpos = plan.shaper->gpos_tag && plan.shaper->gpos_tag != plan.map.chosen_script[1]; @@ -124,42 +141,61 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, * Decide who does substitutions. GSUB, morx, or fallback. */ +#ifndef HB_NO_AAT_SHAPE plan.apply_morx = apply_morx; +#endif /* * Decide who does positioning. GPOS, kerx, kern, or fallback. */ - if (hb_options ().aat && hb_aat_layout_has_positioning (face)) + if (0) + ; +#ifndef HB_NO_AAT_SHAPE + else if (hb_aat_layout_has_positioning (face)) plan.apply_kerx = true; +#endif else if (!apply_morx && !disable_gpos && hb_ot_layout_has_positioning (face)) plan.apply_gpos = true; - else if (hb_aat_layout_has_positioning (face)) - plan.apply_kerx = true; - if (!plan.apply_kerx && !has_gpos_kern) + if (!plan.apply_kerx && (!has_gpos_kern || !plan.apply_gpos)) { /* Apparently Apple applies kerx if GPOS kern was not applied. */ +#ifndef HB_NO_AAT_SHAPE if (hb_aat_layout_has_positioning (face)) plan.apply_kerx = true; - else if (hb_ot_layout_has_kerning (face)) + else +#endif +#ifndef HB_NO_OT_KERN + if (hb_ot_layout_has_kerning (face)) plan.apply_kern = true; +#endif } plan.zero_marks = script_zero_marks && !plan.apply_kerx && - (!plan.apply_kern || !hb_ot_layout_has_machine_kerning (face)); + (!plan.apply_kern +#ifndef HB_NO_OT_KERN + || !hb_ot_layout_has_machine_kerning (face) +#endif + ); plan.has_gpos_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k')); plan.adjust_mark_positioning_when_zeroing = !plan.apply_gpos && !plan.apply_kerx && - (!plan.apply_kern || !hb_ot_layout_has_cross_kerning (face)); + (!plan.apply_kern +#ifndef HB_NO_OT_KERN + || !hb_ot_layout_has_cross_kerning (face) +#endif + ); plan.fallback_mark_positioning = plan.adjust_mark_positioning_when_zeroing && script_fallback_mark_positioning; +#ifndef HB_NO_AAT_SHAPE /* Currently we always apply trak. */ plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face); +#endif } bool @@ -167,7 +203,9 @@ hb_ot_shape_plan_t::init0 (hb_face_t *face, const hb_shape_plan_key_t *key) { map.init (); +#ifndef HB_NO_AAT_SHAPE aat_map.init (); +#endif hb_ot_shape_planner_t planner (face, &key->props); @@ -182,7 +220,13 @@ hb_ot_shape_plan_t::init0 (hb_face_t *face, { data = shaper->data_create (this); if (unlikely (!data)) + { + map.fini (); +#ifndef HB_NO_AAT_SHAPE + aat_map.fini (); +#endif return false; + } } return true; @@ -195,16 +239,20 @@ hb_ot_shape_plan_t::fini () shaper->data_destroy (const_cast (data)); map.fini (); +#ifndef HB_NO_AAT_SHAPE aat_map.fini (); +#endif } void hb_ot_shape_plan_t::substitute (hb_font_t *font, hb_buffer_t *buffer) const { +#ifndef HB_NO_AAT_SHAPE if (unlikely (apply_morx)) hb_aat_layout_substitute (this, font, buffer); else +#endif map.substitute (this, font, buffer); } @@ -214,21 +262,29 @@ hb_ot_shape_plan_t::position (hb_font_t *font, { if (this->apply_gpos) map.position (this, font, buffer); +#ifndef HB_NO_AAT_SHAPE else if (this->apply_kerx) hb_aat_layout_position (this, font, buffer); +#endif +#ifndef HB_NO_OT_KERN else if (this->apply_kern) hb_ot_layout_kern (this, font, buffer); +#endif else _hb_ot_shape_fallback_kern (this, font, buffer); +#ifndef HB_NO_AAT_SHAPE if (this->apply_trak) hb_aat_layout_track (this, font, buffer); +#endif } static const hb_ot_map_feature_t common_features[] = { + {HB_TAG('a','b','v','m'), F_GLOBAL}, + {HB_TAG('b','l','w','m'), F_GLOBAL}, {HB_TAG('c','c','m','p'), F_GLOBAL}, {HB_TAG('l','o','c','l'), F_GLOBAL}, {HB_TAG('m','a','r','k'), F_GLOBAL_MANUAL_JOINERS}, @@ -243,6 +299,7 @@ horizontal_features[] = {HB_TAG('c','a','l','t'), F_GLOBAL}, {HB_TAG('c','l','i','g'), F_GLOBAL}, {HB_TAG('c','u','r','s'), F_GLOBAL}, + {HB_TAG('d','i','s','t'), F_GLOBAL}, {HB_TAG('k','e','r','n'), F_GLOBAL_HAS_FALLBACK}, {HB_TAG('l','i','g','a'), F_GLOBAL}, {HB_TAG('r','c','l','t'), F_GLOBAL}, @@ -274,18 +331,22 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, break; } +#ifndef HB_NO_OT_SHAPE_FRACTIONS /* Automatic fractions. */ map->add_feature (HB_TAG ('f','r','a','c')); map->add_feature (HB_TAG ('n','u','m','r')); map->add_feature (HB_TAG ('d','n','o','m')); +#endif /* Random! */ map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE); +#ifndef HB_NO_AAT_SHAPE /* Tracking. We enable dummy feature here just to allow disabling * AAT 'trak' table using features. * https://github.com/harfbuzz/harfbuzz/issues/1303 */ map->enable_feature (HB_TAG ('t','r','a','k'), F_HAS_FALLBACK); +#endif map->enable_feature (HB_TAG ('H','A','R','F')); @@ -318,6 +379,7 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, feature->value); } +#ifndef HB_NO_AAT_SHAPE if (planner->apply_morx) { hb_aat_map_builder_t *aat_map = &planner->aat_map; @@ -327,6 +389,7 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, aat_map->add_feature (feature->tag, feature->value); } } +#endif if (planner->shaper->override_features) planner->shaper->override_features (planner); @@ -417,6 +480,7 @@ hb_set_unicode_props (hb_buffer_t *buffer) { _hb_glyph_info_set_continuation (&info[i]); } +#ifndef HB_NO_EMOJI_SEQUENCES else if (unlikely (_hb_glyph_info_is_zwj (&info[i]))) { _hb_glyph_info_set_continuation (&info[i]); @@ -428,6 +492,7 @@ hb_set_unicode_props (hb_buffer_t *buffer) _hb_glyph_info_set_continuation (&info[i]); } } +#endif /* Or part of the Other_Grapheme_Extend that is not marks. * As of Unicode 11 that is just: * @@ -448,6 +513,9 @@ hb_set_unicode_props (hb_buffer_t *buffer) static void hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font) { + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + if (!(buffer->flags & HB_BUFFER_FLAG_BOT) || buffer->context_len[0] || !_hb_glyph_info_is_unicode_mark (&buffer->info[0])) @@ -524,30 +592,95 @@ hb_ensure_native_direction (hb_buffer_t *buffer) * Substitute */ -static inline void -hb_ot_mirror_chars (const hb_ot_shape_context_t *c) +static hb_codepoint_t +hb_vert_char_for (hb_codepoint_t u) { - if (HB_DIRECTION_IS_FORWARD (c->target_direction)) - return; + switch (u >> 8) + { + case 0x20: switch (u) { + case 0x2013u: return 0xfe32u; // EN DASH + case 0x2014u: return 0xfe31u; // EM DASH + case 0x2025u: return 0xfe30u; // TWO DOT LEADER + case 0x2026u: return 0xfe19u; // HORIZONTAL ELLIPSIS + } break; + case 0x30: switch (u) { + case 0x3001u: return 0xfe11u; // IDEOGRAPHIC COMMA + case 0x3002u: return 0xfe12u; // IDEOGRAPHIC FULL STOP + case 0x3008u: return 0xfe3fu; // LEFT ANGLE BRACKET + case 0x3009u: return 0xfe40u; // RIGHT ANGLE BRACKET + case 0x300au: return 0xfe3du; // LEFT DOUBLE ANGLE BRACKET + case 0x300bu: return 0xfe3eu; // RIGHT DOUBLE ANGLE BRACKET + case 0x300cu: return 0xfe41u; // LEFT CORNER BRACKET + case 0x300du: return 0xfe42u; // RIGHT CORNER BRACKET + case 0x300eu: return 0xfe43u; // LEFT WHITE CORNER BRACKET + case 0x300fu: return 0xfe44u; // RIGHT WHITE CORNER BRACKET + case 0x3010u: return 0xfe3bu; // LEFT BLACK LENTICULAR BRACKET + case 0x3011u: return 0xfe3cu; // RIGHT BLACK LENTICULAR BRACKET + case 0x3014u: return 0xfe39u; // LEFT TORTOISE SHELL BRACKET + case 0x3015u: return 0xfe3au; // RIGHT TORTOISE SHELL BRACKET + case 0x3016u: return 0xfe17u; // LEFT WHITE LENTICULAR BRACKET + case 0x3017u: return 0xfe18u; // RIGHT WHITE LENTICULAR BRACKET + } break; + case 0xfe: switch (u) { + case 0xfe4fu: return 0xfe34u; // WAVY LOW LINE + } break; + case 0xff: switch (u) { + case 0xff01u: return 0xfe15u; // FULLWIDTH EXCLAMATION MARK + case 0xff08u: return 0xfe35u; // FULLWIDTH LEFT PARENTHESIS + case 0xff09u: return 0xfe36u; // FULLWIDTH RIGHT PARENTHESIS + case 0xff0cu: return 0xfe10u; // FULLWIDTH COMMA + case 0xff1au: return 0xfe13u; // FULLWIDTH COLON + case 0xff1bu: return 0xfe14u; // FULLWIDTH SEMICOLON + case 0xff1fu: return 0xfe16u; // FULLWIDTH QUESTION MARK + case 0xff3bu: return 0xfe47u; // FULLWIDTH LEFT SQUARE BRACKET + case 0xff3du: return 0xfe48u; // FULLWIDTH RIGHT SQUARE BRACKET + case 0xff3fu: return 0xfe33u; // FULLWIDTH LOW LINE + case 0xff5bu: return 0xfe37u; // FULLWIDTH LEFT CURLY BRACKET + case 0xff5du: return 0xfe38u; // FULLWIDTH RIGHT CURLY BRACKET + } break; + } - hb_buffer_t *buffer = c->buffer; - hb_unicode_funcs_t *unicode = buffer->unicode; - hb_mask_t rtlm_mask = c->plan->rtlm_mask; + return u; +} +static inline void +hb_ot_rotate_chars (const hb_ot_shape_context_t *c) +{ + hb_buffer_t *buffer = c->buffer; unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; - for (unsigned int i = 0; i < count; i++) { - hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint); - if (likely (codepoint == info[i].codepoint || !c->font->has_glyph (codepoint))) - info[i].mask |= rtlm_mask; - else - info[i].codepoint = codepoint; + + if (HB_DIRECTION_IS_BACKWARD (c->target_direction)) + { + hb_unicode_funcs_t *unicode = buffer->unicode; + hb_mask_t rtlm_mask = c->plan->rtlm_mask; + + for (unsigned int i = 0; i < count; i++) { + hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint); + if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint))) + info[i].codepoint = codepoint; + else + info[i].mask |= rtlm_mask; + } + } + + if (HB_DIRECTION_IS_VERTICAL (c->target_direction) && !c->plan->has_vert) + { + for (unsigned int i = 0; i < count; i++) { + hb_codepoint_t codepoint = hb_vert_char_for (info[i].codepoint); + if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint))) + info[i].codepoint = codepoint; + } } } static inline void hb_ot_shape_setup_masks_fraction (const hb_ot_shape_context_t *c) { +#ifdef HB_NO_OT_SHAPE_FRACTIONS + return; +#endif + if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) || !c->plan->has_frac) return; @@ -619,7 +752,7 @@ hb_ot_shape_setup_masks (const hb_ot_shape_context_t *c) for (unsigned int i = 0; i < c->num_user_features; i++) { const hb_feature_t *feature = &c->user_features[i]; - if (!(feature->start == 0 && feature->end == (unsigned int)-1)) { + if (!(feature->start == HB_FEATURE_GLOBAL_START && feature->end == HB_FEATURE_GLOBAL_END)) { unsigned int shift; hb_mask_t mask = map->get_mask (feature->tag, &shift); buffer->set_masks (feature->value << shift, mask, feature->start, feature->end); @@ -714,7 +847,7 @@ hb_ot_substitute_default (const hb_ot_shape_context_t *c) { hb_buffer_t *buffer = c->buffer; - hb_ot_mirror_chars (c); + hb_ot_rotate_chars (c); HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index); @@ -758,8 +891,10 @@ static inline void hb_ot_substitute_post (const hb_ot_shape_context_t *c) { hb_ot_hide_default_ignorables (c->buffer, c->font); +#ifndef HB_NO_AAT_SHAPE if (c->plan->apply_morx) hb_aat_layout_remove_deleted_glyphs (c->buffer); +#endif if (c->plan->shaper->postprocess_glyphs) c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font); @@ -893,8 +1028,10 @@ hb_ot_position_complex (const hb_ot_shape_context_t *c) /* Finish off. Has to follow a certain order. */ hb_ot_layout_position_finish_advances (c->font, c->buffer); hb_ot_zero_width_default_ignorables (c->buffer); +#ifndef HB_NO_AAT_SHAPE if (c->plan->apply_morx) hb_aat_layout_zero_width_deleted_glyphs (c->buffer); +#endif hb_ot_layout_position_finish_offsets (c->font, c->buffer); /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ @@ -959,13 +1096,13 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c) c->buffer->scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_LEN_FACTOR))) { - c->buffer->max_len = MAX (c->buffer->len * HB_BUFFER_MAX_LEN_FACTOR, - (unsigned) HB_BUFFER_MAX_LEN_MIN); + c->buffer->max_len = hb_max (c->buffer->len * HB_BUFFER_MAX_LEN_FACTOR, + (unsigned) HB_BUFFER_MAX_LEN_MIN); } if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_OPS_FACTOR))) { - c->buffer->max_ops = MAX (c->buffer->len * HB_BUFFER_MAX_OPS_FACTOR, - (unsigned) HB_BUFFER_MAX_OPS_MIN); + c->buffer->max_ops = hb_max (c->buffer->len * HB_BUFFER_MAX_OPS_FACTOR, + (unsigned) HB_BUFFER_MAX_OPS_MIN); } /* Save the original direction, we use it later. */ @@ -1081,3 +1218,6 @@ hb_ot_shape_glyphs_closure (hb_font_t *font, hb_shape_plan_destroy (shape_plan); } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh index 5adc05827e6..7823126ee46 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh @@ -37,9 +37,9 @@ struct hb_ot_shape_plan_key_t { unsigned int variations_index[2]; - void init (hb_face_t *face, - const int *coords, - unsigned int num_coords) + void init (hb_face_t *face, + const int *coords, + unsigned num_coords) { for (unsigned int table_index = 0; table_index < 2; table_index++) hb_ot_layout_table_find_feature_variations (face, @@ -65,14 +65,41 @@ struct hb_ot_shape_plan_t hb_ot_map_t map; hb_aat_map_t aat_map; const void *data; +#ifndef HB_NO_OT_SHAPE_FRACTIONS hb_mask_t frac_mask, numr_mask, dnom_mask; +#else + static constexpr hb_mask_t frac_mask = 0; + static constexpr hb_mask_t numr_mask = 0; + static constexpr hb_mask_t dnom_mask = 0; +#endif hb_mask_t rtlm_mask; +#ifndef HB_NO_OT_KERN hb_mask_t kern_mask; +#else + static constexpr hb_mask_t kern_mask = 0; +#endif +#ifndef HB_NO_AAT_SHAPE hb_mask_t trak_mask; +#else + static constexpr hb_mask_t trak_mask = 0; +#endif +#ifndef HB_NO_OT_KERN bool requested_kerning : 1; +#else + static constexpr bool requested_kerning = false; +#endif +#ifndef HB_NO_AAT_SHAPE bool requested_tracking : 1; +#else + static constexpr bool requested_tracking = false; +#endif +#ifndef HB_NO_OT_SHAPE_FRACTIONS bool has_frac : 1; +#else + static constexpr bool has_frac = false; +#endif + bool has_vert : 1; bool has_gpos_mark : 1; bool zero_marks : 1; bool fallback_glyph_classes : 1; @@ -80,10 +107,20 @@ struct hb_ot_shape_plan_t bool adjust_mark_positioning_when_zeroing : 1; bool apply_gpos : 1; - bool apply_kerx : 1; +#ifndef HB_NO_OT_KERN bool apply_kern : 1; +#else + static constexpr bool apply_kern = false; +#endif +#ifndef HB_NO_AAT_SHAPE + bool apply_kerx : 1; bool apply_morx : 1; bool apply_trak : 1; +#else + static constexpr bool apply_kerx = false; + static constexpr bool apply_morx = false; + static constexpr bool apply_trak = false; +#endif void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const { @@ -113,7 +150,11 @@ struct hb_ot_shape_planner_t hb_segment_properties_t props; hb_ot_map_builder_t map; hb_aat_map_builder_t aat_map; +#ifndef HB_NO_AAT_SHAPE bool apply_morx : 1; +#else + static constexpr bool apply_morx = false; +#endif bool script_zero_marks : 1; bool script_fallback_mark_positioning : 1; const struct hb_ot_complex_shaper_t *shaper; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh index 63dbfce4551..ca97affc9a6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh @@ -59,6 +59,11 @@ enum struct AxisValueFormat1 { + unsigned int get_axis_index () const { return axisIndex; } + float get_value () const { return value.to_float (); } + + hb_ot_name_id_t get_value_name_id () const { return valueNameID; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -75,13 +80,18 @@ struct AxisValueFormat1 NameID valueNameID; /* The name ID for entries in the 'name' table * that provide a display string for this * attribute value. */ - Fixed value; /* A numeric value for this attribute value. */ + HBFixed value; /* A numeric value for this attribute value. */ public: DEFINE_SIZE_STATIC (12); }; struct AxisValueFormat2 { + unsigned int get_axis_index () const { return axisIndex; } + float get_value () const { return nominalValue.to_float (); } + + hb_ot_name_id_t get_value_name_id () const { return valueNameID; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -98,10 +108,10 @@ struct AxisValueFormat2 NameID valueNameID; /* The name ID for entries in the 'name' table * that provide a display string for this * attribute value. */ - Fixed nominalValue; /* A numeric value for this attribute value. */ - Fixed rangeMinValue; /* The minimum value for a range associated + HBFixed nominalValue; /* A numeric value for this attribute value. */ + HBFixed rangeMinValue; /* The minimum value for a range associated * with the specified name ID. */ - Fixed rangeMaxValue; /* The maximum value for a range associated + HBFixed rangeMaxValue; /* The maximum value for a range associated * with the specified name ID. */ public: DEFINE_SIZE_STATIC (20); @@ -109,6 +119,11 @@ struct AxisValueFormat2 struct AxisValueFormat3 { + unsigned int get_axis_index () const { return axisIndex; } + float get_value () const { return value.to_float (); } + + hb_ot_name_id_t get_value_name_id () const { return valueNameID; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -125,8 +140,8 @@ struct AxisValueFormat3 NameID valueNameID; /* The name ID for entries in the 'name' table * that provide a display string for this * attribute value. */ - Fixed value; /* A numeric value for this attribute value. */ - Fixed linkedValue; /* The numeric value for a style-linked mapping + HBFixed value; /* A numeric value for this attribute value. */ + HBFixed linkedValue; /* The numeric value for a style-linked mapping * from this value. */ public: DEFINE_SIZE_STATIC (16); @@ -134,6 +149,9 @@ struct AxisValueFormat3 struct AxisValueRecord { + unsigned int get_axis_index () const { return axisIndex; } + float get_value () const { return value.to_float (); } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -144,13 +162,18 @@ struct AxisValueRecord HBUINT16 axisIndex; /* Zero-base index into the axis record array * identifying the axis to which this value * applies. Must be less than designAxisCount. */ - Fixed value; /* A numeric value for this attribute value. */ + HBFixed value; /* A numeric value for this attribute value. */ public: DEFINE_SIZE_STATIC (6); }; struct AxisValueFormat4 { + const AxisValueRecord &get_axis_record (unsigned int axis_index) const + { return axisValues.as_array (axisCount)[axis_index]; } + + hb_ot_name_id_t get_value_name_id () const { return valueNameID; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -175,19 +198,55 @@ struct AxisValueFormat4 struct AxisValue { + bool get_value (unsigned int axis_index) const + { + switch (u.format) + { + case 1: return u.format1.get_value (); + case 2: return u.format2.get_value (); + case 3: return u.format3.get_value (); + case 4: return u.format4.get_axis_record (axis_index).get_value (); + default:return 0; + } + } + + unsigned int get_axis_index () const + { + switch (u.format) + { + case 1: return u.format1.get_axis_index (); + case 2: return u.format2.get_axis_index (); + case 3: return u.format3.get_axis_index (); + /* case 4: Makes more sense for variable fonts which are handled by fvar in hb-style */ + default:return -1; + } + } + + hb_ot_name_id_t get_value_name_id () const + { + switch (u.format) + { + case 1: return u.format1.get_value_name_id (); + case 2: return u.format2.get_value_name_id (); + case 3: return u.format3.get_value_name_id (); + case 4: return u.format4.get_value_name_id (); + default:return HB_OT_NAME_ID_INVALID; + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (unlikely (c->check_struct (this))) + if (unlikely (!c->check_struct (this))) return_trace (false); switch (u.format) { - case 1: return_trace (likely (u.format1.sanitize (c))); - case 2: return_trace (likely (u.format2.sanitize (c))); - case 3: return_trace (likely (u.format3.sanitize (c))); - case 4: return_trace (likely (u.format4.sanitize (c))); - default: return_trace (true); + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 3: return_trace (u.format3.sanitize (c)); + case 4: return_trace (u.format4.sanitize (c)); + default:return_trace (true); } } @@ -206,6 +265,10 @@ struct AxisValue struct StatAxisRecord { + int cmp (hb_tag_t key) const { return tag.cmp (key); } + + hb_ot_name_id_t get_name_id () const { return nameID; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -227,21 +290,82 @@ struct STAT { static constexpr hb_tag_t tableTag = HB_OT_TAG_STAT; + bool has_data () const { return version.to_int (); } + + bool get_value (hb_tag_t tag, float *value) const + { + unsigned int axis_index; + if (!get_design_axes ().lfind (tag, &axis_index)) return false; + + hb_array_t> axis_values = get_axis_value_offsets (); + for (unsigned int i = 0; i < axis_values.length; i++) + { + const AxisValue& axis_value = this+axis_values[i]; + if (axis_value.get_axis_index () == axis_index) + { + if (value) + *value = axis_value.get_value (axis_index); + return true; + } + } + return false; + } + + unsigned get_design_axis_count () const { return designAxisCount; } + + hb_ot_name_id_t get_axis_record_name_id (unsigned axis_record_index) const + { + if (unlikely (axis_record_index >= designAxisCount)) return HB_OT_NAME_ID_INVALID; + const StatAxisRecord &axis_record = get_design_axes ()[axis_record_index]; + return axis_record.get_name_id (); + } + + unsigned get_axis_value_count () const { return axisValueCount; } + + hb_ot_name_id_t get_axis_value_name_id (unsigned axis_value_index) const + { + if (unlikely (axis_value_index >= axisValueCount)) return HB_OT_NAME_ID_INVALID; + const AxisValue &axis_value = (this + get_axis_value_offsets ()[axis_value_index]); + return axis_value.get_value_name_id (); + } + + void collect_name_ids (hb_set_t *nameids_to_retain) const + { + if (!has_data ()) return; + + + get_design_axes () + | hb_map (&StatAxisRecord::get_name_id) + | hb_sink (nameids_to_retain) + ; + + + get_axis_value_offsets () + | hb_map (hb_add (&(this + offsetToAxisValueOffsets))) + | hb_map (&AxisValue::get_value_name_id) + | hb_sink (nameids_to_retain) + ; + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && - majorVersion == 1 && - minorVersion > 0 && + version.major == 1 && + version.minor > 0 && designAxesOffset.sanitize (c, this, designAxisCount) && offsetToAxisValueOffsets.sanitize (c, this, axisValueCount, &(this+offsetToAxisValueOffsets)))); } protected: - HBUINT16 majorVersion; /* Major version number of the style attributes - * table — set to 1. */ - HBUINT16 minorVersion; /* Minor version number of the style attributes - * table — set to 2. */ + hb_array_t const get_design_axes () const + { return (this+designAxesOffset).as_array (designAxisCount); } + + hb_array_t> const get_axis_value_offsets () const + { return (this+offsetToAxisValueOffsets).as_array (axisValueCount); } + + + protected: + FixedVersion<>version; /* Version of the stat table + * initially set to 0x00010002u */ HBUINT16 designAxisSize; /* The size in bytes of each axis record. */ HBUINT16 designAxisCount;/* The number of design axis records. In a * font with an 'fvar' table, this value must be @@ -249,7 +373,7 @@ struct STAT * in the 'fvar' table. In all fonts, must * be greater than zero if axisValueCount * is greater than zero. */ - LNNOffsetTo > + LNNOffsetTo> designAxesOffset; /* Offset in bytes from the beginning of * the STAT table to the start of the design @@ -257,7 +381,7 @@ struct STAT * set to zero; if designAxisCount is greater * than zero, must be greater than zero. */ HBUINT16 axisValueCount; /* The number of axis value tables. */ - LNNOffsetTo > > + LNNOffsetTo>> offsetToAxisValueOffsets; /* Offset in bytes from the beginning of * the STAT table to the start of the design diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh index 93333bb3ff0..9109b487b91 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh @@ -6,1055 +6,1058 @@ * * on files with these headers: * - * - * File-Date: 2018-08-08 + * + * File-Date: 2020-05-12 */ #ifndef HB_OT_TAG_TABLE_HH #define HB_OT_TAG_TABLE_HH static const LangTag ot_languages[] = { - {"aa", {HB_TAG('A','F','R',' ')}}, /* Afar */ - {"aae", {HB_TAG('S','Q','I',' ')}}, /* Arbëreshë Albanian -> Albanian */ - {"aao", {HB_TAG('A','R','A',' ')}}, /* Algerian Saharan Arabic -> Arabic */ - {"aat", {HB_TAG('S','Q','I',' ')}}, /* Arvanitika Albanian -> Albanian */ - {"ab", {HB_TAG('A','B','K',' ')}}, /* Abkhazian */ - {"abh", {HB_TAG('A','R','A',' ')}}, /* Tajiki Arabic -> Arabic */ - {"abq", {HB_TAG('A','B','A',' ')}}, /* Abaza */ - {"abv", {HB_TAG('A','R','A',' ')}}, /* Baharna Arabic -> Arabic */ - {"acf", {HB_TAG('F','A','N',' ')}}, /* Saint Lucian Creole French -> French Antillean */ - {"ach", {HB_TAG('A','C','H',' ')}}, /* Acoli -> Acholi */ - {"acm", {HB_TAG('A','R','A',' ')}}, /* Mesopotamian Arabic -> Arabic */ - {"acq", {HB_TAG('A','R','A',' ')}}, /* Ta'izzi-Adeni Arabic -> Arabic */ - {"acr", {HB_TAG('A','C','R',' ')}}, /* Achi */ - {"acw", {HB_TAG('A','R','A',' ')}}, /* Hijazi Arabic -> Arabic */ - {"acx", {HB_TAG('A','R','A',' ')}}, /* Omani Arabic -> Arabic */ - {"acy", {HB_TAG('A','R','A',' ')}}, /* Cypriot Arabic -> Arabic */ - {"ada", {HB_TAG('D','N','G',' ')}}, /* Adangme -> Dangme */ - {"adf", {HB_TAG('A','R','A',' ')}}, /* Dhofari Arabic -> Arabic */ - {"adp", {HB_TAG('D','Z','N',' ')}}, /* Adap (retired code) -> Dzongkha */ - {"ady", {HB_TAG('A','D','Y',' ')}}, /* Adyghe */ - {"aeb", {HB_TAG('A','R','A',' ')}}, /* Tunisian Arabic -> Arabic */ - {"aec", {HB_TAG('A','R','A',' ')}}, /* Saidi Arabic -> Arabic */ - {"af", {HB_TAG('A','F','K',' ')}}, /* Afrikaans */ - {"afb", {HB_TAG('A','R','A',' ')}}, /* Gulf Arabic -> Arabic */ - {"ahg", {HB_TAG('A','G','W',' ')}}, /* Qimant -> Agaw */ - {"aht", {HB_TAG('A','T','H',' ')}}, /* Ahtena -> Athapaskan */ - {"aii", {HB_TAG('S','W','A',' '), /* Assyrian Neo-Aramaic -> Swadaya Aramaic */ - HB_TAG('S','Y','R',' ')}}, /* Assyrian Neo-Aramaic -> Syriac */ - {"aio", {HB_TAG('A','I','O',' ')}}, /* Aiton */ - {"aiw", {HB_TAG('A','R','I',' ')}}, /* Aari */ - {"ajp", {HB_TAG('A','R','A',' ')}}, /* South Levantine Arabic -> Arabic */ - {"ak", {HB_TAG('A','K','A',' '), /* Akan [macrolanguage] */ - HB_TAG('T','W','I',' ')}}, /* Akan [macrolanguage] -> Twi */ - {"aln", {HB_TAG('S','Q','I',' ')}}, /* Gheg Albanian -> Albanian */ - {"als", {HB_TAG('S','Q','I',' ')}}, /* Tosk Albanian -> Albanian */ - {"alt", {HB_TAG('A','L','T',' ')}}, /* Southern Altai -> Altai */ - {"am", {HB_TAG('A','M','H',' ')}}, /* Amharic */ - {"amf", {HB_TAG('H','B','N',' ')}}, /* Hamer-Banna -> Hammer-Banna */ - {"amw", {HB_TAG('S','Y','R',' ')}}, /* Western Neo-Aramaic -> Syriac */ - {"an", {HB_TAG('A','R','G',' ')}}, /* Aragonese */ - {"ang", {HB_TAG('A','N','G',' ')}}, /* Old English (ca. 450-1100) -> Anglo-Saxon */ - {"apc", {HB_TAG('A','R','A',' ')}}, /* North Levantine Arabic -> Arabic */ - {"apd", {HB_TAG('A','R','A',' ')}}, /* Sudanese Arabic -> Arabic */ - {"apj", {HB_TAG('A','T','H',' ')}}, /* Jicarilla Apache -> Athapaskan */ - {"apk", {HB_TAG('A','T','H',' ')}}, /* Kiowa Apache -> Athapaskan */ - {"apl", {HB_TAG('A','T','H',' ')}}, /* Lipan Apache -> Athapaskan */ - {"apm", {HB_TAG('A','T','H',' ')}}, /* Mescalero-Chiricahua Apache -> Athapaskan */ - {"apw", {HB_TAG('A','T','H',' ')}}, /* Western Apache -> Athapaskan */ - {"ar", {HB_TAG('A','R','A',' ')}}, /* Arabic [macrolanguage] */ - {"arb", {HB_TAG('A','R','A',' ')}}, /* Standard Arabic -> Arabic */ - {"arn", {HB_TAG('M','A','P',' ')}}, /* Mapudungun */ - {"arq", {HB_TAG('A','R','A',' ')}}, /* Algerian Arabic -> Arabic */ - {"ars", {HB_TAG('A','R','A',' ')}}, /* Najdi Arabic -> Arabic */ - {"ary", {HB_TAG('M','O','R',' ')}}, /* Moroccan Arabic -> Moroccan */ - {"arz", {HB_TAG('A','R','A',' ')}}, /* Egyptian Arabic -> Arabic */ - {"as", {HB_TAG('A','S','M',' ')}}, /* Assamese */ - {"ast", {HB_TAG('A','S','T',' ')}}, /* Asturian */ - {"ath", {HB_TAG('A','T','H',' ')}}, /* Athapascan [family] -> Athapaskan */ - {"atj", {HB_TAG('R','C','R',' ')}}, /* Atikamekw -> R-Cree */ - {"atv", {HB_TAG('A','L','T',' ')}}, /* Northern Altai -> Altai */ - {"auz", {HB_TAG('A','R','A',' ')}}, /* Uzbeki Arabic -> Arabic */ - {"av", {HB_TAG('A','V','R',' ')}}, /* Avaric -> Avar */ - {"avl", {HB_TAG('A','R','A',' ')}}, /* Eastern Egyptian Bedawi Arabic -> Arabic */ - {"awa", {HB_TAG('A','W','A',' ')}}, /* Awadhi */ - {"ay", {HB_TAG('A','Y','M',' ')}}, /* Aymara [macrolanguage] */ - {"ayc", {HB_TAG('A','Y','M',' ')}}, /* Southern Aymara -> Aymara */ - {"ayh", {HB_TAG('A','R','A',' ')}}, /* Hadrami Arabic -> Arabic */ - {"ayl", {HB_TAG('A','R','A',' ')}}, /* Libyan Arabic -> Arabic */ - {"ayn", {HB_TAG('A','R','A',' ')}}, /* Sanaani Arabic -> Arabic */ - {"ayp", {HB_TAG('A','R','A',' ')}}, /* North Mesopotamian Arabic -> Arabic */ - {"ayr", {HB_TAG('A','Y','M',' ')}}, /* Central Aymara -> Aymara */ - {"az", {HB_TAG('A','Z','E',' ')}}, /* Azerbaijani [macrolanguage] */ - {"azb", {HB_TAG('A','Z','B',' ')}}, /* South Azerbaijani -> Torki */ - {"azj", {HB_TAG('A','Z','E',' ')}}, /* North Azerbaijani -> Azerbaijani */ - {"ba", {HB_TAG('B','S','H',' ')}}, /* Bashkir */ - {"bad", {HB_TAG('B','A','D','0')}}, /* Banda [family] */ - {"bai", {HB_TAG('B','M','L',' ')}}, /* Bamileke [family] */ - {"bal", {HB_TAG('B','L','I',' ')}}, /* Baluchi [macrolanguage] */ - {"ban", {HB_TAG('B','A','N',' ')}}, /* Balinese */ - {"bar", {HB_TAG('B','A','R',' ')}}, /* Bavarian */ - {"bbc", {HB_TAG('B','B','C',' ')}}, /* Batak Toba */ - {"bbz", {HB_TAG('A','R','A',' ')}}, /* Babalia Creole Arabic -> Arabic */ - {"bcc", {HB_TAG('B','L','I',' ')}}, /* Southern Balochi -> Baluchi */ - {"bci", {HB_TAG('B','A','U',' ')}}, /* Baoulé -> Baulé */ - {"bcl", {HB_TAG('B','I','K',' ')}}, /* Central Bikol -> Bikol */ - {"bcq", {HB_TAG('B','C','H',' ')}}, /* Bench */ - {"bcr", {HB_TAG('A','T','H',' ')}}, /* Babine -> Athapaskan */ - {"bdy", {HB_TAG('B','D','Y',' ')}}, /* Bandjalang */ - {"be", {HB_TAG('B','E','L',' ')}}, /* Belarusian -> Belarussian */ - {"bea", {HB_TAG('A','T','H',' ')}}, /* Beaver -> Athapaskan */ - {"beb", {HB_TAG('B','T','I',' ')}}, /* Bebele -> Beti */ - {"bem", {HB_TAG('B','E','M',' ')}}, /* Bemba (Zambia) */ - {"ber", {HB_TAG('B','B','R',' ')}}, /* Berber [family] */ - {"bfq", {HB_TAG('B','A','D',' ')}}, /* Badaga */ - {"bft", {HB_TAG('B','L','T',' ')}}, /* Balti */ - {"bfu", {HB_TAG('L','A','H',' ')}}, /* Gahri -> Lahuli */ - {"bfy", {HB_TAG('B','A','G',' ')}}, /* Bagheli -> Baghelkhandi */ - {"bg", {HB_TAG('B','G','R',' ')}}, /* Bulgarian */ - {"bgc", {HB_TAG('B','G','C',' ')}}, /* Haryanvi */ - {"bgn", {HB_TAG('B','L','I',' ')}}, /* Western Balochi -> Baluchi */ - {"bgp", {HB_TAG('B','L','I',' ')}}, /* Eastern Balochi -> Baluchi */ - {"bgq", {HB_TAG('B','G','Q',' ')}}, /* Bagri */ - {"bgr", {HB_TAG('Q','I','N',' ')}}, /* Bawm Chin -> Chin */ - {"bhb", {HB_TAG('B','H','I',' ')}}, /* Bhili */ - {"bhi", {HB_TAG('B','H','I',' ')}}, /* Bhilali -> Bhili */ - {"bhk", {HB_TAG('B','I','K',' ')}}, /* Albay Bicolano (retired code) -> Bikol */ - {"bho", {HB_TAG('B','H','O',' ')}}, /* Bhojpuri */ - {"bhr", {HB_TAG('M','L','G',' ')}}, /* Bara Malagasy -> Malagasy */ - {"bi", {HB_TAG('B','I','S',' ')}}, /* Bislama */ - {"bik", {HB_TAG('B','I','K',' ')}}, /* Bikol [macrolanguage] */ - {"bin", {HB_TAG('E','D','O',' ')}}, /* Edo */ - {"bjj", {HB_TAG('B','J','J',' ')}}, /* Kanauji */ - {"bjn", {HB_TAG('M','L','Y',' ')}}, /* Banjar -> Malay */ - {"bjq", {HB_TAG('M','L','G',' ')}}, /* Southern Betsimisaraka Malagasy (retired code) -> Malagasy */ - {"bjt", {HB_TAG('B','L','N',' ')}}, /* Balanta-Ganja -> Balante */ - {"bla", {HB_TAG('B','K','F',' ')}}, /* Siksika -> Blackfoot */ - {"ble", {HB_TAG('B','L','N',' ')}}, /* Balanta-Kentohe -> Balante */ - {"blk", {HB_TAG('B','L','K',' ')}}, /* Pa'o Karen */ - {"bln", {HB_TAG('B','I','K',' ')}}, /* Southern Catanduanes Bikol -> Bikol */ - {"bm", {HB_TAG('B','M','B',' ')}}, /* Bambara (Bamanankan) */ - {"bmm", {HB_TAG('M','L','G',' ')}}, /* Northern Betsimisaraka Malagasy -> Malagasy */ - {"bn", {HB_TAG('B','E','N',' ')}}, /* Bengali */ - {"bo", {HB_TAG('T','I','B',' ')}}, /* Tibetan */ - {"bpy", {HB_TAG('B','P','Y',' ')}}, /* Bishnupriya -> Bishnupriya Manipuri */ - {"bqi", {HB_TAG('L','R','C',' ')}}, /* Bakhtiari -> Luri */ - {"br", {HB_TAG('B','R','E',' ')}}, /* Breton */ - {"bra", {HB_TAG('B','R','I',' ')}}, /* Braj -> Braj Bhasha */ - {"brh", {HB_TAG('B','R','H',' ')}}, /* Brahui */ - {"brx", {HB_TAG('B','R','X',' ')}}, /* Bodo (India) */ - {"bs", {HB_TAG('B','O','S',' ')}}, /* Bosnian */ - {"bsk", {HB_TAG('B','S','K',' ')}}, /* Burushaski */ - {"btb", {HB_TAG('B','T','I',' ')}}, /* Beti (Cameroon) (retired code) */ - {"btj", {HB_TAG('M','L','Y',' ')}}, /* Bacanese Malay -> Malay */ - {"bto", {HB_TAG('B','I','K',' ')}}, /* Rinconada Bikol -> Bikol */ - {"bts", {HB_TAG('B','T','S',' ')}}, /* Batak Simalungun */ - {"bug", {HB_TAG('B','U','G',' ')}}, /* Buginese -> Bugis */ - {"bum", {HB_TAG('B','T','I',' ')}}, /* Bulu (Cameroon) -> Beti */ - {"bve", {HB_TAG('M','L','Y',' ')}}, /* Berau Malay -> Malay */ - {"bvu", {HB_TAG('M','L','Y',' ')}}, /* Bukit Malay -> Malay */ - {"bxk", {HB_TAG('L','U','H',' ')}}, /* Bukusu -> Luyia */ - {"bxp", {HB_TAG('B','T','I',' ')}}, /* Bebil -> Beti */ - {"bxr", {HB_TAG('R','B','U',' ')}}, /* Russia Buriat -> Russian Buriat */ - {"byn", {HB_TAG('B','I','L',' ')}}, /* Bilin -> Bilen */ - {"byv", {HB_TAG('B','Y','V',' ')}}, /* Medumba */ - {"bzc", {HB_TAG('M','L','G',' ')}}, /* Southern Betsimisaraka Malagasy -> Malagasy */ - {"ca", {HB_TAG('C','A','T',' ')}}, /* Catalan */ - {"caf", {HB_TAG('C','R','R',' '), /* Southern Carrier -> Carrier */ - HB_TAG('A','T','H',' ')}}, /* Southern Carrier -> Athapaskan */ - {"cak", {HB_TAG('C','A','K',' ')}}, /* Kaqchikel */ - {"cbk", {HB_TAG('C','B','K',' ')}}, /* Chavacano -> Zamboanga Chavacano */ - {"cbl", {HB_TAG('Q','I','N',' ')}}, /* Bualkhaw Chin -> Chin */ - {"cco", {HB_TAG('C','C','H','N')}}, /* Comaltepec Chinantec -> Chinantec */ - {"ccq", {HB_TAG('A','R','K',' ')}}, /* Chaungtha (retired code) -> Rakhine */ - {"cdo", {HB_TAG('Z','H','S',' ')}}, /* Min Dong Chinese -> Chinese Simplified */ - {"ce", {HB_TAG('C','H','E',' ')}}, /* Chechen */ - {"ceb", {HB_TAG('C','E','B',' ')}}, /* Cebuano */ - {"cfm", {HB_TAG('H','A','L',' ')}}, /* Halam (Falam Chin) */ - {"cgg", {HB_TAG('C','G','G',' ')}}, /* Chiga */ - {"ch", {HB_TAG('C','H','A',' ')}}, /* Chamorro */ - {"chj", {HB_TAG('C','C','H','N')}}, /* Ojitlán Chinantec -> Chinantec */ - {"chk", {HB_TAG('C','H','K','0')}}, /* Chuukese */ - {"cho", {HB_TAG('C','H','O',' ')}}, /* Choctaw */ - {"chp", {HB_TAG('C','H','P',' '), /* Chipewyan */ - HB_TAG('S','A','Y',' '), /* Chipewyan -> Sayisi */ - HB_TAG('A','T','H',' ')}}, /* Chipewyan -> Athapaskan */ - {"chq", {HB_TAG('C','C','H','N')}}, /* Quiotepec Chinantec -> Chinantec */ - {"chr", {HB_TAG('C','H','R',' ')}}, /* Cherokee */ - {"chy", {HB_TAG('C','H','Y',' ')}}, /* Cheyenne */ - {"chz", {HB_TAG('C','C','H','N')}}, /* Ozumacín Chinantec -> Chinantec */ - {"ciw", {HB_TAG('O','J','B',' ')}}, /* Chippewa -> Ojibway */ - {"cja", {HB_TAG('C','J','A',' ')}}, /* Western Cham */ - {"cjm", {HB_TAG('C','J','M',' ')}}, /* Eastern Cham */ - {"cjy", {HB_TAG('Z','H','S',' ')}}, /* Jinyu Chinese -> Chinese Simplified */ - {"cka", {HB_TAG('Q','I','N',' ')}}, /* Khumi Awa Chin (retired code) -> Chin */ - {"ckb", {HB_TAG('K','U','R',' ')}}, /* Central Kurdish -> Kurdish */ - {"ckt", {HB_TAG('C','H','K',' ')}}, /* Chukot -> Chukchi */ - {"clc", {HB_TAG('A','T','H',' ')}}, /* Chilcotin -> Athapaskan */ - {"cld", {HB_TAG('S','Y','R',' ')}}, /* Chaldean Neo-Aramaic -> Syriac */ - {"cle", {HB_TAG('C','C','H','N')}}, /* Lealao Chinantec -> Chinantec */ - {"cmn", {HB_TAG('Z','H','S',' ')}}, /* Mandarin Chinese -> Chinese Simplified */ - {"cmr", {HB_TAG('Q','I','N',' ')}}, /* Mro-Khimi Chin -> Chin */ - {"cnb", {HB_TAG('Q','I','N',' ')}}, /* Chinbon Chin -> Chin */ - {"cnh", {HB_TAG('Q','I','N',' ')}}, /* Hakha Chin -> Chin */ - {"cnk", {HB_TAG('Q','I','N',' ')}}, /* Khumi Chin -> Chin */ - {"cnl", {HB_TAG('C','C','H','N')}}, /* Lalana Chinantec -> Chinantec */ - {"cnt", {HB_TAG('C','C','H','N')}}, /* Tepetotutla Chinantec -> Chinantec */ - {"cnw", {HB_TAG('Q','I','N',' ')}}, /* Ngawn Chin -> Chin */ - {"co", {HB_TAG('C','O','S',' ')}}, /* Corsican */ - {"coa", {HB_TAG('M','L','Y',' ')}}, /* Cocos Islands Malay -> Malay */ - {"cop", {HB_TAG('C','O','P',' ')}}, /* Coptic */ - {"coq", {HB_TAG('A','T','H',' ')}}, /* Coquille -> Athapaskan */ - {"cpa", {HB_TAG('C','C','H','N')}}, /* Palantla Chinantec -> Chinantec */ - {"cpe", {HB_TAG('C','P','P',' ')}}, /* English-based creoles and pidgins [family] -> Creoles */ - {"cpf", {HB_TAG('C','P','P',' ')}}, /* French-based creoles and pidgins [family] -> Creoles */ - {"cpp", {HB_TAG('C','P','P',' ')}}, /* Portuguese-based creoles and pidgins [family] -> Creoles */ - {"cpx", {HB_TAG('Z','H','S',' ')}}, /* Pu-Xian Chinese -> Chinese Simplified */ - {"cqd", {HB_TAG('H','M','N',' ')}}, /* Chuanqiandian Cluster Miao -> Hmong */ - {"cqu", {HB_TAG('Q','U','H',' ')}}, /* Chilean Quechua (retired code) -> Quechua (Bolivia) */ - {"cr", {HB_TAG('C','R','E',' '), /* Cree [macrolanguage] */ - HB_TAG('Y','C','R',' ')}}, /* Cree [macrolanguage] -> Y-Cree */ - {"crh", {HB_TAG('C','R','T',' ')}}, /* Crimean Tatar */ - {"crj", {HB_TAG('E','C','R',' ')}}, /* Southern East Cree -> Eastern Cree */ - {"crk", {HB_TAG('W','C','R',' ')}}, /* Plains Cree -> West-Cree */ - {"crl", {HB_TAG('E','C','R',' ')}}, /* Northern East Cree -> Eastern Cree */ - {"crm", {HB_TAG('M','C','R',' '), /* Moose Cree */ - HB_TAG('L','C','R',' ')}}, /* Moose Cree -> L-Cree */ - {"crp", {HB_TAG('C','P','P',' ')}}, /* Creoles and pidgins [family] -> Creoles */ - {"crx", {HB_TAG('C','R','R',' '), /* Carrier */ - HB_TAG('A','T','H',' ')}}, /* Carrier -> Athapaskan */ - {"cs", {HB_TAG('C','S','Y',' ')}}, /* Czech */ - {"csa", {HB_TAG('C','C','H','N')}}, /* Chiltepec Chinantec -> Chinantec */ - {"csb", {HB_TAG('C','S','B',' ')}}, /* Kashubian */ - {"csh", {HB_TAG('Q','I','N',' ')}}, /* Asho Chin -> Chin */ - {"cso", {HB_TAG('C','C','H','N')}}, /* Sochiapam Chinantec -> Chinantec */ - {"csw", {HB_TAG('N','C','R',' '), /* Swampy Cree -> N-Cree */ - HB_TAG('N','H','C',' ')}}, /* Swampy Cree -> Norway House Cree */ - {"csy", {HB_TAG('Q','I','N',' ')}}, /* Siyin Chin -> Chin */ - {"ctc", {HB_TAG('A','T','H',' ')}}, /* Chetco -> Athapaskan */ - {"ctd", {HB_TAG('Q','I','N',' ')}}, /* Tedim Chin -> Chin */ - {"cte", {HB_TAG('C','C','H','N')}}, /* Tepinapa Chinantec -> Chinantec */ - {"ctg", {HB_TAG('C','T','G',' ')}}, /* Chittagonian */ - {"ctl", {HB_TAG('C','C','H','N')}}, /* Tlacoatzintepec Chinantec -> Chinantec */ - {"cts", {HB_TAG('B','I','K',' ')}}, /* Northern Catanduanes Bikol -> Bikol */ - {"cu", {HB_TAG('C','S','L',' ')}}, /* Church Slavonic */ - {"cuc", {HB_TAG('C','C','H','N')}}, /* Usila Chinantec -> Chinantec */ - {"cuk", {HB_TAG('C','U','K',' ')}}, /* San Blas Kuna */ - {"cv", {HB_TAG('C','H','U',' ')}}, /* Chuvash */ - {"cvn", {HB_TAG('C','C','H','N')}}, /* Valle Nacional Chinantec -> Chinantec */ - {"cwd", {HB_TAG('D','C','R',' '), /* Woods Cree */ - HB_TAG('T','C','R',' ')}}, /* Woods Cree -> TH-Cree */ - {"cy", {HB_TAG('W','E','L',' ')}}, /* Welsh */ - {"czh", {HB_TAG('Z','H','S',' ')}}, /* Huizhou Chinese -> Chinese Simplified */ - {"czo", {HB_TAG('Z','H','S',' ')}}, /* Min Zhong Chinese -> Chinese Simplified */ - {"czt", {HB_TAG('Q','I','N',' ')}}, /* Zotung Chin -> Chin */ - {"da", {HB_TAG('D','A','N',' ')}}, /* Danish */ - {"dao", {HB_TAG('Q','I','N',' ')}}, /* Daai Chin -> Chin */ - {"dap", {HB_TAG('N','I','S',' ')}}, /* Nisi (India) (retired code) */ - {"dar", {HB_TAG('D','A','R',' ')}}, /* Dargwa */ - {"dax", {HB_TAG('D','A','X',' ')}}, /* Dayi */ - {"de", {HB_TAG('D','E','U',' ')}}, /* German */ - {"den", {HB_TAG('S','L','A',' '), /* Slave (Athapascan) [macrolanguage] -> Slavey */ - HB_TAG('A','T','H',' ')}}, /* Slave (Athapascan) [macrolanguage] -> Athapaskan */ - {"dgo", {HB_TAG('D','G','O',' ')}}, /* Dogri */ - {"dgr", {HB_TAG('A','T','H',' ')}}, /* Dogrib -> Athapaskan */ - {"dhd", {HB_TAG('M','A','W',' ')}}, /* Dhundari -> Marwari */ - {"dhg", {HB_TAG('D','H','G',' ')}}, /* Dhangu */ - {"dib", {HB_TAG('D','N','K',' ')}}, /* South Central Dinka -> Dinka */ - {"dik", {HB_TAG('D','N','K',' ')}}, /* Southwestern Dinka -> Dinka */ - {"din", {HB_TAG('D','N','K',' ')}}, /* Dinka [macrolanguage] */ - {"dip", {HB_TAG('D','N','K',' ')}}, /* Northeastern Dinka -> Dinka */ - {"diq", {HB_TAG('D','I','Q',' ')}}, /* Dimli */ - {"diw", {HB_TAG('D','N','K',' ')}}, /* Northwestern Dinka -> Dinka */ - {"dje", {HB_TAG('D','J','R',' ')}}, /* Zarma */ - {"djr", {HB_TAG('D','J','R','0')}}, /* Djambarrpuyngu */ - {"dks", {HB_TAG('D','N','K',' ')}}, /* Southeastern Dinka -> Dinka */ - {"dng", {HB_TAG('D','U','N',' ')}}, /* Dungan */ - {"dnj", {HB_TAG('D','N','J',' ')}}, /* Dan */ - {"doi", {HB_TAG('D','G','R',' ')}}, /* Dogri [macrolanguage] */ - {"drh", {HB_TAG('M','N','G',' ')}}, /* Darkhat (retired code) -> Mongolian */ - {"drw", {HB_TAG('D','R','I',' ')}}, /* Darwazi (retired code) -> Dari */ - {"dsb", {HB_TAG('L','S','B',' ')}}, /* Lower Sorbian */ - {"dty", {HB_TAG('N','E','P',' ')}}, /* Dotyali -> Nepali */ - {"duj", {HB_TAG('D','U','J',' ')}}, /* Dhuwal (retired code) */ - {"dup", {HB_TAG('M','L','Y',' ')}}, /* Duano -> Malay */ - {"dv", {HB_TAG('D','I','V',' '), /* Divehi (Dhivehi, Maldivian) */ - HB_TAG('D','H','V',' ')}}, /* Divehi (Dhivehi, Maldivian) (deprecated) */ - {"dwu", {HB_TAG('D','U','J',' ')}}, /* Dhuwal */ - {"dwy", {HB_TAG('D','U','J',' ')}}, /* Dhuwaya -> Dhuwal */ - {"dyu", {HB_TAG('J','U','L',' ')}}, /* Dyula -> Jula */ - {"dz", {HB_TAG('D','Z','N',' ')}}, /* Dzongkha */ - {"ee", {HB_TAG('E','W','E',' ')}}, /* Ewe */ - {"efi", {HB_TAG('E','F','I',' ')}}, /* Efik */ - {"ekk", {HB_TAG('E','T','I',' ')}}, /* Standard Estonian -> Estonian */ - {"el", {HB_TAG('E','L','L',' ')}}, /* Modern Greek (1453-) -> Greek */ - {"emk", {HB_TAG('E','M','K',' '), /* Eastern Maninkakan */ - HB_TAG('M','N','K',' ')}}, /* Eastern Maninkakan -> Maninka */ - {"en", {HB_TAG('E','N','G',' ')}}, /* English */ - {"enb", {HB_TAG('K','A','L',' ')}}, /* Markweeta -> Kalenjin */ - {"enf", {HB_TAG('F','N','E',' ')}}, /* Forest Enets -> Forest Nenets */ - {"enh", {HB_TAG('T','N','E',' ')}}, /* Tundra Enets -> Tundra Nenets */ - {"eo", {HB_TAG('N','T','O',' ')}}, /* Esperanto */ - {"es", {HB_TAG('E','S','P',' ')}}, /* Spanish */ - {"esg", {HB_TAG('G','O','N',' ')}}, /* Aheri Gondi -> Gondi */ - {"esi", {HB_TAG('I','P','K',' ')}}, /* North Alaskan Inupiatun -> Inupiat */ - {"esk", {HB_TAG('I','P','K',' ')}}, /* Northwest Alaska Inupiatun -> Inupiat */ - {"esu", {HB_TAG('E','S','U',' ')}}, /* Central Yupik */ - {"et", {HB_TAG('E','T','I',' ')}}, /* Estonian [macrolanguage] */ - {"eto", {HB_TAG('B','T','I',' ')}}, /* Eton (Cameroon) -> Beti */ - {"eu", {HB_TAG('E','U','Q',' ')}}, /* Basque */ - {"eve", {HB_TAG('E','V','N',' ')}}, /* Even */ - {"evn", {HB_TAG('E','V','K',' ')}}, /* Evenki */ - {"ewo", {HB_TAG('B','T','I',' ')}}, /* Ewondo -> Beti */ - {"eyo", {HB_TAG('K','A','L',' ')}}, /* Keiyo -> Kalenjin */ - {"fa", {HB_TAG('F','A','R',' ')}}, /* Persian [macrolanguage] */ - {"fan", {HB_TAG('F','A','N','0')}}, /* Fang (Equatorial Guinea) */ - {"fat", {HB_TAG('F','A','T',' ')}}, /* Fanti */ - {"fbl", {HB_TAG('B','I','K',' ')}}, /* West Albay Bikol -> Bikol */ - {"ff", {HB_TAG('F','U','L',' ')}}, /* Fulah [macrolanguage] */ - {"ffm", {HB_TAG('F','U','L',' ')}}, /* Maasina Fulfulde -> Fulah */ - {"fi", {HB_TAG('F','I','N',' ')}}, /* Finnish */ - {"fil", {HB_TAG('P','I','L',' ')}}, /* Filipino */ - {"fj", {HB_TAG('F','J','I',' ')}}, /* Fijian */ - {"flm", {HB_TAG('H','A','L',' '), /* Halam (Falam Chin) (retired code) */ - HB_TAG('Q','I','N',' ')}}, /* Falam Chin (retired code) -> Chin */ - {"fmp", {HB_TAG('F','M','P',' ')}}, /* Fe'fe' */ - {"fo", {HB_TAG('F','O','S',' ')}}, /* Faroese */ - {"fon", {HB_TAG('F','O','N',' ')}}, /* Fon */ - {"fr", {HB_TAG('F','R','A',' ')}}, /* French */ - {"frc", {HB_TAG('F','R','C',' ')}}, /* Cajun French */ - {"frp", {HB_TAG('F','R','P',' ')}}, /* Arpitan */ - {"fub", {HB_TAG('F','U','L',' ')}}, /* Adamawa Fulfulde -> Fulah */ - {"fuc", {HB_TAG('F','U','L',' ')}}, /* Pulaar -> Fulah */ - {"fue", {HB_TAG('F','U','L',' ')}}, /* Borgu Fulfulde -> Fulah */ - {"fuf", {HB_TAG('F','T','A',' ')}}, /* Pular -> Futa */ - {"fuh", {HB_TAG('F','U','L',' ')}}, /* Western Niger Fulfulde -> Fulah */ - {"fui", {HB_TAG('F','U','L',' ')}}, /* Bagirmi Fulfulde -> Fulah */ - {"fuq", {HB_TAG('F','U','L',' ')}}, /* Central-Eastern Niger Fulfulde -> Fulah */ - {"fur", {HB_TAG('F','R','L',' ')}}, /* Friulian */ - {"fuv", {HB_TAG('F','U','V',' ')}}, /* Nigerian Fulfulde */ - {"fy", {HB_TAG('F','R','I',' ')}}, /* Western Frisian -> Frisian */ - {"ga", {HB_TAG('I','R','I',' ')}}, /* Irish */ - {"gaa", {HB_TAG('G','A','D',' ')}}, /* Ga */ - {"gag", {HB_TAG('G','A','G',' ')}}, /* Gagauz */ - {"gan", {HB_TAG('Z','H','S',' ')}}, /* Gan Chinese -> Chinese Simplified */ - {"gax", {HB_TAG('O','R','O',' ')}}, /* Borana-Arsi-Guji Oromo -> Oromo */ - {"gaz", {HB_TAG('O','R','O',' ')}}, /* West Central Oromo -> Oromo */ - {"gbm", {HB_TAG('G','A','W',' ')}}, /* Garhwali */ - {"gce", {HB_TAG('A','T','H',' ')}}, /* Galice -> Athapaskan */ - {"gd", {HB_TAG('G','A','E',' ')}}, /* Scottish Gaelic (Gaelic) */ - {"gda", {HB_TAG('R','A','J',' ')}}, /* Gade Lohar -> Rajasthani */ - {"gez", {HB_TAG('G','E','Z',' ')}}, /* Geez */ - {"ggo", {HB_TAG('G','O','N',' ')}}, /* Southern Gondi (retired code) -> Gondi */ - {"gih", {HB_TAG('G','I','H',' ')}}, /* Githabul */ - {"gil", {HB_TAG('G','I','L','0')}}, /* Kiribati (Gilbertese) */ - {"gju", {HB_TAG('R','A','J',' ')}}, /* Gujari -> Rajasthani */ - {"gkp", {HB_TAG('G','K','P',' ')}}, /* Guinea Kpelle -> Kpelle (Guinea) */ - {"gl", {HB_TAG('G','A','L',' ')}}, /* Galician */ - {"gld", {HB_TAG('N','A','N',' ')}}, /* Nanai */ - {"glk", {HB_TAG('G','L','K',' ')}}, /* Gilaki */ - {"gn", {HB_TAG('G','U','A',' ')}}, /* Guarani [macrolanguage] */ - {"gnn", {HB_TAG('G','N','N',' ')}}, /* Gumatj */ - {"gno", {HB_TAG('G','O','N',' ')}}, /* Northern Gondi -> Gondi */ - {"gnw", {HB_TAG('G','U','A',' ')}}, /* Western Bolivian Guaraní -> Guarani */ - {"gog", {HB_TAG('G','O','G',' ')}}, /* Gogo */ - {"gom", {HB_TAG('K','O','K',' ')}}, /* Goan Konkani -> Konkani */ - {"gon", {HB_TAG('G','O','N',' ')}}, /* Gondi [macrolanguage] */ - {"grt", {HB_TAG('G','R','O',' ')}}, /* Garo */ - {"gru", {HB_TAG('S','O','G',' ')}}, /* Kistane -> Sodo Gurage */ - {"gsw", {HB_TAG('A','L','S',' ')}}, /* Alsatian */ - {"gu", {HB_TAG('G','U','J',' ')}}, /* Gujarati */ - {"guc", {HB_TAG('G','U','C',' ')}}, /* Wayuu */ - {"guf", {HB_TAG('G','U','F',' ')}}, /* Gupapuyngu */ - {"gug", {HB_TAG('G','U','A',' ')}}, /* Paraguayan Guaraní -> Guarani */ - {"gui", {HB_TAG('G','U','A',' ')}}, /* Eastern Bolivian Guaraní -> Guarani */ - {"guk", {HB_TAG('G','M','Z',' '), /* Gumuz */ - HB_TAG('G','U','K',' ')}}, /* Gumuz (SIL fonts) */ - {"gun", {HB_TAG('G','U','A',' ')}}, /* Mbyá Guaraní -> Guarani */ - {"guz", {HB_TAG('G','U','Z',' ')}}, /* Gusii */ - {"gv", {HB_TAG('M','N','X',' ')}}, /* Manx */ - {"gwi", {HB_TAG('A','T','H',' ')}}, /* Gwichʼin -> Athapaskan */ - {"ha", {HB_TAG('H','A','U',' ')}}, /* Hausa */ - {"haa", {HB_TAG('A','T','H',' ')}}, /* Han -> Athapaskan */ - {"hae", {HB_TAG('O','R','O',' ')}}, /* Eastern Oromo -> Oromo */ - {"hak", {HB_TAG('Z','H','S',' ')}}, /* Hakka Chinese -> Chinese Simplified */ - {"har", {HB_TAG('H','R','I',' ')}}, /* Harari */ - {"haw", {HB_TAG('H','A','W',' ')}}, /* Hawaiian */ - {"hay", {HB_TAG('H','A','Y',' ')}}, /* Haya */ - {"haz", {HB_TAG('H','A','Z',' ')}}, /* Hazaragi */ - {"he", {HB_TAG('I','W','R',' ')}}, /* Hebrew */ - {"hea", {HB_TAG('H','M','N',' ')}}, /* Northern Qiandong Miao -> Hmong */ - {"hi", {HB_TAG('H','I','N',' ')}}, /* Hindi */ - {"hil", {HB_TAG('H','I','L',' ')}}, /* Hiligaynon */ - {"hji", {HB_TAG('M','L','Y',' ')}}, /* Haji -> Malay */ - {"hlt", {HB_TAG('Q','I','N',' ')}}, /* Matu Chin -> Chin */ - {"hma", {HB_TAG('H','M','N',' ')}}, /* Southern Mashan Hmong -> Hmong */ - {"hmc", {HB_TAG('H','M','N',' ')}}, /* Central Huishui Hmong -> Hmong */ - {"hmd", {HB_TAG('H','M','N',' ')}}, /* Large Flowery Miao -> Hmong */ - {"hme", {HB_TAG('H','M','N',' ')}}, /* Eastern Huishui Hmong -> Hmong */ - {"hmg", {HB_TAG('H','M','N',' ')}}, /* Southwestern Guiyang Hmong -> Hmong */ - {"hmh", {HB_TAG('H','M','N',' ')}}, /* Southwestern Huishui Hmong -> Hmong */ - {"hmi", {HB_TAG('H','M','N',' ')}}, /* Northern Huishui Hmong -> Hmong */ - {"hmj", {HB_TAG('H','M','N',' ')}}, /* Ge -> Hmong */ - {"hml", {HB_TAG('H','M','N',' ')}}, /* Luopohe Hmong -> Hmong */ - {"hmm", {HB_TAG('H','M','N',' ')}}, /* Central Mashan Hmong -> Hmong */ - {"hmn", {HB_TAG('H','M','N',' ')}}, /* Hmong [macrolanguage] */ - {"hmp", {HB_TAG('H','M','N',' ')}}, /* Northern Mashan Hmong -> Hmong */ - {"hmq", {HB_TAG('H','M','N',' ')}}, /* Eastern Qiandong Miao -> Hmong */ - {"hms", {HB_TAG('H','M','N',' ')}}, /* Southern Qiandong Miao -> Hmong */ - {"hmw", {HB_TAG('H','M','N',' ')}}, /* Western Mashan Hmong -> Hmong */ - {"hmy", {HB_TAG('H','M','N',' ')}}, /* Southern Guiyang Hmong -> Hmong */ - {"hmz", {HB_TAG('H','M','N',' ')}}, /* Hmong Shua -> Hmong */ - {"hnd", {HB_TAG('H','N','D',' ')}}, /* Southern Hindko -> Hindko */ - {"hne", {HB_TAG('C','H','H',' ')}}, /* Chhattisgarhi -> Chattisgarhi */ - {"hnj", {HB_TAG('H','M','N',' ')}}, /* Hmong Njua -> Hmong */ - {"hno", {HB_TAG('H','N','D',' ')}}, /* Northern Hindko -> Hindko */ - {"ho", {HB_TAG('H','M','O',' ')}}, /* Hiri Motu */ - {"hoc", {HB_TAG('H','O',' ',' ')}}, /* Ho */ - {"hoi", {HB_TAG('A','T','H',' ')}}, /* Holikachuk -> Athapaskan */ - {"hoj", {HB_TAG('H','A','R',' ')}}, /* Hadothi -> Harauti */ - {"hr", {HB_TAG('H','R','V',' ')}}, /* Croatian */ - {"hrm", {HB_TAG('H','M','N',' ')}}, /* Horned Miao -> Hmong */ - {"hsb", {HB_TAG('U','S','B',' ')}}, /* Upper Sorbian */ - {"hsn", {HB_TAG('Z','H','S',' ')}}, /* Xiang Chinese -> Chinese Simplified */ - {"ht", {HB_TAG('H','A','I',' ')}}, /* Haitian (Haitian Creole) */ - {"hu", {HB_TAG('H','U','N',' ')}}, /* Hungarian */ - {"huj", {HB_TAG('H','M','N',' ')}}, /* Northern Guiyang Hmong -> Hmong */ - {"hup", {HB_TAG('A','T','H',' ')}}, /* Hupa -> Athapaskan */ - {"hy", {HB_TAG('H','Y','E','0'), /* Armenian -> Armenian East */ - HB_TAG('H','Y','E',' ')}}, /* Armenian */ - {"hyw", {HB_TAG('H','Y','E',' ')}}, /* Western Armenian -> Armenian */ - {"hz", {HB_TAG('H','E','R',' ')}}, /* Herero */ - {"ia", {HB_TAG('I','N','A',' ')}}, /* Interlingua (International Auxiliary Language Association) */ - {"iba", {HB_TAG('I','B','A',' ')}}, /* Iban */ - {"ibb", {HB_TAG('I','B','B',' ')}}, /* Ibibio */ - {"id", {HB_TAG('I','N','D',' ')}}, /* Indonesian */ - {"ida", {HB_TAG('L','U','H',' ')}}, /* Idakho-Isukha-Tiriki -> Luyia */ - {"ie", {HB_TAG('I','L','E',' ')}}, /* Interlingue */ - {"ig", {HB_TAG('I','B','O',' ')}}, /* Igbo */ - {"igb", {HB_TAG('E','B','I',' ')}}, /* Ebira */ - {"ii", {HB_TAG('Y','I','M',' ')}}, /* Sichuan Yi -> Yi Modern */ - {"ijc", {HB_TAG('I','J','O',' ')}}, /* Izon -> Ijo */ - {"ijo", {HB_TAG('I','J','O',' ')}}, /* Ijo [family] */ - {"ik", {HB_TAG('I','P','K',' ')}}, /* Inupiaq [macrolanguage] -> Inupiat */ - {"ike", {HB_TAG('I','N','U',' ')}}, /* Eastern Canadian Inuktitut -> Inuktitut */ - {"ikt", {HB_TAG('I','N','U',' ')}}, /* Inuinnaqtun -> Inuktitut */ - {"ilo", {HB_TAG('I','L','O',' ')}}, /* Iloko -> Ilokano */ - {"in", {HB_TAG('I','N','D',' ')}}, /* Indonesian (retired code) */ - {"ing", {HB_TAG('A','T','H',' ')}}, /* Degexit'an -> Athapaskan */ - {"inh", {HB_TAG('I','N','G',' ')}}, /* Ingush */ - {"io", {HB_TAG('I','D','O',' ')}}, /* Ido */ - {"is", {HB_TAG('I','S','L',' ')}}, /* Icelandic */ - {"it", {HB_TAG('I','T','A',' ')}}, /* Italian */ - {"iu", {HB_TAG('I','N','U',' ')}}, /* Inuktitut [macrolanguage] */ - {"iw", {HB_TAG('I','W','R',' ')}}, /* Hebrew (retired code) */ - {"ja", {HB_TAG('J','A','N',' ')}}, /* Japanese */ - {"jak", {HB_TAG('M','L','Y',' ')}}, /* Jakun -> Malay */ - {"jam", {HB_TAG('J','A','M',' ')}}, /* Jamaican Creole English -> Jamaican Creole */ - {"jax", {HB_TAG('M','L','Y',' ')}}, /* Jambi Malay -> Malay */ - {"jbo", {HB_TAG('J','B','O',' ')}}, /* Lojban */ - {"jct", {HB_TAG('J','C','T',' ')}}, /* Krymchak */ - {"ji", {HB_TAG('J','I','I',' ')}}, /* Yiddish (retired code) */ - {"jv", {HB_TAG('J','A','V',' ')}}, /* Javanese */ - {"jw", {HB_TAG('J','A','V',' ')}}, /* Javanese (retired code) */ - {"ka", {HB_TAG('K','A','T',' ')}}, /* Georgian */ - {"kaa", {HB_TAG('K','R','K',' ')}}, /* Kara-Kalpak -> Karakalpak */ - {"kab", {HB_TAG('K','A','B','0')}}, /* Kabyle */ - {"kam", {HB_TAG('K','M','B',' ')}}, /* Kamba (Kenya) */ - {"kar", {HB_TAG('K','R','N',' ')}}, /* Karen [family] */ - {"kbd", {HB_TAG('K','A','B',' ')}}, /* Kabardian */ - {"kby", {HB_TAG('K','N','R',' ')}}, /* Manga Kanuri -> Kanuri */ - {"kca", {HB_TAG('K','H','K',' '), /* Khanty -> Khanty-Kazim */ - HB_TAG('K','H','S',' '), /* Khanty -> Khanty-Shurishkar */ - HB_TAG('K','H','V',' ')}}, /* Khanty -> Khanty-Vakhi */ - {"kde", {HB_TAG('K','D','E',' ')}}, /* Makonde */ - {"kdr", {HB_TAG('K','R','M',' ')}}, /* Karaim */ - {"kdt", {HB_TAG('K','U','Y',' ')}}, /* Kuy */ - {"kea", {HB_TAG('K','E','A',' ')}}, /* Kabuverdianu (Crioulo) */ - {"kek", {HB_TAG('K','E','K',' ')}}, /* Kekchi */ - {"kex", {HB_TAG('K','K','N',' ')}}, /* Kukna -> Kokni */ - {"kfa", {HB_TAG('K','O','D',' ')}}, /* Kodava -> Kodagu */ - {"kfr", {HB_TAG('K','A','C',' ')}}, /* Kachhi -> Kachchi */ - {"kfx", {HB_TAG('K','U','L',' ')}}, /* Kullu Pahari -> Kulvi */ - {"kfy", {HB_TAG('K','M','N',' ')}}, /* Kumaoni */ - {"kg", {HB_TAG('K','O','N','0')}}, /* Kongo [macrolanguage] */ - {"kha", {HB_TAG('K','S','I',' ')}}, /* Khasi */ - {"khb", {HB_TAG('X','B','D',' ')}}, /* Lü */ - {"khk", {HB_TAG('M','N','G',' ')}}, /* Halh Mongolian -> Mongolian */ - {"kht", {HB_TAG('K','H','N',' '), /* Khamti -> Khamti Shan (Microsoft fonts) */ - HB_TAG('K','H','T',' ')}}, /* Khamti -> Khamti Shan (OpenType spec and SIL fonts) */ - {"khw", {HB_TAG('K','H','W',' ')}}, /* Khowar */ - {"ki", {HB_TAG('K','I','K',' ')}}, /* Kikuyu (Gikuyu) */ - {"kiu", {HB_TAG('K','I','U',' ')}}, /* Kirmanjki */ - {"kj", {HB_TAG('K','U','A',' ')}}, /* Kuanyama */ - {"kjd", {HB_TAG('K','J','D',' ')}}, /* Southern Kiwai */ - {"kjh", {HB_TAG('K','H','A',' ')}}, /* Khakas -> Khakass */ - {"kjp", {HB_TAG('K','J','P',' ')}}, /* Pwo Eastern Karen -> Eastern Pwo Karen */ - {"kjz", {HB_TAG('K','J','Z',' ')}}, /* Bumthangkha */ - {"kk", {HB_TAG('K','A','Z',' ')}}, /* Kazakh */ - {"kkz", {HB_TAG('A','T','H',' ')}}, /* Kaska -> Athapaskan */ - {"kl", {HB_TAG('G','R','N',' ')}}, /* Greenlandic */ - {"kln", {HB_TAG('K','A','L',' ')}}, /* Kalenjin [macrolanguage] */ - {"km", {HB_TAG('K','H','M',' ')}}, /* Khmer */ - {"kmb", {HB_TAG('M','B','N',' ')}}, /* Kimbundu -> Mbundu */ - {"kmr", {HB_TAG('K','U','R',' ')}}, /* Northern Kurdish -> Kurdish */ - {"kmw", {HB_TAG('K','M','O',' ')}}, /* Komo (Democratic Republic of Congo) */ - {"kmz", {HB_TAG('K','M','Z',' ')}}, /* Khorasani Turkish -> Khorasani Turkic */ - {"kn", {HB_TAG('K','A','N',' ')}}, /* Kannada */ - {"knc", {HB_TAG('K','N','R',' ')}}, /* Central Kanuri -> Kanuri */ - {"kng", {HB_TAG('K','O','N','0')}}, /* Koongo -> Kongo */ - {"knn", {HB_TAG('K','O','K',' ')}}, /* Konkani */ - {"ko", {HB_TAG('K','O','R',' ')}}, /* Korean */ - {"koi", {HB_TAG('K','O','P',' ')}}, /* Komi-Permyak */ - {"kok", {HB_TAG('K','O','K',' ')}}, /* Konkani [macrolanguage] */ - {"kos", {HB_TAG('K','O','S',' ')}}, /* Kosraean */ - {"koy", {HB_TAG('A','T','H',' ')}}, /* Koyukon -> Athapaskan */ - {"kpe", {HB_TAG('K','P','L',' ')}}, /* Kpelle [macrolanguage] */ - {"kpv", {HB_TAG('K','O','Z',' ')}}, /* Komi-Zyrian */ - {"kpy", {HB_TAG('K','Y','K',' ')}}, /* Koryak */ - {"kqs", {HB_TAG('K','I','S',' ')}}, /* Northern Kissi -> Kisii */ - {"kqy", {HB_TAG('K','R','T',' ')}}, /* Koorete */ - {"kr", {HB_TAG('K','N','R',' ')}}, /* Kanuri [macrolanguage] */ - {"krc", {HB_TAG('K','A','R',' '), /* Karachay-Balkar -> Karachay */ - HB_TAG('B','A','L',' ')}}, /* Karachay-Balkar -> Balkar */ - {"kri", {HB_TAG('K','R','I',' ')}}, /* Krio */ - {"krl", {HB_TAG('K','R','L',' ')}}, /* Karelian */ - {"krt", {HB_TAG('K','N','R',' ')}}, /* Tumari Kanuri -> Kanuri */ - {"kru", {HB_TAG('K','U','U',' ')}}, /* Kurukh */ - {"ks", {HB_TAG('K','S','H',' ')}}, /* Kashmiri */ - {"ksh", {HB_TAG('K','S','H','0')}}, /* Kölsch -> Ripuarian */ - {"kss", {HB_TAG('K','I','S',' ')}}, /* Southern Kisi -> Kisii */ - {"ksw", {HB_TAG('K','S','W',' ')}}, /* S’gaw Karen */ - {"ktb", {HB_TAG('K','E','B',' ')}}, /* Kambaata -> Kebena */ - {"ktu", {HB_TAG('K','O','N',' ')}}, /* Kituba (Democratic Republic of Congo) -> Kikongo */ - {"ktw", {HB_TAG('A','T','H',' ')}}, /* Kato -> Athapaskan */ - {"ku", {HB_TAG('K','U','R',' ')}}, /* Kurdish [macrolanguage] */ - {"kum", {HB_TAG('K','U','M',' ')}}, /* Kumyk */ - {"kuu", {HB_TAG('A','T','H',' ')}}, /* Upper Kuskokwim -> Athapaskan */ - {"kv", {HB_TAG('K','O','M',' ')}}, /* Komi [macrolanguage] */ - {"kvb", {HB_TAG('M','L','Y',' ')}}, /* Kubu -> Malay */ - {"kvr", {HB_TAG('M','L','Y',' ')}}, /* Kerinci -> Malay */ - {"kw", {HB_TAG('C','O','R',' ')}}, /* Cornish */ - {"kwy", {HB_TAG('K','O','N','0')}}, /* San Salvador Kongo -> Kongo */ - {"kxc", {HB_TAG('K','M','S',' ')}}, /* Konso -> Komso */ - {"kxd", {HB_TAG('M','L','Y',' ')}}, /* Brunei -> Malay */ - {"kxu", {HB_TAG('K','U','I',' ')}}, /* Kui (India) */ - {"ky", {HB_TAG('K','I','R',' ')}}, /* Kirghiz (Kyrgyz) */ - {"kyu", {HB_TAG('K','Y','U',' ')}}, /* Western Kayah */ - {"la", {HB_TAG('L','A','T',' ')}}, /* Latin */ - {"lad", {HB_TAG('J','U','D',' ')}}, /* Ladino */ - {"lb", {HB_TAG('L','T','Z',' ')}}, /* Luxembourgish */ - {"lbe", {HB_TAG('L','A','K',' ')}}, /* Lak */ - {"lbj", {HB_TAG('L','D','K',' ')}}, /* Ladakhi */ - {"lbl", {HB_TAG('B','I','K',' ')}}, /* Libon Bikol -> Bikol */ - {"lce", {HB_TAG('M','L','Y',' ')}}, /* Loncong -> Malay */ - {"lcf", {HB_TAG('M','L','Y',' ')}}, /* Lubu -> Malay */ - {"ldi", {HB_TAG('K','O','N','0')}}, /* Laari -> Kongo */ - {"lez", {HB_TAG('L','E','Z',' ')}}, /* Lezghian -> Lezgi */ - {"lg", {HB_TAG('L','U','G',' ')}}, /* Ganda */ - {"li", {HB_TAG('L','I','M',' ')}}, /* Limburgish */ - {"lif", {HB_TAG('L','M','B',' ')}}, /* Limbu */ - {"lij", {HB_TAG('L','I','J',' ')}}, /* Ligurian */ - {"lis", {HB_TAG('L','I','S',' ')}}, /* Lisu */ - {"liw", {HB_TAG('M','L','Y',' ')}}, /* Col -> Malay */ - {"ljp", {HB_TAG('L','J','P',' ')}}, /* Lampung Api -> Lampung */ - {"lkb", {HB_TAG('L','U','H',' ')}}, /* Kabras -> Luyia */ - {"lki", {HB_TAG('L','K','I',' ')}}, /* Laki */ - {"lko", {HB_TAG('L','U','H',' ')}}, /* Khayo -> Luyia */ - {"lks", {HB_TAG('L','U','H',' ')}}, /* Kisa -> Luyia */ - {"lld", {HB_TAG('L','A','D',' ')}}, /* Ladin */ - {"lmn", {HB_TAG('L','A','M',' ')}}, /* Lambadi -> Lambani */ - {"lmo", {HB_TAG('L','M','O',' ')}}, /* Lombard */ - {"ln", {HB_TAG('L','I','N',' ')}}, /* Lingala */ - {"lo", {HB_TAG('L','A','O',' ')}}, /* Lao */ - {"lom", {HB_TAG('L','O','M',' ')}}, /* Loma (Liberia) */ - {"lrc", {HB_TAG('L','R','C',' ')}}, /* Northern Luri -> Luri */ - {"lri", {HB_TAG('L','U','H',' ')}}, /* Marachi -> Luyia */ - {"lrm", {HB_TAG('L','U','H',' ')}}, /* Marama -> Luyia */ - {"lsm", {HB_TAG('L','U','H',' ')}}, /* Saamia -> Luyia */ - {"lt", {HB_TAG('L','T','H',' ')}}, /* Lithuanian */ - {"ltg", {HB_TAG('L','V','I',' ')}}, /* Latgalian -> Latvian */ - {"lto", {HB_TAG('L','U','H',' ')}}, /* Tsotso -> Luyia */ - {"lts", {HB_TAG('L','U','H',' ')}}, /* Tachoni -> Luyia */ - {"lu", {HB_TAG('L','U','B',' ')}}, /* Luba-Katanga */ - {"lua", {HB_TAG('L','U','A',' ')}}, /* Luba-Lulua */ - {"luo", {HB_TAG('L','U','O',' ')}}, /* Luo (Kenya and Tanzania) */ - {"lus", {HB_TAG('M','I','Z',' ')}}, /* Lushai -> Mizo */ - {"luy", {HB_TAG('L','U','H',' ')}}, /* Luyia [macrolanguage] */ - {"luz", {HB_TAG('L','R','C',' ')}}, /* Southern Luri -> Luri */ - {"lv", {HB_TAG('L','V','I',' ')}}, /* Latvian [macrolanguage] */ - {"lvs", {HB_TAG('L','V','I',' ')}}, /* Standard Latvian -> Latvian */ - {"lwg", {HB_TAG('L','U','H',' ')}}, /* Wanga -> Luyia */ - {"lzh", {HB_TAG('Z','H','T',' ')}}, /* Literary Chinese -> Chinese Traditional */ - {"lzz", {HB_TAG('L','A','Z',' ')}}, /* Laz */ - {"mad", {HB_TAG('M','A','D',' ')}}, /* Madurese -> Madura */ - {"mag", {HB_TAG('M','A','G',' ')}}, /* Magahi */ - {"mai", {HB_TAG('M','T','H',' ')}}, /* Maithili */ - {"mak", {HB_TAG('M','K','R',' ')}}, /* Makasar */ - {"mam", {HB_TAG('M','A','M',' ')}}, /* Mam */ - {"man", {HB_TAG('M','N','K',' ')}}, /* Mandingo [macrolanguage] -> Maninka */ - {"max", {HB_TAG('M','L','Y',' ')}}, /* North Moluccan Malay -> Malay */ - {"mbo", {HB_TAG('M','B','O',' ')}}, /* Mbo (Cameroon) */ - {"mct", {HB_TAG('B','T','I',' ')}}, /* Mengisa -> Beti */ - {"mdf", {HB_TAG('M','O','K',' ')}}, /* Moksha */ - {"mdr", {HB_TAG('M','D','R',' ')}}, /* Mandar */ - {"mdy", {HB_TAG('M','L','E',' ')}}, /* Male (Ethiopia) */ - {"men", {HB_TAG('M','D','E',' ')}}, /* Mende (Sierra Leone) */ - {"meo", {HB_TAG('M','L','Y',' ')}}, /* Kedah Malay -> Malay */ - {"mer", {HB_TAG('M','E','R',' ')}}, /* Meru */ - {"mfa", {HB_TAG('M','F','A',' ')}}, /* Pattani Malay */ - {"mfb", {HB_TAG('M','L','Y',' ')}}, /* Bangka -> Malay */ - {"mfe", {HB_TAG('M','F','E',' ')}}, /* Morisyen */ - {"mg", {HB_TAG('M','L','G',' ')}}, /* Malagasy [macrolanguage] */ - {"mh", {HB_TAG('M','A','H',' ')}}, /* Marshallese */ - {"mhr", {HB_TAG('L','M','A',' ')}}, /* Eastern Mari -> Low Mari */ - {"mhv", {HB_TAG('A','R','K',' ')}}, /* Arakanese (retired code) -> Rakhine */ - {"mi", {HB_TAG('M','R','I',' ')}}, /* Maori */ - {"min", {HB_TAG('M','I','N',' ')}}, /* Minangkabau */ - {"mk", {HB_TAG('M','K','D',' ')}}, /* Macedonian */ - {"mku", {HB_TAG('M','N','K',' ')}}, /* Konyanka Maninka -> Maninka */ - {"mkw", {HB_TAG('M','K','W',' ')}}, /* Kituba (Congo) */ - {"ml", {HB_TAG('M','A','L',' '), /* Malayalam -> Malayalam Traditional */ - HB_TAG('M','L','R',' ')}}, /* Malayalam -> Malayalam Reformed */ - {"mlq", {HB_TAG('M','L','N',' '), /* Western Maninkakan -> Malinke */ - HB_TAG('M','N','K',' ')}}, /* Western Maninkakan -> Maninka */ - {"mmr", {HB_TAG('H','M','N',' ')}}, /* Western Xiangxi Miao -> Hmong */ - {"mn", {HB_TAG('M','N','G',' ')}}, /* Mongolian [macrolanguage] */ - {"mnc", {HB_TAG('M','C','H',' ')}}, /* Manchu */ - {"mni", {HB_TAG('M','N','I',' ')}}, /* Manipuri */ - {"mnk", {HB_TAG('M','N','D',' '), /* Mandinka */ - HB_TAG('M','N','K',' ')}}, /* Mandinka -> Maninka */ - {"mnp", {HB_TAG('Z','H','S',' ')}}, /* Min Bei Chinese -> Chinese Simplified */ - {"mns", {HB_TAG('M','A','N',' ')}}, /* Mansi */ - {"mnw", {HB_TAG('M','O','N',' ')}}, /* Mon */ - {"mo", {HB_TAG('M','O','L',' ')}}, /* Moldavian (retired code) */ - {"moh", {HB_TAG('M','O','H',' ')}}, /* Mohawk */ - {"mos", {HB_TAG('M','O','S',' ')}}, /* Mossi */ - {"mpe", {HB_TAG('M','A','J',' ')}}, /* Majang */ - {"mqg", {HB_TAG('M','L','Y',' ')}}, /* Kota Bangun Kutai Malay -> Malay */ - {"mr", {HB_TAG('M','A','R',' ')}}, /* Marathi */ - {"mrh", {HB_TAG('Q','I','N',' ')}}, /* Mara Chin -> Chin */ - {"mrj", {HB_TAG('H','M','A',' ')}}, /* Western Mari -> High Mari */ - {"ms", {HB_TAG('M','L','Y',' ')}}, /* Malay [macrolanguage] */ - {"msc", {HB_TAG('M','N','K',' ')}}, /* Sankaran Maninka -> Maninka */ - {"msh", {HB_TAG('M','L','G',' ')}}, /* Masikoro Malagasy -> Malagasy */ - {"msi", {HB_TAG('M','L','Y',' ')}}, /* Sabah Malay -> Malay */ - {"mt", {HB_TAG('M','T','S',' ')}}, /* Maltese */ - {"mtr", {HB_TAG('M','A','W',' ')}}, /* Mewari -> Marwari */ - {"mui", {HB_TAG('M','L','Y',' ')}}, /* Musi -> Malay */ - {"mup", {HB_TAG('R','A','J',' ')}}, /* Malvi -> Rajasthani */ - {"muq", {HB_TAG('H','M','N',' ')}}, /* Eastern Xiangxi Miao -> Hmong */ - {"mus", {HB_TAG('M','U','S',' ')}}, /* Creek -> Muscogee */ - {"mvb", {HB_TAG('A','T','H',' ')}}, /* Mattole -> Athapaskan */ - {"mve", {HB_TAG('M','A','W',' ')}}, /* Marwari (Pakistan) */ - {"mvf", {HB_TAG('M','N','G',' ')}}, /* Peripheral Mongolian -> Mongolian */ - {"mwk", {HB_TAG('M','N','K',' ')}}, /* Kita Maninkakan -> Maninka */ - {"mwl", {HB_TAG('M','W','L',' ')}}, /* Mirandese */ - {"mwr", {HB_TAG('M','A','W',' ')}}, /* Marwari [macrolanguage] */ - {"mww", {HB_TAG('M','W','W',' ')}}, /* Hmong Daw */ - {"my", {HB_TAG('B','R','M',' ')}}, /* Burmese */ - {"mym", {HB_TAG('M','E','N',' ')}}, /* Me'en */ - {"myn", {HB_TAG('M','Y','N',' ')}}, /* Mayan [family] */ - {"myq", {HB_TAG('M','N','K',' ')}}, /* Forest Maninka (retired code) -> Maninka */ - {"myv", {HB_TAG('E','R','Z',' ')}}, /* Erzya */ - {"mzn", {HB_TAG('M','Z','N',' ')}}, /* Mazanderani */ - {"na", {HB_TAG('N','A','U',' ')}}, /* Nauru -> Nauruan */ - {"nag", {HB_TAG('N','A','G',' ')}}, /* Naga Pidgin -> Naga-Assamese */ - {"nah", {HB_TAG('N','A','H',' ')}}, /* Nahuatl [family] */ - {"nan", {HB_TAG('Z','H','S',' ')}}, /* Min Nan Chinese -> Chinese Simplified */ - {"nap", {HB_TAG('N','A','P',' ')}}, /* Neapolitan */ - {"nb", {HB_TAG('N','O','R',' ')}}, /* Norwegian Bokmål -> Norwegian */ - {"nd", {HB_TAG('N','D','B',' ')}}, /* North Ndebele -> Ndebele */ - {"ndc", {HB_TAG('N','D','C',' ')}}, /* Ndau */ - {"nds", {HB_TAG('N','D','S',' ')}}, /* Low Saxon */ - {"ne", {HB_TAG('N','E','P',' ')}}, /* Nepali [macrolanguage] */ - {"new", {HB_TAG('N','E','W',' ')}}, /* Newari */ - {"ng", {HB_TAG('N','D','G',' ')}}, /* Ndonga */ - {"nga", {HB_TAG('N','G','A',' ')}}, /* Ngbaka */ - {"ngl", {HB_TAG('L','M','W',' ')}}, /* Lomwe */ - {"ngo", {HB_TAG('S','X','T',' ')}}, /* Ngoni -> Sutu */ - {"nhd", {HB_TAG('G','U','A',' ')}}, /* Chiripá -> Guarani */ - {"niq", {HB_TAG('K','A','L',' ')}}, /* Nandi -> Kalenjin */ - {"niu", {HB_TAG('N','I','U',' ')}}, /* Niuean */ - {"niv", {HB_TAG('G','I','L',' ')}}, /* Gilyak */ - {"njz", {HB_TAG('N','I','S',' ')}}, /* Nyishi -> Nisi */ - {"nl", {HB_TAG('N','L','D',' ')}}, /* Dutch */ - {"nle", {HB_TAG('L','U','H',' ')}}, /* East Nyala -> Luyia */ - {"nn", {HB_TAG('N','Y','N',' ')}}, /* Norwegian Nynorsk (Nynorsk, Norwegian) */ - {"no", {HB_TAG('N','O','R',' ')}}, /* Norwegian [macrolanguage] */ - {"nod", {HB_TAG('N','T','A',' ')}}, /* Northern Thai -> Northern Tai */ - {"noe", {HB_TAG('N','O','E',' ')}}, /* Nimadi */ - {"nog", {HB_TAG('N','O','G',' ')}}, /* Nogai */ - {"nov", {HB_TAG('N','O','V',' ')}}, /* Novial */ - {"npi", {HB_TAG('N','E','P',' ')}}, /* Nepali */ - {"nqo", {HB_TAG('N','K','O',' ')}}, /* N'Ko */ - {"nr", {HB_TAG('N','D','B',' ')}}, /* South Ndebele -> Ndebele */ - {"nsk", {HB_TAG('N','A','S',' ')}}, /* Naskapi */ - {"nso", {HB_TAG('N','S','O',' ')}}, /* Pedi -> Sotho, Northern */ - {"nv", {HB_TAG('N','A','V',' '), /* Navajo */ - HB_TAG('A','T','H',' ')}}, /* Navajo -> Athapaskan */ - {"ny", {HB_TAG('C','H','I',' ')}}, /* Chichewa (Chewa, Nyanja) */ - {"nyd", {HB_TAG('L','U','H',' ')}}, /* Nyore -> Luyia */ - {"nym", {HB_TAG('N','Y','M',' ')}}, /* Nyamwezi */ - {"nyn", {HB_TAG('N','K','L',' ')}}, /* Nyankole */ - {"nza", {HB_TAG('N','Z','A',' ')}}, /* Tigon Mbembe -> Mbembe Tigon */ - {"oc", {HB_TAG('O','C','I',' ')}}, /* Occitan (post 1500) */ - {"oj", {HB_TAG('O','J','B',' ')}}, /* Ojibwa [macrolanguage] -> Ojibway */ - {"ojb", {HB_TAG('O','J','B',' ')}}, /* Northwestern Ojibwa -> Ojibway */ - {"ojc", {HB_TAG('O','J','B',' ')}}, /* Central Ojibwa -> Ojibway */ - {"ojg", {HB_TAG('O','J','B',' ')}}, /* Eastern Ojibwa -> Ojibway */ - {"ojs", {HB_TAG('O','C','R',' ')}}, /* Severn Ojibwa -> Oji-Cree */ - {"ojw", {HB_TAG('O','J','B',' ')}}, /* Western Ojibwa -> Ojibway */ - {"oki", {HB_TAG('K','A','L',' ')}}, /* Okiek -> Kalenjin */ - {"okm", {HB_TAG('K','O','H',' ')}}, /* Middle Korean (10th-16th cent.) -> Korean Old Hangul */ - {"om", {HB_TAG('O','R','O',' ')}}, /* Oromo [macrolanguage] */ - {"or", {HB_TAG('O','R','I',' ')}}, /* Odia (formerly Oriya) [macrolanguage] */ - {"orc", {HB_TAG('O','R','O',' ')}}, /* Orma -> Oromo */ - {"orn", {HB_TAG('M','L','Y',' ')}}, /* Orang Kanaq -> Malay */ - {"ors", {HB_TAG('M','L','Y',' ')}}, /* Orang Seletar -> Malay */ - {"ory", {HB_TAG('O','R','I',' ')}}, /* Odia (formerly Oriya) */ - {"os", {HB_TAG('O','S','S',' ')}}, /* Ossetian */ - {"otw", {HB_TAG('O','J','B',' ')}}, /* Ottawa -> Ojibway */ - {"pa", {HB_TAG('P','A','N',' ')}}, /* Punjabi */ - {"pag", {HB_TAG('P','A','G',' ')}}, /* Pangasinan */ - {"pam", {HB_TAG('P','A','M',' ')}}, /* Pampanga -> Pampangan */ - {"pap", {HB_TAG('P','A','P','0')}}, /* Papiamento -> Papiamentu */ - {"pau", {HB_TAG('P','A','U',' ')}}, /* Palauan */ - {"pbt", {HB_TAG('P','A','S',' ')}}, /* Southern Pashto -> Pashto */ - {"pbu", {HB_TAG('P','A','S',' ')}}, /* Northern Pashto -> Pashto */ - {"pcc", {HB_TAG('P','C','C',' ')}}, /* Bouyei */ - {"pcd", {HB_TAG('P','C','D',' ')}}, /* Picard */ - {"pce", {HB_TAG('P','L','G',' ')}}, /* Ruching Palaung -> Palaung */ - {"pck", {HB_TAG('Q','I','N',' ')}}, /* Paite Chin -> Chin */ - {"pdc", {HB_TAG('P','D','C',' ')}}, /* Pennsylvania German */ - {"pel", {HB_TAG('M','L','Y',' ')}}, /* Pekal -> Malay */ - {"pes", {HB_TAG('F','A','R',' ')}}, /* Iranian Persian -> Persian */ - {"pga", {HB_TAG('A','R','A',' ')}}, /* Sudanese Creole Arabic -> Arabic */ - {"phk", {HB_TAG('P','H','K',' ')}}, /* Phake */ - {"pi", {HB_TAG('P','A','L',' ')}}, /* Pali */ - {"pih", {HB_TAG('P','I','H',' ')}}, /* Pitcairn-Norfolk -> Norfolk */ - {"pko", {HB_TAG('K','A','L',' ')}}, /* Pökoot -> Kalenjin */ - {"pl", {HB_TAG('P','L','K',' ')}}, /* Polish */ - {"pll", {HB_TAG('P','L','G',' ')}}, /* Shwe Palaung -> Palaung */ - {"plp", {HB_TAG('P','A','P',' ')}}, /* Palpa */ - {"plt", {HB_TAG('M','L','G',' ')}}, /* Plateau Malagasy -> Malagasy */ - {"pms", {HB_TAG('P','M','S',' ')}}, /* Piemontese */ - {"pnb", {HB_TAG('P','N','B',' ')}}, /* Western Panjabi */ - {"poh", {HB_TAG('P','O','H',' ')}}, /* Poqomchi' -> Pocomchi */ - {"pon", {HB_TAG('P','O','N',' ')}}, /* Pohnpeian */ - {"ppa", {HB_TAG('B','A','G',' ')}}, /* Pao (retired code) -> Baghelkhandi */ - {"pro", {HB_TAG('P','R','O',' ')}}, /* Old Provençal (to 1500) -> Provençal / Old Provençal */ - {"prs", {HB_TAG('D','R','I',' ')}}, /* Dari */ - {"ps", {HB_TAG('P','A','S',' ')}}, /* Pashto [macrolanguage] */ - {"pse", {HB_TAG('M','L','Y',' ')}}, /* Central Malay -> Malay */ - {"pst", {HB_TAG('P','A','S',' ')}}, /* Central Pashto -> Pashto */ - {"pt", {HB_TAG('P','T','G',' ')}}, /* Portuguese */ - {"pwo", {HB_TAG('P','W','O',' ')}}, /* Pwo Western Karen -> Western Pwo Karen */ - {"qu", {HB_TAG('Q','U','Z',' ')}}, /* Quechua [macrolanguage] */ - {"qub", {HB_TAG('Q','W','H',' ')}}, /* Huallaga Huánuco Quechua -> Quechua (Peru) */ - {"quc", {HB_TAG('Q','U','C',' ')}}, /* K’iche’ */ - {"qud", {HB_TAG('Q','V','I',' ')}}, /* Calderón Highland Quichua -> Quechua (Ecuador) */ - {"quf", {HB_TAG('Q','U','Z',' ')}}, /* Lambayeque Quechua -> Quechua */ - {"qug", {HB_TAG('Q','V','I',' ')}}, /* Chimborazo Highland Quichua -> Quechua (Ecuador) */ - {"quh", {HB_TAG('Q','U','H',' ')}}, /* South Bolivian Quechua -> Quechua (Bolivia) */ - {"quk", {HB_TAG('Q','U','Z',' ')}}, /* Chachapoyas Quechua -> Quechua */ - {"qul", {HB_TAG('Q','U','Z',' ')}}, /* North Bolivian Quechua -> Quechua */ - {"qup", {HB_TAG('Q','V','I',' ')}}, /* Southern Pastaza Quechua -> Quechua (Ecuador) */ - {"qur", {HB_TAG('Q','W','H',' ')}}, /* Yanahuanca Pasco Quechua -> Quechua (Peru) */ - {"qus", {HB_TAG('Q','U','H',' ')}}, /* Santiago del Estero Quichua -> Quechua (Bolivia) */ - {"quw", {HB_TAG('Q','V','I',' ')}}, /* Tena Lowland Quichua -> Quechua (Ecuador) */ - {"qux", {HB_TAG('Q','W','H',' ')}}, /* Yauyos Quechua -> Quechua (Peru) */ - {"quy", {HB_TAG('Q','U','Z',' ')}}, /* Ayacucho Quechua -> Quechua */ - {"quz", {HB_TAG('Q','U','Z',' ')}}, /* Cusco Quechua -> Quechua */ - {"qva", {HB_TAG('Q','W','H',' ')}}, /* Ambo-Pasco Quechua -> Quechua (Peru) */ - {"qvc", {HB_TAG('Q','U','Z',' ')}}, /* Cajamarca Quechua -> Quechua */ - {"qve", {HB_TAG('Q','U','Z',' ')}}, /* Eastern Apurímac Quechua -> Quechua */ - {"qvh", {HB_TAG('Q','W','H',' ')}}, /* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua (Peru) */ - {"qvi", {HB_TAG('Q','V','I',' ')}}, /* Imbabura Highland Quichua -> Quechua (Ecuador) */ - {"qvj", {HB_TAG('Q','V','I',' ')}}, /* Loja Highland Quichua -> Quechua (Ecuador) */ - {"qvl", {HB_TAG('Q','W','H',' ')}}, /* Cajatambo North Lima Quechua -> Quechua (Peru) */ - {"qvm", {HB_TAG('Q','W','H',' ')}}, /* Margos-Yarowilca-Lauricocha Quechua -> Quechua (Peru) */ - {"qvn", {HB_TAG('Q','W','H',' ')}}, /* North Junín Quechua -> Quechua (Peru) */ - {"qvo", {HB_TAG('Q','V','I',' ')}}, /* Napo Lowland Quechua -> Quechua (Ecuador) */ - {"qvp", {HB_TAG('Q','W','H',' ')}}, /* Pacaraos Quechua -> Quechua (Peru) */ - {"qvs", {HB_TAG('Q','U','Z',' ')}}, /* San Martín Quechua -> Quechua */ - {"qvw", {HB_TAG('Q','W','H',' ')}}, /* Huaylla Wanca Quechua -> Quechua (Peru) */ - {"qvz", {HB_TAG('Q','V','I',' ')}}, /* Northern Pastaza Quichua -> Quechua (Ecuador) */ - {"qwa", {HB_TAG('Q','W','H',' ')}}, /* Corongo Ancash Quechua -> Quechua (Peru) */ - {"qwc", {HB_TAG('Q','U','Z',' ')}}, /* Classical Quechua -> Quechua */ - {"qwh", {HB_TAG('Q','W','H',' ')}}, /* Huaylas Ancash Quechua -> Quechua (Peru) */ - {"qws", {HB_TAG('Q','W','H',' ')}}, /* Sihuas Ancash Quechua -> Quechua (Peru) */ - {"qxa", {HB_TAG('Q','W','H',' ')}}, /* Chiquián Ancash Quechua -> Quechua (Peru) */ - {"qxc", {HB_TAG('Q','W','H',' ')}}, /* Chincha Quechua -> Quechua (Peru) */ - {"qxh", {HB_TAG('Q','W','H',' ')}}, /* Panao Huánuco Quechua -> Quechua (Peru) */ - {"qxl", {HB_TAG('Q','V','I',' ')}}, /* Salasaca Highland Quichua -> Quechua (Ecuador) */ - {"qxn", {HB_TAG('Q','W','H',' ')}}, /* Northern Conchucos Ancash Quechua -> Quechua (Peru) */ - {"qxo", {HB_TAG('Q','W','H',' ')}}, /* Southern Conchucos Ancash Quechua -> Quechua (Peru) */ - {"qxp", {HB_TAG('Q','U','Z',' ')}}, /* Puno Quechua -> Quechua */ - {"qxr", {HB_TAG('Q','V','I',' ')}}, /* Cañar Highland Quichua -> Quechua (Ecuador) */ - {"qxt", {HB_TAG('Q','W','H',' ')}}, /* Santa Ana de Tusi Pasco Quechua -> Quechua (Peru) */ - {"qxu", {HB_TAG('Q','U','Z',' ')}}, /* Arequipa-La Unión Quechua -> Quechua */ - {"qxw", {HB_TAG('Q','W','H',' ')}}, /* Jauja Wanca Quechua -> Quechua (Peru) */ - {"rag", {HB_TAG('L','U','H',' ')}}, /* Logooli -> Luyia */ - {"raj", {HB_TAG('R','A','J',' ')}}, /* Rajasthani [macrolanguage] */ - {"rar", {HB_TAG('R','A','R',' ')}}, /* Rarotongan */ - {"rbb", {HB_TAG('P','L','G',' ')}}, /* Rumai Palaung -> Palaung */ - {"rbl", {HB_TAG('B','I','K',' ')}}, /* Miraya Bikol -> Bikol */ - {"rej", {HB_TAG('R','E','J',' ')}}, /* Rejang */ - {"ria", {HB_TAG('R','I','A',' ')}}, /* Riang (India) */ - {"rif", {HB_TAG('R','I','F',' ')}}, /* Tarifit */ - {"rit", {HB_TAG('R','I','T',' ')}}, /* Ritarungo */ - {"rki", {HB_TAG('A','R','K',' ')}}, /* Rakhine */ - {"rkw", {HB_TAG('R','K','W',' ')}}, /* Arakwal */ - {"rm", {HB_TAG('R','M','S',' ')}}, /* Romansh */ - {"rmc", {HB_TAG('R','O','Y',' ')}}, /* Carpathian Romani -> Romany */ - {"rmf", {HB_TAG('R','O','Y',' ')}}, /* Kalo Finnish Romani -> Romany */ - {"rml", {HB_TAG('R','O','Y',' ')}}, /* Baltic Romani -> Romany */ - {"rmn", {HB_TAG('R','O','Y',' ')}}, /* Balkan Romani -> Romany */ - {"rmo", {HB_TAG('R','O','Y',' ')}}, /* Sinte Romani -> Romany */ - {"rmw", {HB_TAG('R','O','Y',' ')}}, /* Welsh Romani -> Romany */ - {"rmy", {HB_TAG('R','M','Y',' ')}}, /* Vlax Romani */ - {"rmz", {HB_TAG('A','R','K',' ')}}, /* Marma -> Rakhine */ - {"rn", {HB_TAG('R','U','N',' ')}}, /* Rundi */ - {"rnl", {HB_TAG('H','A','L',' ')}}, /* Ranglong -> Halam (Falam Chin) */ - {"ro", {HB_TAG('R','O','M',' ')}}, /* Romanian */ - {"rom", {HB_TAG('R','O','Y',' ')}}, /* Romany [macrolanguage] */ - {"rtm", {HB_TAG('R','T','M',' ')}}, /* Rotuman */ - {"ru", {HB_TAG('R','U','S',' ')}}, /* Russian */ - {"rue", {HB_TAG('R','S','Y',' ')}}, /* Rusyn */ - {"rup", {HB_TAG('R','U','P',' ')}}, /* Aromanian */ - {"rw", {HB_TAG('R','U','A',' ')}}, /* Kinyarwanda */ - {"rwr", {HB_TAG('M','A','W',' ')}}, /* Marwari (India) */ - {"sa", {HB_TAG('S','A','N',' ')}}, /* Sanskrit */ - {"sah", {HB_TAG('Y','A','K',' ')}}, /* Yakut -> Sakha */ - {"sam", {HB_TAG('P','A','A',' ')}}, /* Samaritan Aramaic -> Palestinian Aramaic */ - {"sas", {HB_TAG('S','A','S',' ')}}, /* Sasak */ - {"sat", {HB_TAG('S','A','T',' ')}}, /* Santali */ - {"sc", {HB_TAG('S','R','D',' ')}}, /* Sardinian [macrolanguage] */ - {"sck", {HB_TAG('S','A','D',' ')}}, /* Sadri */ - {"scn", {HB_TAG('S','C','N',' ')}}, /* Sicilian */ - {"sco", {HB_TAG('S','C','O',' ')}}, /* Scots */ - {"scs", {HB_TAG('S','C','S',' '), /* North Slavey */ - HB_TAG('S','L','A',' '), /* North Slavey -> Slavey */ - HB_TAG('A','T','H',' ')}}, /* North Slavey -> Athapaskan */ - {"sd", {HB_TAG('S','N','D',' ')}}, /* Sindhi */ - {"sdc", {HB_TAG('S','R','D',' ')}}, /* Sassarese Sardinian -> Sardinian */ - {"sdh", {HB_TAG('K','U','R',' ')}}, /* Southern Kurdish -> Kurdish */ - {"sdn", {HB_TAG('S','R','D',' ')}}, /* Gallurese Sardinian -> Sardinian */ - {"se", {HB_TAG('N','S','M',' ')}}, /* Northern Sami */ - {"seh", {HB_TAG('S','N','A',' ')}}, /* Sena */ - {"sek", {HB_TAG('A','T','H',' ')}}, /* Sekani -> Athapaskan */ - {"sel", {HB_TAG('S','E','L',' ')}}, /* Selkup */ - {"sez", {HB_TAG('Q','I','N',' ')}}, /* Senthang Chin -> Chin */ - {"sfm", {HB_TAG('H','M','N',' ')}}, /* Small Flowery Miao -> Hmong */ - {"sg", {HB_TAG('S','G','O',' ')}}, /* Sango */ - {"sga", {HB_TAG('S','G','A',' ')}}, /* Old Irish (to 900) */ - {"sgc", {HB_TAG('K','A','L',' ')}}, /* Kipsigis -> Kalenjin */ - {"sgs", {HB_TAG('S','G','S',' ')}}, /* Samogitian */ - {"sgw", {HB_TAG('C','H','G',' '), /* Sebat Bet Gurage -> Chaha Gurage */ - HB_TAG('S','G','W',' ')}}, /* Sebat Bet Gurage -> Chaha Gurage (SIL fonts) */ - {"shi", {HB_TAG('S','H','I',' ')}}, /* Tachelhit */ - {"shn", {HB_TAG('S','H','N',' ')}}, /* Shan */ - {"shu", {HB_TAG('A','R','A',' ')}}, /* Chadian Arabic -> Arabic */ - {"si", {HB_TAG('S','N','H',' ')}}, /* Sinhala (Sinhalese) */ - {"sid", {HB_TAG('S','I','D',' ')}}, /* Sidamo */ - {"sjd", {HB_TAG('K','S','M',' ')}}, /* Kildin Sami */ - {"sjo", {HB_TAG('S','I','B',' ')}}, /* Xibe -> Sibe */ - {"sk", {HB_TAG('S','K','Y',' ')}}, /* Slovak */ - {"skg", {HB_TAG('M','L','G',' ')}}, /* Sakalava Malagasy -> Malagasy */ - {"skr", {HB_TAG('S','R','K',' ')}}, /* Saraiki */ - {"sl", {HB_TAG('S','L','V',' ')}}, /* Slovenian */ - {"sm", {HB_TAG('S','M','O',' ')}}, /* Samoan */ - {"sma", {HB_TAG('S','S','M',' ')}}, /* Southern Sami */ - {"smj", {HB_TAG('L','S','M',' ')}}, /* Lule Sami */ - {"smn", {HB_TAG('I','S','M',' ')}}, /* Inari Sami */ - {"sms", {HB_TAG('S','K','S',' ')}}, /* Skolt Sami */ - {"sn", {HB_TAG('S','N','A','0')}}, /* Shona */ - {"snk", {HB_TAG('S','N','K',' ')}}, /* Soninke */ - {"so", {HB_TAG('S','M','L',' ')}}, /* Somali */ - {"sop", {HB_TAG('S','O','P',' ')}}, /* Songe */ - {"spv", {HB_TAG('O','R','I',' ')}}, /* Sambalpuri -> Odia (formerly Oriya) */ - {"spy", {HB_TAG('K','A','L',' ')}}, /* Sabaot -> Kalenjin */ - {"sq", {HB_TAG('S','Q','I',' ')}}, /* Albanian [macrolanguage] */ - {"sr", {HB_TAG('S','R','B',' ')}}, /* Serbian */ - {"src", {HB_TAG('S','R','D',' ')}}, /* Logudorese Sardinian -> Sardinian */ - {"sro", {HB_TAG('S','R','D',' ')}}, /* Campidanese Sardinian -> Sardinian */ - {"srr", {HB_TAG('S','R','R',' ')}}, /* Serer */ - {"srs", {HB_TAG('A','T','H',' ')}}, /* Sarsi -> Athapaskan */ - {"ss", {HB_TAG('S','W','Z',' ')}}, /* Swati */ - {"ssh", {HB_TAG('A','R','A',' ')}}, /* Shihhi Arabic -> Arabic */ - {"st", {HB_TAG('S','O','T',' ')}}, /* Southern Sotho -> Sotho, Southern */ - {"stq", {HB_TAG('S','T','Q',' ')}}, /* Saterfriesisch -> Saterland Frisian */ - {"stv", {HB_TAG('S','I','G',' ')}}, /* Silt'e -> Silte Gurage */ - {"su", {HB_TAG('S','U','N',' ')}}, /* Sundanese */ - {"suk", {HB_TAG('S','U','K',' ')}}, /* Sukuma */ - {"suq", {HB_TAG('S','U','R',' ')}}, /* Suri */ - {"sv", {HB_TAG('S','V','E',' ')}}, /* Swedish */ - {"sva", {HB_TAG('S','V','A',' ')}}, /* Svan */ - {"sw", {HB_TAG('S','W','K',' ')}}, /* Swahili [macrolanguage] */ - {"swb", {HB_TAG('C','M','R',' ')}}, /* Maore Comorian -> Comorian */ - {"swc", {HB_TAG('S','W','K',' ')}}, /* Congo Swahili -> Swahili */ - {"swh", {HB_TAG('S','W','K',' ')}}, /* Swahili */ - {"swv", {HB_TAG('M','A','W',' ')}}, /* Shekhawati -> Marwari */ - {"sxu", {HB_TAG('S','X','U',' ')}}, /* Upper Saxon */ - {"syc", {HB_TAG('S','Y','R',' ')}}, /* Classical Syriac -> Syriac */ - {"syl", {HB_TAG('S','Y','L',' ')}}, /* Sylheti */ - {"syr", {HB_TAG('S','Y','R',' ')}}, /* Syriac [macrolanguage] */ - {"szl", {HB_TAG('S','Z','L',' ')}}, /* Silesian */ - {"ta", {HB_TAG('T','A','M',' ')}}, /* Tamil */ - {"taa", {HB_TAG('A','T','H',' ')}}, /* Lower Tanana -> Athapaskan */ - {"tab", {HB_TAG('T','A','B',' ')}}, /* Tabassaran -> Tabasaran */ - {"taq", {HB_TAG('T','M','H',' ')}}, /* Tamasheq -> Tamashek */ - {"tau", {HB_TAG('A','T','H',' ')}}, /* Upper Tanana -> Athapaskan */ - {"tcb", {HB_TAG('A','T','H',' ')}}, /* Tanacross -> Athapaskan */ - {"tce", {HB_TAG('A','T','H',' ')}}, /* Southern Tutchone -> Athapaskan */ - {"tcp", {HB_TAG('Q','I','N',' ')}}, /* Tawr Chin -> Chin */ - {"tcy", {HB_TAG('T','U','L',' ')}}, /* Tulu -> Tumbuka */ - {"tcz", {HB_TAG('Q','I','N',' ')}}, /* Thado Chin -> Chin */ - {"tdd", {HB_TAG('T','D','D',' ')}}, /* Tai Nüa -> Dehong Dai */ - {"tdx", {HB_TAG('M','L','G',' ')}}, /* Tandroy-Mahafaly Malagasy -> Malagasy */ - {"te", {HB_TAG('T','E','L',' ')}}, /* Telugu */ - {"tec", {HB_TAG('K','A','L',' ')}}, /* Terik -> Kalenjin */ - {"tem", {HB_TAG('T','M','N',' ')}}, /* Timne -> Temne */ - {"tet", {HB_TAG('T','E','T',' ')}}, /* Tetum */ - {"tfn", {HB_TAG('A','T','H',' ')}}, /* Tanaina -> Athapaskan */ - {"tg", {HB_TAG('T','A','J',' ')}}, /* Tajik -> Tajiki */ - {"tgj", {HB_TAG('N','I','S',' ')}}, /* Tagin -> Nisi */ - {"tgx", {HB_TAG('A','T','H',' ')}}, /* Tagish -> Athapaskan */ - {"th", {HB_TAG('T','H','A',' ')}}, /* Thai */ - {"tht", {HB_TAG('A','T','H',' ')}}, /* Tahltan -> Athapaskan */ - {"thv", {HB_TAG('T','M','H',' ')}}, /* Tahaggart Tamahaq -> Tamashek */ - {"thz", {HB_TAG('T','M','H',' ')}}, /* Tayart Tamajeq -> Tamashek */ - {"ti", {HB_TAG('T','G','Y',' ')}}, /* Tigrinya */ - {"tig", {HB_TAG('T','G','R',' ')}}, /* Tigre */ - {"tiv", {HB_TAG('T','I','V',' ')}}, /* Tiv */ - {"tk", {HB_TAG('T','K','M',' ')}}, /* Turkmen */ - {"tkg", {HB_TAG('M','L','G',' ')}}, /* Tesaka Malagasy -> Malagasy */ - {"tl", {HB_TAG('T','G','L',' ')}}, /* Tagalog */ - {"tmh", {HB_TAG('T','M','H',' ')}}, /* Tamashek [macrolanguage] */ - {"tmw", {HB_TAG('M','L','Y',' ')}}, /* Temuan -> Malay */ - {"tn", {HB_TAG('T','N','A',' ')}}, /* Tswana */ - {"tnf", {HB_TAG('D','R','I',' ')}}, /* Tangshewi (retired code) -> Dari */ - {"to", {HB_TAG('T','G','N',' ')}}, /* Tonga (Tonga Islands) -> Tongan */ - {"tod", {HB_TAG('T','O','D','0')}}, /* Toma */ - {"toi", {HB_TAG('T','N','G',' ')}}, /* Tonga (Zambia) */ - {"tol", {HB_TAG('A','T','H',' ')}}, /* Tolowa -> Athapaskan */ - {"tpi", {HB_TAG('T','P','I',' ')}}, /* Tok Pisin */ - {"tr", {HB_TAG('T','R','K',' ')}}, /* Turkish */ - {"tru", {HB_TAG('T','U','A',' '), /* Turoyo -> Turoyo Aramaic */ - HB_TAG('S','Y','R',' ')}}, /* Turoyo -> Syriac */ - {"ts", {HB_TAG('T','S','G',' ')}}, /* Tsonga */ - {"tsj", {HB_TAG('T','S','J',' ')}}, /* Tshangla */ - {"tt", {HB_TAG('T','A','T',' ')}}, /* Tatar */ - {"ttm", {HB_TAG('A','T','H',' ')}}, /* Northern Tutchone -> Athapaskan */ - {"ttq", {HB_TAG('T','M','H',' ')}}, /* Tawallammat Tamajaq -> Tamashek */ - {"tum", {HB_TAG('T','U','M',' ')}}, /* Tumbuka -> Tulu */ - {"tuu", {HB_TAG('A','T','H',' ')}}, /* Tututni -> Athapaskan */ - {"tuy", {HB_TAG('K','A','L',' ')}}, /* Tugen -> Kalenjin */ - {"tvl", {HB_TAG('T','V','L',' ')}}, /* Tuvalu */ - {"tw", {HB_TAG('T','W','I',' '), /* Twi */ - HB_TAG('A','K','A',' ')}}, /* Twi -> Akan */ - {"txc", {HB_TAG('A','T','H',' ')}}, /* Tsetsaut -> Athapaskan */ - {"txy", {HB_TAG('M','L','G',' ')}}, /* Tanosy Malagasy -> Malagasy */ - {"ty", {HB_TAG('T','H','T',' ')}}, /* Tahitian */ - {"tyv", {HB_TAG('T','U','V',' ')}}, /* Tuvinian -> Tuvin */ - {"tyz", {HB_TAG('T','Y','Z',' ')}}, /* Tày */ - {"tzm", {HB_TAG('T','Z','M',' ')}}, /* Central Atlas Tamazight -> Tamazight */ - {"tzo", {HB_TAG('T','Z','O',' ')}}, /* Tzotzil */ - {"ubl", {HB_TAG('B','I','K',' ')}}, /* Buhi'non Bikol -> Bikol */ - {"udm", {HB_TAG('U','D','M',' ')}}, /* Udmurt */ - {"ug", {HB_TAG('U','Y','G',' ')}}, /* Uyghur */ - {"uk", {HB_TAG('U','K','R',' ')}}, /* Ukrainian */ - {"umb", {HB_TAG('U','M','B',' ')}}, /* Umbundu */ - {"unr", {HB_TAG('M','U','N',' ')}}, /* Mundari */ - {"ur", {HB_TAG('U','R','D',' ')}}, /* Urdu */ - {"urk", {HB_TAG('M','L','Y',' ')}}, /* Urak Lawoi' -> Malay */ - {"uz", {HB_TAG('U','Z','B',' ')}}, /* Uzbek [macrolanguage] */ - {"uzn", {HB_TAG('U','Z','B',' ')}}, /* Northern Uzbek -> Uzbek */ - {"uzs", {HB_TAG('U','Z','B',' ')}}, /* Southern Uzbek -> Uzbek */ - {"ve", {HB_TAG('V','E','N',' ')}}, /* Venda */ - {"vec", {HB_TAG('V','E','C',' ')}}, /* Venetian */ - {"vi", {HB_TAG('V','I','T',' ')}}, /* Vietnamese */ - {"vkk", {HB_TAG('M','L','Y',' ')}}, /* Kaur -> Malay */ - {"vkt", {HB_TAG('M','L','Y',' ')}}, /* Tenggarong Kutai Malay -> Malay */ - {"vls", {HB_TAG('F','L','E',' ')}}, /* Vlaams -> Dutch (Flemish) */ - {"vmw", {HB_TAG('M','A','K',' ')}}, /* Makhuwa */ - {"vo", {HB_TAG('V','O','L',' ')}}, /* Volapük */ - {"vro", {HB_TAG('V','R','O',' ')}}, /* Võro */ - {"wa", {HB_TAG('W','L','N',' ')}}, /* Walloon */ - {"war", {HB_TAG('W','A','R',' ')}}, /* Waray (Philippines) -> Waray-Waray */ - {"wbm", {HB_TAG('W','A',' ',' ')}}, /* Wa */ - {"wbr", {HB_TAG('W','A','G',' ')}}, /* Wagdi */ - {"wlc", {HB_TAG('C','M','R',' ')}}, /* Mwali Comorian -> Comorian */ - {"wle", {HB_TAG('S','I','G',' ')}}, /* Wolane -> Silte Gurage */ - {"wlk", {HB_TAG('A','T','H',' ')}}, /* Wailaki -> Athapaskan */ - {"wni", {HB_TAG('C','M','R',' ')}}, /* Ndzwani Comorian -> Comorian */ - {"wo", {HB_TAG('W','L','F',' ')}}, /* Wolof */ - {"wry", {HB_TAG('M','A','W',' ')}}, /* Merwari -> Marwari */ - {"wsg", {HB_TAG('G','O','N',' ')}}, /* Adilabad Gondi -> Gondi */ - {"wtm", {HB_TAG('W','T','M',' ')}}, /* Mewati */ - {"wuu", {HB_TAG('Z','H','S',' ')}}, /* Wu Chinese -> Chinese Simplified */ - {"xal", {HB_TAG('K','L','M',' '), /* Kalmyk */ - HB_TAG('T','O','D',' ')}}, /* Kalmyk -> Todo */ - {"xan", {HB_TAG('S','E','K',' ')}}, /* Xamtanga -> Sekota */ - {"xh", {HB_TAG('X','H','S',' ')}}, /* Xhosa */ - {"xjb", {HB_TAG('X','J','B',' ')}}, /* Minjungbal -> Minjangbal */ - {"xkf", {HB_TAG('X','K','F',' ')}}, /* Khengkha */ - {"xmm", {HB_TAG('M','L','Y',' ')}}, /* Manado Malay -> Malay */ - {"xmv", {HB_TAG('M','L','G',' ')}}, /* Antankarana Malagasy -> Malagasy */ - {"xmw", {HB_TAG('M','L','G',' ')}}, /* Tsimihety Malagasy -> Malagasy */ - {"xnr", {HB_TAG('D','G','R',' ')}}, /* Kangri -> Dogri */ - {"xog", {HB_TAG('X','O','G',' ')}}, /* Soga */ - {"xpe", {HB_TAG('X','P','E',' ')}}, /* Liberia Kpelle -> Kpelle (Liberia) */ - {"xsl", {HB_TAG('S','S','L',' '), /* South Slavey */ - HB_TAG('S','L','A',' '), /* South Slavey -> Slavey */ - HB_TAG('A','T','H',' ')}}, /* South Slavey -> Athapaskan */ - {"xst", {HB_TAG('S','I','G',' ')}}, /* Silt'e (retired code) -> Silte Gurage */ - {"xwo", {HB_TAG('T','O','D',' ')}}, /* Written Oirat -> Todo */ - {"yao", {HB_TAG('Y','A','O',' ')}}, /* Yao */ - {"yap", {HB_TAG('Y','A','P',' ')}}, /* Yapese */ - {"ybd", {HB_TAG('A','R','K',' ')}}, /* Yangbye (retired code) -> Rakhine */ - {"ydd", {HB_TAG('J','I','I',' ')}}, /* Eastern Yiddish -> Yiddish */ - {"yi", {HB_TAG('J','I','I',' ')}}, /* Yiddish [macrolanguage] */ - {"yih", {HB_TAG('J','I','I',' ')}}, /* Western Yiddish -> Yiddish */ - {"yo", {HB_TAG('Y','B','A',' ')}}, /* Yoruba */ - {"yos", {HB_TAG('Q','I','N',' ')}}, /* Yos (retired code) -> Chin */ - {"yrk", {HB_TAG('T','N','E',' '), /* Nenets -> Tundra Nenets */ - HB_TAG('F','N','E',' ')}}, /* Nenets -> Forest Nenets */ - {"yue", {HB_TAG('Z','H','H',' ')}}, /* Yue Chinese -> Chinese, Hong Kong SAR */ - {"za", {HB_TAG('Z','H','A',' ')}}, /* Zhuang [macrolanguage] */ - {"zch", {HB_TAG('Z','H','A',' ')}}, /* Central Hongshuihe Zhuang -> Zhuang */ - {"zdj", {HB_TAG('C','M','R',' ')}}, /* Ngazidja Comorian -> Comorian */ - {"zea", {HB_TAG('Z','E','A',' ')}}, /* Zeeuws -> Zealandic */ - {"zeh", {HB_TAG('Z','H','A',' ')}}, /* Eastern Hongshuihe Zhuang -> Zhuang */ - {"zgb", {HB_TAG('Z','H','A',' ')}}, /* Guibei Zhuang -> Zhuang */ - {"zgh", {HB_TAG('Z','G','H',' ')}}, /* Standard Moroccan Tamazight */ - {"zgm", {HB_TAG('Z','H','A',' ')}}, /* Minz Zhuang -> Zhuang */ - {"zgn", {HB_TAG('Z','H','A',' ')}}, /* Guibian Zhuang -> Zhuang */ - {"zh", {HB_TAG('Z','H','S',' ')}}, /* Chinese [macrolanguage] -> Chinese Simplified */ - {"zhd", {HB_TAG('Z','H','A',' ')}}, /* Dai Zhuang -> Zhuang */ - {"zhn", {HB_TAG('Z','H','A',' ')}}, /* Nong Zhuang -> Zhuang */ - {"zlj", {HB_TAG('Z','H','A',' ')}}, /* Liujiang Zhuang -> Zhuang */ - {"zlm", {HB_TAG('M','L','Y',' ')}}, /* Malay */ - {"zln", {HB_TAG('Z','H','A',' ')}}, /* Lianshan Zhuang -> Zhuang */ - {"zlq", {HB_TAG('Z','H','A',' ')}}, /* Liuqian Zhuang -> Zhuang */ - {"zmi", {HB_TAG('M','L','Y',' ')}}, /* Negeri Sembilan Malay -> Malay */ - {"zne", {HB_TAG('Z','N','D',' ')}}, /* Zande */ - {"zom", {HB_TAG('Q','I','N',' ')}}, /* Zou -> Chin */ - {"zqe", {HB_TAG('Z','H','A',' ')}}, /* Qiubei Zhuang -> Zhuang */ - {"zsm", {HB_TAG('M','L','Y',' ')}}, /* Standard Malay -> Malay */ - {"zu", {HB_TAG('Z','U','L',' ')}}, /* Zulu */ - {"zum", {HB_TAG('L','R','C',' ')}}, /* Kumzari -> Luri */ - {"zyb", {HB_TAG('Z','H','A',' ')}}, /* Yongbei Zhuang -> Zhuang */ - {"zyg", {HB_TAG('Z','H','A',' ')}}, /* Yang Zhuang -> Zhuang */ - {"zyj", {HB_TAG('Z','H','A',' ')}}, /* Youjiang Zhuang -> Zhuang */ - {"zyn", {HB_TAG('Z','H','A',' ')}}, /* Yongnan Zhuang -> Zhuang */ - {"zza", {HB_TAG('Z','Z','A',' ')}}, /* Zazaki [macrolanguage] */ - {"zzj", {HB_TAG('Z','H','A',' ')}}, /* Zuojiang Zhuang -> Zhuang */ + {"aa", HB_TAG('A','F','R',' ')}, /* Afar */ + {"aae", HB_TAG('S','Q','I',' ')}, /* Arbëreshë Albanian -> Albanian */ + {"aao", HB_TAG('A','R','A',' ')}, /* Algerian Saharan Arabic -> Arabic */ + {"aat", HB_TAG('S','Q','I',' ')}, /* Arvanitika Albanian -> Albanian */ + {"ab", HB_TAG('A','B','K',' ')}, /* Abkhazian */ + {"abh", HB_TAG('A','R','A',' ')}, /* Tajiki Arabic -> Arabic */ + {"abq", HB_TAG('A','B','A',' ')}, /* Abaza */ + {"abv", HB_TAG('A','R','A',' ')}, /* Baharna Arabic -> Arabic */ + {"acf", HB_TAG('F','A','N',' ')}, /* Saint Lucian Creole French -> French Antillean */ +/*{"ach", HB_TAG('A','C','H',' ')},*/ /* Acoli -> Acholi */ + {"acm", HB_TAG('A','R','A',' ')}, /* Mesopotamian Arabic -> Arabic */ + {"acq", HB_TAG('A','R','A',' ')}, /* Ta'izzi-Adeni Arabic -> Arabic */ +/*{"acr", HB_TAG('A','C','R',' ')},*/ /* Achi */ + {"acw", HB_TAG('A','R','A',' ')}, /* Hijazi Arabic -> Arabic */ + {"acx", HB_TAG('A','R','A',' ')}, /* Omani Arabic -> Arabic */ + {"acy", HB_TAG('A','R','A',' ')}, /* Cypriot Arabic -> Arabic */ + {"ada", HB_TAG('D','N','G',' ')}, /* Adangme -> Dangme */ + {"adf", HB_TAG('A','R','A',' ')}, /* Dhofari Arabic -> Arabic */ + {"adp", HB_TAG('D','Z','N',' ')}, /* Adap (retired code) -> Dzongkha */ +/*{"ady", HB_TAG('A','D','Y',' ')},*/ /* Adyghe */ + {"aeb", HB_TAG('A','R','A',' ')}, /* Tunisian Arabic -> Arabic */ + {"aec", HB_TAG('A','R','A',' ')}, /* Saidi Arabic -> Arabic */ + {"af", HB_TAG('A','F','K',' ')}, /* Afrikaans */ + {"afb", HB_TAG('A','R','A',' ')}, /* Gulf Arabic -> Arabic */ + {"ahg", HB_TAG('A','G','W',' ')}, /* Qimant -> Agaw */ + {"aht", HB_TAG('A','T','H',' ')}, /* Ahtena -> Athapaskan */ + {"aii", HB_TAG('S','W','A',' ')}, /* Assyrian Neo-Aramaic -> Swadaya Aramaic */ + {"aii", HB_TAG('S','Y','R',' ')}, /* Assyrian Neo-Aramaic -> Syriac */ +/*{"aio", HB_TAG('A','I','O',' ')},*/ /* Aiton */ + {"aiw", HB_TAG('A','R','I',' ')}, /* Aari */ + {"ajp", HB_TAG('A','R','A',' ')}, /* South Levantine Arabic -> Arabic */ + {"ak", HB_TAG('A','K','A',' ')}, /* Akan [macrolanguage] */ + {"ak", HB_TAG('T','W','I',' ')}, /* Akan [macrolanguage] -> Twi */ + {"aln", HB_TAG('S','Q','I',' ')}, /* Gheg Albanian -> Albanian */ + {"als", HB_TAG('S','Q','I',' ')}, /* Tosk Albanian -> Albanian */ +/*{"alt", HB_TAG('A','L','T',' ')},*/ /* Southern Altai -> Altai */ + {"am", HB_TAG('A','M','H',' ')}, /* Amharic */ + {"amf", HB_TAG('H','B','N',' ')}, /* Hamer-Banna -> Hammer-Banna */ + {"amw", HB_TAG('S','Y','R',' ')}, /* Western Neo-Aramaic -> Syriac */ + {"an", HB_TAG('A','R','G',' ')}, /* Aragonese */ +/*{"ang", HB_TAG('A','N','G',' ')},*/ /* Old English (ca. 450-1100) -> Anglo-Saxon */ + {"apc", HB_TAG('A','R','A',' ')}, /* North Levantine Arabic -> Arabic */ + {"apd", HB_TAG('A','R','A',' ')}, /* Sudanese Arabic -> Arabic */ + {"apj", HB_TAG('A','T','H',' ')}, /* Jicarilla Apache -> Athapaskan */ + {"apk", HB_TAG('A','T','H',' ')}, /* Kiowa Apache -> Athapaskan */ + {"apl", HB_TAG('A','T','H',' ')}, /* Lipan Apache -> Athapaskan */ + {"apm", HB_TAG('A','T','H',' ')}, /* Mescalero-Chiricahua Apache -> Athapaskan */ + {"apw", HB_TAG('A','T','H',' ')}, /* Western Apache -> Athapaskan */ + {"ar", HB_TAG('A','R','A',' ')}, /* Arabic [macrolanguage] */ + {"arb", HB_TAG('A','R','A',' ')}, /* Standard Arabic -> Arabic */ + {"arn", HB_TAG('M','A','P',' ')}, /* Mapudungun */ + {"arq", HB_TAG('A','R','A',' ')}, /* Algerian Arabic -> Arabic */ + {"ars", HB_TAG('A','R','A',' ')}, /* Najdi Arabic -> Arabic */ + {"ary", HB_TAG('M','O','R',' ')}, /* Moroccan Arabic -> Moroccan */ + {"arz", HB_TAG('A','R','A',' ')}, /* Egyptian Arabic -> Arabic */ + {"as", HB_TAG('A','S','M',' ')}, /* Assamese */ +/*{"ast", HB_TAG('A','S','T',' ')},*/ /* Asturian */ +/*{"ath", HB_TAG('A','T','H',' ')},*/ /* Athapascan [family] -> Athapaskan */ + {"atj", HB_TAG('R','C','R',' ')}, /* Atikamekw -> R-Cree */ + {"atv", HB_TAG('A','L','T',' ')}, /* Northern Altai -> Altai */ + {"auz", HB_TAG('A','R','A',' ')}, /* Uzbeki Arabic -> Arabic */ + {"av", HB_TAG('A','V','R',' ')}, /* Avaric -> Avar */ + {"avl", HB_TAG('A','R','A',' ')}, /* Eastern Egyptian Bedawi Arabic -> Arabic */ +/*{"awa", HB_TAG('A','W','A',' ')},*/ /* Awadhi */ + {"ay", HB_TAG('A','Y','M',' ')}, /* Aymara [macrolanguage] */ + {"ayc", HB_TAG('A','Y','M',' ')}, /* Southern Aymara -> Aymara */ + {"ayh", HB_TAG('A','R','A',' ')}, /* Hadrami Arabic -> Arabic */ + {"ayl", HB_TAG('A','R','A',' ')}, /* Libyan Arabic -> Arabic */ + {"ayn", HB_TAG('A','R','A',' ')}, /* Sanaani Arabic -> Arabic */ + {"ayp", HB_TAG('A','R','A',' ')}, /* North Mesopotamian Arabic -> Arabic */ + {"ayr", HB_TAG('A','Y','M',' ')}, /* Central Aymara -> Aymara */ + {"az", HB_TAG('A','Z','E',' ')}, /* Azerbaijani [macrolanguage] */ +/*{"azb", HB_TAG('A','Z','B',' ')},*/ /* South Azerbaijani -> Torki */ + {"azj", HB_TAG('A','Z','E',' ')}, /* North Azerbaijani -> Azerbaijani */ + {"ba", HB_TAG('B','S','H',' ')}, /* Bashkir */ + {"bad", HB_TAG('B','A','D','0')}, /* Banda [family] */ + {"bai", HB_TAG('B','M','L',' ')}, /* Bamileke [family] */ + {"bal", HB_TAG('B','L','I',' ')}, /* Baluchi [macrolanguage] */ +/*{"ban", HB_TAG('B','A','N',' ')},*/ /* Balinese */ +/*{"bar", HB_TAG('B','A','R',' ')},*/ /* Bavarian */ +/*{"bbc", HB_TAG('B','B','C',' ')},*/ /* Batak Toba */ + {"bbz", HB_TAG('A','R','A',' ')}, /* Babalia Creole Arabic (retired code) -> Arabic */ + {"bcc", HB_TAG('B','L','I',' ')}, /* Southern Balochi -> Baluchi */ + {"bci", HB_TAG('B','A','U',' ')}, /* Baoulé -> Baulé */ + {"bcl", HB_TAG('B','I','K',' ')}, /* Central Bikol -> Bikol */ + {"bcq", HB_TAG('B','C','H',' ')}, /* Bench */ + {"bcr", HB_TAG('A','T','H',' ')}, /* Babine -> Athapaskan */ +/*{"bdy", HB_TAG('B','D','Y',' ')},*/ /* Bandjalang */ + {"be", HB_TAG('B','E','L',' ')}, /* Belarusian -> Belarussian */ + {"bea", HB_TAG('A','T','H',' ')}, /* Beaver -> Athapaskan */ + {"beb", HB_TAG('B','T','I',' ')}, /* Bebele -> Beti */ +/*{"bem", HB_TAG('B','E','M',' ')},*/ /* Bemba (Zambia) */ + {"ber", HB_TAG('B','B','R',' ')}, /* Berber [family] */ + {"bfq", HB_TAG('B','A','D',' ')}, /* Badaga */ + {"bft", HB_TAG('B','L','T',' ')}, /* Balti */ + {"bfu", HB_TAG('L','A','H',' ')}, /* Gahri -> Lahuli */ + {"bfy", HB_TAG('B','A','G',' ')}, /* Bagheli -> Baghelkhandi */ + {"bg", HB_TAG('B','G','R',' ')}, /* Bulgarian */ +/*{"bgc", HB_TAG('B','G','C',' ')},*/ /* Haryanvi */ + {"bgn", HB_TAG('B','L','I',' ')}, /* Western Balochi -> Baluchi */ + {"bgp", HB_TAG('B','L','I',' ')}, /* Eastern Balochi -> Baluchi */ +/*{"bgq", HB_TAG('B','G','Q',' ')},*/ /* Bagri */ + {"bgr", HB_TAG('Q','I','N',' ')}, /* Bawm Chin -> Chin */ + {"bhb", HB_TAG('B','H','I',' ')}, /* Bhili */ +/*{"bhi", HB_TAG('B','H','I',' ')},*/ /* Bhilali -> Bhili */ + {"bhk", HB_TAG('B','I','K',' ')}, /* Albay Bicolano (retired code) -> Bikol */ +/*{"bho", HB_TAG('B','H','O',' ')},*/ /* Bhojpuri */ + {"bhr", HB_TAG('M','L','G',' ')}, /* Bara Malagasy -> Malagasy */ + {"bi", HB_TAG('B','I','S',' ')}, /* Bislama */ +/*{"bik", HB_TAG('B','I','K',' ')},*/ /* Bikol [macrolanguage] */ + {"bin", HB_TAG('E','D','O',' ')}, /* Edo */ +/*{"bjj", HB_TAG('B','J','J',' ')},*/ /* Kanauji */ + {"bjn", HB_TAG('M','L','Y',' ')}, /* Banjar -> Malay */ + {"bjq", HB_TAG('M','L','G',' ')}, /* Southern Betsimisaraka Malagasy (retired code) -> Malagasy */ + {"bjt", HB_TAG('B','L','N',' ')}, /* Balanta-Ganja -> Balante */ + {"bla", HB_TAG('B','K','F',' ')}, /* Siksika -> Blackfoot */ + {"ble", HB_TAG('B','L','N',' ')}, /* Balanta-Kentohe -> Balante */ +/*{"blk", HB_TAG('B','L','K',' ')},*/ /* Pa’o Karen */ + {"bln", HB_TAG('B','I','K',' ')}, /* Southern Catanduanes Bikol -> Bikol */ + {"bm", HB_TAG('B','M','B',' ')}, /* Bambara (Bamanankan) */ + {"bmm", HB_TAG('M','L','G',' ')}, /* Northern Betsimisaraka Malagasy -> Malagasy */ + {"bn", HB_TAG('B','E','N',' ')}, /* Bengali */ + {"bo", HB_TAG('T','I','B',' ')}, /* Tibetan */ +/*{"bpy", HB_TAG('B','P','Y',' ')},*/ /* Bishnupriya -> Bishnupriya Manipuri */ + {"bqi", HB_TAG('L','R','C',' ')}, /* Bakhtiari -> Luri */ + {"br", HB_TAG('B','R','E',' ')}, /* Breton */ + {"bra", HB_TAG('B','R','I',' ')}, /* Braj -> Braj Bhasha */ +/*{"brh", HB_TAG('B','R','H',' ')},*/ /* Brahui */ +/*{"brx", HB_TAG('B','R','X',' ')},*/ /* Bodo (India) */ + {"bs", HB_TAG('B','O','S',' ')}, /* Bosnian */ +/*{"bsk", HB_TAG('B','S','K',' ')},*/ /* Burushaski */ + {"btb", HB_TAG('B','T','I',' ')}, /* Beti (Cameroon) (retired code) */ + {"btj", HB_TAG('M','L','Y',' ')}, /* Bacanese Malay -> Malay */ + {"bto", HB_TAG('B','I','K',' ')}, /* Rinconada Bikol -> Bikol */ +/*{"bts", HB_TAG('B','T','S',' ')},*/ /* Batak Simalungun */ +/*{"bug", HB_TAG('B','U','G',' ')},*/ /* Buginese -> Bugis */ + {"bum", HB_TAG('B','T','I',' ')}, /* Bulu (Cameroon) -> Beti */ + {"bve", HB_TAG('M','L','Y',' ')}, /* Berau Malay -> Malay */ + {"bvu", HB_TAG('M','L','Y',' ')}, /* Bukit Malay -> Malay */ + {"bxk", HB_TAG('L','U','H',' ')}, /* Bukusu -> Luyia */ + {"bxp", HB_TAG('B','T','I',' ')}, /* Bebil -> Beti */ + {"bxr", HB_TAG('R','B','U',' ')}, /* Russia Buriat -> Russian Buriat */ + {"byn", HB_TAG('B','I','L',' ')}, /* Bilin -> Bilen */ +/*{"byv", HB_TAG('B','Y','V',' ')},*/ /* Medumba */ + {"bzc", HB_TAG('M','L','G',' ')}, /* Southern Betsimisaraka Malagasy -> Malagasy */ + {"ca", HB_TAG('C','A','T',' ')}, /* Catalan */ + {"caf", HB_TAG('C','R','R',' ')}, /* Southern Carrier -> Carrier */ + {"caf", HB_TAG('A','T','H',' ')}, /* Southern Carrier -> Athapaskan */ +/*{"cak", HB_TAG('C','A','K',' ')},*/ /* Kaqchikel */ +/*{"cbk", HB_TAG('C','B','K',' ')},*/ /* Chavacano -> Zamboanga Chavacano */ + {"cbl", HB_TAG('Q','I','N',' ')}, /* Bualkhaw Chin -> Chin */ + {"cco", HB_TAG('C','C','H','N')}, /* Comaltepec Chinantec -> Chinantec */ + {"ccq", HB_TAG('A','R','K',' ')}, /* Chaungtha (retired code) -> Rakhine */ + {"cdo", HB_TAG('Z','H','S',' ')}, /* Min Dong Chinese -> Chinese Simplified */ + {"ce", HB_TAG('C','H','E',' ')}, /* Chechen */ +/*{"ceb", HB_TAG('C','E','B',' ')},*/ /* Cebuano */ + {"cfm", HB_TAG('H','A','L',' ')}, /* Halam (Falam Chin) */ +/*{"cgg", HB_TAG('C','G','G',' ')},*/ /* Chiga */ + {"ch", HB_TAG('C','H','A',' ')}, /* Chamorro */ + {"chj", HB_TAG('C','C','H','N')}, /* Ojitlán Chinantec -> Chinantec */ + {"chk", HB_TAG('C','H','K','0')}, /* Chuukese */ +/*{"cho", HB_TAG('C','H','O',' ')},*/ /* Choctaw */ + {"chp", HB_TAG('C','H','P',' ')}, /* Chipewyan */ + {"chp", HB_TAG('S','A','Y',' ')}, /* Chipewyan -> Sayisi */ + {"chp", HB_TAG('A','T','H',' ')}, /* Chipewyan -> Athapaskan */ + {"chq", HB_TAG('C','C','H','N')}, /* Quiotepec Chinantec -> Chinantec */ +/*{"chr", HB_TAG('C','H','R',' ')},*/ /* Cherokee */ +/*{"chy", HB_TAG('C','H','Y',' ')},*/ /* Cheyenne */ + {"chz", HB_TAG('C','C','H','N')}, /* Ozumacín Chinantec -> Chinantec */ + {"ciw", HB_TAG('O','J','B',' ')}, /* Chippewa -> Ojibway */ +/*{"cja", HB_TAG('C','J','A',' ')},*/ /* Western Cham */ +/*{"cjm", HB_TAG('C','J','M',' ')},*/ /* Eastern Cham */ + {"cjy", HB_TAG('Z','H','S',' ')}, /* Jinyu Chinese -> Chinese Simplified */ + {"cka", HB_TAG('Q','I','N',' ')}, /* Khumi Awa Chin (retired code) -> Chin */ + {"ckb", HB_TAG('K','U','R',' ')}, /* Central Kurdish -> Kurdish */ + {"ckt", HB_TAG('C','H','K',' ')}, /* Chukot -> Chukchi */ + {"clc", HB_TAG('A','T','H',' ')}, /* Chilcotin -> Athapaskan */ + {"cld", HB_TAG('S','Y','R',' ')}, /* Chaldean Neo-Aramaic -> Syriac */ + {"cle", HB_TAG('C','C','H','N')}, /* Lealao Chinantec -> Chinantec */ + {"cmn", HB_TAG('Z','H','S',' ')}, /* Mandarin Chinese -> Chinese Simplified */ + {"cmr", HB_TAG('Q','I','N',' ')}, /* Mro-Khimi Chin -> Chin */ + {"cnb", HB_TAG('Q','I','N',' ')}, /* Chinbon Chin -> Chin */ + {"cnh", HB_TAG('Q','I','N',' ')}, /* Hakha Chin -> Chin */ + {"cnk", HB_TAG('Q','I','N',' ')}, /* Khumi Chin -> Chin */ + {"cnl", HB_TAG('C','C','H','N')}, /* Lalana Chinantec -> Chinantec */ + {"cnp", HB_TAG('Z','H','S',' ')}, /* Northern Ping Chinese -> Chinese Simplified */ + {"cnt", HB_TAG('C','C','H','N')}, /* Tepetotutla Chinantec -> Chinantec */ + {"cnw", HB_TAG('Q','I','N',' ')}, /* Ngawn Chin -> Chin */ + {"co", HB_TAG('C','O','S',' ')}, /* Corsican */ + {"coa", HB_TAG('M','L','Y',' ')}, /* Cocos Islands Malay -> Malay */ +/*{"cop", HB_TAG('C','O','P',' ')},*/ /* Coptic */ + {"coq", HB_TAG('A','T','H',' ')}, /* Coquille -> Athapaskan */ + {"cpa", HB_TAG('C','C','H','N')}, /* Palantla Chinantec -> Chinantec */ + {"cpe", HB_TAG('C','P','P',' ')}, /* English-based creoles and pidgins [family] -> Creoles */ + {"cpf", HB_TAG('C','P','P',' ')}, /* French-based creoles and pidgins [family] -> Creoles */ +/*{"cpp", HB_TAG('C','P','P',' ')},*/ /* Portuguese-based creoles and pidgins [family] -> Creoles */ + {"cpx", HB_TAG('Z','H','S',' ')}, /* Pu-Xian Chinese -> Chinese Simplified */ + {"cqd", HB_TAG('H','M','N',' ')}, /* Chuanqiandian Cluster Miao -> Hmong */ + {"cqu", HB_TAG('Q','U','H',' ')}, /* Chilean Quechua (retired code) -> Quechua (Bolivia) */ + {"cr", HB_TAG('C','R','E',' ')}, /* Cree [macrolanguage] */ + {"cr", HB_TAG('Y','C','R',' ')}, /* Cree [macrolanguage] -> Y-Cree */ + {"crh", HB_TAG('C','R','T',' ')}, /* Crimean Tatar */ + {"crj", HB_TAG('E','C','R',' ')}, /* Southern East Cree -> Eastern Cree */ + {"crk", HB_TAG('W','C','R',' ')}, /* Plains Cree -> West-Cree */ + {"crl", HB_TAG('E','C','R',' ')}, /* Northern East Cree -> Eastern Cree */ + {"crm", HB_TAG('M','C','R',' ')}, /* Moose Cree */ + {"crm", HB_TAG('L','C','R',' ')}, /* Moose Cree -> L-Cree */ + {"crp", HB_TAG('C','P','P',' ')}, /* Creoles and pidgins [family] -> Creoles */ + {"crx", HB_TAG('C','R','R',' ')}, /* Carrier */ + {"crx", HB_TAG('A','T','H',' ')}, /* Carrier -> Athapaskan */ + {"cs", HB_TAG('C','S','Y',' ')}, /* Czech */ + {"csa", HB_TAG('C','C','H','N')}, /* Chiltepec Chinantec -> Chinantec */ +/*{"csb", HB_TAG('C','S','B',' ')},*/ /* Kashubian */ + {"csh", HB_TAG('Q','I','N',' ')}, /* Asho Chin -> Chin */ + {"cso", HB_TAG('C','C','H','N')}, /* Sochiapam Chinantec -> Chinantec */ + {"csp", HB_TAG('Z','H','S',' ')}, /* Southern Ping Chinese -> Chinese Simplified */ + {"csw", HB_TAG('N','C','R',' ')}, /* Swampy Cree -> N-Cree */ + {"csw", HB_TAG('N','H','C',' ')}, /* Swampy Cree -> Norway House Cree */ + {"csy", HB_TAG('Q','I','N',' ')}, /* Siyin Chin -> Chin */ + {"ctc", HB_TAG('A','T','H',' ')}, /* Chetco -> Athapaskan */ + {"ctd", HB_TAG('Q','I','N',' ')}, /* Tedim Chin -> Chin */ + {"cte", HB_TAG('C','C','H','N')}, /* Tepinapa Chinantec -> Chinantec */ +/*{"ctg", HB_TAG('C','T','G',' ')},*/ /* Chittagonian */ + {"ctl", HB_TAG('C','C','H','N')}, /* Tlacoatzintepec Chinantec -> Chinantec */ + {"cts", HB_TAG('B','I','K',' ')}, /* Northern Catanduanes Bikol -> Bikol */ + {"cu", HB_TAG('C','S','L',' ')}, /* Church Slavonic */ + {"cuc", HB_TAG('C','C','H','N')}, /* Usila Chinantec -> Chinantec */ +/*{"cuk", HB_TAG('C','U','K',' ')},*/ /* San Blas Kuna */ + {"cv", HB_TAG('C','H','U',' ')}, /* Chuvash */ + {"cvn", HB_TAG('C','C','H','N')}, /* Valle Nacional Chinantec -> Chinantec */ + {"cwd", HB_TAG('D','C','R',' ')}, /* Woods Cree */ + {"cwd", HB_TAG('T','C','R',' ')}, /* Woods Cree -> TH-Cree */ + {"cy", HB_TAG('W','E','L',' ')}, /* Welsh */ + {"czh", HB_TAG('Z','H','S',' ')}, /* Huizhou Chinese -> Chinese Simplified */ + {"czo", HB_TAG('Z','H','S',' ')}, /* Min Zhong Chinese -> Chinese Simplified */ + {"czt", HB_TAG('Q','I','N',' ')}, /* Zotung Chin -> Chin */ + {"da", HB_TAG('D','A','N',' ')}, /* Danish */ + {"dao", HB_TAG('Q','I','N',' ')}, /* Daai Chin -> Chin */ + {"dap", HB_TAG('N','I','S',' ')}, /* Nisi (India) (retired code) */ +/*{"dar", HB_TAG('D','A','R',' ')},*/ /* Dargwa */ +/*{"dax", HB_TAG('D','A','X',' ')},*/ /* Dayi */ + {"de", HB_TAG('D','E','U',' ')}, /* German */ + {"den", HB_TAG('S','L','A',' ')}, /* Slave (Athapascan) [macrolanguage] -> Slavey */ + {"den", HB_TAG('A','T','H',' ')}, /* Slave (Athapascan) [macrolanguage] -> Athapaskan */ +/*{"dgo", HB_TAG('D','G','O',' ')},*/ /* Dogri */ + {"dgr", HB_TAG('A','T','H',' ')}, /* Dogrib -> Athapaskan */ + {"dhd", HB_TAG('M','A','W',' ')}, /* Dhundari -> Marwari */ +/*{"dhg", HB_TAG('D','H','G',' ')},*/ /* Dhangu */ + {"dib", HB_TAG('D','N','K',' ')}, /* South Central Dinka -> Dinka */ + {"dik", HB_TAG('D','N','K',' ')}, /* Southwestern Dinka -> Dinka */ + {"din", HB_TAG('D','N','K',' ')}, /* Dinka [macrolanguage] */ + {"dip", HB_TAG('D','N','K',' ')}, /* Northeastern Dinka -> Dinka */ +/*{"diq", HB_TAG('D','I','Q',' ')},*/ /* Dimli */ + {"diw", HB_TAG('D','N','K',' ')}, /* Northwestern Dinka -> Dinka */ + {"dje", HB_TAG('D','J','R',' ')}, /* Zarma */ + {"djr", HB_TAG('D','J','R','0')}, /* Djambarrpuyngu */ + {"dks", HB_TAG('D','N','K',' ')}, /* Southeastern Dinka -> Dinka */ + {"dng", HB_TAG('D','U','N',' ')}, /* Dungan */ +/*{"dnj", HB_TAG('D','N','J',' ')},*/ /* Dan */ + {"doi", HB_TAG('D','G','R',' ')}, /* Dogri [macrolanguage] */ + {"drh", HB_TAG('M','N','G',' ')}, /* Darkhat (retired code) -> Mongolian */ + {"drw", HB_TAG('D','R','I',' ')}, /* Darwazi (retired code) -> Dari */ + {"dsb", HB_TAG('L','S','B',' ')}, /* Lower Sorbian */ + {"dty", HB_TAG('N','E','P',' ')}, /* Dotyali -> Nepali */ +/*{"duj", HB_TAG('D','U','J',' ')},*/ /* Dhuwal (retired code) */ + {"dup", HB_TAG('M','L','Y',' ')}, /* Duano -> Malay */ + {"dv", HB_TAG('D','I','V',' ')}, /* Divehi (Dhivehi, Maldivian) */ + {"dv", HB_TAG('D','H','V',' ')}, /* Divehi (Dhivehi, Maldivian) (deprecated) */ + {"dwk", HB_TAG('K','U','I',' ')}, /* Dawik Kui -> Kui */ + {"dwu", HB_TAG('D','U','J',' ')}, /* Dhuwal */ + {"dwy", HB_TAG('D','U','J',' ')}, /* Dhuwaya -> Dhuwal */ + {"dyu", HB_TAG('J','U','L',' ')}, /* Dyula -> Jula */ + {"dz", HB_TAG('D','Z','N',' ')}, /* Dzongkha */ + {"ee", HB_TAG('E','W','E',' ')}, /* Ewe */ +/*{"efi", HB_TAG('E','F','I',' ')},*/ /* Efik */ + {"ekk", HB_TAG('E','T','I',' ')}, /* Standard Estonian -> Estonian */ + {"el", HB_TAG('E','L','L',' ')}, /* Modern Greek (1453-) -> Greek */ + {"emk", HB_TAG('E','M','K',' ')}, /* Eastern Maninkakan */ + {"emk", HB_TAG('M','N','K',' ')}, /* Eastern Maninkakan -> Maninka */ + {"en", HB_TAG('E','N','G',' ')}, /* English */ + {"enb", HB_TAG('K','A','L',' ')}, /* Markweeta -> Kalenjin */ + {"enf", HB_TAG('F','N','E',' ')}, /* Forest Enets -> Forest Nenets */ + {"enh", HB_TAG('T','N','E',' ')}, /* Tundra Enets -> Tundra Nenets */ + {"eo", HB_TAG('N','T','O',' ')}, /* Esperanto */ + {"es", HB_TAG('E','S','P',' ')}, /* Spanish */ + {"esg", HB_TAG('G','O','N',' ')}, /* Aheri Gondi -> Gondi */ + {"esi", HB_TAG('I','P','K',' ')}, /* North Alaskan Inupiatun -> Inupiat */ + {"esk", HB_TAG('I','P','K',' ')}, /* Northwest Alaska Inupiatun -> Inupiat */ +/*{"esu", HB_TAG('E','S','U',' ')},*/ /* Central Yupik */ + {"et", HB_TAG('E','T','I',' ')}, /* Estonian [macrolanguage] */ + {"eto", HB_TAG('B','T','I',' ')}, /* Eton (Cameroon) -> Beti */ + {"eu", HB_TAG('E','U','Q',' ')}, /* Basque */ + {"eve", HB_TAG('E','V','N',' ')}, /* Even */ + {"evn", HB_TAG('E','V','K',' ')}, /* Evenki */ + {"ewo", HB_TAG('B','T','I',' ')}, /* Ewondo -> Beti */ + {"eyo", HB_TAG('K','A','L',' ')}, /* Keiyo -> Kalenjin */ + {"fa", HB_TAG('F','A','R',' ')}, /* Persian [macrolanguage] */ + {"fan", HB_TAG('F','A','N','0')}, /* Fang (Equatorial Guinea) */ +/*{"fat", HB_TAG('F','A','T',' ')},*/ /* Fanti */ + {"fbl", HB_TAG('B','I','K',' ')}, /* West Albay Bikol -> Bikol */ + {"ff", HB_TAG('F','U','L',' ')}, /* Fulah [macrolanguage] */ + {"ffm", HB_TAG('F','U','L',' ')}, /* Maasina Fulfulde -> Fulah */ + {"fi", HB_TAG('F','I','N',' ')}, /* Finnish */ + {"fil", HB_TAG('P','I','L',' ')}, /* Filipino */ + {"fj", HB_TAG('F','J','I',' ')}, /* Fijian */ + {"flm", HB_TAG('H','A','L',' ')}, /* Halam (Falam Chin) (retired code) */ + {"flm", HB_TAG('Q','I','N',' ')}, /* Falam Chin (retired code) -> Chin */ +/*{"fmp", HB_TAG('F','M','P',' ')},*/ /* Fe’fe’ */ + {"fo", HB_TAG('F','O','S',' ')}, /* Faroese */ +/*{"fon", HB_TAG('F','O','N',' ')},*/ /* Fon */ + {"fr", HB_TAG('F','R','A',' ')}, /* French */ +/*{"frc", HB_TAG('F','R','C',' ')},*/ /* Cajun French */ +/*{"frp", HB_TAG('F','R','P',' ')},*/ /* Arpitan */ + {"fub", HB_TAG('F','U','L',' ')}, /* Adamawa Fulfulde -> Fulah */ + {"fuc", HB_TAG('F','U','L',' ')}, /* Pulaar -> Fulah */ + {"fue", HB_TAG('F','U','L',' ')}, /* Borgu Fulfulde -> Fulah */ + {"fuf", HB_TAG('F','T','A',' ')}, /* Pular -> Futa */ + {"fuh", HB_TAG('F','U','L',' ')}, /* Western Niger Fulfulde -> Fulah */ + {"fui", HB_TAG('F','U','L',' ')}, /* Bagirmi Fulfulde -> Fulah */ + {"fuq", HB_TAG('F','U','L',' ')}, /* Central-Eastern Niger Fulfulde -> Fulah */ + {"fur", HB_TAG('F','R','L',' ')}, /* Friulian */ +/*{"fuv", HB_TAG('F','U','V',' ')},*/ /* Nigerian Fulfulde */ + {"fy", HB_TAG('F','R','I',' ')}, /* Western Frisian -> Frisian */ + {"ga", HB_TAG('I','R','I',' ')}, /* Irish */ + {"gaa", HB_TAG('G','A','D',' ')}, /* Ga */ +/*{"gag", HB_TAG('G','A','G',' ')},*/ /* Gagauz */ + {"gan", HB_TAG('Z','H','S',' ')}, /* Gan Chinese -> Chinese Simplified */ + {"gax", HB_TAG('O','R','O',' ')}, /* Borana-Arsi-Guji Oromo -> Oromo */ + {"gaz", HB_TAG('O','R','O',' ')}, /* West Central Oromo -> Oromo */ + {"gbm", HB_TAG('G','A','W',' ')}, /* Garhwali */ + {"gce", HB_TAG('A','T','H',' ')}, /* Galice -> Athapaskan */ + {"gd", HB_TAG('G','A','E',' ')}, /* Scottish Gaelic (Gaelic) */ + {"gda", HB_TAG('R','A','J',' ')}, /* Gade Lohar -> Rajasthani */ +/*{"gez", HB_TAG('G','E','Z',' ')},*/ /* Geez */ + {"ggo", HB_TAG('G','O','N',' ')}, /* Southern Gondi (retired code) -> Gondi */ +/*{"gih", HB_TAG('G','I','H',' ')},*/ /* Githabul */ + {"gil", HB_TAG('G','I','L','0')}, /* Kiribati (Gilbertese) */ + {"gju", HB_TAG('R','A','J',' ')}, /* Gujari -> Rajasthani */ +/*{"gkp", HB_TAG('G','K','P',' ')},*/ /* Guinea Kpelle -> Kpelle (Guinea) */ + {"gl", HB_TAG('G','A','L',' ')}, /* Galician */ + {"gld", HB_TAG('N','A','N',' ')}, /* Nanai */ +/*{"glk", HB_TAG('G','L','K',' ')},*/ /* Gilaki */ + {"gn", HB_TAG('G','U','A',' ')}, /* Guarani [macrolanguage] */ +/*{"gnn", HB_TAG('G','N','N',' ')},*/ /* Gumatj */ + {"gno", HB_TAG('G','O','N',' ')}, /* Northern Gondi -> Gondi */ + {"gnw", HB_TAG('G','U','A',' ')}, /* Western Bolivian Guaraní -> Guarani */ +/*{"gog", HB_TAG('G','O','G',' ')},*/ /* Gogo */ + {"gom", HB_TAG('K','O','K',' ')}, /* Goan Konkani -> Konkani */ +/*{"gon", HB_TAG('G','O','N',' ')},*/ /* Gondi [macrolanguage] */ + {"grt", HB_TAG('G','R','O',' ')}, /* Garo */ + {"gru", HB_TAG('S','O','G',' ')}, /* Kistane -> Sodo Gurage */ + {"gsw", HB_TAG('A','L','S',' ')}, /* Alsatian */ + {"gu", HB_TAG('G','U','J',' ')}, /* Gujarati */ +/*{"guc", HB_TAG('G','U','C',' ')},*/ /* Wayuu */ +/*{"guf", HB_TAG('G','U','F',' ')},*/ /* Gupapuyngu */ + {"gug", HB_TAG('G','U','A',' ')}, /* Paraguayan Guaraní -> Guarani */ + {"gui", HB_TAG('G','U','A',' ')}, /* Eastern Bolivian Guaraní -> Guarani */ + {"guk", HB_TAG('G','M','Z',' ')}, /* Gumuz */ + {"guk", HB_TAG('G','U','K',' ')}, /* Gumuz (SIL fonts) */ + {"gun", HB_TAG('G','U','A',' ')}, /* Mbyá Guaraní -> Guarani */ +/*{"guz", HB_TAG('G','U','Z',' ')},*/ /* Gusii */ + {"gv", HB_TAG('M','N','X',' ')}, /* Manx */ + {"gwi", HB_TAG('A','T','H',' ')}, /* Gwichʼin -> Athapaskan */ + {"ha", HB_TAG('H','A','U',' ')}, /* Hausa */ + {"haa", HB_TAG('A','T','H',' ')}, /* Han -> Athapaskan */ + {"hae", HB_TAG('O','R','O',' ')}, /* Eastern Oromo -> Oromo */ + {"hak", HB_TAG('Z','H','S',' ')}, /* Hakka Chinese -> Chinese Simplified */ + {"har", HB_TAG('H','R','I',' ')}, /* Harari */ +/*{"haw", HB_TAG('H','A','W',' ')},*/ /* Hawaiian */ +/*{"hay", HB_TAG('H','A','Y',' ')},*/ /* Haya */ +/*{"haz", HB_TAG('H','A','Z',' ')},*/ /* Hazaragi */ + {"he", HB_TAG('I','W','R',' ')}, /* Hebrew */ + {"hea", HB_TAG('H','M','N',' ')}, /* Northern Qiandong Miao -> Hmong */ + {"hi", HB_TAG('H','I','N',' ')}, /* Hindi */ +/*{"hil", HB_TAG('H','I','L',' ')},*/ /* Hiligaynon */ + {"hji", HB_TAG('M','L','Y',' ')}, /* Haji -> Malay */ + {"hlt", HB_TAG('Q','I','N',' ')}, /* Matu Chin -> Chin */ + {"hma", HB_TAG('H','M','N',' ')}, /* Southern Mashan Hmong -> Hmong */ + {"hmc", HB_TAG('H','M','N',' ')}, /* Central Huishui Hmong -> Hmong */ + {"hmd", HB_TAG('H','M','N',' ')}, /* Large Flowery Miao -> Hmong */ + {"hme", HB_TAG('H','M','N',' ')}, /* Eastern Huishui Hmong -> Hmong */ + {"hmg", HB_TAG('H','M','N',' ')}, /* Southwestern Guiyang Hmong -> Hmong */ + {"hmh", HB_TAG('H','M','N',' ')}, /* Southwestern Huishui Hmong -> Hmong */ + {"hmi", HB_TAG('H','M','N',' ')}, /* Northern Huishui Hmong -> Hmong */ + {"hmj", HB_TAG('H','M','N',' ')}, /* Ge -> Hmong */ + {"hml", HB_TAG('H','M','N',' ')}, /* Luopohe Hmong -> Hmong */ + {"hmm", HB_TAG('H','M','N',' ')}, /* Central Mashan Hmong -> Hmong */ +/*{"hmn", HB_TAG('H','M','N',' ')},*/ /* Hmong [macrolanguage] */ + {"hmp", HB_TAG('H','M','N',' ')}, /* Northern Mashan Hmong -> Hmong */ + {"hmq", HB_TAG('H','M','N',' ')}, /* Eastern Qiandong Miao -> Hmong */ + {"hms", HB_TAG('H','M','N',' ')}, /* Southern Qiandong Miao -> Hmong */ + {"hmw", HB_TAG('H','M','N',' ')}, /* Western Mashan Hmong -> Hmong */ + {"hmy", HB_TAG('H','M','N',' ')}, /* Southern Guiyang Hmong -> Hmong */ + {"hmz", HB_TAG('H','M','N',' ')}, /* Hmong Shua -> Hmong */ +/*{"hnd", HB_TAG('H','N','D',' ')},*/ /* Southern Hindko -> Hindko */ + {"hne", HB_TAG('C','H','H',' ')}, /* Chhattisgarhi -> Chattisgarhi */ + {"hnj", HB_TAG('H','M','N',' ')}, /* Hmong Njua -> Hmong */ + {"hno", HB_TAG('H','N','D',' ')}, /* Northern Hindko -> Hindko */ + {"ho", HB_TAG('H','M','O',' ')}, /* Hiri Motu */ + {"hoc", HB_TAG('H','O',' ',' ')}, /* Ho */ + {"hoi", HB_TAG('A','T','H',' ')}, /* Holikachuk -> Athapaskan */ + {"hoj", HB_TAG('H','A','R',' ')}, /* Hadothi -> Harauti */ + {"hr", HB_TAG('H','R','V',' ')}, /* Croatian */ + {"hrm", HB_TAG('H','M','N',' ')}, /* Horned Miao -> Hmong */ + {"hsb", HB_TAG('U','S','B',' ')}, /* Upper Sorbian */ + {"hsn", HB_TAG('Z','H','S',' ')}, /* Xiang Chinese -> Chinese Simplified */ + {"ht", HB_TAG('H','A','I',' ')}, /* Haitian (Haitian Creole) */ + {"hu", HB_TAG('H','U','N',' ')}, /* Hungarian */ + {"huj", HB_TAG('H','M','N',' ')}, /* Northern Guiyang Hmong -> Hmong */ + {"hup", HB_TAG('A','T','H',' ')}, /* Hupa -> Athapaskan */ + {"hy", HB_TAG('H','Y','E','0')}, /* Armenian -> Armenian East */ + {"hy", HB_TAG('H','Y','E',' ')}, /* Armenian */ + {"hyw", HB_TAG('H','Y','E',' ')}, /* Western Armenian -> Armenian */ + {"hz", HB_TAG('H','E','R',' ')}, /* Herero */ + {"ia", HB_TAG('I','N','A',' ')}, /* Interlingua (International Auxiliary Language Association) */ +/*{"iba", HB_TAG('I','B','A',' ')},*/ /* Iban */ +/*{"ibb", HB_TAG('I','B','B',' ')},*/ /* Ibibio */ + {"id", HB_TAG('I','N','D',' ')}, /* Indonesian */ + {"ida", HB_TAG('L','U','H',' ')}, /* Idakho-Isukha-Tiriki -> Luyia */ + {"ie", HB_TAG('I','L','E',' ')}, /* Interlingue */ + {"ig", HB_TAG('I','B','O',' ')}, /* Igbo */ + {"igb", HB_TAG('E','B','I',' ')}, /* Ebira */ + {"ii", HB_TAG('Y','I','M',' ')}, /* Sichuan Yi -> Yi Modern */ + {"ijc", HB_TAG('I','J','O',' ')}, /* Izon -> Ijo */ +/*{"ijo", HB_TAG('I','J','O',' ')},*/ /* Ijo [family] */ + {"ik", HB_TAG('I','P','K',' ')}, /* Inupiaq [macrolanguage] -> Inupiat */ + {"ike", HB_TAG('I','N','U',' ')}, /* Eastern Canadian Inuktitut -> Inuktitut */ + {"ikt", HB_TAG('I','N','U',' ')}, /* Inuinnaqtun -> Inuktitut */ +/*{"ilo", HB_TAG('I','L','O',' ')},*/ /* Iloko -> Ilokano */ + {"in", HB_TAG('I','N','D',' ')}, /* Indonesian (retired code) */ + {"ing", HB_TAG('A','T','H',' ')}, /* Degexit'an -> Athapaskan */ + {"inh", HB_TAG('I','N','G',' ')}, /* Ingush */ + {"io", HB_TAG('I','D','O',' ')}, /* Ido */ + {"is", HB_TAG('I','S','L',' ')}, /* Icelandic */ + {"it", HB_TAG('I','T','A',' ')}, /* Italian */ + {"iu", HB_TAG('I','N','U',' ')}, /* Inuktitut [macrolanguage] */ + {"iw", HB_TAG('I','W','R',' ')}, /* Hebrew (retired code) */ + {"ja", HB_TAG('J','A','N',' ')}, /* Japanese */ + {"jak", HB_TAG('M','L','Y',' ')}, /* Jakun -> Malay */ +/*{"jam", HB_TAG('J','A','M',' ')},*/ /* Jamaican Creole English -> Jamaican Creole */ + {"jax", HB_TAG('M','L','Y',' ')}, /* Jambi Malay -> Malay */ +/*{"jbo", HB_TAG('J','B','O',' ')},*/ /* Lojban */ +/*{"jct", HB_TAG('J','C','T',' ')},*/ /* Krymchak */ + {"ji", HB_TAG('J','I','I',' ')}, /* Yiddish (retired code) */ + {"jv", HB_TAG('J','A','V',' ')}, /* Javanese */ + {"jw", HB_TAG('J','A','V',' ')}, /* Javanese (retired code) */ + {"ka", HB_TAG('K','A','T',' ')}, /* Georgian */ + {"kaa", HB_TAG('K','R','K',' ')}, /* Karakalpak */ + {"kab", HB_TAG('K','A','B','0')}, /* Kabyle */ + {"kam", HB_TAG('K','M','B',' ')}, /* Kamba (Kenya) */ + {"kar", HB_TAG('K','R','N',' ')}, /* Karen [family] */ + {"kbd", HB_TAG('K','A','B',' ')}, /* Kabardian */ + {"kby", HB_TAG('K','N','R',' ')}, /* Manga Kanuri -> Kanuri */ + {"kca", HB_TAG('K','H','K',' ')}, /* Khanty -> Khanty-Kazim */ + {"kca", HB_TAG('K','H','S',' ')}, /* Khanty -> Khanty-Shurishkar */ + {"kca", HB_TAG('K','H','V',' ')}, /* Khanty -> Khanty-Vakhi */ +/*{"kde", HB_TAG('K','D','E',' ')},*/ /* Makonde */ + {"kdr", HB_TAG('K','R','M',' ')}, /* Karaim */ + {"kdt", HB_TAG('K','U','Y',' ')}, /* Kuy */ +/*{"kea", HB_TAG('K','E','A',' ')},*/ /* Kabuverdianu (Crioulo) */ +/*{"kek", HB_TAG('K','E','K',' ')},*/ /* Kekchi */ + {"kex", HB_TAG('K','K','N',' ')}, /* Kukna -> Kokni */ + {"kfa", HB_TAG('K','O','D',' ')}, /* Kodava -> Kodagu */ + {"kfr", HB_TAG('K','A','C',' ')}, /* Kachhi -> Kachchi */ + {"kfx", HB_TAG('K','U','L',' ')}, /* Kullu Pahari -> Kulvi */ + {"kfy", HB_TAG('K','M','N',' ')}, /* Kumaoni */ + {"kg", HB_TAG('K','O','N','0')}, /* Kongo [macrolanguage] */ + {"kha", HB_TAG('K','S','I',' ')}, /* Khasi */ + {"khb", HB_TAG('X','B','D',' ')}, /* Lü */ + {"khk", HB_TAG('M','N','G',' ')}, /* Halh Mongolian -> Mongolian */ + {"kht", HB_TAG('K','H','N',' ')}, /* Khamti -> Khamti Shan (Microsoft fonts) */ + {"kht", HB_TAG('K','H','T',' ')}, /* Khamti -> Khamti Shan (OpenType spec and SIL fonts) */ +/*{"khw", HB_TAG('K','H','W',' ')},*/ /* Khowar */ + {"ki", HB_TAG('K','I','K',' ')}, /* Kikuyu (Gikuyu) */ +/*{"kiu", HB_TAG('K','I','U',' ')},*/ /* Kirmanjki */ + {"kj", HB_TAG('K','U','A',' ')}, /* Kuanyama */ +/*{"kjd", HB_TAG('K','J','D',' ')},*/ /* Southern Kiwai */ + {"kjh", HB_TAG('K','H','A',' ')}, /* Khakas -> Khakass */ +/*{"kjp", HB_TAG('K','J','P',' ')},*/ /* Pwo Eastern Karen -> Eastern Pwo Karen */ +/*{"kjz", HB_TAG('K','J','Z',' ')},*/ /* Bumthangkha */ + {"kk", HB_TAG('K','A','Z',' ')}, /* Kazakh */ + {"kkz", HB_TAG('A','T','H',' ')}, /* Kaska -> Athapaskan */ + {"kl", HB_TAG('G','R','N',' ')}, /* Greenlandic */ + {"kln", HB_TAG('K','A','L',' ')}, /* Kalenjin [macrolanguage] */ + {"km", HB_TAG('K','H','M',' ')}, /* Khmer */ + {"kmb", HB_TAG('M','B','N',' ')}, /* Kimbundu -> Mbundu */ + {"kmr", HB_TAG('K','U','R',' ')}, /* Northern Kurdish -> Kurdish */ + {"kmw", HB_TAG('K','M','O',' ')}, /* Komo (Democratic Republic of Congo) */ +/*{"kmz", HB_TAG('K','M','Z',' ')},*/ /* Khorasani Turkish -> Khorasani Turkic */ + {"kn", HB_TAG('K','A','N',' ')}, /* Kannada */ + {"knc", HB_TAG('K','N','R',' ')}, /* Central Kanuri -> Kanuri */ + {"kng", HB_TAG('K','O','N','0')}, /* Koongo -> Kongo */ + {"knn", HB_TAG('K','O','K',' ')}, /* Konkani */ + {"ko", HB_TAG('K','O','R',' ')}, /* Korean */ + {"koi", HB_TAG('K','O','P',' ')}, /* Komi-Permyak */ +/*{"kok", HB_TAG('K','O','K',' ')},*/ /* Konkani [macrolanguage] */ +/*{"kos", HB_TAG('K','O','S',' ')},*/ /* Kosraean */ + {"koy", HB_TAG('A','T','H',' ')}, /* Koyukon -> Athapaskan */ + {"kpe", HB_TAG('K','P','L',' ')}, /* Kpelle [macrolanguage] */ + {"kpv", HB_TAG('K','O','Z',' ')}, /* Komi-Zyrian */ + {"kpy", HB_TAG('K','Y','K',' ')}, /* Koryak */ + {"kqs", HB_TAG('K','I','S',' ')}, /* Northern Kissi -> Kisii */ + {"kqy", HB_TAG('K','R','T',' ')}, /* Koorete */ + {"kr", HB_TAG('K','N','R',' ')}, /* Kanuri [macrolanguage] */ + {"krc", HB_TAG('K','A','R',' ')}, /* Karachay-Balkar -> Karachay */ + {"krc", HB_TAG('B','A','L',' ')}, /* Karachay-Balkar -> Balkar */ +/*{"kri", HB_TAG('K','R','I',' ')},*/ /* Krio */ +/*{"krl", HB_TAG('K','R','L',' ')},*/ /* Karelian */ + {"krt", HB_TAG('K','N','R',' ')}, /* Tumari Kanuri -> Kanuri */ + {"kru", HB_TAG('K','U','U',' ')}, /* Kurukh */ + {"ks", HB_TAG('K','S','H',' ')}, /* Kashmiri */ + {"ksh", HB_TAG('K','S','H','0')}, /* Kölsch -> Ripuarian */ + {"kss", HB_TAG('K','I','S',' ')}, /* Southern Kisi -> Kisii */ +/*{"ksw", HB_TAG('K','S','W',' ')},*/ /* S’gaw Karen */ + {"ktb", HB_TAG('K','E','B',' ')}, /* Kambaata -> Kebena */ + {"ktu", HB_TAG('K','O','N',' ')}, /* Kituba (Democratic Republic of Congo) -> Kikongo */ + {"ktw", HB_TAG('A','T','H',' ')}, /* Kato -> Athapaskan */ + {"ku", HB_TAG('K','U','R',' ')}, /* Kurdish [macrolanguage] */ +/*{"kum", HB_TAG('K','U','M',' ')},*/ /* Kumyk */ + {"kuu", HB_TAG('A','T','H',' ')}, /* Upper Kuskokwim -> Athapaskan */ + {"kv", HB_TAG('K','O','M',' ')}, /* Komi [macrolanguage] */ + {"kvb", HB_TAG('M','L','Y',' ')}, /* Kubu -> Malay */ + {"kvr", HB_TAG('M','L','Y',' ')}, /* Kerinci -> Malay */ + {"kw", HB_TAG('C','O','R',' ')}, /* Cornish */ + {"kwy", HB_TAG('K','O','N','0')}, /* San Salvador Kongo -> Kongo */ + {"kxc", HB_TAG('K','M','S',' ')}, /* Konso -> Komso */ + {"kxd", HB_TAG('M','L','Y',' ')}, /* Brunei -> Malay */ + {"kxl", HB_TAG('K','U','U',' ')}, /* Nepali Kurux (retired code) -> Kurukh */ + {"kxu", HB_TAG('K','U','I',' ')}, /* Kui (India) (retired code) */ + {"ky", HB_TAG('K','I','R',' ')}, /* Kirghiz (Kyrgyz) */ +/*{"kyu", HB_TAG('K','Y','U',' ')},*/ /* Western Kayah */ + {"la", HB_TAG('L','A','T',' ')}, /* Latin */ + {"lad", HB_TAG('J','U','D',' ')}, /* Ladino */ + {"lb", HB_TAG('L','T','Z',' ')}, /* Luxembourgish */ + {"lbe", HB_TAG('L','A','K',' ')}, /* Lak */ + {"lbj", HB_TAG('L','D','K',' ')}, /* Ladakhi */ + {"lbl", HB_TAG('B','I','K',' ')}, /* Libon Bikol -> Bikol */ + {"lce", HB_TAG('M','L','Y',' ')}, /* Loncong -> Malay */ + {"lcf", HB_TAG('M','L','Y',' ')}, /* Lubu -> Malay */ + {"ldi", HB_TAG('K','O','N','0')}, /* Laari -> Kongo */ +/*{"lez", HB_TAG('L','E','Z',' ')},*/ /* Lezghian -> Lezgi */ + {"lg", HB_TAG('L','U','G',' ')}, /* Ganda */ + {"li", HB_TAG('L','I','M',' ')}, /* Limburgish */ + {"lif", HB_TAG('L','M','B',' ')}, /* Limbu */ +/*{"lij", HB_TAG('L','I','J',' ')},*/ /* Ligurian */ +/*{"lis", HB_TAG('L','I','S',' ')},*/ /* Lisu */ + {"liw", HB_TAG('M','L','Y',' ')}, /* Col -> Malay */ +/*{"ljp", HB_TAG('L','J','P',' ')},*/ /* Lampung Api -> Lampung */ + {"lkb", HB_TAG('L','U','H',' ')}, /* Kabras -> Luyia */ +/*{"lki", HB_TAG('L','K','I',' ')},*/ /* Laki */ + {"lko", HB_TAG('L','U','H',' ')}, /* Khayo -> Luyia */ + {"lks", HB_TAG('L','U','H',' ')}, /* Kisa -> Luyia */ + {"lld", HB_TAG('L','A','D',' ')}, /* Ladin */ + {"lmn", HB_TAG('L','A','M',' ')}, /* Lambadi -> Lambani */ +/*{"lmo", HB_TAG('L','M','O',' ')},*/ /* Lombard */ + {"ln", HB_TAG('L','I','N',' ')}, /* Lingala */ + {"lo", HB_TAG('L','A','O',' ')}, /* Lao */ +/*{"lom", HB_TAG('L','O','M',' ')},*/ /* Loma (Liberia) */ +/*{"lrc", HB_TAG('L','R','C',' ')},*/ /* Northern Luri -> Luri */ + {"lri", HB_TAG('L','U','H',' ')}, /* Marachi -> Luyia */ + {"lrm", HB_TAG('L','U','H',' ')}, /* Marama -> Luyia */ + {"lsm", HB_TAG('L','U','H',' ')}, /* Saamia -> Luyia */ + {"lt", HB_TAG('L','T','H',' ')}, /* Lithuanian */ + {"ltg", HB_TAG('L','V','I',' ')}, /* Latgalian -> Latvian */ + {"lto", HB_TAG('L','U','H',' ')}, /* Tsotso -> Luyia */ + {"lts", HB_TAG('L','U','H',' ')}, /* Tachoni -> Luyia */ + {"lu", HB_TAG('L','U','B',' ')}, /* Luba-Katanga */ +/*{"lua", HB_TAG('L','U','A',' ')},*/ /* Luba-Lulua */ +/*{"luo", HB_TAG('L','U','O',' ')},*/ /* Luo (Kenya and Tanzania) */ + {"lus", HB_TAG('M','I','Z',' ')}, /* Lushai -> Mizo */ + {"luy", HB_TAG('L','U','H',' ')}, /* Luyia [macrolanguage] */ + {"luz", HB_TAG('L','R','C',' ')}, /* Southern Luri -> Luri */ + {"lv", HB_TAG('L','V','I',' ')}, /* Latvian [macrolanguage] */ + {"lvs", HB_TAG('L','V','I',' ')}, /* Standard Latvian -> Latvian */ + {"lwg", HB_TAG('L','U','H',' ')}, /* Wanga -> Luyia */ + {"lzh", HB_TAG('Z','H','T',' ')}, /* Literary Chinese -> Chinese Traditional */ + {"lzz", HB_TAG('L','A','Z',' ')}, /* Laz */ +/*{"mad", HB_TAG('M','A','D',' ')},*/ /* Madurese -> Madura */ +/*{"mag", HB_TAG('M','A','G',' ')},*/ /* Magahi */ + {"mai", HB_TAG('M','T','H',' ')}, /* Maithili */ + {"mak", HB_TAG('M','K','R',' ')}, /* Makasar */ +/*{"mam", HB_TAG('M','A','M',' ')},*/ /* Mam */ + {"man", HB_TAG('M','N','K',' ')}, /* Mandingo [macrolanguage] -> Maninka */ + {"max", HB_TAG('M','L','Y',' ')}, /* North Moluccan Malay -> Malay */ +/*{"mbo", HB_TAG('M','B','O',' ')},*/ /* Mbo (Cameroon) */ + {"mct", HB_TAG('B','T','I',' ')}, /* Mengisa -> Beti */ + {"mdf", HB_TAG('M','O','K',' ')}, /* Moksha */ +/*{"mdr", HB_TAG('M','D','R',' ')},*/ /* Mandar */ + {"mdy", HB_TAG('M','L','E',' ')}, /* Male (Ethiopia) */ + {"men", HB_TAG('M','D','E',' ')}, /* Mende (Sierra Leone) */ + {"meo", HB_TAG('M','L','Y',' ')}, /* Kedah Malay -> Malay */ +/*{"mer", HB_TAG('M','E','R',' ')},*/ /* Meru */ +/*{"mfa", HB_TAG('M','F','A',' ')},*/ /* Pattani Malay */ + {"mfb", HB_TAG('M','L','Y',' ')}, /* Bangka -> Malay */ +/*{"mfe", HB_TAG('M','F','E',' ')},*/ /* Morisyen */ + {"mg", HB_TAG('M','L','G',' ')}, /* Malagasy [macrolanguage] */ + {"mh", HB_TAG('M','A','H',' ')}, /* Marshallese */ + {"mhr", HB_TAG('L','M','A',' ')}, /* Eastern Mari -> Low Mari */ + {"mhv", HB_TAG('A','R','K',' ')}, /* Arakanese (retired code) -> Rakhine */ + {"mi", HB_TAG('M','R','I',' ')}, /* Maori */ +/*{"min", HB_TAG('M','I','N',' ')},*/ /* Minangkabau */ + {"mk", HB_TAG('M','K','D',' ')}, /* Macedonian */ + {"mku", HB_TAG('M','N','K',' ')}, /* Konyanka Maninka -> Maninka */ +/*{"mkw", HB_TAG('M','K','W',' ')},*/ /* Kituba (Congo) */ + {"ml", HB_TAG('M','A','L',' ')}, /* Malayalam -> Malayalam Traditional */ + {"ml", HB_TAG('M','L','R',' ')}, /* Malayalam -> Malayalam Reformed */ + {"mlq", HB_TAG('M','L','N',' ')}, /* Western Maninkakan -> Malinke */ + {"mlq", HB_TAG('M','N','K',' ')}, /* Western Maninkakan -> Maninka */ + {"mmr", HB_TAG('H','M','N',' ')}, /* Western Xiangxi Miao -> Hmong */ + {"mn", HB_TAG('M','N','G',' ')}, /* Mongolian [macrolanguage] */ + {"mnc", HB_TAG('M','C','H',' ')}, /* Manchu */ +/*{"mni", HB_TAG('M','N','I',' ')},*/ /* Manipuri */ + {"mnk", HB_TAG('M','N','D',' ')}, /* Mandinka */ + {"mnk", HB_TAG('M','N','K',' ')}, /* Mandinka -> Maninka */ + {"mnp", HB_TAG('Z','H','S',' ')}, /* Min Bei Chinese -> Chinese Simplified */ + {"mns", HB_TAG('M','A','N',' ')}, /* Mansi */ + {"mnw", HB_TAG('M','O','N',' ')}, /* Mon */ + {"mo", HB_TAG('M','O','L',' ')}, /* Moldavian (retired code) */ +/*{"moh", HB_TAG('M','O','H',' ')},*/ /* Mohawk */ +/*{"mos", HB_TAG('M','O','S',' ')},*/ /* Mossi */ + {"mpe", HB_TAG('M','A','J',' ')}, /* Majang */ + {"mqg", HB_TAG('M','L','Y',' ')}, /* Kota Bangun Kutai Malay -> Malay */ + {"mr", HB_TAG('M','A','R',' ')}, /* Marathi */ + {"mrh", HB_TAG('Q','I','N',' ')}, /* Mara Chin -> Chin */ + {"mrj", HB_TAG('H','M','A',' ')}, /* Western Mari -> High Mari */ + {"ms", HB_TAG('M','L','Y',' ')}, /* Malay [macrolanguage] */ + {"msc", HB_TAG('M','N','K',' ')}, /* Sankaran Maninka -> Maninka */ + {"msh", HB_TAG('M','L','G',' ')}, /* Masikoro Malagasy -> Malagasy */ + {"msi", HB_TAG('M','L','Y',' ')}, /* Sabah Malay -> Malay */ + {"mt", HB_TAG('M','T','S',' ')}, /* Maltese */ + {"mtr", HB_TAG('M','A','W',' ')}, /* Mewari -> Marwari */ + {"mui", HB_TAG('M','L','Y',' ')}, /* Musi -> Malay */ + {"mup", HB_TAG('R','A','J',' ')}, /* Malvi -> Rajasthani */ + {"muq", HB_TAG('H','M','N',' ')}, /* Eastern Xiangxi Miao -> Hmong */ +/*{"mus", HB_TAG('M','U','S',' ')},*/ /* Creek -> Muscogee */ + {"mvb", HB_TAG('A','T','H',' ')}, /* Mattole -> Athapaskan */ + {"mve", HB_TAG('M','A','W',' ')}, /* Marwari (Pakistan) */ + {"mvf", HB_TAG('M','N','G',' ')}, /* Peripheral Mongolian -> Mongolian */ + {"mwk", HB_TAG('M','N','K',' ')}, /* Kita Maninkakan -> Maninka */ +/*{"mwl", HB_TAG('M','W','L',' ')},*/ /* Mirandese */ + {"mwr", HB_TAG('M','A','W',' ')}, /* Marwari [macrolanguage] */ +/*{"mww", HB_TAG('M','W','W',' ')},*/ /* Hmong Daw */ + {"my", HB_TAG('B','R','M',' ')}, /* Burmese */ + {"mym", HB_TAG('M','E','N',' ')}, /* Me’en */ +/*{"myn", HB_TAG('M','Y','N',' ')},*/ /* Mayan [family] */ + {"myq", HB_TAG('M','N','K',' ')}, /* Forest Maninka (retired code) -> Maninka */ + {"myv", HB_TAG('E','R','Z',' ')}, /* Erzya */ +/*{"mzn", HB_TAG('M','Z','N',' ')},*/ /* Mazanderani */ + {"na", HB_TAG('N','A','U',' ')}, /* Nauru -> Nauruan */ +/*{"nag", HB_TAG('N','A','G',' ')},*/ /* Naga Pidgin -> Naga-Assamese */ +/*{"nah", HB_TAG('N','A','H',' ')},*/ /* Nahuatl [family] */ + {"nan", HB_TAG('Z','H','S',' ')}, /* Min Nan Chinese -> Chinese Simplified */ +/*{"nap", HB_TAG('N','A','P',' ')},*/ /* Neapolitan */ + {"nb", HB_TAG('N','O','R',' ')}, /* Norwegian Bokmål -> Norwegian */ + {"nd", HB_TAG('N','D','B',' ')}, /* North Ndebele -> Ndebele */ +/*{"ndc", HB_TAG('N','D','C',' ')},*/ /* Ndau */ +/*{"nds", HB_TAG('N','D','S',' ')},*/ /* Low Saxon */ + {"ne", HB_TAG('N','E','P',' ')}, /* Nepali [macrolanguage] */ +/*{"new", HB_TAG('N','E','W',' ')},*/ /* Newari */ + {"ng", HB_TAG('N','D','G',' ')}, /* Ndonga */ +/*{"nga", HB_TAG('N','G','A',' ')},*/ /* Ngbaka */ + {"ngl", HB_TAG('L','M','W',' ')}, /* Lomwe */ + {"ngo", HB_TAG('S','X','T',' ')}, /* Ngoni -> Sutu */ + {"nhd", HB_TAG('G','U','A',' ')}, /* Chiripá -> Guarani */ + {"niq", HB_TAG('K','A','L',' ')}, /* Nandi -> Kalenjin */ +/*{"niu", HB_TAG('N','I','U',' ')},*/ /* Niuean */ + {"niv", HB_TAG('G','I','L',' ')}, /* Gilyak */ + {"njz", HB_TAG('N','I','S',' ')}, /* Nyishi -> Nisi */ + {"nl", HB_TAG('N','L','D',' ')}, /* Dutch */ + {"nle", HB_TAG('L','U','H',' ')}, /* East Nyala -> Luyia */ + {"nn", HB_TAG('N','Y','N',' ')}, /* Norwegian Nynorsk (Nynorsk, Norwegian) */ + {"no", HB_TAG('N','O','R',' ')}, /* Norwegian [macrolanguage] */ + {"nod", HB_TAG('N','T','A',' ')}, /* Northern Thai -> Northern Tai */ +/*{"noe", HB_TAG('N','O','E',' ')},*/ /* Nimadi */ +/*{"nog", HB_TAG('N','O','G',' ')},*/ /* Nogai */ +/*{"nov", HB_TAG('N','O','V',' ')},*/ /* Novial */ + {"npi", HB_TAG('N','E','P',' ')}, /* Nepali */ + {"nqo", HB_TAG('N','K','O',' ')}, /* N’Ko */ + {"nr", HB_TAG('N','D','B',' ')}, /* South Ndebele -> Ndebele */ + {"nsk", HB_TAG('N','A','S',' ')}, /* Naskapi */ +/*{"nso", HB_TAG('N','S','O',' ')},*/ /* Pedi -> Sotho, Northern */ + {"nv", HB_TAG('N','A','V',' ')}, /* Navajo */ + {"nv", HB_TAG('A','T','H',' ')}, /* Navajo -> Athapaskan */ + {"ny", HB_TAG('C','H','I',' ')}, /* Chichewa (Chewa, Nyanja) */ + {"nyd", HB_TAG('L','U','H',' ')}, /* Nyore -> Luyia */ +/*{"nym", HB_TAG('N','Y','M',' ')},*/ /* Nyamwezi */ + {"nyn", HB_TAG('N','K','L',' ')}, /* Nyankole */ +/*{"nza", HB_TAG('N','Z','A',' ')},*/ /* Tigon Mbembe -> Mbembe Tigon */ + {"oc", HB_TAG('O','C','I',' ')}, /* Occitan (post 1500) */ + {"oj", HB_TAG('O','J','B',' ')}, /* Ojibwa [macrolanguage] -> Ojibway */ +/*{"ojb", HB_TAG('O','J','B',' ')},*/ /* Northwestern Ojibwa -> Ojibway */ + {"ojc", HB_TAG('O','J','B',' ')}, /* Central Ojibwa -> Ojibway */ + {"ojg", HB_TAG('O','J','B',' ')}, /* Eastern Ojibwa -> Ojibway */ + {"ojs", HB_TAG('O','C','R',' ')}, /* Severn Ojibwa -> Oji-Cree */ + {"ojw", HB_TAG('O','J','B',' ')}, /* Western Ojibwa -> Ojibway */ + {"oki", HB_TAG('K','A','L',' ')}, /* Okiek -> Kalenjin */ + {"okm", HB_TAG('K','O','H',' ')}, /* Middle Korean (10th-16th cent.) -> Korean Old Hangul */ + {"om", HB_TAG('O','R','O',' ')}, /* Oromo [macrolanguage] */ + {"or", HB_TAG('O','R','I',' ')}, /* Odia (formerly Oriya) [macrolanguage] */ + {"orc", HB_TAG('O','R','O',' ')}, /* Orma -> Oromo */ + {"orn", HB_TAG('M','L','Y',' ')}, /* Orang Kanaq -> Malay */ + {"ors", HB_TAG('M','L','Y',' ')}, /* Orang Seletar -> Malay */ + {"ory", HB_TAG('O','R','I',' ')}, /* Odia (formerly Oriya) */ + {"os", HB_TAG('O','S','S',' ')}, /* Ossetian */ + {"otw", HB_TAG('O','J','B',' ')}, /* Ottawa -> Ojibway */ + {"pa", HB_TAG('P','A','N',' ')}, /* Punjabi */ +/*{"pag", HB_TAG('P','A','G',' ')},*/ /* Pangasinan */ +/*{"pam", HB_TAG('P','A','M',' ')},*/ /* Pampanga -> Pampangan */ + {"pap", HB_TAG('P','A','P','0')}, /* Papiamento -> Papiamentu */ +/*{"pau", HB_TAG('P','A','U',' ')},*/ /* Palauan */ + {"pbt", HB_TAG('P','A','S',' ')}, /* Southern Pashto -> Pashto */ + {"pbu", HB_TAG('P','A','S',' ')}, /* Northern Pashto -> Pashto */ +/*{"pcc", HB_TAG('P','C','C',' ')},*/ /* Bouyei */ +/*{"pcd", HB_TAG('P','C','D',' ')},*/ /* Picard */ + {"pce", HB_TAG('P','L','G',' ')}, /* Ruching Palaung -> Palaung */ + {"pck", HB_TAG('Q','I','N',' ')}, /* Paite Chin -> Chin */ +/*{"pdc", HB_TAG('P','D','C',' ')},*/ /* Pennsylvania German */ + {"pel", HB_TAG('M','L','Y',' ')}, /* Pekal -> Malay */ + {"pes", HB_TAG('F','A','R',' ')}, /* Iranian Persian -> Persian */ + {"pga", HB_TAG('A','R','A',' ')}, /* Sudanese Creole Arabic -> Arabic */ +/*{"phk", HB_TAG('P','H','K',' ')},*/ /* Phake */ + {"pi", HB_TAG('P','A','L',' ')}, /* Pali */ +/*{"pih", HB_TAG('P','I','H',' ')},*/ /* Pitcairn-Norfolk -> Norfolk */ + {"pko", HB_TAG('K','A','L',' ')}, /* Pökoot -> Kalenjin */ + {"pl", HB_TAG('P','L','K',' ')}, /* Polish */ + {"pll", HB_TAG('P','L','G',' ')}, /* Shwe Palaung -> Palaung */ + {"plp", HB_TAG('P','A','P',' ')}, /* Palpa (retired code) */ + {"plt", HB_TAG('M','L','G',' ')}, /* Plateau Malagasy -> Malagasy */ +/*{"pms", HB_TAG('P','M','S',' ')},*/ /* Piemontese */ +/*{"pnb", HB_TAG('P','N','B',' ')},*/ /* Western Panjabi */ +/*{"poh", HB_TAG('P','O','H',' ')},*/ /* Poqomchi' -> Pocomchi */ +/*{"pon", HB_TAG('P','O','N',' ')},*/ /* Pohnpeian */ + {"ppa", HB_TAG('B','A','G',' ')}, /* Pao (retired code) -> Baghelkhandi */ +/*{"pro", HB_TAG('P','R','O',' ')},*/ /* Old Provençal (to 1500) -> Provençal / Old Provençal */ + {"prs", HB_TAG('D','R','I',' ')}, /* Dari */ + {"ps", HB_TAG('P','A','S',' ')}, /* Pashto [macrolanguage] */ + {"pse", HB_TAG('M','L','Y',' ')}, /* Central Malay -> Malay */ + {"pst", HB_TAG('P','A','S',' ')}, /* Central Pashto -> Pashto */ + {"pt", HB_TAG('P','T','G',' ')}, /* Portuguese */ +/*{"pwo", HB_TAG('P','W','O',' ')},*/ /* Pwo Western Karen -> Western Pwo Karen */ + {"qu", HB_TAG('Q','U','Z',' ')}, /* Quechua [macrolanguage] */ + {"qub", HB_TAG('Q','W','H',' ')}, /* Huallaga Huánuco Quechua -> Quechua (Peru) */ +/*{"quc", HB_TAG('Q','U','C',' ')},*/ /* K’iche’ */ + {"qud", HB_TAG('Q','V','I',' ')}, /* Calderón Highland Quichua -> Quechua (Ecuador) */ + {"quf", HB_TAG('Q','U','Z',' ')}, /* Lambayeque Quechua -> Quechua */ + {"qug", HB_TAG('Q','V','I',' ')}, /* Chimborazo Highland Quichua -> Quechua (Ecuador) */ +/*{"quh", HB_TAG('Q','U','H',' ')},*/ /* South Bolivian Quechua -> Quechua (Bolivia) */ + {"quk", HB_TAG('Q','U','Z',' ')}, /* Chachapoyas Quechua -> Quechua */ + {"qul", HB_TAG('Q','U','Z',' ')}, /* North Bolivian Quechua -> Quechua */ + {"qup", HB_TAG('Q','V','I',' ')}, /* Southern Pastaza Quechua -> Quechua (Ecuador) */ + {"qur", HB_TAG('Q','W','H',' ')}, /* Yanahuanca Pasco Quechua -> Quechua (Peru) */ + {"qus", HB_TAG('Q','U','H',' ')}, /* Santiago del Estero Quichua -> Quechua (Bolivia) */ + {"quw", HB_TAG('Q','V','I',' ')}, /* Tena Lowland Quichua -> Quechua (Ecuador) */ + {"qux", HB_TAG('Q','W','H',' ')}, /* Yauyos Quechua -> Quechua (Peru) */ + {"quy", HB_TAG('Q','U','Z',' ')}, /* Ayacucho Quechua -> Quechua */ +/*{"quz", HB_TAG('Q','U','Z',' ')},*/ /* Cusco Quechua -> Quechua */ + {"qva", HB_TAG('Q','W','H',' ')}, /* Ambo-Pasco Quechua -> Quechua (Peru) */ + {"qvc", HB_TAG('Q','U','Z',' ')}, /* Cajamarca Quechua -> Quechua */ + {"qve", HB_TAG('Q','U','Z',' ')}, /* Eastern Apurímac Quechua -> Quechua */ + {"qvh", HB_TAG('Q','W','H',' ')}, /* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua (Peru) */ +/*{"qvi", HB_TAG('Q','V','I',' ')},*/ /* Imbabura Highland Quichua -> Quechua (Ecuador) */ + {"qvj", HB_TAG('Q','V','I',' ')}, /* Loja Highland Quichua -> Quechua (Ecuador) */ + {"qvl", HB_TAG('Q','W','H',' ')}, /* Cajatambo North Lima Quechua -> Quechua (Peru) */ + {"qvm", HB_TAG('Q','W','H',' ')}, /* Margos-Yarowilca-Lauricocha Quechua -> Quechua (Peru) */ + {"qvn", HB_TAG('Q','W','H',' ')}, /* North Junín Quechua -> Quechua (Peru) */ + {"qvo", HB_TAG('Q','V','I',' ')}, /* Napo Lowland Quechua -> Quechua (Ecuador) */ + {"qvp", HB_TAG('Q','W','H',' ')}, /* Pacaraos Quechua -> Quechua (Peru) */ + {"qvs", HB_TAG('Q','U','Z',' ')}, /* San Martín Quechua -> Quechua */ + {"qvw", HB_TAG('Q','W','H',' ')}, /* Huaylla Wanca Quechua -> Quechua (Peru) */ + {"qvz", HB_TAG('Q','V','I',' ')}, /* Northern Pastaza Quichua -> Quechua (Ecuador) */ + {"qwa", HB_TAG('Q','W','H',' ')}, /* Corongo Ancash Quechua -> Quechua (Peru) */ + {"qwc", HB_TAG('Q','U','Z',' ')}, /* Classical Quechua -> Quechua */ +/*{"qwh", HB_TAG('Q','W','H',' ')},*/ /* Huaylas Ancash Quechua -> Quechua (Peru) */ + {"qws", HB_TAG('Q','W','H',' ')}, /* Sihuas Ancash Quechua -> Quechua (Peru) */ + {"qxa", HB_TAG('Q','W','H',' ')}, /* Chiquián Ancash Quechua -> Quechua (Peru) */ + {"qxc", HB_TAG('Q','W','H',' ')}, /* Chincha Quechua -> Quechua (Peru) */ + {"qxh", HB_TAG('Q','W','H',' ')}, /* Panao Huánuco Quechua -> Quechua (Peru) */ + {"qxl", HB_TAG('Q','V','I',' ')}, /* Salasaca Highland Quichua -> Quechua (Ecuador) */ + {"qxn", HB_TAG('Q','W','H',' ')}, /* Northern Conchucos Ancash Quechua -> Quechua (Peru) */ + {"qxo", HB_TAG('Q','W','H',' ')}, /* Southern Conchucos Ancash Quechua -> Quechua (Peru) */ + {"qxp", HB_TAG('Q','U','Z',' ')}, /* Puno Quechua -> Quechua */ + {"qxr", HB_TAG('Q','V','I',' ')}, /* Cañar Highland Quichua -> Quechua (Ecuador) */ + {"qxt", HB_TAG('Q','W','H',' ')}, /* Santa Ana de Tusi Pasco Quechua -> Quechua (Peru) */ + {"qxu", HB_TAG('Q','U','Z',' ')}, /* Arequipa-La Unión Quechua -> Quechua */ + {"qxw", HB_TAG('Q','W','H',' ')}, /* Jauja Wanca Quechua -> Quechua (Peru) */ + {"rag", HB_TAG('L','U','H',' ')}, /* Logooli -> Luyia */ +/*{"raj", HB_TAG('R','A','J',' ')},*/ /* Rajasthani [macrolanguage] */ +/*{"rar", HB_TAG('R','A','R',' ')},*/ /* Rarotongan */ + {"rbb", HB_TAG('P','L','G',' ')}, /* Rumai Palaung -> Palaung */ + {"rbl", HB_TAG('B','I','K',' ')}, /* Miraya Bikol -> Bikol */ +/*{"rej", HB_TAG('R','E','J',' ')},*/ /* Rejang */ +/*{"ria", HB_TAG('R','I','A',' ')},*/ /* Riang (India) */ +/*{"rif", HB_TAG('R','I','F',' ')},*/ /* Tarifit */ +/*{"rit", HB_TAG('R','I','T',' ')},*/ /* Ritharrngu -> Ritarungo */ + {"rki", HB_TAG('A','R','K',' ')}, /* Rakhine */ +/*{"rkw", HB_TAG('R','K','W',' ')},*/ /* Arakwal */ + {"rm", HB_TAG('R','M','S',' ')}, /* Romansh */ + {"rmc", HB_TAG('R','O','Y',' ')}, /* Carpathian Romani -> Romany */ + {"rmf", HB_TAG('R','O','Y',' ')}, /* Kalo Finnish Romani -> Romany */ + {"rml", HB_TAG('R','O','Y',' ')}, /* Baltic Romani -> Romany */ + {"rmn", HB_TAG('R','O','Y',' ')}, /* Balkan Romani -> Romany */ + {"rmo", HB_TAG('R','O','Y',' ')}, /* Sinte Romani -> Romany */ + {"rmw", HB_TAG('R','O','Y',' ')}, /* Welsh Romani -> Romany */ +/*{"rmy", HB_TAG('R','M','Y',' ')},*/ /* Vlax Romani */ + {"rmz", HB_TAG('A','R','K',' ')}, /* Marma -> Rakhine */ + {"rn", HB_TAG('R','U','N',' ')}, /* Rundi */ + {"rnl", HB_TAG('H','A','L',' ')}, /* Ranglong -> Halam (Falam Chin) */ + {"ro", HB_TAG('R','O','M',' ')}, /* Romanian */ + {"rom", HB_TAG('R','O','Y',' ')}, /* Romany [macrolanguage] */ +/*{"rtm", HB_TAG('R','T','M',' ')},*/ /* Rotuman */ + {"ru", HB_TAG('R','U','S',' ')}, /* Russian */ + {"rue", HB_TAG('R','S','Y',' ')}, /* Rusyn */ +/*{"rup", HB_TAG('R','U','P',' ')},*/ /* Aromanian */ + {"rw", HB_TAG('R','U','A',' ')}, /* Kinyarwanda */ + {"rwr", HB_TAG('M','A','W',' ')}, /* Marwari (India) */ + {"sa", HB_TAG('S','A','N',' ')}, /* Sanskrit */ + {"sah", HB_TAG('Y','A','K',' ')}, /* Yakut -> Sakha */ + {"sam", HB_TAG('P','A','A',' ')}, /* Samaritan Aramaic -> Palestinian Aramaic */ +/*{"sas", HB_TAG('S','A','S',' ')},*/ /* Sasak */ +/*{"sat", HB_TAG('S','A','T',' ')},*/ /* Santali */ + {"sc", HB_TAG('S','R','D',' ')}, /* Sardinian [macrolanguage] */ + {"sck", HB_TAG('S','A','D',' ')}, /* Sadri */ +/*{"scn", HB_TAG('S','C','N',' ')},*/ /* Sicilian */ +/*{"sco", HB_TAG('S','C','O',' ')},*/ /* Scots */ + {"scs", HB_TAG('S','C','S',' ')}, /* North Slavey */ + {"scs", HB_TAG('S','L','A',' ')}, /* North Slavey -> Slavey */ + {"scs", HB_TAG('A','T','H',' ')}, /* North Slavey -> Athapaskan */ + {"sd", HB_TAG('S','N','D',' ')}, /* Sindhi */ + {"sdc", HB_TAG('S','R','D',' ')}, /* Sassarese Sardinian -> Sardinian */ + {"sdh", HB_TAG('K','U','R',' ')}, /* Southern Kurdish -> Kurdish */ + {"sdn", HB_TAG('S','R','D',' ')}, /* Gallurese Sardinian -> Sardinian */ + {"se", HB_TAG('N','S','M',' ')}, /* Northern Sami */ + {"seh", HB_TAG('S','N','A',' ')}, /* Sena */ + {"sek", HB_TAG('A','T','H',' ')}, /* Sekani -> Athapaskan */ +/*{"sel", HB_TAG('S','E','L',' ')},*/ /* Selkup */ + {"sez", HB_TAG('Q','I','N',' ')}, /* Senthang Chin -> Chin */ + {"sfm", HB_TAG('H','M','N',' ')}, /* Small Flowery Miao -> Hmong */ + {"sg", HB_TAG('S','G','O',' ')}, /* Sango */ +/*{"sga", HB_TAG('S','G','A',' ')},*/ /* Old Irish (to 900) */ + {"sgc", HB_TAG('K','A','L',' ')}, /* Kipsigis -> Kalenjin */ +/*{"sgs", HB_TAG('S','G','S',' ')},*/ /* Samogitian */ + {"sgw", HB_TAG('C','H','G',' ')}, /* Sebat Bet Gurage -> Chaha Gurage */ + {"sgw", HB_TAG('S','G','W',' ')}, /* Sebat Bet Gurage -> Chaha Gurage (SIL fonts) */ +/*{"shi", HB_TAG('S','H','I',' ')},*/ /* Tachelhit */ +/*{"shn", HB_TAG('S','H','N',' ')},*/ /* Shan */ + {"shu", HB_TAG('A','R','A',' ')}, /* Chadian Arabic -> Arabic */ + {"si", HB_TAG('S','N','H',' ')}, /* Sinhala (Sinhalese) */ +/*{"sid", HB_TAG('S','I','D',' ')},*/ /* Sidamo */ + {"sjd", HB_TAG('K','S','M',' ')}, /* Kildin Sami */ + {"sjo", HB_TAG('S','I','B',' ')}, /* Xibe -> Sibe */ + {"sk", HB_TAG('S','K','Y',' ')}, /* Slovak */ + {"skg", HB_TAG('M','L','G',' ')}, /* Sakalava Malagasy -> Malagasy */ + {"skr", HB_TAG('S','R','K',' ')}, /* Saraiki */ + {"sl", HB_TAG('S','L','V',' ')}, /* Slovenian */ + {"sm", HB_TAG('S','M','O',' ')}, /* Samoan */ + {"sma", HB_TAG('S','S','M',' ')}, /* Southern Sami */ + {"smj", HB_TAG('L','S','M',' ')}, /* Lule Sami */ + {"smn", HB_TAG('I','S','M',' ')}, /* Inari Sami */ + {"sms", HB_TAG('S','K','S',' ')}, /* Skolt Sami */ + {"sn", HB_TAG('S','N','A','0')}, /* Shona */ +/*{"snk", HB_TAG('S','N','K',' ')},*/ /* Soninke */ + {"so", HB_TAG('S','M','L',' ')}, /* Somali */ +/*{"sop", HB_TAG('S','O','P',' ')},*/ /* Songe */ + {"spv", HB_TAG('O','R','I',' ')}, /* Sambalpuri -> Odia (formerly Oriya) */ + {"spy", HB_TAG('K','A','L',' ')}, /* Sabaot -> Kalenjin */ + {"sq", HB_TAG('S','Q','I',' ')}, /* Albanian [macrolanguage] */ + {"sr", HB_TAG('S','R','B',' ')}, /* Serbian */ + {"src", HB_TAG('S','R','D',' ')}, /* Logudorese Sardinian -> Sardinian */ + {"sro", HB_TAG('S','R','D',' ')}, /* Campidanese Sardinian -> Sardinian */ +/*{"srr", HB_TAG('S','R','R',' ')},*/ /* Serer */ + {"srs", HB_TAG('A','T','H',' ')}, /* Sarsi -> Athapaskan */ + {"ss", HB_TAG('S','W','Z',' ')}, /* Swati */ + {"ssh", HB_TAG('A','R','A',' ')}, /* Shihhi Arabic -> Arabic */ + {"st", HB_TAG('S','O','T',' ')}, /* Southern Sotho -> Sotho, Southern */ +/*{"stq", HB_TAG('S','T','Q',' ')},*/ /* Saterfriesisch -> Saterland Frisian */ + {"stv", HB_TAG('S','I','G',' ')}, /* Silt'e -> Silte Gurage */ + {"su", HB_TAG('S','U','N',' ')}, /* Sundanese */ +/*{"suk", HB_TAG('S','U','K',' ')},*/ /* Sukuma */ + {"suq", HB_TAG('S','U','R',' ')}, /* Suri */ + {"sv", HB_TAG('S','V','E',' ')}, /* Swedish */ +/*{"sva", HB_TAG('S','V','A',' ')},*/ /* Svan */ + {"sw", HB_TAG('S','W','K',' ')}, /* Swahili [macrolanguage] */ + {"swb", HB_TAG('C','M','R',' ')}, /* Maore Comorian -> Comorian */ + {"swc", HB_TAG('S','W','K',' ')}, /* Congo Swahili -> Swahili */ + {"swh", HB_TAG('S','W','K',' ')}, /* Swahili */ + {"swv", HB_TAG('M','A','W',' ')}, /* Shekhawati -> Marwari */ +/*{"sxu", HB_TAG('S','X','U',' ')},*/ /* Upper Saxon */ + {"syc", HB_TAG('S','Y','R',' ')}, /* Classical Syriac -> Syriac */ +/*{"syl", HB_TAG('S','Y','L',' ')},*/ /* Sylheti */ +/*{"syr", HB_TAG('S','Y','R',' ')},*/ /* Syriac [macrolanguage] */ +/*{"szl", HB_TAG('S','Z','L',' ')},*/ /* Silesian */ + {"ta", HB_TAG('T','A','M',' ')}, /* Tamil */ + {"taa", HB_TAG('A','T','H',' ')}, /* Lower Tanana -> Athapaskan */ +/*{"tab", HB_TAG('T','A','B',' ')},*/ /* Tabassaran -> Tabasaran */ + {"taq", HB_TAG('T','M','H',' ')}, /* Tamasheq -> Tamashek */ + {"tau", HB_TAG('A','T','H',' ')}, /* Upper Tanana -> Athapaskan */ + {"tcb", HB_TAG('A','T','H',' ')}, /* Tanacross -> Athapaskan */ + {"tce", HB_TAG('A','T','H',' ')}, /* Southern Tutchone -> Athapaskan */ + {"tcp", HB_TAG('Q','I','N',' ')}, /* Tawr Chin -> Chin */ + {"tcy", HB_TAG('T','U','L',' ')}, /* Tulu -> Tumbuka */ + {"tcz", HB_TAG('Q','I','N',' ')}, /* Thado Chin -> Chin */ +/*{"tdd", HB_TAG('T','D','D',' ')},*/ /* Tai Nüa -> Dehong Dai */ + {"tdx", HB_TAG('M','L','G',' ')}, /* Tandroy-Mahafaly Malagasy -> Malagasy */ + {"te", HB_TAG('T','E','L',' ')}, /* Telugu */ + {"tec", HB_TAG('K','A','L',' ')}, /* Terik -> Kalenjin */ + {"tem", HB_TAG('T','M','N',' ')}, /* Timne -> Temne */ +/*{"tet", HB_TAG('T','E','T',' ')},*/ /* Tetum */ + {"tfn", HB_TAG('A','T','H',' ')}, /* Tanaina -> Athapaskan */ + {"tg", HB_TAG('T','A','J',' ')}, /* Tajik -> Tajiki */ + {"tgj", HB_TAG('N','I','S',' ')}, /* Tagin -> Nisi */ + {"tgx", HB_TAG('A','T','H',' ')}, /* Tagish -> Athapaskan */ + {"th", HB_TAG('T','H','A',' ')}, /* Thai */ + {"tht", HB_TAG('A','T','H',' ')}, /* Tahltan -> Athapaskan */ + {"thv", HB_TAG('T','M','H',' ')}, /* Tahaggart Tamahaq -> Tamashek */ + {"thz", HB_TAG('T','M','H',' ')}, /* Tayart Tamajeq -> Tamashek */ + {"ti", HB_TAG('T','G','Y',' ')}, /* Tigrinya */ + {"tig", HB_TAG('T','G','R',' ')}, /* Tigre */ +/*{"tiv", HB_TAG('T','I','V',' ')},*/ /* Tiv */ + {"tk", HB_TAG('T','K','M',' ')}, /* Turkmen */ + {"tkg", HB_TAG('M','L','G',' ')}, /* Tesaka Malagasy -> Malagasy */ + {"tl", HB_TAG('T','G','L',' ')}, /* Tagalog */ +/*{"tmh", HB_TAG('T','M','H',' ')},*/ /* Tamashek [macrolanguage] */ + {"tmw", HB_TAG('M','L','Y',' ')}, /* Temuan -> Malay */ + {"tn", HB_TAG('T','N','A',' ')}, /* Tswana */ + {"tnf", HB_TAG('D','R','I',' ')}, /* Tangshewi (retired code) -> Dari */ + {"to", HB_TAG('T','G','N',' ')}, /* Tonga (Tonga Islands) -> Tongan */ + {"tod", HB_TAG('T','O','D','0')}, /* Toma */ + {"toi", HB_TAG('T','N','G',' ')}, /* Tonga (Zambia) */ + {"tol", HB_TAG('A','T','H',' ')}, /* Tolowa -> Athapaskan */ +/*{"tpi", HB_TAG('T','P','I',' ')},*/ /* Tok Pisin */ + {"tr", HB_TAG('T','R','K',' ')}, /* Turkish */ + {"tru", HB_TAG('T','U','A',' ')}, /* Turoyo -> Turoyo Aramaic */ + {"tru", HB_TAG('S','Y','R',' ')}, /* Turoyo -> Syriac */ + {"ts", HB_TAG('T','S','G',' ')}, /* Tsonga */ +/*{"tsj", HB_TAG('T','S','J',' ')},*/ /* Tshangla */ + {"tt", HB_TAG('T','A','T',' ')}, /* Tatar */ + {"ttm", HB_TAG('A','T','H',' ')}, /* Northern Tutchone -> Athapaskan */ + {"ttq", HB_TAG('T','M','H',' ')}, /* Tawallammat Tamajaq -> Tamashek */ +/*{"tum", HB_TAG('T','U','M',' ')},*/ /* Tumbuka -> Tulu */ + {"tuu", HB_TAG('A','T','H',' ')}, /* Tututni -> Athapaskan */ + {"tuy", HB_TAG('K','A','L',' ')}, /* Tugen -> Kalenjin */ +/*{"tvl", HB_TAG('T','V','L',' ')},*/ /* Tuvalu */ + {"tw", HB_TAG('T','W','I',' ')}, /* Twi */ + {"tw", HB_TAG('A','K','A',' ')}, /* Twi -> Akan */ + {"txc", HB_TAG('A','T','H',' ')}, /* Tsetsaut -> Athapaskan */ + {"txy", HB_TAG('M','L','G',' ')}, /* Tanosy Malagasy -> Malagasy */ + {"ty", HB_TAG('T','H','T',' ')}, /* Tahitian */ + {"tyv", HB_TAG('T','U','V',' ')}, /* Tuvinian -> Tuvin */ +/*{"tyz", HB_TAG('T','Y','Z',' ')},*/ /* Tày */ +/*{"tzm", HB_TAG('T','Z','M',' ')},*/ /* Central Atlas Tamazight -> Tamazight */ +/*{"tzo", HB_TAG('T','Z','O',' ')},*/ /* Tzotzil */ + {"ubl", HB_TAG('B','I','K',' ')}, /* Buhi'non Bikol -> Bikol */ +/*{"udm", HB_TAG('U','D','M',' ')},*/ /* Udmurt */ + {"ug", HB_TAG('U','Y','G',' ')}, /* Uyghur */ + {"uk", HB_TAG('U','K','R',' ')}, /* Ukrainian */ + {"uki", HB_TAG('K','U','I',' ')}, /* Kui (India) */ +/*{"umb", HB_TAG('U','M','B',' ')},*/ /* Umbundu */ + {"unr", HB_TAG('M','U','N',' ')}, /* Mundari */ + {"ur", HB_TAG('U','R','D',' ')}, /* Urdu */ + {"urk", HB_TAG('M','L','Y',' ')}, /* Urak Lawoi' -> Malay */ + {"uz", HB_TAG('U','Z','B',' ')}, /* Uzbek [macrolanguage] */ + {"uzn", HB_TAG('U','Z','B',' ')}, /* Northern Uzbek -> Uzbek */ + {"uzs", HB_TAG('U','Z','B',' ')}, /* Southern Uzbek -> Uzbek */ + {"ve", HB_TAG('V','E','N',' ')}, /* Venda */ +/*{"vec", HB_TAG('V','E','C',' ')},*/ /* Venetian */ + {"vi", HB_TAG('V','I','T',' ')}, /* Vietnamese */ + {"vkk", HB_TAG('M','L','Y',' ')}, /* Kaur -> Malay */ + {"vkt", HB_TAG('M','L','Y',' ')}, /* Tenggarong Kutai Malay -> Malay */ + {"vls", HB_TAG('F','L','E',' ')}, /* Vlaams -> Dutch (Flemish) */ + {"vmw", HB_TAG('M','A','K',' ')}, /* Makhuwa */ + {"vo", HB_TAG('V','O','L',' ')}, /* Volapük */ +/*{"vro", HB_TAG('V','R','O',' ')},*/ /* Võro */ + {"wa", HB_TAG('W','L','N',' ')}, /* Walloon */ +/*{"war", HB_TAG('W','A','R',' ')},*/ /* Waray (Philippines) -> Waray-Waray */ + {"wbm", HB_TAG('W','A',' ',' ')}, /* Wa */ + {"wbr", HB_TAG('W','A','G',' ')}, /* Wagdi */ + {"wlc", HB_TAG('C','M','R',' ')}, /* Mwali Comorian -> Comorian */ + {"wle", HB_TAG('S','I','G',' ')}, /* Wolane -> Silte Gurage */ + {"wlk", HB_TAG('A','T','H',' ')}, /* Wailaki -> Athapaskan */ + {"wni", HB_TAG('C','M','R',' ')}, /* Ndzwani Comorian -> Comorian */ + {"wo", HB_TAG('W','L','F',' ')}, /* Wolof */ + {"wry", HB_TAG('M','A','W',' ')}, /* Merwari -> Marwari */ + {"wsg", HB_TAG('G','O','N',' ')}, /* Adilabad Gondi -> Gondi */ +/*{"wtm", HB_TAG('W','T','M',' ')},*/ /* Mewati */ + {"wuu", HB_TAG('Z','H','S',' ')}, /* Wu Chinese -> Chinese Simplified */ + {"xal", HB_TAG('K','L','M',' ')}, /* Kalmyk */ + {"xal", HB_TAG('T','O','D',' ')}, /* Kalmyk -> Todo */ + {"xan", HB_TAG('S','E','K',' ')}, /* Xamtanga -> Sekota */ + {"xh", HB_TAG('X','H','S',' ')}, /* Xhosa */ +/*{"xjb", HB_TAG('X','J','B',' ')},*/ /* Minjungbal -> Minjangbal */ +/*{"xkf", HB_TAG('X','K','F',' ')},*/ /* Khengkha */ + {"xmm", HB_TAG('M','L','Y',' ')}, /* Manado Malay -> Malay */ + {"xmv", HB_TAG('M','L','G',' ')}, /* Antankarana Malagasy -> Malagasy */ + {"xmw", HB_TAG('M','L','G',' ')}, /* Tsimihety Malagasy -> Malagasy */ + {"xnr", HB_TAG('D','G','R',' ')}, /* Kangri -> Dogri */ +/*{"xog", HB_TAG('X','O','G',' ')},*/ /* Soga */ +/*{"xpe", HB_TAG('X','P','E',' ')},*/ /* Liberia Kpelle -> Kpelle (Liberia) */ + {"xsl", HB_TAG('S','S','L',' ')}, /* South Slavey */ + {"xsl", HB_TAG('S','L','A',' ')}, /* South Slavey -> Slavey */ + {"xsl", HB_TAG('A','T','H',' ')}, /* South Slavey -> Athapaskan */ + {"xst", HB_TAG('S','I','G',' ')}, /* Silt'e (retired code) -> Silte Gurage */ + {"xwo", HB_TAG('T','O','D',' ')}, /* Written Oirat -> Todo */ +/*{"yao", HB_TAG('Y','A','O',' ')},*/ /* Yao */ +/*{"yap", HB_TAG('Y','A','P',' ')},*/ /* Yapese */ + {"ybd", HB_TAG('A','R','K',' ')}, /* Yangbye (retired code) -> Rakhine */ + {"ydd", HB_TAG('J','I','I',' ')}, /* Eastern Yiddish -> Yiddish */ + {"yi", HB_TAG('J','I','I',' ')}, /* Yiddish [macrolanguage] */ + {"yih", HB_TAG('J','I','I',' ')}, /* Western Yiddish -> Yiddish */ + {"yo", HB_TAG('Y','B','A',' ')}, /* Yoruba */ + {"yos", HB_TAG('Q','I','N',' ')}, /* Yos (retired code) -> Chin */ + {"yrk", HB_TAG('T','N','E',' ')}, /* Nenets -> Tundra Nenets */ + {"yrk", HB_TAG('F','N','E',' ')}, /* Nenets -> Forest Nenets */ + {"yue", HB_TAG('Z','H','H',' ')}, /* Yue Chinese -> Chinese, Hong Kong SAR */ + {"za", HB_TAG('Z','H','A',' ')}, /* Zhuang [macrolanguage] */ + {"zch", HB_TAG('Z','H','A',' ')}, /* Central Hongshuihe Zhuang -> Zhuang */ + {"zdj", HB_TAG('C','M','R',' ')}, /* Ngazidja Comorian -> Comorian */ +/*{"zea", HB_TAG('Z','E','A',' ')},*/ /* Zeeuws -> Zealandic */ + {"zeh", HB_TAG('Z','H','A',' ')}, /* Eastern Hongshuihe Zhuang -> Zhuang */ + {"zgb", HB_TAG('Z','H','A',' ')}, /* Guibei Zhuang -> Zhuang */ +/*{"zgh", HB_TAG('Z','G','H',' ')},*/ /* Standard Moroccan Tamazight */ + {"zgm", HB_TAG('Z','H','A',' ')}, /* Minz Zhuang -> Zhuang */ + {"zgn", HB_TAG('Z','H','A',' ')}, /* Guibian Zhuang -> Zhuang */ + {"zh", HB_TAG('Z','H','S',' ')}, /* Chinese [macrolanguage] -> Chinese Simplified */ + {"zhd", HB_TAG('Z','H','A',' ')}, /* Dai Zhuang -> Zhuang */ + {"zhn", HB_TAG('Z','H','A',' ')}, /* Nong Zhuang -> Zhuang */ + {"zlj", HB_TAG('Z','H','A',' ')}, /* Liujiang Zhuang -> Zhuang */ + {"zlm", HB_TAG('M','L','Y',' ')}, /* Malay */ + {"zln", HB_TAG('Z','H','A',' ')}, /* Lianshan Zhuang -> Zhuang */ + {"zlq", HB_TAG('Z','H','A',' ')}, /* Liuqian Zhuang -> Zhuang */ + {"zmi", HB_TAG('M','L','Y',' ')}, /* Negeri Sembilan Malay -> Malay */ + {"zne", HB_TAG('Z','N','D',' ')}, /* Zande */ + {"zom", HB_TAG('Q','I','N',' ')}, /* Zou -> Chin */ + {"zqe", HB_TAG('Z','H','A',' ')}, /* Qiubei Zhuang -> Zhuang */ + {"zsm", HB_TAG('M','L','Y',' ')}, /* Standard Malay -> Malay */ + {"zu", HB_TAG('Z','U','L',' ')}, /* Zulu */ + {"zum", HB_TAG('L','R','C',' ')}, /* Kumzari -> Luri */ + {"zyb", HB_TAG('Z','H','A',' ')}, /* Yongbei Zhuang -> Zhuang */ + {"zyg", HB_TAG('Z','H','A',' ')}, /* Yang Zhuang -> Zhuang */ + {"zyj", HB_TAG('Z','H','A',' ')}, /* Youjiang Zhuang -> Zhuang */ + {"zyn", HB_TAG('Z','H','A',' ')}, /* Yongnan Zhuang -> Zhuang */ +/*{"zza", HB_TAG('Z','Z','A',' ')},*/ /* Zazaki [macrolanguage] */ + {"zzj", HB_TAG('Z','H','A',' ')}, /* Zuojiang Zhuang -> Zhuang */ }; -static_assert (HB_OT_MAX_TAGS_PER_LANGUAGE == 3u, ""); - /** * hb_ot_tags_from_complex_language: * @lang_str: a BCP 47 language tag to convert. @@ -1185,6 +1188,20 @@ hb_ot_tags_from_complex_language (const char *lang_str, *count = 1; return true; } + if (lang_matches (&lang_str[1], "np-hant-hk")) + { + /* Northern Ping Chinese */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], "np-hant-mo")) + { + /* Northern Ping Chinese */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */ + *count = 1; + return true; + } if (lang_matches (&lang_str[1], "px-hant-hk")) { /* Pu-Xian Chinese */ @@ -1199,6 +1216,20 @@ hb_ot_tags_from_complex_language (const char *lang_str, *count = 1; return true; } + if (lang_matches (&lang_str[1], "sp-hant-hk")) + { + /* Southern Ping Chinese */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], "sp-hant-mo")) + { + /* Southern Ping Chinese */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */ + *count = 1; + return true; + } if (lang_matches (&lang_str[1], "zh-hant-hk")) { /* Huizhou Chinese */ @@ -1269,6 +1300,20 @@ hb_ot_tags_from_complex_language (const char *lang_str, *count = 1; return true; } + if (lang_matches (&lang_str[1], "np-hans")) + { + /* Northern Ping Chinese */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], "np-hant")) + { + /* Northern Ping Chinese */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */ + *count = 1; + return true; + } if (lang_matches (&lang_str[1], "px-hans")) { /* Pu-Xian Chinese */ @@ -1283,6 +1328,20 @@ hb_ot_tags_from_complex_language (const char *lang_str, *count = 1; return true; } + if (lang_matches (&lang_str[1], "sp-hans")) + { + /* Southern Ping Chinese */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], "sp-hant")) + { + /* Southern Ping Chinese */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */ + *count = 1; + return true; + } if (lang_matches (&lang_str[1], "zh-hans")) { /* Huizhou Chinese */ @@ -1383,6 +1442,30 @@ hb_ot_tags_from_complex_language (const char *lang_str, *count = 1; return true; } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-hk")) + { + /* Northern Ping Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-mo")) + { + /* Northern Ping Chinese; Macao */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-tw")) + { + /* Northern Ping Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */ + *count = 1; + return true; + } if (0 == strncmp (&lang_str[1], "px-", 3) && subtag_matches (lang_str, limit, "-hk")) { @@ -1407,6 +1490,30 @@ hb_ot_tags_from_complex_language (const char *lang_str, *count = 1; return true; } + if (0 == strncmp (&lang_str[1], "sp-", 3) + && subtag_matches (lang_str, limit, "-hk")) + { + /* Southern Ping Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "sp-", 3) + && subtag_matches (lang_str, limit, "-mo")) + { + /* Southern Ping Chinese; Macao */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "sp-", 3) + && subtag_matches (lang_str, limit, "-tw")) + { + /* Southern Ping Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */ + *count = 1; + return true; + } if (0 == strncmp (&lang_str[1], "zh-", 3) && subtag_matches (lang_str, limit, "-hk")) { @@ -1934,7 +2041,8 @@ hb_ot_tags_from_complex_language (const char *lang_str, * * Converts @tag to a BCP 47 language tag if it is ambiguous (it corresponds to * many language tags) and the best tag is not the alphabetically first, or if - * the best tag consists of multiple subtags. + * the best tag consists of multiple subtags, or if the best tag does not appear + * in #ot_languages. * * Return value: The #hb_language_t corresponding to the BCP 47 language tag, * or #HB_LANGUAGE_INVALID if @tag is not ambiguous. @@ -1944,6 +2052,8 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag) { switch (tag) { + case HB_TAG('A','L','T',' '): /* Altai */ + return hb_language_from_string ("alt", -1); /* Southern Altai */ case HB_TAG('A','P','P','H'): /* Phonetic transcription—Americanist conventions */ return hb_language_from_string ("und-fonnapa", -1); /* Undetermined; North American Phonetic Alphabet */ case HB_TAG('A','R','A',' '): /* Arabic */ @@ -1962,8 +2072,6 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag) return hb_language_from_string ("din", -1); /* Dinka */ case HB_TAG('D','R','I',' '): /* Dari */ return hb_language_from_string ("prs", -1); /* Dari */ - case HB_TAG('D','U','J',' '): /* Dhuwal */ - return hb_language_from_string ("dwu", -1); /* Dhuwal */ case HB_TAG('D','Z','N',' '): /* Dzongkha */ return hb_language_from_string ("dz", -1); /* Dzongkha */ case HB_TAG('E','T','I',' '): /* Estonian */ @@ -1972,6 +2080,8 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag) return hb_language_from_string ("gon", -1); /* Gondi */ case HB_TAG('H','M','N',' '): /* Hmong */ return hb_language_from_string ("hmn", -1); /* Hmong */ + case HB_TAG('H','N','D',' '): /* Hindko */ + return hb_language_from_string ("hnd", -1); /* Southern Hindko */ case HB_TAG('I','J','O',' '): /* Ijo */ return hb_language_from_string ("ijo", -1); /* Ijo */ case HB_TAG('I','N','U',' '): /* Inuktitut */ @@ -1992,6 +2102,8 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag) return hb_language_from_string ("kr", -1); /* Kanuri */ case HB_TAG('K','O','K',' '): /* Konkani */ return hb_language_from_string ("kok", -1); /* Konkani */ + case HB_TAG('K','U','I',' '): /* Kui */ + return hb_language_from_string ("uki", -1); /* Kui (India) */ case HB_TAG('K','U','R',' '): /* Kurdish */ return hb_language_from_string ("ku", -1); /* Kurdish */ case HB_TAG('L','U','H',' '): /* Luyia */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag.cc index 18b5686b5c8..36ff854e3ed 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag.cc @@ -28,6 +28,8 @@ #include "hb.hh" +#ifndef HB_NO_OT_TAG + /* hb_script_t */ @@ -113,6 +115,7 @@ hb_ot_new_tag_to_script (hb_tag_t tag) return HB_SCRIPT_UNKNOWN; } +#ifndef HB_DISABLE_DEPRECATED void hb_ot_tags_from_script (hb_script_t script, hb_tag_t *script_tag_1, @@ -124,6 +127,7 @@ hb_ot_tags_from_script (hb_script_t script, *script_tag_1 = count > 0 ? tags[0] : HB_OT_TAG_DEFAULT_SCRIPT; *script_tag_2 = count > 1 ? tags[1] : HB_OT_TAG_DEFAULT_SCRIPT; } +#endif /* * Complete list at: @@ -143,7 +147,9 @@ hb_ot_all_tags_from_script (hb_script_t script, hb_tag_t new_tag = hb_ot_new_tag_from_script (script); if (unlikely (new_tag != HB_OT_TAG_DEFAULT_SCRIPT)) { - tags[i++] = new_tag | '3'; + /* HB_SCRIPT_MYANMAR maps to 'mym2', but there is no 'mym3'. */ + if (new_tag != HB_TAG('m','y','m','2')) + tags[i++] = new_tag | '3'; if (*count > i) tags[i++] = new_tag; } @@ -171,24 +177,6 @@ hb_ot_tag_to_script (hb_tag_t tag) /* hb_language_t */ -static int -lang_compare_first_component (const void *pa, - const void *pb) -{ - const char *a = (const char *) pa; - const char *b = (const char *) pb; - unsigned int da, db; - const char *p; - - p = strchr (a, '-'); - da = p ? (unsigned int) (p - a) : strlen (a); - - p = strchr (b, '-'); - db = p ? (unsigned int) (p - b) : strlen (b); - - return strncmp (a, b, MAX (da, db)); -} - static bool subtag_matches (const char *lang_str, const char *limit, @@ -213,10 +201,28 @@ lang_matches (const char *lang_str, const char *spec) (lang_str[len] == '\0' || lang_str[len] == '-'); } -typedef struct { +struct LangTag +{ char language[4]; - hb_tag_t tags[HB_OT_MAX_TAGS_PER_LANGUAGE]; -} LangTag; + hb_tag_t tag; + + int cmp (const char *a) const + { + const char *b = this->language; + unsigned int da, db; + const char *p; + + p = strchr (a, '-'); + da = p ? (unsigned int) (p - a) : strlen (a); + + p = strchr (b, '-'); + db = p ? (unsigned int) (p - b) : strlen (b); + + return strncmp (a, b, hb_max (da, db)); + } + int cmp (const LangTag *that) const + { return cmp (that->language); } +}; #include "hb-ot-tag-table.hh" @@ -230,6 +236,7 @@ typedef struct { /*{"??", {HB_TAG('Y','I','C',' ')}},*/ /* Yi Classic */ /*{"zh?", {HB_TAG('Z','H','P',' ')}},*/ /* Chinese Phonetic */ +#ifndef HB_DISABLE_DEPRECATED hb_tag_t hb_ot_tag_from_language (hb_language_t language) { @@ -238,6 +245,7 @@ hb_ot_tag_from_language (hb_language_t language) hb_ot_tags_from_script_and_language (HB_SCRIPT_UNKNOWN, language, nullptr, nullptr, &count, tags); return count > 0 ? tags[0] : HB_OT_TAG_DEFAULT_LANGUAGE; } +#endif static void hb_ot_tags_from_language (const char *lang_str, @@ -246,6 +254,7 @@ hb_ot_tags_from_language (const char *lang_str, hb_tag_t *tags) { const char *s; + unsigned int tag_idx; /* Check for matches of multiple subtags. */ if (hb_ot_tags_from_complex_language (lang_str, limit, count, tags)) @@ -254,7 +263,6 @@ hb_ot_tags_from_language (const char *lang_str, /* Find a language matching in the first component. */ s = strchr (lang_str, '-'); { - const LangTag *lang_tag; if (s && limit - lang_str >= 6) { const char *extlang_end = strchr (s + 1, '-'); @@ -263,14 +271,18 @@ hb_ot_tags_from_language (const char *lang_str, ISALPHA (s[1])) lang_str = s + 1; } - lang_tag = (LangTag *) bsearch (lang_str, ot_languages, - ARRAY_LENGTH (ot_languages), sizeof (LangTag), - lang_compare_first_component); - if (lang_tag) + if (hb_sorted_array (ot_languages).bfind (lang_str, &tag_idx)) { unsigned int i; - for (i = 0; i < *count && lang_tag->tags[i] != HB_TAG_NONE; i++) - tags[i] = lang_tag->tags[i]; + while (tag_idx != 0 && + 0 == strcmp (ot_languages[tag_idx].language, ot_languages[tag_idx - 1].language)) + tag_idx--; + for (i = 0; + i < *count && + tag_idx + i < ARRAY_LENGTH (ot_languages) && + 0 == strcmp (ot_languages[tag_idx + i].language, ot_languages[tag_idx].language); + i++) + tags[i] = ot_languages[tag_idx + i].tag; *count = i; return; } @@ -295,28 +307,42 @@ parse_private_use_subtag (const char *private_use_subtag, const char *prefix, unsigned char (*normalize) (unsigned char)) { - if (private_use_subtag && count && tags && *count) - { - const char *s = strstr (private_use_subtag, prefix); - if (s) +#ifdef HB_NO_LANGUAGE_PRIVATE_SUBTAG + return false; +#endif + + if (!(private_use_subtag && count && tags && *count)) return false; + + const char *s = strstr (private_use_subtag, prefix); + if (!s) return false; + + char tag[4]; + int i; + s += strlen (prefix); + if (s[0] == '-') { + s += 1; + char c; + for (i = 0; i < 8 && ISHEX (s[i]); i++) { - char tag[4]; - int i; - s += strlen (prefix); - for (i = 0; i < 4 && ISALNUM (s[i]); i++) - tag[i] = normalize (s[i]); - if (i) - { - for (; i < 4; i++) - tag[i] = ' '; - tags[0] = HB_TAG (tag[0], tag[1], tag[2], tag[3]); - if ((tags[0] & 0xDFDFDFDF) == HB_OT_TAG_DEFAULT_SCRIPT) - tags[0] ^= ~0xDFDFDFDF; - *count = 1; - return false; - } + c = FROMHEX (s[i]); + if (i % 2 == 0) + tag[i / 2] = c << 4; + else + tag[i / 2] += c; } + if (i != 8) return false; + } else { + for (i = 0; i < 4 && ISALNUM (s[i]); i++) + tag[i] = normalize (s[i]); + if (!i) return false; + + for (; i < 4; i++) + tag[i] = ' '; } + tags[0] = HB_TAG (tag[0], tag[1], tag[2], tag[3]); + if ((tags[0] & 0xDFDFDFDF) == HB_OT_TAG_DEFAULT_SCRIPT) + tags[0] ^= ~0xDFDFDFDF; + *count = 1; return true; } @@ -384,8 +410,8 @@ hb_ot_tags_from_script_and_language (hb_script_t script, limit = s; } - needs_script = parse_private_use_subtag (private_use_subtag, script_count, script_tags, "-hbsc", TOLOWER); - needs_language = parse_private_use_subtag (private_use_subtag, language_count, language_tags, "-hbot", TOUPPER); + needs_script = !parse_private_use_subtag (private_use_subtag, script_count, script_tags, "-hbsc", TOLOWER); + needs_language = !parse_private_use_subtag (private_use_subtag, language_count, language_tags, "-hbot", TOUPPER); if (needs_language && language_count && language_tags && *language_count) hb_ot_tags_from_language (lang_str, limit, language_count, language_tags); @@ -419,20 +445,31 @@ hb_ot_tag_to_language (hb_tag_t tag) } for (i = 0; i < ARRAY_LENGTH (ot_languages); i++) - if (ot_languages[i].tags[0] == tag) + if (ot_languages[i].tag == tag) return hb_language_from_string (ot_languages[i].language, -1); - /* Else return a custom language in the form of "x-hbotABCD" */ + /* Return a custom language in the form of "x-hbot-AABBCCDD". + * If it's three letters long, also guess it's ISO 639-3 and lower-case and + * prepend it (if it's not a registered tag, the private use subtags will + * ensure that calling hb_ot_tag_from_language on the result will still return + * the same tag as the original tag). + */ { - unsigned char buf[11] = "x-hbot"; - buf[6] = tag >> 24; - buf[7] = (tag >> 16) & 0xFF; - buf[8] = (tag >> 8) & 0xFF; - buf[9] = tag & 0xFF; - if (buf[9] == 0x20) - buf[9] = '\0'; - buf[10] = '\0'; - return hb_language_from_string ((char *) buf, -1); + char buf[20]; + char *str = buf; + if (ISALPHA (tag >> 24) + && ISALPHA ((tag >> 16) & 0xFF) + && ISALPHA ((tag >> 8) & 0xFF) + && (tag & 0xFF) == ' ') + { + buf[0] = TOLOWER (tag >> 24); + buf[1] = TOLOWER ((tag >> 16) & 0xFF); + buf[2] = TOLOWER ((tag >> 8) & 0xFF); + buf[3] = '-'; + str += 4; + } + snprintf (str, 16, "x-hbot-%08x", tag); + return hb_language_from_string (&*buf, -1); } } @@ -473,13 +510,14 @@ hb_ot_tags_to_script_and_language (hb_tag_t script_tag, unsigned char *buf; const char *lang_str = hb_language_to_string (*language); size_t len = strlen (lang_str); - buf = (unsigned char *) malloc (len + 11); + buf = (unsigned char *) malloc (len + 16); if (unlikely (!buf)) { *language = nullptr; } else { + int shift; memcpy (buf, lang_str, len); if (lang_str[0] != 'x' || lang_str[1] != '-') { buf[len++] = '-'; @@ -490,10 +528,9 @@ hb_ot_tags_to_script_and_language (hb_tag_t script_tag, buf[len++] = 'b'; buf[len++] = 's'; buf[len++] = 'c'; - buf[len++] = script_tag >> 24; - buf[len++] = (script_tag >> 16) & 0xFF; - buf[len++] = (script_tag >> 8) & 0xFF; - buf[len++] = script_tag & 0xFF; + buf[len++] = '-'; + for (shift = 28; shift >= 0; shift -= 4) + buf[len++] = TOHEX (script_tag >> shift); *language = hb_language_from_string ((char *) buf, len); free (buf); } @@ -507,8 +544,8 @@ test_langs_sorted () { for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages); i++) { - int c = lang_compare_first_component (ot_languages[i-1].language, ot_languages[i].language); - if (c >= 0) + int c = ot_languages[i].cmp (&ot_languages[i - 1]); + if (c > 0) { fprintf (stderr, "ot_languages not sorted at index %d: %s %d %s\n", i, ot_languages[i-1].language, c, ot_languages[i].language); @@ -525,3 +562,6 @@ main () } #endif + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh index 5ae0359e5b8..2c2934dd6fa 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh @@ -49,9 +49,10 @@ struct AxisValueMap } public: - F2DOT14 fromCoord; /* A normalized coordinate value obtained using - * default normalization. */ - F2DOT14 toCoord; /* The modified, normalized coordinate value. */ + F2DOT14 coords[2]; +// F2DOT14 fromCoord; /* A normalized coordinate value obtained using +// * default normalization. */ +// F2DOT14 toCoord; /* The modified, normalized coordinate value. */ public: DEFINE_SIZE_STATIC (4); @@ -59,12 +60,13 @@ struct AxisValueMap struct SegmentMaps : ArrayOf { - int map (int value) const + int map (int value, unsigned int from_offset = 0, unsigned int to_offset = 1) const { +#define fromCoord coords[from_offset] +#define toCoord coords[to_offset] /* The following special-cases are not part of OpenType, which requires * that at least -1, 0, and +1 must be mapped. But we include these as * part of a better error recovery scheme. */ - if (len < 2) { if (!len) @@ -77,7 +79,7 @@ struct SegmentMaps : ArrayOf return value - arrayZ[0].fromCoord + arrayZ[0].toCoord; unsigned int i; - unsigned int count = len; + unsigned int count = len - 1; for (i = 1; i < count && value > arrayZ[i].fromCoord; i++) ; @@ -88,11 +90,14 @@ struct SegmentMaps : ArrayOf return arrayZ[i-1].toCoord; int denom = arrayZ[i].fromCoord - arrayZ[i-1].fromCoord; - return arrayZ[i-1].toCoord + - ((arrayZ[i].toCoord - arrayZ[i-1].toCoord) * - (value - arrayZ[i-1].fromCoord) + denom/2) / denom; + return roundf (arrayZ[i-1].toCoord + ((float) (arrayZ[i].toCoord - arrayZ[i-1].toCoord) * + (value - arrayZ[i-1].fromCoord)) / denom); +#undef toCoord +#undef fromCoord } + int unmap (int value) const { return map (value, 1, 0); } + public: DEFINE_SIZE_ARRAY (2, *this); }; @@ -123,7 +128,7 @@ struct avar void map_coords (int *coords, unsigned int coords_length) const { - unsigned int count = MIN (coords_length, axisCount); + unsigned int count = hb_min (coords_length, axisCount); const SegmentMaps *map = &firstAxisSegmentMaps; for (unsigned int i = 0; i < count; i++) @@ -133,6 +138,18 @@ struct avar } } + void unmap_coords (int *coords, unsigned int coords_length) const + { + unsigned int count = hb_min (coords_length, axisCount); + + const SegmentMaps *map = &firstAxisSegmentMaps; + for (unsigned int i = 0; i < count; i++) + { + coords[i] = map->unmap (coords[i]); + map = &StructAfter (*map); + } + } + protected: FixedVersion<>version; /* Version of the avar table * initially set to 0x00010000u */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh index 3785d099b5c..e20cfa4df0f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh @@ -44,7 +44,7 @@ struct InstanceRecord { friend struct fvar; - hb_array_t get_coordinates (unsigned int axis_count) const + hb_array_t get_coordinates (unsigned int axis_count) const { return coordinatesZ.as_array (axis_count); } bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const @@ -58,7 +58,7 @@ struct InstanceRecord NameID subfamilyNameID;/* The name ID for entries in the 'name' table * that provide subfamily names for this instance. */ HBUINT16 flags; /* Reserved for future use — set to 0. */ - UnsizedArrayOf + UnsizedArrayOf coordinatesZ; /* The coordinates array for this instance. */ //NameID postScriptNameIDX;/*Optional. The name ID for entries in the 'name' // * table that provide PostScript names for this @@ -70,22 +70,83 @@ struct InstanceRecord struct AxisRecord { + int cmp (hb_tag_t key) const { return axisTag.cmp (key); } + enum { AXIS_FLAG_HIDDEN = 0x0001, }; +#ifndef HB_DISABLE_DEPRECATED + void get_axis_deprecated (hb_ot_var_axis_t *info) const + { + info->tag = axisTag; + info->name_id = axisNameID; + get_coordinates (info->min_value, info->default_value, info->max_value); + } +#endif + + void get_axis_info (unsigned axis_index, hb_ot_var_axis_info_t *info) const + { + info->axis_index = axis_index; + info->tag = axisTag; + info->name_id = axisNameID; + info->flags = (hb_ot_var_axis_flags_t) (unsigned int) flags; + get_coordinates (info->min_value, info->default_value, info->max_value); + info->reserved = 0; + } + + int normalize_axis_value (float v) const + { + float min_value, default_value, max_value; + get_coordinates (min_value, default_value, max_value); + + v = hb_clamp (v, min_value, max_value); + + if (v == default_value) + return 0; + else if (v < default_value) + v = (v - default_value) / (default_value - min_value); + else + v = (v - default_value) / (max_value - default_value); + return roundf (v * 16384.f); + } + + float unnormalize_axis_value (int v) const + { + float min_value, default_value, max_value; + get_coordinates (min_value, default_value, max_value); + + if (v == 0) + return default_value; + else if (v < 0) + return v * (default_value - min_value) / 16384.f + default_value; + else + return v * (max_value - default_value) / 16384.f + default_value; + } + + hb_ot_name_id_t get_name_id () const { return axisNameID; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this)); } - public: + protected: + void get_coordinates (float &min, float &default_, float &max) const + { + default_ = defaultValue / 65536.f; + /* Ensure order, to simplify client math. */ + min = hb_min (default_, minValue / 65536.f); + max = hb_max (default_, maxValue / 65536.f); + } + + protected: Tag axisTag; /* Tag identifying the design variation for the axis. */ - Fixed minValue; /* The minimum coordinate value for the axis. */ - Fixed defaultValue; /* The default coordinate value for the axis. */ - Fixed maxValue; /* The maximum coordinate value for the axis. */ + HBFixed minValue; /* The minimum coordinate value for the axis. */ + HBFixed defaultValue; /* The default coordinate value for the axis. */ + HBFixed maxValue; /* The maximum coordinate value for the axis. */ HBUINT16 flags; /* Axis flags. */ NameID axisNameID; /* The name ID for entries in the 'name' table that * provide a display name for this axis. */ @@ -114,54 +175,20 @@ struct fvar unsigned int get_axis_count () const { return axisCount; } - void get_axis_deprecated (unsigned int axis_index, - hb_ot_var_axis_t *info) const - { - const AxisRecord &axis = get_axes ()[axis_index]; - info->tag = axis.axisTag; - info->name_id = axis.axisNameID; - info->default_value = axis.defaultValue / 65536.; - /* Ensure order, to simplify client math. */ - info->min_value = MIN (info->default_value, axis.minValue / 65536.); - info->max_value = MAX (info->default_value, axis.maxValue / 65536.); - } - - void get_axis_info (unsigned int axis_index, - hb_ot_var_axis_info_t *info) const - { - const AxisRecord &axis = get_axes ()[axis_index]; - info->axis_index = axis_index; - info->tag = axis.axisTag; - info->name_id = axis.axisNameID; - info->flags = (hb_ot_var_axis_flags_t) (unsigned int) axis.flags; - info->default_value = axis.defaultValue / 65536.; - /* Ensure order, to simplify client math. */ - info->min_value = MIN (info->default_value, axis.minValue / 65536.); - info->max_value = MAX (info->default_value, axis.maxValue / 65536.); - info->reserved = 0; - } - +#ifndef HB_DISABLE_DEPRECATED unsigned int get_axes_deprecated (unsigned int start_offset, unsigned int *axes_count /* IN/OUT */, hb_ot_var_axis_t *axes_array /* OUT */) const { if (axes_count) { - /* TODO Rewrite as hb_array_t<>::sub-array() */ - unsigned int count = axisCount; - start_offset = MIN (start_offset, count); - - count -= start_offset; - axes_array += start_offset; - - count = MIN (count, *axes_count); - *axes_count = count; - - for (unsigned int i = 0; i < count; i++) - get_axis_deprecated (start_offset + i, axes_array + i); + hb_array_t arr = get_axes ().sub_array (start_offset, axes_count); + for (unsigned i = 0; i < arr.length; ++i) + arr[i].get_axis_deprecated (&axes_array[i]); } return axisCount; } +#endif unsigned int get_axis_infos (unsigned int start_offset, unsigned int *axes_count /* IN/OUT */, @@ -169,70 +196,38 @@ struct fvar { if (axes_count) { - /* TODO Rewrite as hb_array_t<>::sub-array() */ - unsigned int count = axisCount; - start_offset = MIN (start_offset, count); - - count -= start_offset; - axes_array += start_offset; - - count = MIN (count, *axes_count); - *axes_count = count; - - for (unsigned int i = 0; i < count; i++) - get_axis_info (start_offset + i, axes_array + i); + hb_array_t arr = get_axes ().sub_array (start_offset, axes_count); + for (unsigned i = 0; i < arr.length; ++i) + arr[i].get_axis_info (start_offset + i, &axes_array[i]); } return axisCount; } - bool find_axis_deprecated (hb_tag_t tag, - unsigned int *axis_index, - hb_ot_var_axis_t *info) const +#ifndef HB_DISABLE_DEPRECATED + bool + find_axis_deprecated (hb_tag_t tag, unsigned *axis_index, hb_ot_var_axis_t *info) const { - const AxisRecord *axes = get_axes (); - unsigned int count = get_axis_count (); - for (unsigned int i = 0; i < count; i++) - if (axes[i].axisTag == tag) - { - if (axis_index) - *axis_index = i; - get_axis_deprecated (i, info); - return true; - } - if (axis_index) - *axis_index = HB_OT_VAR_NO_AXIS_INDEX; - return false; + unsigned i; + if (!axis_index) axis_index = &i; + *axis_index = HB_OT_VAR_NO_AXIS_INDEX; + auto axes = get_axes (); + return axes.lfind (tag, axis_index) && (axes[*axis_index].get_axis_deprecated (info), true); } +#endif - bool find_axis_info (hb_tag_t tag, - hb_ot_var_axis_info_t *info) const + bool + find_axis_info (hb_tag_t tag, hb_ot_var_axis_info_t *info) const { - const AxisRecord *axes = get_axes (); - unsigned int count = get_axis_count (); - for (unsigned int i = 0; i < count; i++) - if (axes[i].axisTag == tag) - { - get_axis_info (i, info); - return true; - } - return false; + unsigned i; + auto axes = get_axes (); + return axes.lfind (tag, &i) && (axes[i].get_axis_info (i, info), true); } int normalize_axis_value (unsigned int axis_index, float v) const - { - hb_ot_var_axis_info_t axis; - get_axis_info (axis_index, &axis); + { return get_axes ()[axis_index].normalize_axis_value (v); } - v = MAX (MIN (v, axis.max_value), axis.min_value); /* Clamp. */ - - if (v == axis.default_value) - return 0; - else if (v < axis.default_value) - v = (v - axis.default_value) / (axis.default_value - axis.min_value); - else - v = (v - axis.default_value) / (axis.max_value - axis.default_value); - return (int) (v * 16384.f + (v >= 0.f ? .5f : -.5f)); - } + float unnormalize_axis_value (unsigned int axis_index, int v) const + { return get_axes ()[axis_index].unnormalize_axis_value (v); } unsigned int get_instance_count () const { return instanceCount; } @@ -253,8 +248,8 @@ struct fvar } unsigned int get_instance_coords (unsigned int instance_index, - unsigned int *coords_length, /* IN/OUT */ - float *coords /* OUT */) const + unsigned int *coords_length, /* IN/OUT */ + float *coords /* OUT */) const { const InstanceRecord *instance = get_instance (instance_index); if (unlikely (!instance)) @@ -266,7 +261,7 @@ struct fvar if (coords_length && *coords_length) { - hb_array_t instanceCoords = instance->get_coordinates (axisCount) + hb_array_t instanceCoords = instance->get_coordinates (axisCount) .sub_array (0, *coords_length); for (unsigned int i = 0; i < instanceCoords.length; i++) coords[i] = instanceCoords.arrayZ[i].to_float (); @@ -274,6 +269,26 @@ struct fvar return axisCount; } + void collect_name_ids (hb_set_t *nameids) const + { + if (!has_data ()) return; + + + get_axes () + | hb_map (&AxisRecord::get_name_id) + | hb_sink (nameids) + ; + + + hb_range ((unsigned) instanceCount) + | hb_map ([this] (const unsigned _) { return get_instance_subfamily_name_id (_); }) + | hb_sink (nameids) + ; + + + hb_range ((unsigned) instanceCount) + | hb_map ([this] (const unsigned _) { return get_instance_postscript_name_id (_); }) + | hb_sink (nameids) + ; + } + protected: hb_array_t get_axes () const { return hb_array (&(this+firstAxis), axisCount); } @@ -299,8 +314,8 @@ struct fvar HBUINT16 instanceCount; /* The number of named instances defined in the font * (the number of records in the instances array). */ HBUINT16 instanceSize; /* The size in bytes of each InstanceRecord — set - * to either axisCount * sizeof(Fixed) + 4, or to - * axisCount * sizeof(Fixed) + 6. */ + * to either axisCount * sizeof(HBFixed) + 4, or to + * axisCount * sizeof(HBFixed) + 6. */ public: DEFINE_SIZE_STATIC (16); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh new file mode 100644 index 00000000000..7c99b5de061 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh @@ -0,0 +1,701 @@ +/* + * Copyright © 2019 Adobe Inc. + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_VAR_GVAR_TABLE_HH +#define HB_OT_VAR_GVAR_TABLE_HH + +#include "hb-open-type.hh" + +/* + * gvar -- Glyph Variation Table + * https://docs.microsoft.com/en-us/typography/opentype/spec/gvar + */ +#define HB_OT_TAG_gvar HB_TAG('g','v','a','r') + +namespace OT { + +struct contour_point_t +{ + void init (float x_ = 0.f, float y_ = 0.f, bool is_end_point_ = false) + { flag = 0; x = x_; y = y_; is_end_point = is_end_point_; } + + void translate (const contour_point_t &p) { x += p.x; y += p.y; } + + uint8_t flag; + float x, y; + bool is_end_point; +}; + +struct contour_point_vector_t : hb_vector_t +{ + void extend (const hb_array_t &a) + { + unsigned int old_len = length; + resize (old_len + a.length); + for (unsigned int i = 0; i < a.length; i++) + (*this)[old_len + i] = a[i]; + } + + void transform (const float (&matrix)[4]) + { + for (unsigned int i = 0; i < length; i++) + { + contour_point_t &p = (*this)[i]; + float x_ = p.x * matrix[0] + p.y * matrix[2]; + p.y = p.x * matrix[1] + p.y * matrix[3]; + p.x = x_; + } + } + + void translate (const contour_point_t& delta) + { + for (unsigned int i = 0; i < length; i++) + (*this)[i].translate (delta); + } +}; + +/* https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuplevariationheader */ +struct TupleVariationHeader +{ + unsigned get_size (unsigned axis_count) const + { return min_size + get_all_tuples (axis_count).get_size (); } + + unsigned get_data_size () const { return varDataSize; } + + const TupleVariationHeader &get_next (unsigned axis_count) const + { return StructAtOffset (this, get_size (axis_count)); } + + float calculate_scalar (const int *coords, unsigned int coord_count, + const hb_array_t shared_tuples) const + { + hb_array_t peak_tuple; + + if (has_peak ()) + peak_tuple = get_peak_tuple (coord_count); + else + { + unsigned int index = get_index (); + if (unlikely (index * coord_count >= shared_tuples.length)) + return 0.f; + peak_tuple = shared_tuples.sub_array (coord_count * index, coord_count); + } + + hb_array_t start_tuple; + hb_array_t end_tuple; + if (has_intermediate ()) + { + start_tuple = get_start_tuple (coord_count); + end_tuple = get_end_tuple (coord_count); + } + + float scalar = 1.f; + for (unsigned int i = 0; i < coord_count; i++) + { + int v = coords[i]; + int peak = peak_tuple[i]; + if (!peak || v == peak) continue; + + if (has_intermediate ()) + { + int start = start_tuple[i]; + int end = end_tuple[i]; + if (unlikely (start > peak || peak > end || + (start < 0 && end > 0 && peak))) continue; + if (v < start || v > end) return 0.f; + if (v < peak) + { if (peak != start) scalar *= (float) (v - start) / (peak - start); } + else + { if (peak != end) scalar *= (float) (end - v) / (end - peak); } + } + else if (!v || v < hb_min (0, peak) || v > hb_max (0, peak)) return 0.f; + else + scalar *= (float) v / peak; + } + return scalar; + } + + bool has_peak () const { return tupleIndex & TuppleIndex::EmbeddedPeakTuple; } + bool has_intermediate () const { return tupleIndex & TuppleIndex::IntermediateRegion; } + bool has_private_points () const { return tupleIndex & TuppleIndex::PrivatePointNumbers; } + unsigned get_index () const { return tupleIndex & TuppleIndex::TupleIndexMask; } + + protected: + struct TuppleIndex : HBUINT16 + { + enum Flags { + EmbeddedPeakTuple = 0x8000u, + IntermediateRegion = 0x4000u, + PrivatePointNumbers = 0x2000u, + TupleIndexMask = 0x0FFFu + }; + + DEFINE_SIZE_STATIC (2); + }; + + hb_array_t get_all_tuples (unsigned axis_count) const + { return StructAfter> (tupleIndex).as_array ((has_peak () + has_intermediate () * 2) * axis_count); } + hb_array_t get_peak_tuple (unsigned axis_count) const + { return get_all_tuples (axis_count).sub_array (0, axis_count); } + hb_array_t get_start_tuple (unsigned axis_count) const + { return get_all_tuples (axis_count).sub_array (has_peak () * axis_count, axis_count); } + hb_array_t get_end_tuple (unsigned axis_count) const + { return get_all_tuples (axis_count).sub_array (has_peak () * axis_count + axis_count, axis_count); } + + HBUINT16 varDataSize; /* The size in bytes of the serialized + * data for this tuple variation table. */ + TuppleIndex tupleIndex; /* A packed field. The high 4 bits are flags (see below). + The low 12 bits are an index into a shared tuple + records array. */ + /* UnsizedArrayOf peakTuple - optional */ + /* Peak tuple record for this tuple variation table — optional, + * determined by flags in the tupleIndex value. + * + * Note that this must always be included in the 'cvar' table. */ + /* UnsizedArrayOf intermediateStartTuple - optional */ + /* Intermediate start tuple record for this tuple variation table — optional, + determined by flags in the tupleIndex value. */ + /* UnsizedArrayOf intermediateEndTuple - optional */ + /* Intermediate end tuple record for this tuple variation table — optional, + * determined by flags in the tupleIndex value. */ + public: + DEFINE_SIZE_MIN (4); +}; + +struct GlyphVariationData +{ + const TupleVariationHeader &get_tuple_var_header (void) const + { return StructAfter (data); } + + struct tuple_iterator_t + { + void init (hb_bytes_t var_data_bytes_, unsigned int axis_count_) + { + var_data_bytes = var_data_bytes_; + var_data = var_data_bytes_.as (); + index = 0; + axis_count = axis_count_; + current_tuple = &var_data->get_tuple_var_header (); + data_offset = 0; + } + + bool get_shared_indices (hb_vector_t &shared_indices /* OUT */) + { + if (var_data->has_shared_point_numbers ()) + { + const HBUINT8 *base = &(var_data+var_data->data); + const HBUINT8 *p = base; + if (!unpack_points (p, shared_indices, var_data_bytes)) return false; + data_offset = p - base; + } + return true; + } + + bool is_valid () const + { + return (index < var_data->tupleVarCount.get_count ()) && + var_data_bytes.check_range (current_tuple, TupleVariationHeader::min_size) && + var_data_bytes.check_range (current_tuple, hb_max (current_tuple->get_data_size (), current_tuple->get_size (axis_count))) && + current_tuple->get_size (axis_count); + } + + bool move_to_next () + { + data_offset += current_tuple->get_data_size (); + current_tuple = ¤t_tuple->get_next (axis_count); + index++; + return is_valid (); + } + + const HBUINT8 *get_serialized_data () const + { return &(var_data+var_data->data) + data_offset; } + + private: + const GlyphVariationData *var_data; + unsigned int index; + unsigned int axis_count; + unsigned int data_offset; + + public: + hb_bytes_t var_data_bytes; + const TupleVariationHeader *current_tuple; + }; + + static bool get_tuple_iterator (hb_bytes_t var_data_bytes, unsigned axis_count, + hb_vector_t &shared_indices /* OUT */, + tuple_iterator_t *iterator /* OUT */) + { + iterator->init (var_data_bytes, axis_count); + if (!iterator->get_shared_indices (shared_indices)) + return false; + return iterator->is_valid (); + } + + bool has_shared_point_numbers () const { return tupleVarCount.has_shared_point_numbers (); } + + static bool unpack_points (const HBUINT8 *&p /* IN/OUT */, + hb_vector_t &points /* OUT */, + const hb_bytes_t &bytes) + { + enum packed_point_flag_t + { + POINTS_ARE_WORDS = 0x80, + POINT_RUN_COUNT_MASK = 0x7F + }; + + if (unlikely (!bytes.check_range (p))) return false; + + uint16_t count = *p++; + if (count & POINTS_ARE_WORDS) + { + if (unlikely (!bytes.check_range (p))) return false; + count = ((count & POINT_RUN_COUNT_MASK) << 8) | *p++; + } + points.resize (count); + + unsigned int n = 0; + uint16_t i = 0; + while (i < count) + { + if (unlikely (!bytes.check_range (p))) return false; + uint16_t j; + uint8_t control = *p++; + uint16_t run_count = (control & POINT_RUN_COUNT_MASK) + 1; + if (control & POINTS_ARE_WORDS) + { + for (j = 0; j < run_count && i < count; j++, i++) + { + if (unlikely (!bytes.check_range ((const HBUINT16 *) p))) + return false; + n += *(const HBUINT16 *)p; + points[i] = n; + p += HBUINT16::static_size; + } + } + else + { + for (j = 0; j < run_count && i < count; j++, i++) + { + if (unlikely (!bytes.check_range (p))) return false; + n += *p++; + points[i] = n; + } + } + if (j < run_count) return false; + } + return true; + } + + static bool unpack_deltas (const HBUINT8 *&p /* IN/OUT */, + hb_vector_t &deltas /* IN/OUT */, + const hb_bytes_t &bytes) + { + enum packed_delta_flag_t + { + DELTAS_ARE_ZERO = 0x80, + DELTAS_ARE_WORDS = 0x40, + DELTA_RUN_COUNT_MASK = 0x3F + }; + + unsigned int i = 0; + unsigned int count = deltas.length; + while (i < count) + { + if (unlikely (!bytes.check_range (p))) return false; + uint8_t control = *p++; + unsigned int run_count = (control & DELTA_RUN_COUNT_MASK) + 1; + unsigned int j; + if (control & DELTAS_ARE_ZERO) + for (j = 0; j < run_count && i < count; j++, i++) + deltas[i] = 0; + else if (control & DELTAS_ARE_WORDS) + for (j = 0; j < run_count && i < count; j++, i++) + { + if (unlikely (!bytes.check_range ((const HBUINT16 *) p))) + return false; + deltas[i] = *(const HBINT16 *) p; + p += HBUINT16::static_size; + } + else + for (j = 0; j < run_count && i < count; j++, i++) + { + if (unlikely (!bytes.check_range (p))) + return false; + deltas[i] = *(const HBINT8 *) p++; + } + if (j < run_count) + return false; + } + return true; + } + + bool has_data () const { return tupleVarCount; } + + protected: + struct TupleVarCount : HBUINT16 + { + bool has_shared_point_numbers () const { return ((*this) & SharedPointNumbers); } + unsigned int get_count () const { return (*this) & CountMask; } + + protected: + enum Flags + { + SharedPointNumbers= 0x8000u, + CountMask = 0x0FFFu + }; + public: + DEFINE_SIZE_STATIC (2); + }; + + TupleVarCount tupleVarCount; /* A packed field. The high 4 bits are flags, and the + * low 12 bits are the number of tuple variation tables + * for this glyph. The number of tuple variation tables + * can be any number between 1 and 4095. */ + OffsetTo + data; /* Offset from the start of the GlyphVariationData table + * to the serialized data. */ + /* TupleVariationHeader tupleVariationHeaders[] *//* Array of tuple variation headers. */ + public: + DEFINE_SIZE_MIN (4); +}; + +struct gvar +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_gvar; + + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && (version.major == 1) && + (glyphCount == c->get_num_glyphs ()) && + sharedTuples.sanitize (c, this, axisCount * sharedTupleCount) && + (is_long_offset () ? + c->check_array (get_long_offset_array (), glyphCount+1) : + c->check_array (get_short_offset_array (), glyphCount+1)) && + c->check_array (((const HBUINT8*)&(this+dataZ)) + get_offset (0), + get_offset (glyphCount) - get_offset (0))); + } + + /* GlyphVariationData not sanitized here; must be checked while accessing each glyph varation data */ + bool sanitize (hb_sanitize_context_t *c) const + { return sanitize_shallow (c); } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + gvar *out = c->serializer->allocate_min (); + if (unlikely (!out)) return_trace (false); + + out->version.major = 1; + out->version.minor = 0; + out->axisCount = axisCount; + out->sharedTupleCount = sharedTupleCount; + + unsigned int num_glyphs = c->plan->num_output_glyphs (); + out->glyphCount = num_glyphs; + + unsigned int subset_data_size = 0; + for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) + { + hb_codepoint_t old_gid; + if (!c->plan->old_gid_for_new_gid (gid, &old_gid)) continue; + subset_data_size += get_glyph_var_data_bytes (c->source_blob, old_gid).length; + } + + bool long_offset = subset_data_size & ~0xFFFFu; + out->flags = long_offset ? 1 : 0; + + HBUINT8 *subset_offsets = c->serializer->allocate_size ((long_offset ? 4 : 2) * (num_glyphs + 1)); + if (!subset_offsets) return_trace (false); + + /* shared tuples */ + if (!sharedTupleCount || !sharedTuples) + out->sharedTuples = 0; + else + { + unsigned int shared_tuple_size = F2DOT14::static_size * axisCount * sharedTupleCount; + F2DOT14 *tuples = c->serializer->allocate_size (shared_tuple_size); + if (!tuples) return_trace (false); + out->sharedTuples = (char *) tuples - (char *) out; + memcpy (tuples, this+sharedTuples, shared_tuple_size); + } + + char *subset_data = c->serializer->allocate_size (subset_data_size); + if (!subset_data) return_trace (false); + out->dataZ = subset_data - (char *) out; + + unsigned int glyph_offset = 0; + for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) + { + hb_codepoint_t old_gid; + hb_bytes_t var_data_bytes = c->plan->old_gid_for_new_gid (gid, &old_gid) + ? get_glyph_var_data_bytes (c->source_blob, old_gid) + : hb_bytes_t (); + + if (long_offset) + ((HBUINT32 *) subset_offsets)[gid] = glyph_offset; + else + ((HBUINT16 *) subset_offsets)[gid] = glyph_offset / 2; + + if (var_data_bytes.length > 0) + memcpy (subset_data, var_data_bytes.arrayZ, var_data_bytes.length); + subset_data += var_data_bytes.length; + glyph_offset += var_data_bytes.length; + } + if (long_offset) + ((HBUINT32 *) subset_offsets)[num_glyphs] = glyph_offset; + else + ((HBUINT16 *) subset_offsets)[num_glyphs] = glyph_offset / 2; + + return_trace (true); + } + + protected: + const hb_bytes_t get_glyph_var_data_bytes (hb_blob_t *blob, hb_codepoint_t glyph) const + { + unsigned start_offset = get_offset (glyph); + unsigned length = get_offset (glyph+1) - start_offset; + hb_bytes_t var_data = blob->as_bytes ().sub_array (((unsigned) dataZ) + start_offset, length); + return likely (var_data.length >= GlyphVariationData::min_size) ? var_data : hb_bytes_t (); + } + + bool is_long_offset () const { return flags & 1; } + + unsigned get_offset (unsigned i) const + { return is_long_offset () ? get_long_offset_array ()[i] : get_short_offset_array ()[i] * 2; } + + const HBUINT32 * get_long_offset_array () const { return (const HBUINT32 *) &offsetZ; } + const HBUINT16 *get_short_offset_array () const { return (const HBUINT16 *) &offsetZ; } + + public: + struct accelerator_t + { + void init (hb_face_t *face) + { table = hb_sanitize_context_t ().reference_table (face); } + void fini () { table.destroy (); } + + private: + struct x_getter { static float get (const contour_point_t &p) { return p.x; } }; + struct y_getter { static float get (const contour_point_t &p) { return p.y; } }; + + template + static float infer_delta (const hb_array_t points, + const hb_array_t deltas, + unsigned int target, unsigned int prev, unsigned int next) + { + float target_val = T::get (points[target]); + float prev_val = T::get (points[prev]); + float next_val = T::get (points[next]); + float prev_delta = T::get (deltas[prev]); + float next_delta = T::get (deltas[next]); + + if (prev_val == next_val) + return (prev_delta == next_delta) ? prev_delta : 0.f; + else if (target_val <= hb_min (prev_val, next_val)) + return (prev_val < next_val) ? prev_delta : next_delta; + else if (target_val >= hb_max (prev_val, next_val)) + return (prev_val > next_val) ? prev_delta : next_delta; + + /* linear interpolation */ + float r = (target_val - prev_val) / (next_val - prev_val); + return (1.f - r) * prev_delta + r * next_delta; + } + + static unsigned int next_index (unsigned int i, unsigned int start, unsigned int end) + { return (i >= end) ? start : (i + 1); } + + public: + bool apply_deltas_to_points (hb_codepoint_t glyph, hb_font_t *font, + const hb_array_t points) const + { + /* num_coords should exactly match gvar's axisCount due to how GlyphVariationData tuples are aligned */ + if (!font->num_coords || font->num_coords != table->axisCount) return true; + + if (unlikely (glyph >= table->glyphCount)) return true; + + hb_bytes_t var_data_bytes = table->get_glyph_var_data_bytes (table.get_blob (), glyph); + if (!var_data_bytes.as ()->has_data ()) return true; + hb_vector_t shared_indices; + GlyphVariationData::tuple_iterator_t iterator; + if (!GlyphVariationData::get_tuple_iterator (var_data_bytes, table->axisCount, + shared_indices, &iterator)) + return true; /* so isn't applied at all */ + + /* Save original points for inferred delta calculation */ + contour_point_vector_t orig_points; + orig_points.resize (points.length); + for (unsigned int i = 0; i < orig_points.length; i++) + orig_points[i] = points[i]; + + contour_point_vector_t deltas; /* flag is used to indicate referenced point */ + deltas.resize (points.length); + + hb_vector_t end_points; + for (unsigned i = 0; i < points.length; ++i) + if (points[i].is_end_point) + end_points.push (i); + + int *coords = font->coords; + unsigned num_coords = font->num_coords; + hb_array_t shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * table->axisCount); + do + { + float scalar = iterator.current_tuple->calculate_scalar (coords, num_coords, shared_tuples); + if (scalar == 0.f) continue; + const HBUINT8 *p = iterator.get_serialized_data (); + unsigned int length = iterator.current_tuple->get_data_size (); + if (unlikely (!iterator.var_data_bytes.check_range (p, length))) + return false; + + hb_bytes_t bytes ((const char *) p, length); + hb_vector_t private_indices; + if (iterator.current_tuple->has_private_points () && + !GlyphVariationData::unpack_points (p, private_indices, bytes)) + return false; + const hb_array_t &indices = private_indices.length ? private_indices : shared_indices; + + bool apply_to_all = (indices.length == 0); + unsigned int num_deltas = apply_to_all ? points.length : indices.length; + hb_vector_t x_deltas; + x_deltas.resize (num_deltas); + if (!GlyphVariationData::unpack_deltas (p, x_deltas, bytes)) + return false; + hb_vector_t y_deltas; + y_deltas.resize (num_deltas); + if (!GlyphVariationData::unpack_deltas (p, y_deltas, bytes)) + return false; + + for (unsigned int i = 0; i < deltas.length; i++) + deltas[i].init (); + for (unsigned int i = 0; i < num_deltas; i++) + { + unsigned int pt_index = apply_to_all ? i : indices[i]; + deltas[pt_index].flag = 1; /* this point is referenced, i.e., explicit deltas specified */ + deltas[pt_index].x += x_deltas[i] * scalar; + deltas[pt_index].y += y_deltas[i] * scalar; + } + + /* infer deltas for unreferenced points */ + unsigned start_point = 0; + for (unsigned c = 0; c < end_points.length; c++) + { + unsigned end_point = end_points[c]; + + /* Check the number of unreferenced points in a contour. If no unref points or no ref points, nothing to do. */ + unsigned unref_count = 0; + for (unsigned i = start_point; i <= end_point; i++) + if (!deltas[i].flag) unref_count++; + + unsigned j = start_point; + if (unref_count == 0 || unref_count > end_point - start_point) + goto no_more_gaps; + + for (;;) + { + /* Locate the next gap of unreferenced points between two referenced points prev and next. + * Note that a gap may wrap around at left (start_point) and/or at right (end_point). + */ + unsigned int prev, next, i; + for (;;) + { + i = j; + j = next_index (i, start_point, end_point); + if (deltas[i].flag && !deltas[j].flag) break; + } + prev = j = i; + for (;;) + { + i = j; + j = next_index (i, start_point, end_point); + if (!deltas[i].flag && deltas[j].flag) break; + } + next = j; + /* Infer deltas for all unref points in the gap between prev and next */ + i = prev; + for (;;) + { + i = next_index (i, start_point, end_point); + if (i == next) break; + deltas[i].x = infer_delta (orig_points.as_array (), deltas.as_array (), i, prev, next); + deltas[i].y = infer_delta (orig_points.as_array (), deltas.as_array (), i, prev, next); + if (--unref_count == 0) goto no_more_gaps; + } + } +no_more_gaps: + start_point = end_point + 1; + } + + /* apply specified / inferred deltas to points */ + for (unsigned int i = 0; i < points.length; i++) + { + points[i].x += roundf (deltas[i].x); + points[i].y += roundf (deltas[i].y); + } + } while (iterator.move_to_next ()); + + return true; + } + + unsigned int get_axis_count () const { return table->axisCount; } + + private: + hb_blob_ptr_t table; + }; + + protected: + FixedVersion<>version; /* Version number of the glyph variations table + * Set to 0x00010000u. */ + HBUINT16 axisCount; /* The number of variation axes for this font. This must be + * the same number as axisCount in the 'fvar' table. */ + HBUINT16 sharedTupleCount; + /* The number of shared tuple records. Shared tuple records + * can be referenced within glyph variation data tables for + * multiple glyphs, as opposed to other tuple records stored + * directly within a glyph variation data table. */ + LNNOffsetTo> + sharedTuples; /* Offset from the start of this table to the shared tuple records. + * Array of tuple records shared across all glyph variation data tables. */ + HBUINT16 glyphCount; /* The number of glyphs in this font. This must match the number of + * glyphs stored elsewhere in the font. */ + HBUINT16 flags; /* Bit-field that gives the format of the offset array that follows. + * If bit 0 is clear, the offsets are uint16; if bit 0 is set, the + * offsets are uint32. */ + LOffsetTo + dataZ; /* Offset from the start of this table to the array of + * GlyphVariationData tables. */ + UnsizedArrayOf + offsetZ; /* Offsets from the start of the GlyphVariationData array + * to each GlyphVariationData table. */ + public: + DEFINE_SIZE_MIN (20); +}; + +struct gvar_accelerator_t : gvar::accelerator_t {}; + +} /* namespace OT */ + +#endif /* HB_OT_VAR_GVAR_TABLE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh index 4d030f3d7cf..d024147183f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh @@ -44,6 +44,38 @@ struct DeltaSetIndexMap get_width ())); } + template + bool serialize (hb_serialize_context_t *c, const T &plan) + { + unsigned int width = plan.get_width (); + unsigned int inner_bit_count = plan.get_inner_bit_count (); + const hb_array_t output_map = plan.get_output_map (); + + TRACE_SERIALIZE (this); + if (unlikely (output_map.length && ((((inner_bit_count-1)&~0xF)!=0) || (((width-1)&~0x3)!=0)))) + return_trace (false); + if (unlikely (!c->extend_min (*this))) return_trace (false); + + format = ((width-1)<<4)|(inner_bit_count-1); + mapCount = output_map.length; + HBUINT8 *p = c->allocate_size (width * output_map.length); + if (unlikely (!p)) return_trace (false); + for (unsigned int i = 0; i < output_map.length; i++) + { + unsigned int v = output_map[i]; + unsigned int outer = v >> 16; + unsigned int inner = v & 0xFFFF; + unsigned int u = (outer << inner_bit_count) | inner; + for (unsigned int w = width; w > 0;) + { + p[--w] = u; + u >>= 8; + } + p += width; + } + return_trace (true); + } + unsigned int map (unsigned int v) const /* Returns 16.16 outer.inner. */ { /* If count is zero, pass value unchanged. This takes @@ -63,7 +95,7 @@ struct DeltaSetIndexMap } { /* Repack it. */ - unsigned int n = get_inner_bitcount (); + unsigned int n = get_inner_bit_count (); unsigned int outer = u >> n; unsigned int inner = u & ((1 << n) - 1); u = (outer<<16) | inner; @@ -72,10 +104,9 @@ struct DeltaSetIndexMap return u; } - protected: - unsigned int get_width () const { return ((format >> 4) & 3) + 1; } - - unsigned int get_inner_bitcount () const { return (format & 0xF) + 1; } + unsigned int get_map_count () const { return mapCount; } + unsigned int get_width () const { return ((format >> 4) & 3) + 1; } + unsigned int get_inner_bit_count () const { return (format & 0xF) + 1; } protected: HBUINT16 format; /* A packed field that describes the compressed @@ -88,6 +119,215 @@ struct DeltaSetIndexMap DEFINE_SIZE_ARRAY (4, mapDataZ); }; +struct index_map_subset_plan_t +{ + enum index_map_index_t { + ADV_INDEX, + LSB_INDEX, /* dual as TSB */ + RSB_INDEX, /* dual as BSB */ + VORG_INDEX + }; + + void init (const DeltaSetIndexMap &index_map, + hb_inc_bimap_t &outer_map, + hb_vector_t &inner_sets, + const hb_subset_plan_t *plan) + { + map_count = 0; + outer_bit_count = 0; + inner_bit_count = 1; + max_inners.init (); + output_map.init (); + + if (&index_map == &Null (DeltaSetIndexMap)) return; + + unsigned int last_val = (unsigned int)-1; + hb_codepoint_t last_gid = (hb_codepoint_t)-1; + hb_codepoint_t gid = (hb_codepoint_t) hb_min (index_map.get_map_count (), plan->num_output_glyphs ()); + + outer_bit_count = (index_map.get_width () * 8) - index_map.get_inner_bit_count (); + max_inners.resize (inner_sets.length); + for (unsigned i = 0; i < inner_sets.length; i++) max_inners[i] = 0; + + /* Search backwards for a map value different from the last map value */ + for (; gid > 0; gid--) + { + hb_codepoint_t old_gid; + if (!plan->old_gid_for_new_gid (gid - 1, &old_gid)) + { + if (last_gid == (hb_codepoint_t) -1) + continue; + else + break; + } + + unsigned int v = index_map.map (old_gid); + if (last_gid == (hb_codepoint_t) -1) + { + last_val = v; + last_gid = gid; + continue; + } + if (v != last_val) break; + + last_gid = gid; + } + + if (unlikely (last_gid == (hb_codepoint_t)-1)) return; + map_count = last_gid; + for (gid = 0; gid < map_count; gid++) + { + hb_codepoint_t old_gid; + if (plan->old_gid_for_new_gid (gid, &old_gid)) + { + unsigned int v = index_map.map (old_gid); + unsigned int outer = v >> 16; + unsigned int inner = v & 0xFFFF; + outer_map.add (outer); + if (inner > max_inners[outer]) max_inners[outer] = inner; + if (outer >= inner_sets.length) return; + inner_sets[outer]->add (inner); + } + } + } + + void fini () + { + max_inners.fini (); + output_map.fini (); + } + + void remap (const DeltaSetIndexMap *input_map, + const hb_inc_bimap_t &outer_map, + const hb_vector_t &inner_maps, + const hb_subset_plan_t *plan) + { + if (input_map == &Null (DeltaSetIndexMap)) return; + + for (unsigned int i = 0; i < max_inners.length; i++) + { + if (inner_maps[i].get_population () == 0) continue; + unsigned int bit_count = (max_inners[i]==0)? 1: hb_bit_storage (inner_maps[i][max_inners[i]]); + if (bit_count > inner_bit_count) inner_bit_count = bit_count; + } + + output_map.resize (map_count); + for (hb_codepoint_t gid = 0; gid < output_map.length; gid++) + { + hb_codepoint_t old_gid; + if (plan->old_gid_for_new_gid (gid, &old_gid)) + { + unsigned int v = input_map->map (old_gid); + unsigned int outer = v >> 16; + output_map[gid] = (outer_map[outer] << 16) | (inner_maps[outer][v & 0xFFFF]); + } + else + output_map[gid] = 0; /* Map unused glyph to outer/inner=0/0 */ + } + } + + unsigned int get_inner_bit_count () const { return inner_bit_count; } + unsigned int get_width () const { return ((outer_bit_count + inner_bit_count + 7) / 8); } + unsigned int get_map_count () const { return map_count; } + + unsigned int get_size () const + { return (map_count? (DeltaSetIndexMap::min_size + get_width () * map_count): 0); } + + bool is_identity () const { return get_output_map ().length == 0; } + hb_array_t get_output_map () const { return output_map.as_array (); } + + protected: + unsigned int map_count; + hb_vector_t max_inners; + unsigned int outer_bit_count; + unsigned int inner_bit_count; + hb_vector_t output_map; +}; + +struct hvarvvar_subset_plan_t +{ + hvarvvar_subset_plan_t() : inner_maps (), index_map_plans () {} + ~hvarvvar_subset_plan_t() { fini (); } + + void init (const hb_array_t &index_maps, + const VariationStore &_var_store, + const hb_subset_plan_t *plan) + { + index_map_plans.resize (index_maps.length); + + var_store = &_var_store; + inner_sets.resize (var_store->get_sub_table_count ()); + for (unsigned int i = 0; i < inner_sets.length; i++) + inner_sets[i] = hb_set_create (); + adv_set = hb_set_create (); + + inner_maps.resize (var_store->get_sub_table_count ()); + + for (unsigned int i = 0; i < inner_maps.length; i++) + inner_maps[i].init (); + + if (unlikely (!index_map_plans.length || !inner_sets.length || !inner_maps.length)) return; + + bool retain_adv_map = false; + index_map_plans[0].init (*index_maps[0], outer_map, inner_sets, plan); + if (index_maps[0] == &Null (DeltaSetIndexMap)) + { + retain_adv_map = plan->retain_gids; + outer_map.add (0); + for (hb_codepoint_t gid = 0; gid < plan->num_output_glyphs (); gid++) + { + hb_codepoint_t old_gid; + if (plan->old_gid_for_new_gid (gid, &old_gid)) + inner_sets[0]->add (old_gid); + } + hb_set_union (adv_set, inner_sets[0]); + } + + for (unsigned int i = 1; i < index_maps.length; i++) + index_map_plans[i].init (*index_maps[i], outer_map, inner_sets, plan); + + outer_map.sort (); + + if (retain_adv_map) + { + for (hb_codepoint_t gid = 0; gid < plan->num_output_glyphs (); gid++) + if (inner_sets[0]->has (gid)) + inner_maps[0].add (gid); + else + inner_maps[0].skip (); + } + else + { + inner_maps[0].add_set (adv_set); + hb_set_subtract (inner_sets[0], adv_set); + inner_maps[0].add_set (inner_sets[0]); + } + + for (unsigned int i = 1; i < inner_maps.length; i++) + inner_maps[i].add_set (inner_sets[i]); + + for (unsigned int i = 0; i < index_maps.length; i++) + index_map_plans[i].remap (index_maps[i], outer_map, inner_maps, plan); + } + + void fini () + { + for (unsigned int i = 0; i < inner_sets.length; i++) + hb_set_destroy (inner_sets[i]); + hb_set_destroy (adv_set); + inner_maps.fini_deep (); + index_map_plans.fini_deep (); + } + + hb_inc_bimap_t outer_map; + hb_vector_t inner_maps; + hb_vector_t index_map_plans; + const VariationStore *var_store; + + protected: + hb_vector_t inner_sets; + hb_set_t *adv_set; +}; /* * HVAR -- Horizontal Metrics Variations @@ -114,14 +354,73 @@ struct HVARVVAR rsbMap.sanitize (c, this)); } - float get_advance_var (hb_codepoint_t glyph, - const int *coords, unsigned int coord_count) const + void listup_index_maps (hb_vector_t &index_maps) const + { + index_maps.push (&(this+advMap)); + index_maps.push (&(this+lsbMap)); + index_maps.push (&(this+rsbMap)); + } + + bool serialize_index_maps (hb_serialize_context_t *c, + const hb_array_t &im_plans) + { + TRACE_SERIALIZE (this); + if (im_plans[index_map_subset_plan_t::ADV_INDEX].is_identity ()) + advMap = 0; + else if (unlikely (!advMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::ADV_INDEX]))) + return_trace (false); + if (im_plans[index_map_subset_plan_t::LSB_INDEX].is_identity ()) + lsbMap = 0; + else if (unlikely (!lsbMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::LSB_INDEX]))) + return_trace (false); + if (im_plans[index_map_subset_plan_t::RSB_INDEX].is_identity ()) + rsbMap = 0; + else if (unlikely (!rsbMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::RSB_INDEX]))) + return_trace (false); + + return_trace (true); + } + + template + bool _subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + hvarvvar_subset_plan_t hvar_plan; + hb_vector_t + index_maps; + + ((T*)this)->listup_index_maps (index_maps); + hvar_plan.init (index_maps.as_array (), this+varStore, c->plan); + + T *out = c->serializer->allocate_min (); + if (unlikely (!out)) return_trace (false); + + out->version.major = 1; + out->version.minor = 0; + + if (unlikely (!out->varStore.serialize (c->serializer, out) + .serialize (c->serializer, hvar_plan.var_store, hvar_plan.inner_maps.as_array ()))) + return_trace (false); + + return_trace (out->T::serialize_index_maps (c->serializer, + hvar_plan.index_map_plans.as_array ())); + } + + float get_advance_var (hb_codepoint_t glyph, hb_font_t *font) const { unsigned int varidx = (this+advMap).map (glyph); + return (this+varStore).get_delta (varidx, font->coords, font->num_coords); + } + + float get_side_bearing_var (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count) const + { + if (!has_side_bearing_deltas ()) return 0.f; + unsigned int varidx = (this+lsbMap).map (glyph); return (this+varStore).get_delta (varidx, coords, coord_count); } - bool has_sidebearing_deltas () const { return lsbMap && rsbMap; } + bool has_side_bearing_deltas () const { return lsbMap && rsbMap; } protected: FixedVersion<>version; /* Version of the metrics variation table @@ -141,6 +440,7 @@ struct HVARVVAR struct HVAR : HVARVVAR { static constexpr hb_tag_t tableTag = HB_OT_TAG_HVAR; + bool subset (hb_subset_context_t *c) const { return HVARVVAR::_subset (c); } }; struct VVAR : HVARVVAR { static constexpr hb_tag_t tableTag = HB_OT_TAG_VVAR; @@ -152,6 +452,28 @@ struct VVAR : HVARVVAR { vorgMap.sanitize (c, this)); } + void listup_index_maps (hb_vector_t &index_maps) const + { + HVARVVAR::listup_index_maps (index_maps); + index_maps.push (&(this+vorgMap)); + } + + bool serialize_index_maps (hb_serialize_context_t *c, + const hb_array_t &im_plans) + { + TRACE_SERIALIZE (this); + if (unlikely (!HVARVVAR::serialize_index_maps (c, im_plans))) + return_trace (false); + if (!im_plans[index_map_subset_plan_t::VORG_INDEX].get_map_count ()) + vorgMap = 0; + else if (unlikely (!vorgMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::VORG_INDEX]))) + return_trace (false); + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const { return HVARVVAR::_subset (c); } + protected: LOffsetTo vorgMap; /* Offset to vertical-origin var-idx mapping. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-mvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-mvar-table.hh index 374ce665f76..37a47ff1d41 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-mvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-mvar-table.hh @@ -77,9 +77,11 @@ struct MVAR const int *coords, unsigned int coord_count) const { const VariationValueRecord *record; - record = (VariationValueRecord *) bsearch (&tag, valuesZ.arrayZ, - valueRecordCount, valueRecordSize, - tag_compare); + record = (VariationValueRecord *) hb_bsearch (tag, + (const VariationValueRecord *) + (const HBUINT8 *) valuesZ, + valueRecordCount, valueRecordSize, + tag_compare); if (!record) return 0.; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc index ed360125148..1c12d566582 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc @@ -24,13 +24,15 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-open-type.hh" +#include "hb.hh" + +#ifndef HB_NO_VAR + +#include "hb-ot-var.h" -#include "hb-ot-face.hh" #include "hb-ot-var-avar-table.hh" #include "hb-ot-var-fvar-table.hh" #include "hb-ot-var-mvar-table.hh" -#include "hb-ot-var.h" /** @@ -75,6 +77,7 @@ hb_ot_var_get_axis_count (hb_face_t *face) return face->table.fvar->get_axis_count (); } +#ifndef HB_DISABLE_DEPRECATED /** * hb_ot_var_get_axes: * @@ -104,6 +107,7 @@ hb_ot_var_find_axis (hb_face_t *face, { return face->table.fvar->find_axis_deprecated (axis_tag, axis_index, axis_info); } +#endif /** * hb_ot_var_get_axis_infos: @@ -211,3 +215,6 @@ hb_ot_var_normalize_coords (hb_face_t *face, face->table.avar->map_coords (normalized_coords, coords_length); } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-var.h index 5b028240d30..92494c24a0f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var.h @@ -68,7 +68,7 @@ hb_ot_var_get_axis_count (hb_face_t *face); typedef enum { /*< flags >*/ HB_OT_VAR_AXIS_FLAG_HIDDEN = 0x00000001u, - _HB_OT_VAR_AXIS_FLAG_MAX_VALUE= 0x7FFFFFFFu /*< skip >*/ + _HB_OT_VAR_AXIS_FLAG_MAX_VALUE= HB_TAG_MAX_SIGNED /*< skip >*/ } hb_ot_var_axis_flags_t; /** diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh index c2dc77b62bb..4dd8dfdb02a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh @@ -48,7 +48,7 @@ struct VertOriginMetric } public: - GlyphID glyph; + HBGlyphID glyph; FWORD vertOriginY; public: @@ -69,94 +69,48 @@ struct VORG return vertYOrigins[i].vertOriginY; } - bool _subset (const hb_subset_plan_t *plan HB_UNUSED, - const VORG *vorg_table, - const hb_vector_t &subset_metrics, - unsigned int dest_sz, - void *dest) const + template + void serialize (hb_serialize_context_t *c, + Iterator it, + FWORD defaultVertOriginY) { - hb_serialize_context_t c (dest, dest_sz); - - VORG *subset_table = c.start_serialize (); - if (unlikely (!c.extend_min (*subset_table))) - return false; - - subset_table->version.major.set (1); - subset_table->version.minor.set (0); - - subset_table->defaultVertOriginY.set (vorg_table->defaultVertOriginY); - subset_table->vertYOrigins.len.set (subset_metrics.length); - - bool success = true; - if (subset_metrics.length > 0) - { - unsigned int size = VertOriginMetric::static_size * subset_metrics.length; - VertOriginMetric *metrics = c.allocate_size (size); - if (likely (metrics != nullptr)) - memcpy (metrics, &subset_metrics[0], size); - else - success = false; - } - c.end_serialize (); - - return success; + + if (unlikely (!c->extend_min ((*this)))) return; + + this->version.major = 1; + this->version.minor = 0; + + this->defaultVertOriginY = defaultVertOriginY; + this->vertYOrigins.len = it.len (); + + c->copy_all (it); } - bool subset (hb_subset_plan_t *plan) const + bool subset (hb_subset_context_t *c) const { - hb_blob_t *vorg_blob = hb_sanitize_context_t().reference_table (plan->source); - const VORG *vorg_table = vorg_blob->as (); - - /* count the number of glyphs to be included in the subset table */ - hb_vector_t subset_metrics; - subset_metrics.init (); - unsigned int glyph = 0; - unsigned int i = 0; - while ((glyph < plan->glyphs.length) && (i < vertYOrigins.len)) - { - if (plan->glyphs[glyph] > vertYOrigins[i].glyph) - i++; - else if (plan->glyphs[glyph] < vertYOrigins[i].glyph) - glyph++; - else - { - VertOriginMetric *metrics = subset_metrics.push (); - metrics->glyph.set (glyph); - metrics->vertOriginY.set (vertYOrigins[i].vertOriginY); - glyph++; - i++; - } - } - - /* alloc the new table */ - unsigned int dest_sz = VORG::min_size + VertOriginMetric::static_size * subset_metrics.length; - void *dest = (void *) malloc (dest_sz); - if (unlikely (!dest)) - { - subset_metrics.fini (); - hb_blob_destroy (vorg_blob); - return false; - } + TRACE_SUBSET (this); + VORG *vorg_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->check_success (vorg_prime))) return_trace (false); + + auto it = + + vertYOrigins.as_array () + | hb_filter (c->plan->glyphset (), &VertOriginMetric::glyph) + | hb_map ([&] (const VertOriginMetric& _) + { + hb_codepoint_t new_glyph = HB_SET_VALUE_INVALID; + c->plan->new_gid_for_old_gid (_.glyph, &new_glyph); + + VertOriginMetric metric; + metric.glyph = new_glyph; + metric.vertOriginY = _.vertOriginY; + return metric; + }) + ; /* serialize the new table */ - if (!_subset (plan, vorg_table, subset_metrics, dest_sz, dest)) - { - subset_metrics.fini (); - free (dest); - hb_blob_destroy (vorg_blob); - return false; - } - - hb_blob_t *result = hb_blob_create ((const char *)dest, - dest_sz, - HB_MEMORY_MODE_READONLY, - dest, - free); - bool success = plan->add_table (HB_OT_TAG_VORG, result); - hb_blob_destroy (result); - subset_metrics.fini (); - hb_blob_destroy (vorg_blob); - return success; + vorg_prime->serialize (c->serializer, it, defaultVertOriginY); + return_trace (true); } bool sanitize (hb_sanitize_context_t *c) const @@ -168,10 +122,11 @@ struct VORG } protected: - FixedVersion<> version; /* Version of VORG table. Set to 0x00010000u. */ - FWORD defaultVertOriginY; /* The default vertical origin. */ + FixedVersion<>version; /* Version of VORG table. Set to 0x00010000u. */ + FWORD defaultVertOriginY; + /* The default vertical origin. */ SortedArrayOf - vertYOrigins; /* The array of vertical origins. */ + vertYOrigins; /* The array of vertical origins. */ public: DEFINE_SIZE_ARRAY(8, vertYOrigins); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot.h b/src/java.desktop/share/native/libharfbuzz/hb-ot.h index db784694c6a..f2dbaa1b317 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot.h @@ -35,6 +35,8 @@ #include "hb-ot-font.h" #include "hb-ot-layout.h" #include "hb-ot-math.h" +#include "hb-ot-meta.h" +#include "hb-ot-metrics.h" #include "hb-ot-name.h" #include "hb-ot-shape.h" #include "hb-ot-var.h" diff --git a/src/java.desktop/share/native/libharfbuzz/hb-pool.hh b/src/java.desktop/share/native/libharfbuzz/hb-pool.hh new file mode 100644 index 00000000000..a2266a38461 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-pool.hh @@ -0,0 +1,100 @@ +/* + * Copyright © 2019 Facebook, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_POOL_HH +#define HB_POOL_HH + +#include "hb.hh" + +/* Memory pool for persistent allocation of small objects. */ + +template +struct hb_pool_t +{ + hb_pool_t () : next (nullptr) {} + ~hb_pool_t () { fini (); } + + void fini () + { + next = nullptr; + + for (chunk_t *_ : chunks) ::free (_); + + chunks.fini (); + } + + T* alloc () + { + if (unlikely (!next)) + { + if (unlikely (!chunks.alloc (chunks.length + 1))) return nullptr; + chunk_t *chunk = (chunk_t *) calloc (1, sizeof (chunk_t)); + if (unlikely (!chunk)) return nullptr; + chunks.push (chunk); + next = chunk->thread (); + } + + T* obj = next; + next = * ((T**) next); + + memset (obj, 0, sizeof (T)); + + return obj; + } + + void free (T* obj) + { + * (T**) obj = next; + next = obj; + } + + private: + + static_assert (ChunkLen > 1, ""); + static_assert (sizeof (T) >= sizeof (void *), ""); + static_assert (alignof (T) % alignof (void *) == 0, ""); + + struct chunk_t + { + T* thread () + { + for (unsigned i = 0; i < ARRAY_LENGTH (arrayZ) - 1; i++) + * (T**) &arrayZ[i] = &arrayZ[i + 1]; + + * (T**) &arrayZ[ARRAY_LENGTH (arrayZ) - 1] = nullptr; + + return arrayZ; + } + + T arrayZ[ChunkLen]; + }; + + T* next; + hb_vector_t chunks; +}; + + +#endif /* HB_POOL_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh b/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh new file mode 100644 index 00000000000..0e293e9eb1b --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh @@ -0,0 +1,412 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012,2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_SANITIZE_HH +#define HB_SANITIZE_HH + +#include "hb.hh" +#include "hb-blob.hh" +#include "hb-dispatch.hh" + + +/* + * Sanitize + * + * + * === Introduction === + * + * The sanitize machinery is at the core of our zero-cost font loading. We + * mmap() font file into memory and create a blob out of it. Font subtables + * are returned as a readonly sub-blob of the main font blob. These table + * blobs are then sanitized before use, to ensure invalid memory access does + * not happen. The toplevel sanitize API use is like, eg. to load the 'head' + * table: + * + * hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table (face); + * + * The blob then can be converted to a head table struct with: + * + * const head *head_table = head_blob->as (); + * + * What the reference_table does is, to call hb_face_reference_table() to load + * the table blob, sanitize it and return either the sanitized blob, or empty + * blob if sanitization failed. The blob->as() function returns the null + * object of its template type argument if the blob is empty. Otherwise, it + * just casts the blob contents to the desired type. + * + * Sanitizing a blob of data with a type T works as follows (with minor + * simplification): + * + * - Cast blob content to T*, call sanitize() method of it, + * - If sanitize succeeded, return blob. + * - Otherwise, if blob is not writable, try making it writable, + * or copy if cannot be made writable in-place, + * - Call sanitize() again. Return blob if sanitize succeeded. + * - Return empty blob otherwise. + * + * + * === The sanitize() contract === + * + * The sanitize() method of each object type shall return true if it's safe to + * call other methods of the object, and false otherwise. + * + * Note that what sanitize() checks for might align with what the specification + * describes as valid table data, but does not have to be. In particular, we + * do NOT want to be pedantic and concern ourselves with validity checks that + * are irrelevant to our use of the table. On the contrary, we want to be + * lenient with error handling and accept invalid data to the extent that it + * does not impose extra burden on us. + * + * Based on the sanitize contract, one can see that what we check for depends + * on how we use the data in other table methods. Ie. if other table methods + * assume that offsets do NOT point out of the table data block, then that's + * something sanitize() must check for (GSUB/GPOS/GDEF/etc work this way). On + * the other hand, if other methods do such checks themselves, then sanitize() + * does not have to bother with them (glyf/local work this way). The choice + * depends on the table structure and sanitize() performance. For example, to + * check glyf/loca offsets in sanitize() would cost O(num-glyphs). We try hard + * to avoid such costs during font loading. By postponing such checks to the + * actual glyph loading, we reduce the sanitize cost to O(1) and total runtime + * cost to O(used-glyphs). As such, this is preferred. + * + * The same argument can be made re GSUB/GPOS/GDEF, but there, the table + * structure is so complicated that by checking all offsets at sanitize() time, + * we make the code much simpler in other methods, as offsets and referenced + * objects do not need to be validated at each use site. + */ + +/* This limits sanitizing time on really broken fonts. */ +#ifndef HB_SANITIZE_MAX_EDITS +#define HB_SANITIZE_MAX_EDITS 32 +#endif +#ifndef HB_SANITIZE_MAX_OPS_FACTOR +#define HB_SANITIZE_MAX_OPS_FACTOR 8 +#endif +#ifndef HB_SANITIZE_MAX_OPS_MIN +#define HB_SANITIZE_MAX_OPS_MIN 16384 +#endif +#ifndef HB_SANITIZE_MAX_OPS_MAX +#define HB_SANITIZE_MAX_OPS_MAX 0x3FFFFFFF +#endif +#ifndef HB_SANITIZE_MAX_SUTABLES +#define HB_SANITIZE_MAX_SUTABLES 0x4000 +#endif + +struct hb_sanitize_context_t : + hb_dispatch_context_t +{ + hb_sanitize_context_t () : + start (nullptr), end (nullptr), + max_ops (0), max_subtables (0), + writable (false), edit_count (0), + blob (nullptr), + num_glyphs (65536), + num_glyphs_set (false) {} + + const char *get_name () { return "SANITIZE"; } + template + bool may_dispatch (const T *obj HB_UNUSED, const F *format) + { return format->sanitize (this); } + static return_t default_return_value () { return true; } + static return_t no_dispatch_return_value () { return false; } + bool stop_sublookup_iteration (const return_t r) const { return !r; } + + bool visit_subtables (unsigned count) + { + max_subtables += count; + return max_subtables < HB_SANITIZE_MAX_SUTABLES; + } + + private: + template auto + _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN + ( obj.sanitize (this, hb_forward (ds)...) ) + template auto + _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN + ( obj.dispatch (this, hb_forward (ds)...) ) + public: + template auto + dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN + ( _dispatch (obj, hb_prioritize, hb_forward (ds)...) ) + + + void init (hb_blob_t *b) + { + this->blob = hb_blob_reference (b); + this->writable = false; + } + + void set_num_glyphs (unsigned int num_glyphs_) + { + num_glyphs = num_glyphs_; + num_glyphs_set = true; + } + unsigned int get_num_glyphs () { return num_glyphs; } + + void set_max_ops (int max_ops_) { max_ops = max_ops_; } + + template + void set_object (const T *obj) + { + reset_object (); + + if (!obj) return; + + const char *obj_start = (const char *) obj; + if (unlikely (obj_start < this->start || this->end <= obj_start)) + this->start = this->end = nullptr; + else + { + this->start = obj_start; + this->end = obj_start + hb_min (size_t (this->end - obj_start), obj->get_size ()); + } + } + + void reset_object () + { + this->start = this->blob->data; + this->end = this->start + this->blob->length; + assert (this->start <= this->end); /* Must not overflow. */ + } + + void start_processing () + { + reset_object (); + if (unlikely (hb_unsigned_mul_overflows (this->end - this->start, HB_SANITIZE_MAX_OPS_FACTOR))) + this->max_ops = HB_SANITIZE_MAX_OPS_MAX; + else + this->max_ops = hb_clamp ((unsigned) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR, + (unsigned) HB_SANITIZE_MAX_OPS_MIN, + (unsigned) HB_SANITIZE_MAX_OPS_MAX); + this->edit_count = 0; + this->debug_depth = 0; + + DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1, + "start [%p..%p] (%lu bytes)", + this->start, this->end, + (unsigned long) (this->end - this->start)); + } + + void end_processing () + { + DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1, + "end [%p..%p] %u edit requests", + this->start, this->end, this->edit_count); + + hb_blob_destroy (this->blob); + this->blob = nullptr; + this->start = this->end = nullptr; + } + + unsigned get_edit_count () { return edit_count; } + + bool check_range (const void *base, + unsigned int len) const + { + const char *p = (const char *) base; + bool ok = !len || + (this->start <= p && + p <= this->end && + (unsigned int) (this->end - p) >= len && + this->max_ops-- > 0); + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "check_range [%p..%p]" + " (%d bytes) in [%p..%p] -> %s", + p, p + len, len, + this->start, this->end, + ok ? "OK" : "OUT-OF-RANGE"); + + return likely (ok); + } + + template + bool check_range (const T *base, + unsigned int a, + unsigned int b) const + { + return !hb_unsigned_mul_overflows (a, b) && + this->check_range (base, a * b); + } + + template + bool check_range (const T *base, + unsigned int a, + unsigned int b, + unsigned int c) const + { + return !hb_unsigned_mul_overflows (a, b) && + this->check_range (base, a * b, c); + } + + template + bool check_array (const T *base, unsigned int len) const + { + return this->check_range (base, len, hb_static_size (T)); + } + + template + bool check_array (const T *base, + unsigned int a, + unsigned int b) const + { + return this->check_range (base, a, b, hb_static_size (T)); + } + + template + bool check_struct (const Type *obj) const + { return likely (this->check_range (obj, obj->min_size)); } + + bool may_edit (const void *base, unsigned int len) + { + if (this->edit_count >= HB_SANITIZE_MAX_EDITS) + return false; + + const char *p = (const char *) base; + this->edit_count++; + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s", + this->edit_count, + p, p + len, len, + this->start, this->end, + this->writable ? "GRANTED" : "DENIED"); + + return this->writable; + } + + template + bool try_set (const Type *obj, const ValueType &v) + { + if (this->may_edit (obj, hb_static_size (Type))) + { + * const_cast (obj) = v; + return true; + } + return false; + } + + template + hb_blob_t *sanitize_blob (hb_blob_t *blob) + { + bool sane; + + init (blob); + + retry: + DEBUG_MSG_FUNC (SANITIZE, start, "start"); + + start_processing (); + + if (unlikely (!start)) + { + end_processing (); + return blob; + } + + Type *t = reinterpret_cast (const_cast (start)); + + sane = t->sanitize (this); + if (sane) + { + if (edit_count) + { + DEBUG_MSG_FUNC (SANITIZE, start, "passed first round with %d edits; going for second round", edit_count); + + /* sanitize again to ensure no toe-stepping */ + edit_count = 0; + sane = t->sanitize (this); + if (edit_count) { + DEBUG_MSG_FUNC (SANITIZE, start, "requested %d edits in second round; FAILLING", edit_count); + sane = false; + } + } + } + else + { + if (edit_count && !writable) { + start = hb_blob_get_data_writable (blob, nullptr); + end = start + blob->length; + + if (start) + { + writable = true; + /* ok, we made it writable by relocating. try again */ + DEBUG_MSG_FUNC (SANITIZE, start, "retry"); + goto retry; + } + } + } + + end_processing (); + + DEBUG_MSG_FUNC (SANITIZE, start, sane ? "PASSED" : "FAILED"); + if (sane) + { + hb_blob_make_immutable (blob); + return blob; + } + else + { + hb_blob_destroy (blob); + return hb_blob_get_empty (); + } + } + + template + hb_blob_t *reference_table (const hb_face_t *face, hb_tag_t tableTag = Type::tableTag) + { + if (!num_glyphs_set) + set_num_glyphs (hb_face_get_glyph_count (face)); + return sanitize_blob (hb_face_reference_table (face, tableTag)); + } + + const char *start, *end; + mutable int max_ops, max_subtables; + private: + bool writable; + unsigned int edit_count; + hb_blob_t *blob; + unsigned int num_glyphs; + bool num_glyphs_set; +}; + +struct hb_sanitize_with_object_t +{ + template + hb_sanitize_with_object_t (hb_sanitize_context_t *c, const T& obj) : c (c) + { c->set_object (obj); } + ~hb_sanitize_with_object_t () + { c->reset_object (); } + + private: + hb_sanitize_context_t *c; +}; + + +#endif /* HB_SANITIZE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh b/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh new file mode 100644 index 00000000000..e0ceae2c4a1 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh @@ -0,0 +1,553 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012,2018 Google, Inc. + * Copyright © 2019 Facebook, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_SERIALIZE_HH +#define HB_SERIALIZE_HH + +#include "hb.hh" +#include "hb-blob.hh" +#include "hb-map.hh" +#include "hb-pool.hh" + + +/* + * Serialize + */ + +struct hb_serialize_context_t +{ + typedef unsigned objidx_t; + + enum whence_t { + Head, /* Relative to the current object head (default). */ + Tail, /* Relative to the current object tail after packed. */ + Absolute /* Absolute: from the start of the serialize buffer. */ + }; + + struct object_t + { + void fini () { links.fini (); } + + bool operator == (const object_t &o) const + { + return (tail - head == o.tail - o.head) + && (links.length == o.links.length) + && 0 == hb_memcmp (head, o.head, tail - head) + && links.as_bytes () == o.links.as_bytes (); + } + uint32_t hash () const + { + return hb_bytes_t (head, tail - head).hash () ^ + links.as_bytes ().hash (); + } + + struct link_t + { + bool is_wide: 1; + bool is_signed: 1; + unsigned whence: 2; + unsigned position: 28; + unsigned bias; + objidx_t objidx; + }; + + char *head; + char *tail; + hb_vector_t links; + object_t *next; + }; + + struct snapshot_t + { + char *head; + char *tail; + object_t *current; // Just for sanity check + unsigned num_links; + }; + + snapshot_t snapshot () + { return snapshot_t { head, tail, current, current->links.length }; } + + hb_serialize_context_t (void *start_, unsigned int size) : + start ((char *) start_), + end (start + size), + current (nullptr) + { reset (); } + ~hb_serialize_context_t () { fini (); } + + void fini () + { + for (object_t *_ : ++hb_iter (packed)) _->fini (); + packed.fini (); + this->packed_map.fini (); + + while (current) + { + auto *_ = current; + current = current->next; + _->fini (); + } + object_pool.fini (); + } + + bool in_error () const { return !this->successful; } + + void reset () + { + this->successful = true; + this->ran_out_of_room = false; + this->head = this->start; + this->tail = this->end; + this->debug_depth = 0; + + fini (); + this->packed.push (nullptr); + } + + bool check_success (bool success) + { return this->successful && (success || (err_other_error (), false)); } + + template + bool check_equal (T1 &&v1, T2 &&v2) + { return check_success ((long long) v1 == (long long) v2); } + + template + bool check_assign (T1 &v1, T2 &&v2) + { return check_equal (v1 = v2, v2); } + + template bool propagate_error (T &&obj) + { return check_success (!hb_deref (obj).in_error ()); } + + template bool propagate_error (T1 &&o1, Ts&&... os) + { return propagate_error (hb_forward (o1)) && + propagate_error (hb_forward (os)...); } + + /* To be called around main operation. */ + template + Type *start_serialize () + { + DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1, + "start [%p..%p] (%lu bytes)", + this->start, this->end, + (unsigned long) (this->end - this->start)); + + assert (!current); + return push (); + } + void end_serialize () + { + DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1, + "end [%p..%p] serialized %u bytes; %s", + this->start, this->end, + (unsigned) (this->head - this->start), + this->successful ? "successful" : "UNSUCCESSFUL"); + + propagate_error (packed, packed_map); + + if (unlikely (!current)) return; + if (unlikely (in_error())) return; + + assert (!current->next); + + /* Only "pack" if there exist other objects... Otherwise, don't bother. + * Saves a move. */ + if (packed.length <= 1) + return; + + pop_pack (false); + + resolve_links (); + } + + template + Type *push () + { + if (unlikely (in_error ())) return start_embed (); + + object_t *obj = object_pool.alloc (); + if (unlikely (!obj)) + check_success (false); + else + { + obj->head = head; + obj->tail = tail; + obj->next = current; + current = obj; + } + return start_embed (); + } + void pop_discard () + { + object_t *obj = current; + if (unlikely (!obj)) return; + if (unlikely (in_error())) return; + + current = current->next; + revert (obj->head, obj->tail); + obj->fini (); + object_pool.free (obj); + } + + /* Set share to false when an object is unlikely sharable with others + * so not worth an attempt, or a contiguous table is serialized as + * multiple consecutive objects in the reverse order so can't be shared. + */ + objidx_t pop_pack (bool share=true) + { + object_t *obj = current; + if (unlikely (!obj)) return 0; + if (unlikely (in_error())) return 0; + + current = current->next; + obj->tail = head; + obj->next = nullptr; + unsigned len = obj->tail - obj->head; + head = obj->head; /* Rewind head. */ + + if (!len) + { + assert (!obj->links.length); + return 0; + } + + objidx_t objidx; + if (share) + { + objidx = packed_map.get (obj); + if (objidx) + { + obj->fini (); + return objidx; + } + } + + tail -= len; + memmove (tail, obj->head, len); + + obj->head = tail; + obj->tail = tail + len; + + packed.push (obj); + + if (unlikely (packed.in_error ())) { + // obj wasn't successfully added to packed, so clean it up otherwise it's + // links will be leaked. + propagate_error (packed); + obj->fini (); + return 0; + } + + objidx = packed.length - 1; + + if (share) packed_map.set (obj, objidx); + propagate_error (packed_map); + + return objidx; + } + + void revert (snapshot_t snap) + { + if (unlikely (in_error ())) return; + assert (snap.current == current); + current->links.shrink (snap.num_links); + revert (snap.head, snap.tail); + } + + void revert (char *snap_head, + char *snap_tail) + { + if (unlikely (in_error ())) return; + assert (snap_head <= head); + assert (tail <= snap_tail); + head = snap_head; + tail = snap_tail; + discard_stale_objects (); + } + + void discard_stale_objects () + { + if (unlikely (in_error ())) return; + while (packed.length > 1 && + packed.tail ()->head < tail) + { + packed_map.del (packed.tail ()); + assert (!packed.tail ()->next); + packed.tail ()->fini (); + packed.pop (); + } + if (packed.length > 1) + assert (packed.tail ()->head == tail); + } + + template + void add_link (T &ofs, objidx_t objidx, + whence_t whence = Head, + unsigned bias = 0) + { + static_assert (sizeof (T) == 2 || sizeof (T) == 4, ""); + if (unlikely (in_error ())) return; + + if (!objidx) + return; + + assert (current); + assert (current->head <= (const char *) &ofs); + + auto& link = *current->links.push (); + + link.is_wide = sizeof (T) == 4; + link.is_signed = hb_is_signed (hb_unwrap_type (T)); + link.whence = (unsigned) whence; + link.position = (const char *) &ofs - current->head; + link.bias = bias; + link.objidx = objidx; + } + + unsigned to_bias (const void *base) const + { + if (unlikely (in_error ())) return 0; + if (!base) return 0; + assert (current); + assert (current->head <= (const char *) base); + return (const char *) base - current->head; + } + + void resolve_links () + { + if (unlikely (in_error ())) return; + + assert (!current); + assert (packed.length > 1); + + for (const object_t* parent : ++hb_iter (packed)) + for (const object_t::link_t &link : parent->links) + { + const object_t* child = packed[link.objidx]; + if (unlikely (!child)) { err_other_error(); return; } + unsigned offset = 0; + switch ((whence_t) link.whence) { + case Head: offset = child->head - parent->head; break; + case Tail: offset = child->head - parent->tail; break; + case Absolute: offset = (head - start) + (child->head - tail); break; + } + + assert (offset >= link.bias); + offset -= link.bias; + if (link.is_signed) + { + if (link.is_wide) + assign_offset (parent, link, offset); + else + assign_offset (parent, link, offset); + } + else + { + if (link.is_wide) + assign_offset (parent, link, offset); + else + assign_offset (parent, link, offset); + } + } + } + + unsigned int length () const + { + if (unlikely (!current)) return 0; + return this->head - current->head; + } + + void align (unsigned int alignment) + { + unsigned int l = length () % alignment; + if (l) + allocate_size (alignment - l); + } + + template + Type *start_embed (const Type *obj HB_UNUSED = nullptr) const + { return reinterpret_cast (this->head); } + template + Type *start_embed (const Type &obj) const + { return start_embed (hb_addressof (obj)); } + + /* Following two functions exist to allow setting breakpoint on. */ + void err_ran_out_of_room () { this->ran_out_of_room = true; } + void err_other_error () { this->successful = false; } + + template + Type *allocate_size (unsigned int size) + { + if (unlikely (!this->successful)) return nullptr; + + if (this->tail - this->head < ptrdiff_t (size)) + { + err_ran_out_of_room (); + this->successful = false; + return nullptr; + } + memset (this->head, 0, size); + char *ret = this->head; + this->head += size; + return reinterpret_cast (ret); + } + + template + Type *allocate_min () + { return this->allocate_size (Type::min_size); } + + template + Type *embed (const Type *obj) + { + unsigned int size = obj->get_size (); + Type *ret = this->allocate_size (size); + if (unlikely (!ret)) return nullptr; + memcpy (ret, obj, size); + return ret; + } + template + Type *embed (const Type &obj) + { return embed (hb_addressof (obj)); } + + template auto + _copy (const Type &src, hb_priority<1>, Ts&&... ds) HB_RETURN + (Type *, src.copy (this, hb_forward (ds)...)) + + template auto + _copy (const Type &src, hb_priority<0>) -> decltype (&(hb_declval () = src)) + { + Type *ret = this->allocate_size (sizeof (Type)); + if (unlikely (!ret)) return nullptr; + *ret = src; + return ret; + } + + /* Like embed, but active: calls obj.operator=() or obj.copy() to transfer data + * instead of memcpy(). */ + template + Type *copy (const Type &src, Ts&&... ds) + { return _copy (src, hb_prioritize, hb_forward (ds)...); } + template + Type *copy (const Type *src, Ts&&... ds) + { return copy (*src, hb_forward (ds)...); } + + template + void copy_all (Iterator it, Ts&&... ds) + { for (decltype (*it) _ : it) copy (_, hb_forward (ds)...); } + + template + hb_serialize_context_t& operator << (const Type &obj) & { embed (obj); return *this; } + + template + Type *extend_size (Type *obj, unsigned int size) + { + if (unlikely (in_error ())) return nullptr; + + assert (this->start <= (char *) obj); + assert ((char *) obj <= this->head); + assert ((char *) obj + size >= this->head); + if (unlikely (!this->allocate_size (((char *) obj) + size - this->head))) return nullptr; + return reinterpret_cast (obj); + } + template + Type *extend_size (Type &obj, unsigned int size) + { return extend_size (hb_addressof (obj), size); } + + template + Type *extend_min (Type *obj) { return extend_size (obj, obj->min_size); } + template + Type *extend_min (Type &obj) { return extend_min (hb_addressof (obj)); } + + template + Type *extend (Type *obj, Ts&&... ds) + { return extend_size (obj, obj->get_size (hb_forward (ds)...)); } + template + Type *extend (Type &obj, Ts&&... ds) + { return extend (hb_addressof (obj), hb_forward (ds)...); } + + /* Output routines. */ + hb_bytes_t copy_bytes () const + { + assert (this->successful); + /* Copy both items from head side and tail side... */ + unsigned int len = (this->head - this->start) + + (this->end - this->tail); + + char *p = (char *) malloc (len); + if (unlikely (!p)) return hb_bytes_t (); + + memcpy (p, this->start, this->head - this->start); + memcpy (p + (this->head - this->start), this->tail, this->end - this->tail); + return hb_bytes_t (p, len); + } + template + Type *copy () const + { return reinterpret_cast ((char *) copy_bytes ().arrayZ); } + hb_blob_t *copy_blob () const + { + hb_bytes_t b = copy_bytes (); + return hb_blob_create (b.arrayZ, b.length, + HB_MEMORY_MODE_WRITABLE, + (char *) b.arrayZ, free); + } + + private: + template + void assign_offset (const object_t* parent, const object_t::link_t &link, unsigned offset) + { + auto &off = * ((BEInt *) (parent->head + link.position)); + assert (0 == off); + check_assign (off, offset); + } + + public: /* TODO Make private. */ + char *start, *head, *tail, *end; + unsigned int debug_depth; + bool successful; + bool ran_out_of_room; + + private: + + /* Object memory pool. */ + hb_pool_t object_pool; + + /* Stack of currently under construction objects. */ + object_t *current; + + /* Stack of packed objects. Object 0 is always nil object. */ + hb_vector_t packed; + + /* Map view of packed objects. */ + hb_hashmap_t packed_map; +}; + + +#endif /* HB_SERIALIZE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-set.cc b/src/java.desktop/share/native/libharfbuzz/hb-set.cc index 7dff3319963..fb40368965e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-set.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-set.cc @@ -69,7 +69,7 @@ hb_set_create () hb_set_t * hb_set_get_empty () { - return const_cast (&Null(hb_set_t)); + return const_cast (&Null (hb_set_t)); } /** @@ -389,6 +389,7 @@ hb_set_symmetric_difference (hb_set_t *set, set->symmetric_difference (other); } +#ifndef HB_DISABLE_DEPRECATED /** * hb_set_invert: * @set: a set. @@ -403,6 +404,7 @@ void hb_set_invert (hb_set_t *set HB_UNUSED) { } +#endif /** * hb_set_get_population: @@ -477,7 +479,7 @@ hb_set_next (const hb_set_t *set, * @set: a set. * @codepoint: (inout): * - * Gets the previous number in @set that is slower than current value of @codepoint. + * Gets the previous number in @set that is lower than current value of @codepoint. * * Set @codepoint to %HB_SET_VALUE_INVALID to get started. * @@ -522,7 +524,7 @@ hb_set_next_range (const hb_set_t *set, * @last: (out): output last codepoint in the range. * * Gets the previous consecutive range of numbers in @set that - * are greater than current value of @last. + * are less than current value of @first. * * Set @first to %HB_SET_VALUE_INVALID to get started. * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-set.hh b/src/java.desktop/share/native/libharfbuzz/hb-set.hh index 6b426949eb1..93ba0d0aa74 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-set.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-set.hh @@ -28,6 +28,7 @@ #define HB_SET_HH #include "hb.hh" +#include "hb-machinery.hh" /* @@ -39,7 +40,7 @@ struct hb_set_t { - HB_NO_COPY_ASSIGN (hb_set_t); + HB_DELETE_COPY_ASSIGN (hb_set_t); hb_set_t () { init (); } ~hb_set_t () { fini (); } @@ -69,7 +70,7 @@ struct hb_set_t void add (hb_codepoint_t g) { elt (g) |= mask (g); } void del (hb_codepoint_t g) { elt (g) &= ~mask (g); } - bool has (hb_codepoint_t g) const { return !!(elt (g) & mask (g)); } + bool get (hb_codepoint_t g) const { return elt (g) & mask (g); } void add_range (hb_codepoint_t a, hb_codepoint_t b) { @@ -88,6 +89,23 @@ struct hb_set_t } } + void del_range (hb_codepoint_t a, hb_codepoint_t b) + { + elt_t *la = &elt (a); + elt_t *lb = &elt (b); + if (la == lb) + *la &= ~((mask (b) << 1) - mask(a)); + else + { + *la &= mask (a) - 1; + la++; + + memset (la, 0, (char *) lb - (char *) la); + + *lb &= ~((mask (b) << 1) - 1); + } + } + bool is_equal (const page_t *other) const { return 0 == hb_memcmp (&v, &other->v, sizeof (v)); @@ -134,13 +152,22 @@ struct hb_set_t unsigned int i = m / ELT_BITS; unsigned int j = m & ELT_MASK; - const elt_t vv = v[i] & ((elt_t (1) << (j + 1)) - 1); - for (const elt_t *p = &vv; (int) i >= 0; p = &v[--i]) + /* Fancy mask to avoid shifting by elt_t bitsize, which is undefined. */ + const elt_t mask = j < 8 * sizeof (elt_t) - 1 ? + ((elt_t (1) << (j + 1)) - 1) : + (elt_t) -1; + const elt_t vv = v[i] & mask; + const elt_t *p = &vv; + while (true) + { if (*p) { *codepoint = i * ELT_BITS + elt_get_max (*p); return true; } + if ((int) i <= 0) break; + p = &v[--i]; + } *codepoint = INVALID; return false; @@ -186,7 +213,7 @@ struct hb_set_t hb_object_header_t header; bool successful; /* Allocations successful */ mutable unsigned int population; - hb_vector_t page_map; + hb_sorted_vector_t page_map; hb_vector_t pages; void init_shallow () @@ -227,11 +254,18 @@ struct hb_set_t return true; } - void clear () + void reset () { if (unlikely (hb_object_is_immutable (this))) return; + clear (); successful = true; + } + + void clear () + { + if (unlikely (hb_object_is_immutable (this))) + return; population = 0; page_map.resize (0); pages.resize (0); @@ -245,7 +279,7 @@ struct hb_set_t return true; } - void dirty () { population = (unsigned int) -1; } + void dirty () { population = UINT_MAX; } void add (hb_codepoint_t g) { @@ -301,7 +335,7 @@ struct hb_set_t { page->add (g); - array = (const T *) ((const char *) array + stride); + array = &StructAtOffsetUnaligned (array, stride); count--; } while (count && (g = *array, start <= g && g < end)); @@ -349,23 +383,79 @@ struct hb_set_t dirty (); page->del (g); } + + private: + void del_pages (int ds, int de) + { + if (ds <= de) + { + unsigned int write_index = 0; + for (unsigned int i = 0; i < page_map.length; i++) + { + int m = (int) page_map[i].major; + if (m < ds || de < m) + page_map[write_index++] = page_map[i]; + } + compact (write_index); + resize (write_index); + } + } + + public: void del_range (hb_codepoint_t a, hb_codepoint_t b) { /* TODO perform op even if !successful. */ - /* TODO Optimize, like add_range(). */ if (unlikely (!successful)) return; - for (unsigned int i = a; i < b + 1; i++) - del (i); + if (unlikely (a > b || a == INVALID || b == INVALID)) return; + dirty (); + unsigned int ma = get_major (a); + unsigned int mb = get_major (b); + /* Delete pages from ds through de if ds <= de. */ + int ds = (a == major_start (ma))? (int) ma: (int) (ma + 1); + int de = (b + 1 == major_start (mb + 1))? (int) mb: ((int) mb - 1); + if (ds > de || (int) ma < ds) + { + page_t *page = page_for (a); + if (page) + { + if (ma == mb) + page->del_range (a, b); + else + page->del_range (a, major_start (ma + 1) - 1); + } + } + if (de < (int) mb && ma != mb) + { + page_t *page = page_for (b); + if (page) + page->del_range (major_start (mb), b); + } + del_pages (ds, de); } - bool has (hb_codepoint_t g) const + + bool get (hb_codepoint_t g) const { const page_t *page = page_for (g); if (!page) return false; - return page->has (g); + return page->get (g); } - bool intersects (hb_codepoint_t first, - hb_codepoint_t last) const + + /* Has interface. */ + static constexpr bool SENTINEL = false; + typedef bool value_t; + value_t operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + /* Predicate. */ + bool operator () (hb_codepoint_t k) const { return has (k); } + + /* Sink interface. */ + hb_set_t& operator << (hb_codepoint_t v) + { add (v); return *this; } + hb_set_t& operator << (const hb_pair_t& range) + { add_range (range.first, range.second); return *this; } + + bool intersects (hb_codepoint_t first, hb_codepoint_t last) const { hb_codepoint_t c = first - 1; return next (&c) && c <= last; @@ -422,8 +512,36 @@ struct hb_set_t return true; } - template - void process (const hb_set_t *other) + void compact (unsigned int length) + { + hb_vector_t old_index_to_page_map_index; + old_index_to_page_map_index.resize(pages.length); + for (uint32_t i = 0; i < old_index_to_page_map_index.length; i++) + old_index_to_page_map_index[i] = 0xFFFFFFFF; + + for (uint32_t i = 0; i < length; i++) + old_index_to_page_map_index[page_map[i].index] = i; + + compact_pages (old_index_to_page_map_index); + } + + void compact_pages (const hb_vector_t& old_index_to_page_map_index) + { + unsigned int write_index = 0; + for (unsigned int i = 0; i < pages.length; i++) + { + if (old_index_to_page_map_index[i] == 0xFFFFFFFF) continue; + + if (write_index < i) + pages[write_index] = pages[i]; + + page_map[old_index_to_page_map_index[i]].index = write_index; + write_index++; + } + } + + template + void process (const Op& op, const hb_set_t *other) { if (unlikely (!successful)) return; @@ -435,10 +553,22 @@ struct hb_set_t unsigned int count = 0, newCount = 0; unsigned int a = 0, b = 0; + unsigned int write_index = 0; for (; a < na && b < nb; ) { if (page_map[a].major == other->page_map[b].major) { + if (!Op::passthru_left) + { + // Move page_map entries that we're keeping from the left side set + // to the front of the page_map vector. This isn't necessary if + // passthru_left is set since no left side pages will be removed + // in that case. + if (write_index < a) + page_map[write_index] = page_map[a]; + write_index++; + } + count++; a++; b++; @@ -461,9 +591,16 @@ struct hb_set_t if (Op::passthru_right) count += nb - b; - if (count > pages.length) - if (!resize (count)) - return; + if (!Op::passthru_left) + { + na = write_index; + next_page = write_index; + compact (write_index); + } + + if (!resize (count)) + return; + newCount = count; /* Process in-place backward. */ @@ -477,7 +614,7 @@ struct hb_set_t b--; count--; page_map[count] = page_map[a]; - Op::process (page_at (count).v, page_at (a).v, other->page_at (b).v); + page_at (count).v = op (page_at (a).v, other->page_at (b).v); } else if (page_map[a - 1].major > other->page_map[b - 1].major) { @@ -523,19 +660,19 @@ struct hb_set_t void union_ (const hb_set_t *other) { - process (other); + process (hb_bitwise_or, other); } void intersect (const hb_set_t *other) { - process (other); + process (hb_bitwise_and, other); } void subtract (const hb_set_t *other) { - process (other); + process (hb_bitwise_sub, other); } void symmetric_difference (const hb_set_t *other) { - process (other); + process (hb_bitwise_xor, other); } bool next (hb_codepoint_t *codepoint) const { @@ -638,7 +775,7 @@ struct hb_set_t unsigned int get_population () const { - if (population != (unsigned int) -1) + if (population != UINT_MAX) return population; unsigned int pop = 0; @@ -671,27 +808,36 @@ struct hb_set_t /* * Iterator implementation. */ - struct const_iter_t : hb_sorted_iter_t + struct iter_t : hb_iter_with_fallback_t { - const_iter_t (const hb_set_t &s_) : - s (s_), v (INVALID), l (s.get_population () + 1) { __next__ (); } + static constexpr bool is_sorted_iterator = true; + iter_t (const hb_set_t &s_ = Null (hb_set_t), + bool init = true) : s (&s_), v (INVALID), l(0) + { + if (init) + { + l = s->get_population () + 1; + __next__ (); + } + } - typedef hb_codepoint_t __item_type__; + typedef hb_codepoint_t __item_t__; hb_codepoint_t __item__ () const { return v; } bool __more__ () const { return v != INVALID; } - void __next__ () { s.next (&v); if (l) l--; } - void __prev__ () { s.previous (&v); } - unsigned __len__ () { return l; } + void __next__ () { s->next (&v); if (l) l--; } + void __prev__ () { s->previous (&v); } + unsigned __len__ () const { return l; } + iter_t end () const { return iter_t (*s, false); } + bool operator != (const iter_t& o) const + { return s != o.s || v != o.v; } protected: - const hb_set_t &s; + const hb_set_t *s; hb_codepoint_t v; unsigned l; }; - const_iter_t const_iter () const { return const_iter_t (*this); } - operator const_iter_t () const { return const_iter (); } - typedef const_iter_t iter_t; - iter_t iter () const { return const_iter (); } + iter_t iter () const { return iter_t (*this); } + operator iter_t () const { return iter (); } protected: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.cc b/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.cc index 800cf0a12f5..3243effb3a5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.cc @@ -79,7 +79,9 @@ hb_shape_plan_key_t::init (bool copy, } this->shaper_func = nullptr; this->shaper_name = nullptr; +#ifndef HB_NO_OT_SHAPE this->ot.init (face, coords, num_coords); +#endif /* * Choose shaper. @@ -148,7 +150,9 @@ hb_shape_plan_key_t::equal (const hb_shape_plan_key_t *other) { return hb_segment_properties_equal (&this->props, &other->props) && this->user_features_match (other) && +#ifndef HB_NO_OT_SHAPE this->ot.equal (&other->ot) && +#endif this->shaper_func == other->shaper_func; } @@ -224,12 +228,16 @@ hb_shape_plan_create2 (hb_face_t *face, num_coords, shaper_list))) goto bail2; +#ifndef HB_NO_OT_SHAPE if (unlikely (!shape_plan->ot.init0 (face, &shape_plan->key))) goto bail3; +#endif return shape_plan; +#ifndef HB_NO_OT_SHAPE bail3: +#endif shape_plan->key.free (); bail2: free (shape_plan); @@ -249,7 +257,7 @@ hb_shape_plan_create2 (hb_face_t *face, hb_shape_plan_t * hb_shape_plan_get_empty () { - return const_cast (&Null(hb_shape_plan_t)); + return const_cast (&Null (hb_shape_plan_t)); } /** @@ -281,7 +289,9 @@ hb_shape_plan_destroy (hb_shape_plan_t *shape_plan) { if (!hb_object_destroy (shape_plan)) return; +#ifndef HB_NO_OT_SHAPE shape_plan->ot.fini (); +#endif shape_plan->key.free (); free (shape_plan); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.hh b/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.hh index a99cfc3eea7..debe6596330 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.hh @@ -39,21 +39,23 @@ struct hb_shape_plan_key_t const hb_feature_t *user_features; unsigned int num_user_features; +#ifndef HB_NO_OT_SHAPE hb_ot_shape_plan_key_t ot; +#endif hb_shape_func_t *shaper_func; const char *shaper_name; - HB_INTERNAL inline bool init (bool copy, - hb_face_t *face, - const hb_segment_properties_t *props, - const hb_feature_t *user_features, - unsigned int num_user_features, - const int *coords, - unsigned int num_coords, - const char * const *shaper_list); + HB_INTERNAL bool init (bool copy, + hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list); - HB_INTERNAL inline void free () { ::free ((void *) user_features); } + HB_INTERNAL void free () { ::free ((void *) user_features); } HB_INTERNAL bool user_features_match (const hb_shape_plan_key_t *other); @@ -65,7 +67,9 @@ struct hb_shape_plan_t hb_object_header_t header; hb_face_t *face_unsafe; /* We don't carry a reference to face. */ hb_shape_plan_key_t key; +#ifndef HB_NO_OT_SHAPE hb_ot_shape_plan_t ot; +#endif }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shape.cc b/src/java.desktop/share/native/libharfbuzz/hb-shape.cc index 90876d658bd..5dc55febb02 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shape.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-shape.cc @@ -132,6 +132,8 @@ hb_shape_full (hb_font_t *font, unsigned int num_features, const char * const *shaper_list) { + if (unlikely (hb_object_is_immutable (buffer))) return false; + hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached2 (font->face, &buffer->props, features, num_features, font->coords, font->num_coords, @@ -154,7 +156,9 @@ hb_shape_full (hb_font_t *font, * * Shapes @buffer using @font turning its Unicode characters content to * positioned glyphs. If @features is not %NULL, it will be used to control the - * features applied during shaping. + * features applied during shaping. If two @features have the same tag but + * overlapping ranges the value of the feature with the higher index takes + * precedence. * * Since: 0.9.2 **/ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shaper-list.hh b/src/java.desktop/share/native/libharfbuzz/hb-shaper-list.hh index 36d8fc7f660..0d63933a766 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shaper-list.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-shaper-list.hh @@ -28,6 +28,9 @@ #define HB_SHAPER_LIST_HH #endif /* HB_SHAPER_LIST_HH */ /* Dummy header guards */ +#ifndef HB_NO_SHAPER + + /* v--- Add new shapers in the right place here. */ #ifdef HAVE_GRAPHITE2 @@ -35,7 +38,9 @@ HB_SHAPER_IMPLEMENT (graphite2) #endif +#ifndef HB_NO_OT_SHAPE HB_SHAPER_IMPLEMENT (ot) /* <--- This is our main OpenType shaper. */ +#endif #ifdef HAVE_UNISCRIBE HB_SHAPER_IMPLEMENT (uniscribe) @@ -45,12 +50,11 @@ HB_SHAPER_IMPLEMENT (directwrite) #endif #ifdef HAVE_CORETEXT HB_SHAPER_IMPLEMENT (coretext) - -/* Only picks up fonts that have a "mort" or "morx" table. - Probably going to be removed https://github.com/harfbuzz/harfbuzz/issues/1478 */ -HB_SHAPER_IMPLEMENT (coretext_aat) #endif -#ifdef HAVE_FALLBACK +#ifndef HB_NO_FALLBACK_SHAPE HB_SHAPER_IMPLEMENT (fallback) /* <--- This should be last. */ #endif + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shaper.cc b/src/java.desktop/share/native/libharfbuzz/hb-shaper.cc index 158b454bf1d..b1d76dcc4ee 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shaper.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-shaper.cc @@ -34,6 +34,9 @@ static const hb_shaper_entry_t all_shapers[] = { #include "hb-shaper-list.hh" #undef HB_SHAPER_IMPLEMENT }; +#ifndef HB_NO_SHAPER +static_assert (0 != ARRAY_LENGTH_CONST (all_shapers), "No shaper enabled."); +#endif #if HB_USE_ATEXIT static void free_static_shapers (); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shaper.hh b/src/java.desktop/share/native/libharfbuzz/hb-shaper.hh index 37ca9ffb665..0c9b681519d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shaper.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-shaper.hh @@ -102,7 +102,7 @@ template struct hb_shaper_object static void destroy (Type *p) { HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (p); } \ }; \ \ - static_assert (true, "") /* Require semicolon. */ + static_assert (true, "") /* Require semicolon after. */ template diff --git a/src/java.desktop/share/native/libharfbuzz/hb-static.cc b/src/java.desktop/share/native/libharfbuzz/hb-static.cc index 4c515886024..b213c57c7e5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-static.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-static.cc @@ -37,9 +37,10 @@ #include "hb-ot-maxp-table.hh" #ifndef HB_NO_VISIBILITY +#include "hb-ot-name-language-static.hh" -hb_vector_size_impl_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)] = {}; -/*thread_local*/ hb_vector_size_impl_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)] = {}; +uint64_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)] = {}; +/*thread_local*/ uint64_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)] = {}; DEFINE_NULL_NAMESPACE_BYTES (OT, Index) = {0xFF,0xFF}; DEFINE_NULL_NAMESPACE_BYTES (OT, LangSys) = {0x00,0x00, 0xFF,0xFF, 0x00,0x00}; @@ -50,6 +51,9 @@ DEFINE_NULL_NAMESPACE_BYTES (AAT, SettingName) = {0xFF,0xFF, 0xFF,0xFF}; const unsigned char _hb_Null_AAT_Lookup[2] = {0xFF, 0xFF}; + +/* hb_face_t */ + unsigned int hb_face_t::load_num_glyphs () const { @@ -72,4 +76,37 @@ hb_face_t::load_upem () const return ret; } + +/* hb_user_data_array_t */ + +bool +hb_user_data_array_t::set (hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + if (!key) + return false; + + if (replace) { + if (!data && !destroy) { + items.remove (key, lock); + return true; + } + } + hb_user_data_item_t item = {key, data, destroy}; + bool ret = !!items.replace_or_insert (item, lock, (bool) replace); + + return ret; +} + +void * +hb_user_data_array_t::get (hb_user_data_key_t *key) +{ + hb_user_data_item_t item = {nullptr, nullptr, nullptr}; + + return items.find (key, &item, lock) ? item.data : nullptr; +} + + #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-string-array.hh b/src/java.desktop/share/native/libharfbuzz/hb-string-array.hh index dffa9f3beba..9d00cae6c32 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-string-array.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-string-array.hh @@ -37,6 +37,7 @@ #define HB_STRING_ARRAY_TYPE_NAME HB_PASTE(HB_STRING_ARRAY_NAME, _msgstr_t) #define HB_STRING_ARRAY_POOL_NAME HB_PASTE(HB_STRING_ARRAY_NAME, _msgstr) #define HB_STRING_ARRAY_OFFS_NAME HB_PASTE(HB_STRING_ARRAY_NAME, _msgidx) +#define HB_STRING_ARRAY_LENG_NAME HB_PASTE(HB_STRING_ARRAY_NAME, _length) static const union HB_STRING_ARRAY_TYPE_NAME { struct { @@ -48,7 +49,7 @@ static const union HB_STRING_ARRAY_TYPE_NAME { #include HB_STRING_ARRAY_LIST #undef _S } st; - char str[VAR]; + char str[HB_VAR_ARRAY]; } HB_STRING_ARRAY_POOL_NAME = { @@ -66,6 +67,8 @@ static const unsigned int HB_STRING_ARRAY_OFFS_NAME[] = sizeof (HB_STRING_ARRAY_TYPE_NAME) }; +static const unsigned int HB_STRING_ARRAY_LENG_NAME = ARRAY_LENGTH_CONST (HB_STRING_ARRAY_OFFS_NAME) - 1; + static inline hb_bytes_t HB_STRING_ARRAY_NAME (unsigned int i) { @@ -77,5 +80,6 @@ HB_STRING_ARRAY_NAME (unsigned int i) #undef HB_STRING_ARRAY_TYPE_NAME #undef HB_STRING_ARRAY_POOL_NAME #undef HB_STRING_ARRAY_OFFS_NAME +#undef HB_STRING_ARRAY_LENG_NAME #endif /* HB_STRING_ARRAY_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-style.cc b/src/java.desktop/share/native/libharfbuzz/hb-style.cc new file mode 100644 index 00000000000..50f9df9a282 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-style.cc @@ -0,0 +1,135 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.hh" + +#ifndef HB_NO_STYLE +#ifdef HB_EXPERIMENTAL_API + +#include "hb-ot-var-avar-table.hh" +#include "hb-ot-var-fvar-table.hh" +#include "hb-ot-stat-table.hh" +#include "hb-ot-os2-table.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-post-table.hh" +#include "hb-ot-face.hh" + +/** + * hb_style_tag_t: + * @HB_STYLE_TAG_ITALIC: Used to vary between non-italic and italic. + * A value of 0 can be interpreted as "Roman" (non-italic); a value of 1 can + * be interpreted as (fully) italic. + * @HB_STYLE_TAG_OPTICAL_SIZE: Used to vary design to suit different text sizes. + * Non-zero. Values can be interpreted as text size, in points. + * @HB_STYLE_TAG_SLANT: Used to vary between upright and slanted text. Values + * must be greater than -90 and less than +90. Values can be interpreted as + * the angle, in counter-clockwise degrees, of oblique slant from whatever the + * designer considers to be upright for that font design. + * @HB_STYLE_TAG_WIDTH: Used to vary width of text from narrower to wider. + * Non-zero. Values can be interpreted as a percentage of whatever the font + * designer considers “normal width” for that font design. + * @HB_STYLE_TAG_WEIGHT: Used to vary stroke thicknesses or other design details + * to give variation from lighter to blacker. Values can be interpreted in direct + * comparison to values for usWeightClass in the OS/2 table, + * or the CSS font-weight property. + * + * Defined by https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg + * + * Since: EXPERIMENTAL + **/ +typedef enum { + HB_STYLE_TAG_ITALIC = HB_TAG ('i','t','a','l'), + HB_STYLE_TAG_OPTICAL_SIZE = HB_TAG ('o','p','s','z'), + HB_STYLE_TAG_SLANT = HB_TAG ('s','l','n','t'), + HB_STYLE_TAG_WIDTH = HB_TAG ('w','d','t','h'), + HB_STYLE_TAG_WEIGHT = HB_TAG ('w','g','h','t'), + + _HB_STYLE_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_style_tag_t; + +/** + * hb_style_get_value: + * @font: a #hb_font_t object. + * @style_tag: a style tag. + * + * Searches variation axes of a hb_font_t object for a specific axis first, + * if not set, then tries to get default style values from different + * tables of the font. + * + * Returns: Corresponding axis or default value to a style tag. + * + * Since: EXPERIMENTAL + **/ +float +hb_style_get_value (hb_font_t *font, hb_tag_t tag) +{ + hb_style_tag_t style_tag = (hb_style_tag_t) tag; + hb_face_t *face = font->face; + +#ifndef HB_NO_VAR + hb_ot_var_axis_info_t axis; + if (hb_ot_var_find_axis_info (face, style_tag, &axis)) + { + if (axis.axis_index < font->num_coords) return font->design_coords[axis.axis_index]; + /* If a face is variable, fvar's default_value is better than STAT records */ + return axis.default_value; + } +#endif + + if (style_tag == HB_STYLE_TAG_OPTICAL_SIZE && font->ptem) + return font->ptem; + + /* STAT */ + float value; + if (face->table.STAT->get_value (style_tag, &value)) + return value; + + switch ((unsigned) style_tag) + { + case HB_STYLE_TAG_ITALIC: + return face->table.OS2->is_italic () || face->table.head->is_italic () ? 1 : 0; + case HB_STYLE_TAG_OPTICAL_SIZE: + { + unsigned int lower, upper; + return face->table.OS2->v5 ().get_optical_size (&lower, &upper) + ? (float) (lower + upper) / 2.f + : 12.f; + } + case HB_STYLE_TAG_SLANT: + return face->table.post->table->italicAngle.to_float (); + case HB_STYLE_TAG_WIDTH: + return face->table.OS2->has_data () + ? face->table.OS2->get_width () + : (face->table.head->is_condensed () ? 75 : 100); + case HB_STYLE_TAG_WEIGHT: + return face->table.OS2->has_data () + ? face->table.OS2->usWeightClass + : (face->table.head->is_bold () ? 700 : 400); + default: + return 0; + } +} + +#endif +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-warning.cc b/src/java.desktop/share/native/libharfbuzz/hb-style.h similarity index 70% rename from src/java.desktop/share/native/libharfbuzz/hb-warning.cc rename to src/java.desktop/share/native/libharfbuzz/hb-style.h index 9fb41003808..1209c79e945 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-warning.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-style.h @@ -1,5 +1,5 @@ /* - * Copyright © 2012 Google, Inc. + * Copyright © 2019 Ebrahim Byagowi * * This is part of HarfBuzz, a text shaping library. * @@ -20,18 +20,24 @@ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod */ -#include "hb.hh" - -#if defined(HB_ATOMIC_INT_NIL) -#error "Could not find any system to define atomic_int macros, library WILL NOT be thread-safe" -#error "Check hb-atomic.hh for possible resolutions." +#ifndef HB_H_IN +#error "Include instead." #endif -#if defined(HB_MUTEX_IMPL_NIL) -#error "Could not find any system to define mutex macros, library WILL NOT be thread-safe" -#error "Check hb-mutex.hh for possible resolutions." +#ifndef HB_STYLE_H +#define HB_STYLE_H + +#include "hb.h" + +HB_BEGIN_DECLS + +#ifdef HB_EXPERIMENTAL_API +HB_EXTERN float +hb_style_get_value (hb_font_t *font, hb_tag_t style_tag); #endif + +HB_END_DECLS + +#endif /* HB_STYLE_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.cc index 75004fe77ff..e17433152b0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.cc @@ -24,6 +24,10 @@ * Adobe Author(s): Michiharu Ariza */ +#include "hb.hh" + +#ifndef HB_NO_SUBSET_CFF + #include "hb-ot-cff-common.hh" #include "hb-ot-cff2-table.hh" #include "hb-subset-cff-common.hh" @@ -43,33 +47,39 @@ using namespace CFF; **/ bool -hb_plan_subset_cff_fdselect (const hb_vector_t &glyphs, - unsigned int fdCount, - const FDSelect &src, /* IN */ - unsigned int &subset_fd_count /* OUT */, - unsigned int &subset_fdselect_size /* OUT */, - unsigned int &subset_fdselect_format /* OUT */, - hb_vector_t &fdselect_ranges /* OUT */, - remap_t &fdmap /* OUT */) +hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, + unsigned int fdCount, + const FDSelect &src, /* IN */ + unsigned int &subset_fd_count /* OUT */, + unsigned int &subset_fdselect_size /* OUT */, + unsigned int &subset_fdselect_format /* OUT */, + hb_vector_t &fdselect_ranges /* OUT */, + hb_inc_bimap_t &fdmap /* OUT */) { subset_fd_count = 0; subset_fdselect_size = 0; subset_fdselect_format = 0; - unsigned int num_ranges = 0; + unsigned int num_ranges = 0; - unsigned int subset_num_glyphs = glyphs.length; + unsigned int subset_num_glyphs = plan->num_output_glyphs (); if (subset_num_glyphs == 0) return true; { /* use hb_set to determine the subset of font dicts */ - hb_set_t *set = hb_set_create (); - if (set == &Null (hb_set_t)) - return false; - hb_codepoint_t prev_fd = CFF_UNDEF_CODE; + hb_set_t *set = hb_set_create (); + if (unlikely (set == &Null (hb_set_t))) return false; + hb_codepoint_t prev_fd = CFF_UNDEF_CODE; for (hb_codepoint_t i = 0; i < subset_num_glyphs; i++) { - hb_codepoint_t fd = src.get_fd (glyphs[i]); + hb_codepoint_t glyph; + hb_codepoint_t fd; + if (!plan->old_gid_for_new_gid (i, &glyph)) + { + /* fonttools retains FDSelect & font dicts for missing glyphs. do the same */ + glyph = i; + } + fd = src.get_fd (glyph); set->add (fd); if (fd != prev_fd) @@ -91,17 +101,13 @@ hb_plan_subset_cff_fdselect (const hb_vector_t &glyphs, else { /* create a fdmap */ - if (!fdmap.reset (fdCount)) - { - hb_set_destroy (set); - return false; - } + fdmap.reset (); - hb_codepoint_t fd = CFF_UNDEF_CODE; + hb_codepoint_t fd = CFF_UNDEF_CODE; while (set->next (&fd)) fdmap.add (fd); hb_set_destroy (set); - if (unlikely (fdmap.get_count () != subset_fd_count)) + if (unlikely (fdmap.get_population () != subset_fd_count)) return false; } @@ -145,21 +151,21 @@ hb_plan_subset_cff_fdselect (const hb_vector_t &glyphs, template static inline bool serialize_fdselect_3_4 (hb_serialize_context_t *c, - const unsigned int num_glyphs, - const FDSelect &src, - unsigned int size, - const hb_vector_t &fdselect_ranges) + const unsigned int num_glyphs, + const FDSelect &src, + unsigned int size, + const hb_vector_t &fdselect_ranges) { TRACE_SERIALIZE (this); FDSELECT3_4 *p = c->allocate_size (size); - if (unlikely (p == nullptr)) return_trace (false); - p->nRanges ().set (fdselect_ranges.length); + if (unlikely (!p)) return_trace (false); + p->nRanges () = fdselect_ranges.length; for (unsigned int i = 0; i < fdselect_ranges.length; i++) { - p->ranges[i].first.set (fdselect_ranges[i].glyph); - p->ranges[i].fd.set (fdselect_ranges[i].code); + p->ranges[i].first = fdselect_ranges[i].glyph; + p->ranges[i].fd = fdselect_ranges[i].code; } - p->sentinel().set (num_glyphs); + p->sentinel () = num_glyphs; return_trace (true); } @@ -169,58 +175,53 @@ serialize_fdselect_3_4 (hb_serialize_context_t *c, **/ bool hb_serialize_cff_fdselect (hb_serialize_context_t *c, - const unsigned int num_glyphs, - const FDSelect &src, - unsigned int fd_count, - unsigned int fdselect_format, - unsigned int size, - const hb_vector_t &fdselect_ranges) + const unsigned int num_glyphs, + const FDSelect &src, + unsigned int fd_count, + unsigned int fdselect_format, + unsigned int size, + const hb_vector_t &fdselect_ranges) { TRACE_SERIALIZE (this); - FDSelect *p = c->allocate_min (); - if (unlikely (p == nullptr)) return_trace (false); - p->format.set (fdselect_format); + FDSelect *p = c->allocate_min (); + if (unlikely (!p)) return_trace (false); + p->format = fdselect_format; size -= FDSelect::min_size; switch (fdselect_format) { #if CFF_SERIALIZE_FDSELECT_0 - case 0: + case 0: + { + FDSelect0 *p = c->allocate_size (size); + if (unlikely (!p)) return_trace (false); + unsigned int range_index = 0; + unsigned int fd = fdselect_ranges[range_index++].code; + for (unsigned int i = 0; i < num_glyphs; i++) { - FDSelect0 *p = c->allocate_size (size); - if (unlikely (p == nullptr)) return_trace (false); - unsigned int range_index = 0; - unsigned int fd = fdselect_ranges[range_index++].code; - for (unsigned int i = 0; i < num_glyphs; i++) + if ((range_index < fdselect_ranges.len) && + (i >= fdselect_ranges[range_index].glyph)) { - if ((range_index < fdselect_ranges.len) && - (i >= fdselect_ranges[range_index].glyph)) - { - fd = fdselect_ranges[range_index++].code; - } - p->fds[i].set (fd); + fd = fdselect_ranges[range_index++].code; } - break; + p->fds[i] = fd; } + return_trace (true); + } #endif /* CFF_SERIALIZE_FDSELECT_0 */ - case 3: - return serialize_fdselect_3_4 (c, - num_glyphs, - src, - size, - fdselect_ranges); - - case 4: - return serialize_fdselect_3_4 (c, - num_glyphs, - src, - size, - fdselect_ranges); - - default: - assert(false); - } + case 3: + return serialize_fdselect_3_4 (c, num_glyphs, src, + size, fdselect_ranges); - return_trace (true); + case 4: + return serialize_fdselect_3_4 (c, num_glyphs, src, + size, fdselect_ranges); + + default: + return_trace (false); + } } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh index a3492545ec9..b81864164a9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh @@ -44,7 +44,7 @@ struct str_encoder_t void encode_byte (unsigned char b) { - if (unlikely (buff.push (b) == &Crap(unsigned char))) + if (unlikely (buff.push (b) == &Crap (unsigned char))) set_error (); } @@ -110,7 +110,11 @@ struct str_encoder_t void copy_str (const byte_str_t &str) { unsigned int offset = buff.length; - buff.resize (offset + str.length); + if (unlikely (!buff.resize (offset + str.length))) + { + set_error (); + return; + } if (unlikely (buff.length < offset + str.length)) { set_error (); @@ -128,26 +132,17 @@ struct str_encoder_t bool error; }; -struct cff_sub_table_offsets_t { - cff_sub_table_offsets_t () : privateDictsOffset (0) +struct cff_sub_table_info_t { + cff_sub_table_info_t () + : fd_array_link (0), + char_strings_link (0) { - topDictInfo.init (); - FDSelectInfo.init (); - FDArrayInfo.init (); - charStringsInfo.init (); - globalSubrsInfo.init (); - localSubrsInfos.init (); + fd_select.init (); } - ~cff_sub_table_offsets_t () { localSubrsInfos.fini (); } - - table_info_t topDictInfo; - table_info_t FDSelectInfo; - table_info_t FDArrayInfo; - table_info_t charStringsInfo; - unsigned int privateDictsOffset; - table_info_t globalSubrsInfo; - hb_vector_t localSubrsInfos; + table_info_t fd_select; + objidx_t fd_array_link; + objidx_t char_strings_link; }; template @@ -155,40 +150,26 @@ struct cff_top_dict_op_serializer_t : op_serializer_t { bool serialize (hb_serialize_context_t *c, const OPSTR &opstr, - const cff_sub_table_offsets_t &offsets) const + const cff_sub_table_info_t &info) const { TRACE_SERIALIZE (this); switch (opstr.op) { case OpCode_CharStrings: - return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charStringsInfo.offset)); + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.char_strings_link, whence_t::Absolute)); case OpCode_FDArray: - return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDArrayInfo.offset)); + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_array_link, whence_t::Absolute)); case OpCode_FDSelect: - return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectInfo.offset)); + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_select.link, whence_t::Absolute)); default: return_trace (copy_opstr (c, opstr)); } return_trace (true); } - - unsigned int calculate_serialized_size (const OPSTR &opstr) const - { - switch (opstr.op) - { - case OpCode_CharStrings: - case OpCode_FDArray: - case OpCode_FDSelect: - return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op); - - default: - return opstr.str.length; - } - } }; struct cff_font_dict_op_serializer_t : op_serializer_t @@ -202,33 +183,17 @@ struct cff_font_dict_op_serializer_t : op_serializer_t if (opstr.op == OpCode_Private) { /* serialize the private dict size & offset as 2-byte & 4-byte integers */ - if (unlikely (!UnsizedByteStr::serialize_int2 (c, privateDictInfo.size) || - !UnsizedByteStr::serialize_int4 (c, privateDictInfo.offset))) - return_trace (false); - - /* serialize the opcode */ - HBUINT8 *p = c->allocate_size (1); - if (unlikely (p == nullptr)) return_trace (false); - p->set (OpCode_Private); - - return_trace (true); + return_trace (UnsizedByteStr::serialize_int2 (c, privateDictInfo.size) && + Dict::serialize_link4_op (c, opstr.op, privateDictInfo.link, whence_t::Absolute)); } else { HBUINT8 *d = c->allocate_size (opstr.str.length); - if (unlikely (d == nullptr)) return_trace (false); + if (unlikely (!d)) return_trace (false); memcpy (d, &opstr.str[0], opstr.str.length); } return_trace (true); } - - unsigned int calculate_serialized_size (const op_str_t &opstr) const - { - if (opstr.op == OpCode_Private) - return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private); - else - return opstr.str.length; - } }; struct cff_private_dict_op_serializer_t : op_serializer_t @@ -238,7 +203,7 @@ struct cff_private_dict_op_serializer_t : op_serializer_t bool serialize (hb_serialize_context_t *c, const op_str_t &opstr, - const unsigned int subrsOffset) const + objidx_t subrs_link) const { TRACE_SERIALIZE (this); @@ -246,31 +211,15 @@ struct cff_private_dict_op_serializer_t : op_serializer_t return true; if (opstr.op == OpCode_Subrs) { - if (desubroutinize || (subrsOffset == 0)) + if (desubroutinize || !subrs_link) return_trace (true); else - return_trace (FontDict::serialize_offset2_op (c, opstr.op, subrsOffset)); + return_trace (FontDict::serialize_link2_op (c, opstr.op, subrs_link)); } else return_trace (copy_opstr (c, opstr)); } - unsigned int calculate_serialized_size (const op_str_t &opstr, - bool has_localsubr=true) const - { - if (drop_hints && dict_opset_t::is_hint_op (opstr.op)) - return 0; - if (opstr.op == OpCode_Subrs) - { - if (desubroutinize || !has_localsubr) - return 0; - else - return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (opstr.op); - } - else - return opstr.str.length; - } - protected: const bool desubroutinize; const bool drop_hints; @@ -282,30 +231,35 @@ struct flatten_param_t bool drop_hints; }; -template +template struct subr_flattener_t { subr_flattener_t (const ACC &acc_, - const hb_vector_t &glyphs_, - bool drop_hints_) : acc (acc_), glyphs (glyphs_), - drop_hints (drop_hints_) {} + const hb_subset_plan_t *plan_) + : acc (acc_), plan (plan_) {} bool flatten (str_buff_vec_t &flat_charstrings) { - if (!flat_charstrings.resize (glyphs.length)) + if (!flat_charstrings.resize (plan->num_output_glyphs ())) return false; - for (unsigned int i = 0; i < glyphs.length; i++) + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) flat_charstrings[i].init (); - for (unsigned int i = 0; i < glyphs.length; i++) + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) { - hb_codepoint_t glyph = glyphs[i]; + hb_codepoint_t glyph; + if (!plan->old_gid_for_new_gid (i, &glyph)) + { + /* add an endchar only charstring for a missing glyph if CFF1 */ + if (endchar_op != OpCode_Invalid) flat_charstrings[i].push (endchar_op); + continue; + } const byte_str_t str = (*acc.charStrings)[glyph]; unsigned int fd = acc.fdSelect->get_fd (glyph); if (unlikely (fd >= acc.fdCount)) return false; cs_interpreter_t interp; interp.env.init (str, acc, fd); - flatten_param_t param = { flat_charstrings[i], drop_hints }; + flatten_param_t param = { flat_charstrings[i], plan->drop_hints }; if (unlikely (!interp.interpret (param))) return false; } @@ -313,8 +267,7 @@ struct subr_flattener_t } const ACC &acc; - const hb_vector_t &glyphs; - bool drop_hints; + const hb_subset_plan_t *plan; }; struct subr_closures_t @@ -463,7 +416,8 @@ struct parsed_cs_str_vec_t : hb_vector_t void init (unsigned int len_ = 0) { SUPER::init (); - resize (len_); + if (unlikely (!resize (len_))) + return; for (unsigned int i = 0; i < length; i++) (*this)[i].init (); } @@ -512,19 +466,19 @@ struct subr_subset_param_t template void set_current_str (ENV &env, bool calling) { - parsed_cs_str_t *parsed_str = get_parsed_str_for_context (env.context); - if (likely (parsed_str != nullptr)) + parsed_cs_str_t *parsed_str = get_parsed_str_for_context (env.context); + if (unlikely (!parsed_str)) { - /* If the called subroutine is parsed partially but not completely yet, - * it must be because we are calling it recursively. - * Handle it as an error. */ - if (unlikely (calling && !parsed_str->is_parsed () && (parsed_str->values.length > 0))) - env.set_error (); - else - current_parsed_str = parsed_str; + env.set_error (); + return; } - else + /* If the called subroutine is parsed partially but not completely yet, + * it must be because we are calling it recursively. + * Handle it as an error. */ + if (unlikely (calling && !parsed_str->is_parsed () && (parsed_str->values.length > 0))) env.set_error (); + else + current_parsed_str = parsed_str; } parsed_cs_str_t *current_parsed_str; @@ -537,39 +491,29 @@ struct subr_subset_param_t bool drop_hints; }; -struct subr_remap_t : remap_t +struct subr_remap_t : hb_inc_bimap_t { void create (hb_set_t *closure) { /* create a remapping of subroutine numbers from old to new. * no optimization based on usage counts. fonttools doesn't appear doing that either. */ - reset (closure->get_max () + 1); - for (hb_codepoint_t old_num = 0; old_num < length; old_num++) - { - if (hb_set_has (closure, old_num)) - add (old_num); - } - if (get_count () < 1240) + hb_codepoint_t old_num = HB_SET_VALUE_INVALID; + while (hb_set_next (closure, &old_num)) + add (old_num); + + if (get_population () < 1240) bias = 107; - else if (get_count () < 33900) + else if (get_population () < 33900) bias = 1131; else bias = 32768; } - hb_codepoint_t operator[] (unsigned int old_num) const - { - if (old_num >= length) - return CFF_UNDEF_CODE; - else - return remap_t::operator[] (old_num); - } - int biased_num (unsigned int old_num) const { - hb_codepoint_t new_num = (*this)[old_num]; + hb_codepoint_t new_num = get (old_num); return (int)new_num - bias; } @@ -577,23 +521,28 @@ struct subr_remap_t : remap_t int bias; }; -struct subr_remap_ts +struct subr_remaps_t { - subr_remap_ts () + subr_remaps_t () { global_remap.init (); local_remaps.init (); } - ~subr_remap_ts () { fini (); } + ~subr_remaps_t () { fini (); } void init (unsigned int fdCount) { - local_remaps.resize (fdCount); + if (unlikely (!local_remaps.resize (fdCount))) return; for (unsigned int i = 0; i < fdCount; i++) local_remaps[i].init (); } + bool in_error() + { + return local_remaps.in_error (); + } + void create (subr_closures_t& closures) { global_remap.create (closures.global_closure); @@ -611,10 +560,11 @@ struct subr_remap_ts hb_vector_t local_remaps; }; -template +template struct subr_subsetter_t { - subr_subsetter_t () + subr_subsetter_t (ACC &acc_, const hb_subset_plan_t *plan_) + : acc (acc_), plan (plan_) { parsed_charstrings.init (); parsed_global_subrs.init (); @@ -644,25 +594,36 @@ struct subr_subsetter_t * Assumption: a callsubr/callgsubr operator must immediately follow a (biased) subroutine number * within the same charstring/subroutine, e.g., not split across a charstring and a subroutine. */ - bool subset (ACC &acc, const hb_vector_t &glyphs, bool drop_hints) + bool subset (void) { closures.init (acc.fdCount); remaps.init (acc.fdCount); - parsed_charstrings.init (glyphs.length); + parsed_charstrings.init (plan->num_output_glyphs ()); parsed_global_subrs.init (acc.globalSubrs->count); - parsed_local_subrs.resize (acc.fdCount); + + if (unlikely (remaps.in_error() + || parsed_charstrings.in_error () + || parsed_global_subrs.in_error ())) { + return false; + } + + if (unlikely (!parsed_local_subrs.resize (acc.fdCount))) return false; + for (unsigned int i = 0; i < acc.fdCount; i++) { parsed_local_subrs[i].init (acc.privateDicts[i].localSubrs->count); + if (unlikely (parsed_local_subrs[i].in_error ())) return false; } if (unlikely (!closures.valid)) return false; /* phase 1 & 2 */ - for (unsigned int i = 0; i < glyphs.length; i++) + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) { - hb_codepoint_t glyph = glyphs[i]; + hb_codepoint_t glyph; + if (!plan->old_gid_for_new_gid (i, &glyph)) + continue; const byte_str_t str = (*acc.charStrings)[glyph]; unsigned int fd = acc.fdSelect->get_fd (glyph); if (unlikely (fd >= acc.fdCount)) @@ -675,28 +636,31 @@ struct subr_subsetter_t param.init (&parsed_charstrings[i], &parsed_global_subrs, &parsed_local_subrs[fd], closures.global_closure, closures.local_closures[fd], - drop_hints); + plan->drop_hints); if (unlikely (!interp.interpret (param))) return false; - /* finalize parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */ - SUBSETTER::finalize_parsed_str (interp.env, param, parsed_charstrings[i]); + /* complete parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */ + SUBSETTER::complete_parsed_str (interp.env, param, parsed_charstrings[i]); } - if (drop_hints) + if (plan->drop_hints) { /* mark hint ops and arguments for drop */ - for (unsigned int i = 0; i < glyphs.length; i++) + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) { - unsigned int fd = acc.fdSelect->get_fd (glyphs[i]); + hb_codepoint_t glyph; + if (!plan->old_gid_for_new_gid (i, &glyph)) + continue; + unsigned int fd = acc.fdSelect->get_fd (glyph); if (unlikely (fd >= acc.fdCount)) return false; subr_subset_param_t param; param.init (&parsed_charstrings[i], &parsed_global_subrs, &parsed_local_subrs[fd], closures.global_closure, closures.local_closures[fd], - drop_hints); + plan->drop_hints); drop_hints_param_t drop; if (drop_hints_in_str (parsed_charstrings[i], param, drop)) @@ -709,16 +673,19 @@ struct subr_subsetter_t /* after dropping hints recreate closures of actually used subrs */ closures.reset (); - for (unsigned int i = 0; i < glyphs.length; i++) + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) { - unsigned int fd = acc.fdSelect->get_fd (glyphs[i]); + hb_codepoint_t glyph; + if (!plan->old_gid_for_new_gid (i, &glyph)) + continue; + unsigned int fd = acc.fdSelect->get_fd (glyph); if (unlikely (fd >= acc.fdCount)) return false; subr_subset_param_t param; param.init (&parsed_charstrings[i], &parsed_global_subrs, &parsed_local_subrs[fd], closures.global_closure, closures.local_closures[fd], - drop_hints); + plan->drop_hints); collect_subr_refs_in_str (parsed_charstrings[i], param); } } @@ -728,13 +695,20 @@ struct subr_subsetter_t return true; } - bool encode_charstrings (ACC &acc, const hb_vector_t &glyphs, str_buff_vec_t &buffArray) const + bool encode_charstrings (str_buff_vec_t &buffArray) const { - if (unlikely (!buffArray.resize (glyphs.length))) + if (unlikely (!buffArray.resize (plan->num_output_glyphs ()))) return false; - for (unsigned int i = 0; i < glyphs.length; i++) + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) { - unsigned int fd = acc.fdSelect->get_fd (glyphs[i]); + hb_codepoint_t glyph; + if (!plan->old_gid_for_new_gid (i, &glyph)) + { + /* add an endchar only charstring for a missing glyph if CFF1 */ + if (endchar_op != OpCode_Invalid) buffArray[i].push (endchar_op); + continue; + } + unsigned int fd = acc.fdSelect->get_fd (glyph); if (unlikely (fd >= acc.fdCount)) return false; if (unlikely (!encode_str (parsed_charstrings[i], fd, buffArray[i]))) @@ -745,7 +719,7 @@ struct subr_subsetter_t bool encode_subrs (const parsed_cs_str_vec_t &subrs, const subr_remap_t& remap, unsigned int fd, str_buff_vec_t &buffArray) const { - unsigned int count = remap.get_count (); + unsigned int count = remap.get_population (); if (unlikely (!buffArray.resize (count))) return false; @@ -777,10 +751,12 @@ struct subr_subsetter_t drop_hints_param_t () : seen_moveto (false), ends_in_hint (false), + all_dropped (false), vsindex_dropped (false) {} bool seen_moveto; bool ends_in_hint; + bool all_dropped; bool vsindex_dropped; }; @@ -791,7 +767,7 @@ struct subr_subsetter_t drop.ends_in_hint = false; bool has_hint = drop_hints_in_str (subrs[subr_num], param, drop); - /* if this subr ends with a stem hint (i.e., not a number a potential argument for moveto), + /* if this subr ends with a stem hint (i.e., not a number; potential argument for moveto), * then this entire subroutine must be a hint. drop its call. */ if (drop.ends_in_hint) { @@ -801,6 +777,10 @@ struct subr_subsetter_t if (!str.at_end (pos)) drop.ends_in_hint = false; } + else if (drop.all_dropped) + { + str.values[pos].set_drop (); + } return has_hint; } @@ -819,7 +799,6 @@ struct subr_subsetter_t has_hint = drop_hints_in_subr (str, pos, *param.parsed_local_subrs, str.values[pos].subr_num, param, drop); - break; case OpCode_callgsubr: @@ -876,6 +855,23 @@ struct subr_subsetter_t } } + /* Raise all_dropped flag if all operators except return are dropped from a subr. + * It may happen even after seeing the first moveto if a subr contains + * only (usually one) hintmask operator, then calls to this subr can be dropped. + */ + drop.all_dropped = true; + for (unsigned int pos = 0; pos < str.values.length; pos++) + { + parsed_cs_op_t &csop = str.values[pos]; + if (csop.op == OpCode_return) + break; + if (!csop.for_drop ()) + { + drop.all_dropped = false; + break; + } + } + return seen_hint; } @@ -884,7 +880,7 @@ struct subr_subsetter_t hb_set_t *closure, const subr_subset_param_t ¶m) { - hb_set_add (closure, subr_num); + closure->add (subr_num); collect_subr_refs_in_str (subrs[subr_num], param); } @@ -954,13 +950,16 @@ struct subr_subsetter_t } protected: - subr_closures_t closures; + const ACC &acc; + const hb_subset_plan_t *plan; + + subr_closures_t closures; - parsed_cs_str_vec_t parsed_charstrings; - parsed_cs_str_vec_t parsed_global_subrs; + parsed_cs_str_vec_t parsed_charstrings; + parsed_cs_str_vec_t parsed_global_subrs; hb_vector_t parsed_local_subrs; - subr_remap_ts remaps; + subr_remaps_t remaps; private: typedef typename SUBRS::count_type subr_count_type; @@ -969,14 +968,14 @@ struct subr_subsetter_t } /* namespace CFF */ HB_INTERNAL bool -hb_plan_subset_cff_fdselect (const hb_vector_t &glyphs, +hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, unsigned int fdCount, const CFF::FDSelect &src, /* IN */ unsigned int &subset_fd_count /* OUT */, unsigned int &subset_fdselect_size /* OUT */, unsigned int &subset_fdselect_format /* OUT */, hb_vector_t &fdselect_ranges /* OUT */, - CFF::remap_t &fdmap /* OUT */); + hb_inc_bimap_t &fdmap /* OUT */); HB_INTERNAL bool hb_serialize_cff_fdselect (hb_serialize_context_t *c, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.cc index 7f35e5ecfef..650c3aeec9b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.cc @@ -24,9 +24,14 @@ * Adobe Author(s): Michiharu Ariza */ +#include "hb.hh" + +#ifndef HB_NO_SUBSET_CFF + #include "hb-open-type.hh" #include "hb-ot-cff1-table.hh" #include "hb-set.h" +#include "hb-bimap.hh" #include "hb-subset-cff1.hh" #include "hb-subset-plan.hh" #include "hb-subset-cff-common.hh" @@ -34,12 +39,12 @@ using namespace CFF; -struct remap_sid_t : remap_t +struct remap_sid_t : hb_inc_bimap_t { unsigned int add (unsigned int sid) { if ((sid != CFF_UNDEF_SID) && !is_std_std (sid)) - return offset_sid (remap_t::add (unoffset_sid (sid))); + return offset_sid (hb_inc_bimap_t::add (unoffset_sid (sid))); else return sid; } @@ -49,7 +54,7 @@ struct remap_sid_t : remap_t if (is_std_std (sid) || (sid == CFF_UNDEF_SID)) return sid; else - return offset_sid (remap_t::operator [] (unoffset_sid (sid))); + return offset_sid (get (unoffset_sid (sid))); } static const unsigned int num_std_strings = 391; @@ -59,29 +64,25 @@ struct remap_sid_t : remap_t static unsigned int unoffset_sid (unsigned int sid) { return sid - num_std_strings; } }; -struct cff1_sub_table_offsets_t : cff_sub_table_offsets_t +struct cff1_sub_table_info_t : cff_sub_table_info_t { - cff1_sub_table_offsets_t () - : cff_sub_table_offsets_t (), - nameIndexOffset (0), - encodingOffset (0) - { - stringIndexInfo.init (); - charsetInfo.init (); + cff1_sub_table_info_t () + : cff_sub_table_info_t (), + encoding_link (0), + charset_link (0) + { privateDictInfo.init (); } - unsigned int nameIndexOffset; - table_info_t stringIndexInfo; - unsigned int encodingOffset; - table_info_t charsetInfo; + objidx_t encoding_link; + objidx_t charset_link; table_info_t privateDictInfo; }; /* a copy of a parsed out cff1_top_dict_values_t augmented with additional operators */ struct cff1_top_dict_values_mod_t : cff1_top_dict_values_t { - void init (const cff1_top_dict_values_t *base_= &Null(cff1_top_dict_values_t)) + void init (const cff1_top_dict_values_t *base_= &Null (cff1_top_dict_values_t)) { SUPER::init (); base = base_; @@ -112,13 +113,13 @@ struct cff1_top_dict_values_mod_t : cff1_top_dict_values_t struct top_dict_modifiers_t { - top_dict_modifiers_t (const cff1_sub_table_offsets_t &offsets_, - const unsigned int (&nameSIDs_)[name_dict_values_t::ValCount]) - : offsets (offsets_), + top_dict_modifiers_t (const cff1_sub_table_info_t &info_, + const unsigned int (&nameSIDs_)[name_dict_values_t::ValCount]) + : info (info_), nameSIDs (nameSIDs_) {} - const cff1_sub_table_offsets_t &offsets; + const cff1_sub_table_info_t &info; const unsigned int (&nameSIDs)[name_dict_values_t::ValCount]; }; @@ -134,22 +135,20 @@ struct cff1_top_dict_op_serializer_t : cff_top_dict_op_serializer_tallocate_size (1); - if (unlikely (p == nullptr)) return_trace (false); - p->set (OpCode_Private); - } - break; + return_trace (UnsizedByteStr::serialize_int2 (c, mod.info.privateDictInfo.size) && + Dict::serialize_link4_op (c, op, mod.info.privateDictInfo.link, whence_t::Absolute)); case OpCode_version: case OpCode_Notice: @@ -160,7 +159,7 @@ struct cff1_top_dict_op_serializer_t : cff_top_dict_op_serializer_t::serialize (c, opstr, mod.offsets)); + return_trace (cff_top_dict_op_serializer_t::serialize (c, opstr, mod.info)); } return_trace (true); } - unsigned int calculate_serialized_size (const cff1_top_dict_val_t &opstr) const - { - op_code_t op = opstr.op; - switch (op) - { - case OpCode_charset: - case OpCode_Encoding: - return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op); - - case OpCode_Private: - return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private); - - case OpCode_version: - case OpCode_Notice: - case OpCode_Copyright: - case OpCode_FullName: - case OpCode_FamilyName: - case OpCode_Weight: - case OpCode_PostScript: - case OpCode_BaseFontName: - case OpCode_FontName: - return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (op); - - case OpCode_ROS: - return ((OpCode_Size (OpCode_shortint) + 2) * 2) + (opstr.str.length - opstr.last_arg_offset)/* supplement + op */; - - default: - return cff_top_dict_op_serializer_t::calculate_serialized_size (opstr); - } - } -}; - -struct font_dict_values_mod_t -{ - void init (const cff1_font_dict_values_t *base_, - unsigned int fontName_, - const table_info_t &privateDictInfo_) - { - base = base_; - fontName = fontName_; - privateDictInfo = privateDictInfo_; - } - - unsigned get_count () const { return base->get_count (); } - - const op_str_t &operator [] (unsigned int i) const { return (*base)[i]; } - - const cff1_font_dict_values_t *base; - table_info_t privateDictInfo; - unsigned int fontName; }; struct cff1_font_dict_op_serializer_t : cff_font_dict_op_serializer_t { bool serialize (hb_serialize_context_t *c, const op_str_t &opstr, - const font_dict_values_mod_t &mod) const + const cff1_font_dict_values_mod_t &mod) const { TRACE_SERIALIZE (this); if (opstr.op == OpCode_FontName) - return_trace (FontDict::serialize_uint2_op (c, opstr.op, mod.fontName)); + return_trace (FontDict::serialize_int2_op (c, opstr.op, mod.fontName)); else return_trace (SUPER::serialize (c, opstr, mod.privateDictInfo)); } - unsigned int calculate_serialized_size (const op_str_t &opstr) const - { - if (opstr.op == OpCode_FontName) - return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_FontName); - else - return SUPER::calculate_serialized_size (opstr); - } - private: typedef cff_font_dict_op_serializer_t SUPER; }; @@ -326,7 +268,7 @@ struct cff1_cs_opset_flatten_t : cff1_cs_opset_t { /* replace the first glyph ID in the "glyph" field each range with a nLeft value */ - bool finalize (unsigned int last_glyph) + bool complete (unsigned int last_glyph) { bool two_byte = false; for (unsigned int i = (*this).length; i > 0; i--) @@ -351,7 +293,7 @@ struct cff1_cs_opset_subr_subset_t : cff1_cs_opset_tadd_op (op, env.str_ref); param.current_parsed_str->set_parsed (); - env.returnFromSubr (); + env.return_from_subr (); param.set_current_str (env, false); break; @@ -382,9 +324,9 @@ struct cff1_cs_opset_subr_subset_t : cff1_cs_opset_tadd_call_op (op, str_ref, env.context.subr_num); - hb_set_add (closure, env.context.subr_num); + closure->add (env.context.subr_num); param.set_current_str (env, true); } @@ -392,9 +334,12 @@ struct cff1_cs_opset_subr_subset_t : cff1_cs_opset_t SUPER; }; -struct cff1_subr_subsetter_t : subr_subsetter_t +struct cff1_subr_subsetter_t : subr_subsetter_t { - static void finalize_parsed_str (cff1_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring) + cff1_subr_subsetter_t (const OT::cff1::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_) + : subr_subsetter_t (acc_, plan_) {} + + static void complete_parsed_str (cff1_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring) { /* insert width at the beginning of the charstring as necessary */ if (env.has_width) @@ -406,8 +351,8 @@ struct cff1_subr_subsetter_t : subr_subsetter_tset_parsed (); for (unsigned int i = 0; i < env.callStack.get_count (); i++) { - parsed_cs_str_t *parsed_str = param.get_parsed_str_for_context (env.callStack[i]); - if (likely (parsed_str != nullptr)) + parsed_cs_str_t *parsed_str = param.get_parsed_str_for_context (env.callStack[i]); + if (likely (parsed_str)) parsed_str->set_parsed (); else env.set_error (); @@ -417,16 +362,13 @@ struct cff1_subr_subsetter_t : subr_subsetter_t supp_codes; - subset_enc_code_ranges.resize (0); + if (unlikely (!subset_enc_code_ranges.resize (0))) + { + plan->check_success (false); + return; + } + supp_size = 0; supp_codes.init (); - subset_enc_num_codes = plan->glyphs.length - 1; + subset_enc_num_codes = plan->num_output_glyphs () - 1; unsigned int glyph; - for (glyph = 1; glyph < plan->glyphs.length; glyph++) + for (glyph = 1; glyph < plan->num_output_glyphs (); glyph++) { - hb_codepoint_t orig_glyph = plan->glyphs[glyph]; - code = acc.glyph_to_code (orig_glyph); + hb_codepoint_t old_glyph; + if (!plan->old_gid_for_new_gid (glyph, &old_glyph)) + { + /* Retain the code for the old missing glyph ID */ + old_glyph = glyph; + } + code = acc.glyph_to_code (old_glyph); if (code == CFF_UNDEF_CODE) { subset_enc_num_codes = glyph - 1; break; } - if (code != last_code + 1) + if ((last_code == CFF_UNDEF_CODE) || (code != last_code + 1)) { code_pair_t pair = { code, glyph }; subset_enc_code_ranges.push (pair); } last_code = code; - if (encoding != &Null(Encoding)) + if (encoding != &Null (Encoding)) { - hb_codepoint_t sid = acc.glyph_to_sid (orig_glyph); + hb_codepoint_t sid = acc.glyph_to_sid (old_glyph); encoding->get_supplement_codes (sid, supp_codes); for (unsigned int i = 0; i < supp_codes.length; i++) { @@ -502,7 +453,7 @@ struct cff_subset_plan { } supp_codes.fini (); - subset_enc_code_ranges.finalize (glyph); + subset_enc_code_ranges.complete (glyph); assert (subset_enc_num_codes <= 0xFF); size0 = Encoding0::min_size + HBUINT8::static_size * subset_enc_num_codes; @@ -512,29 +463,34 @@ struct cff_subset_plan { subset_enc_format = 0; else subset_enc_format = 1; - - return Encoding::calculate_serialized_size ( - subset_enc_format, - subset_enc_format? subset_enc_code_ranges.length: subset_enc_num_codes, - subset_enc_supp_codes.length); } - unsigned int plan_subset_charset (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan) + void plan_subset_charset (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan) { unsigned int size0, size_ranges; hb_codepoint_t sid, last_sid = CFF_UNDEF_CODE; - subset_charset_ranges.resize (0); + if (unlikely (!subset_charset_ranges.resize (0))) + { + plan->check_success (false); + return; + } + unsigned int glyph; - for (glyph = 1; glyph < plan->glyphs.length; glyph++) + for (glyph = 1; glyph < plan->num_output_glyphs (); glyph++) { - hb_codepoint_t orig_glyph = plan->glyphs[glyph]; - sid = acc.glyph_to_sid (orig_glyph); + hb_codepoint_t old_glyph; + if (!plan->old_gid_for_new_gid (glyph, &old_glyph)) + { + /* Retain the SID for the old missing glyph ID */ + old_glyph = glyph; + } + sid = acc.glyph_to_sid (old_glyph); if (!acc.is_CID ()) sid = sidmap.add (sid); - if (sid != last_sid + 1) + if ((last_sid == CFF_UNDEF_CODE) || (sid != last_sid + 1)) { code_pair_t pair = { sid, glyph }; subset_charset_ranges.push (pair); @@ -542,9 +498,9 @@ struct cff_subset_plan { last_sid = sid; } - bool two_byte = subset_charset_ranges.finalize (glyph); + bool two_byte = subset_charset_ranges.complete (glyph); - size0 = Charset0::min_size + HBUINT16::static_size * (plan->glyphs.length - 1); + size0 = Charset0::min_size + HBUINT16::static_size * (plan->num_output_glyphs () - 1); if (!two_byte) size_ranges = Charset1::min_size + Charset1_Range::static_size * subset_charset_ranges.length; else @@ -556,16 +512,11 @@ struct cff_subset_plan { subset_charset_format = 1; else subset_charset_format = 2; - - return Charset::calculate_serialized_size ( - subset_charset_format, - subset_charset_format? subset_charset_ranges.length: plan->glyphs.length); } bool collect_sids_in_dicts (const OT::cff1::accelerator_subset_t &acc) { - if (unlikely (!sidmap.reset (acc.stringIndex->count))) - return false; + sidmap.reset (); for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++) { @@ -577,31 +528,33 @@ struct cff_subset_plan { } } - if (acc.fdArray != &Null(CFF1FDArray)) + if (acc.fdArray != &Null (CFF1FDArray)) for (unsigned int i = 0; i < orig_fdcount; i++) - if (fdmap.includes (i)) + if (fdmap.has (i)) (void)sidmap.add (acc.fontDicts[i].fontName); return true; } bool create (const OT::cff1::accelerator_subset_t &acc, - hb_subset_plan_t *plan) + hb_subset_plan_t *plan) { - /* make sure notdef is first */ - if ((plan->glyphs.length == 0) || (plan->glyphs[0] != 0)) return false; + /* make sure notdef is first */ + hb_codepoint_t old_glyph; + if (!plan->old_gid_for_new_gid (0, &old_glyph) || (old_glyph != 0)) return false; - final_size = 0; - num_glyphs = plan->glyphs.length; + num_glyphs = plan->num_output_glyphs (); orig_fdcount = acc.fdCount; drop_hints = plan->drop_hints; desubroutinize = plan->desubroutinize; /* check whether the subset renumbers any glyph IDs */ gid_renum = false; - for (unsigned int glyph = 0; glyph < plan->glyphs.length; glyph++) + for (hb_codepoint_t new_glyph = 0; new_glyph < plan->num_output_glyphs (); new_glyph++) { - if (plan->glyphs[glyph] != glyph) { + if (!plan->old_gid_for_new_gid(new_glyph, &old_glyph)) + continue; + if (new_glyph != old_glyph) { gid_renum = true; break; } @@ -610,13 +563,6 @@ struct cff_subset_plan { subset_charset = gid_renum || !acc.is_predef_charset (); subset_encoding = !acc.is_CID() && !acc.is_predef_encoding (); - /* CFF header */ - final_size += OT::cff1::static_size; - - /* Name INDEX */ - offsets.nameIndexOffset = final_size; - final_size += acc.nameIndex->get_size (); - /* top dict INDEX */ { /* Add encoding/charset to a (copy of) top dict as necessary */ @@ -630,25 +576,16 @@ struct cff_subset_plan { if (need_to_add_set) topdict_mod.add_op (OpCode_charset); } - offsets.topDictInfo.offset = final_size; - cff1_top_dict_op_serializer_t topSzr; - unsigned int topDictSize = TopDict::calculate_serialized_size (topdict_mod, topSzr); - offsets.topDictInfo.offSize = calcOffSize(topDictSize); - if (unlikely (offsets.topDictInfo.offSize > 4)) - return false; - final_size += CFF1IndexOf::calculate_serialized_size - (offsets.topDictInfo.offSize, - &topdict_mod, 1, topdict_sizes, topSzr); } /* Determine re-mapping of font index as fdmap among other info */ - if (acc.fdSelect != &Null(CFF1FDSelect)) + if (acc.fdSelect != &Null (CFF1FDSelect)) { - if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs, + if (unlikely (!hb_plan_subset_cff_fdselect (plan, orig_fdcount, *acc.fdSelect, subset_fdcount, - offsets.FDSelectInfo.size, + info.fd_select.size, subset_fdselect_format, subset_fdselect_ranges, fdmap))) @@ -662,170 +599,79 @@ struct cff_subset_plan { /* SIDs for name strings in dicts are added before glyph names so they fit in 16-bit int range */ if (unlikely (!collect_sids_in_dicts (acc))) return false; - if (unlikely (sidmap.get_count () > 0x8000)) /* assumption: a dict won't reference that many strings */ + if (unlikely (sidmap.get_population () > 0x8000)) /* assumption: a dict won't reference that many strings */ return false; - if (subset_charset) - offsets.charsetInfo.size = plan_subset_charset (acc, plan); - topdict_mod.reassignSIDs (sidmap); - } + if (subset_charset) plan_subset_charset (acc, plan); - /* String INDEX */ - { - offsets.stringIndexInfo.offset = final_size; - offsets.stringIndexInfo.size = acc.stringIndex->calculate_serialized_size (offsets.stringIndexInfo.offSize, sidmap); - final_size += offsets.stringIndexInfo.size; + topdict_mod.reassignSIDs (sidmap); } if (desubroutinize) { /* Flatten global & local subrs */ - subr_flattener_t - flattener(acc, plan->glyphs, plan->drop_hints); + subr_flattener_t + flattener(acc, plan); if (!flattener.flatten (subset_charstrings)) return false; - - /* no global/local subroutines */ - offsets.globalSubrsInfo.size = CFF1Subrs::calculate_serialized_size (1, 0, 0); } else { + cff1_subr_subsetter_t subr_subsetter (acc, plan); + /* Subset subrs: collect used subroutines, leaving all unused ones behind */ - if (!subr_subsetter.subset (acc, plan->glyphs, plan->drop_hints)) + if (!subr_subsetter.subset ()) return false; /* encode charstrings, global subrs, local subrs with new subroutine numbers */ - if (!subr_subsetter.encode_charstrings (acc, plan->glyphs, subset_charstrings)) + if (!subr_subsetter.encode_charstrings (subset_charstrings)) return false; if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs)) return false; - /* global subrs */ - unsigned int dataSize = subset_globalsubrs.total_size (); - offsets.globalSubrsInfo.offSize = calcOffSize (dataSize); - if (unlikely (offsets.globalSubrsInfo.offSize > 4)) - return false; - offsets.globalSubrsInfo.size = CFF1Subrs::calculate_serialized_size (offsets.globalSubrsInfo.offSize, subset_globalsubrs.length, dataSize); - /* local subrs */ - if (!offsets.localSubrsInfos.resize (orig_fdcount)) - return false; if (!subset_localsubrs.resize (orig_fdcount)) return false; for (unsigned int fd = 0; fd < orig_fdcount; fd++) { subset_localsubrs[fd].init (); - offsets.localSubrsInfos[fd].init (); - if (fdmap.includes (fd)) + if (fdmap.has (fd)) { if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) return false; - - unsigned int dataSize = subset_localsubrs[fd].total_size (); - if (dataSize > 0) - { - offsets.localSubrsInfos[fd].offset = final_size; - offsets.localSubrsInfos[fd].offSize = calcOffSize (dataSize); - if (unlikely (offsets.localSubrsInfos[fd].offSize > 4)) - return false; - offsets.localSubrsInfos[fd].size = CFF1Subrs::calculate_serialized_size (offsets.localSubrsInfos[fd].offSize, subset_localsubrs[fd].length, dataSize); - } } } } - /* global subrs */ - offsets.globalSubrsInfo.offset = final_size; - final_size += offsets.globalSubrsInfo.size; - /* Encoding */ - if (!subset_encoding) - offsets.encodingOffset = acc.topDict.EncodingOffset; - else - { - offsets.encodingOffset = final_size; - final_size += plan_subset_encoding (acc, plan); - } - - /* Charset */ - if (!subset_charset && acc.is_predef_charset ()) - offsets.charsetInfo.offset = acc.topDict.CharsetOffset; - else - offsets.charsetInfo.offset = final_size; - final_size += offsets.charsetInfo.size; - - /* FDSelect */ - if (acc.fdSelect != &Null(CFF1FDSelect)) - { - offsets.FDSelectInfo.offset = final_size; - final_size += offsets.FDSelectInfo.size; - } - - /* FDArray (FDIndex) */ - if (acc.fdArray != &Null(CFF1FDArray)) { - offsets.FDArrayInfo.offset = final_size; - cff1_font_dict_op_serializer_t fontSzr; - unsigned int dictsSize = 0; - for (unsigned int i = 0; i < acc.fontDicts.length; i++) - if (fdmap.includes (i)) - dictsSize += FontDict::calculate_serialized_size (acc.fontDicts[i], fontSzr); - - offsets.FDArrayInfo.offSize = calcOffSize (dictsSize); - if (unlikely (offsets.FDArrayInfo.offSize > 4)) - return false; - final_size += CFF1Index::calculate_serialized_size (offsets.FDArrayInfo.offSize, subset_fdcount, dictsSize); - } - - /* CharStrings */ - { - offsets.charStringsInfo.offset = final_size; - unsigned int dataSize = subset_charstrings.total_size (); - offsets.charStringsInfo.offSize = calcOffSize (dataSize); - if (unlikely (offsets.charStringsInfo.offSize > 4)) - return false; - final_size += CFF1CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.length, dataSize); - } + if (subset_encoding) + plan_subset_encoding (acc, plan); /* private dicts & local subrs */ - offsets.privateDictInfo.offset = final_size; - for (unsigned int i = 0; i < orig_fdcount; i++) + if (!acc.is_CID ()) + fontdicts_mod.push (cff1_font_dict_values_mod_t ()); + else { - if (fdmap.includes (i)) - { - bool has_localsubrs = offsets.localSubrsInfos[i].size > 0; - cff_private_dict_op_serializer_t privSzr (desubroutinize, plan->drop_hints); - unsigned int priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr, has_localsubrs); - table_info_t privInfo = { final_size, priv_size, 0 }; - font_dict_values_mod_t fontdict_mod; - if (!acc.is_CID ()) - fontdict_mod.init ( &Null(cff1_font_dict_values_t), CFF_UNDEF_SID, privInfo ); - else - fontdict_mod.init ( &acc.fontDicts[i], sidmap[acc.fontDicts[i].fontName], privInfo ); - fontdicts_mod.push (fontdict_mod); - final_size += privInfo.size; - - if (!plan->desubroutinize && has_localsubrs) + + hb_iter (acc.fontDicts) + | hb_filter ([&] (const cff1_font_dict_values_t &_) + { return fdmap.has (&_ - &acc.fontDicts[0]); } ) + | hb_map ([&] (const cff1_font_dict_values_t &_) { - offsets.localSubrsInfos[i].offset = final_size; - final_size += offsets.localSubrsInfos[i].size; - } - } + cff1_font_dict_values_mod_t mod; + mod.init (&_, sidmap[_.fontName]); + return mod; + }) + | hb_sink (fontdicts_mod) + ; } - if (!acc.is_CID ()) - offsets.privateDictInfo = fontdicts_mod[0].privateDictInfo; - - return ((subset_charstrings.length == plan->glyphs.length) + return ((subset_charstrings.length == plan->num_output_glyphs ()) && (fontdicts_mod.length == subset_fdcount)); } - unsigned int get_final_size () const { return final_size; } - - unsigned int final_size; - hb_vector_t topdict_sizes; cff1_top_dict_values_mod_t topdict_mod; - cff1_sub_table_offsets_t offsets; + cff1_sub_table_info_t info; unsigned int num_glyphs; unsigned int orig_fdcount; @@ -835,12 +681,12 @@ struct cff_subset_plan { /* font dict index remap table from fullset FDArray to subset FDArray. * set to CFF_UNDEF_CODE if excluded from subset */ - remap_t fdmap; + hb_inc_bimap_t fdmap; str_buff_vec_t subset_charstrings; str_buff_vec_t subset_globalsubrs; hb_vector_t subset_localsubrs; - hb_vector_t fontdicts_mod; + hb_vector_t fontdicts_mod; bool drop_hints; @@ -859,94 +705,97 @@ struct cff_subset_plan { unsigned int topDictModSIDs[name_dict_values_t::ValCount]; bool desubroutinize; - cff1_subr_subsetter_t subr_subsetter; }; -static inline bool _write_cff1 (const cff_subset_plan &plan, - const OT::cff1::accelerator_subset_t &acc, - const hb_vector_t& glyphs, - unsigned int dest_sz, - void *dest) +static bool _serialize_cff1 (hb_serialize_context_t *c, + cff_subset_plan &plan, + const OT::cff1::accelerator_subset_t &acc, + unsigned int num_glyphs) { - hb_serialize_context_t c (dest, dest_sz); - - OT::cff1 *cff = c.start_serialize (); - if (unlikely (!c.extend_min (*cff))) - return false; - - /* header */ - cff->version.major.set (0x01); - cff->version.minor.set (0x00); - cff->nameIndex.set (cff->min_size); - cff->offSize.set (4); /* unused? */ - - /* name INDEX */ + /* private dicts & local subrs */ + for (int i = (int)acc.privateDicts.length; --i >= 0 ;) { - assert (cff->nameIndex == (unsigned) (c.head - c.start)); - CFF1NameIndex *dest = c.start_embed (); - if (unlikely (dest == nullptr)) return false; - if (unlikely (!dest->serialize (&c, *acc.nameIndex))) + if (plan.fdmap.has (i)) { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF name INDEX"); - return false; - } - } + objidx_t subrs_link = 0; + if (plan.subset_localsubrs[i].length > 0) + { + CFF1Subrs *dest = c->start_embed (); + if (unlikely (!dest)) return false; + c->push (); + if (likely (dest && dest->serialize (c, plan.subset_localsubrs[i]))) + subrs_link = c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } - /* top dict INDEX */ - { - assert (plan.offsets.topDictInfo.offset == (unsigned) (c.head - c.start)); - CFF1IndexOf *dest = c.start_embed< CFF1IndexOf > (); - if (dest == nullptr) return false; - cff1_top_dict_op_serializer_t topSzr; - top_dict_modifiers_t modifier (plan.offsets, plan.topDictModSIDs); - if (unlikely (!dest->serialize (&c, plan.offsets.topDictInfo.offSize, - &plan.topdict_mod, 1, - plan.topdict_sizes, topSzr, modifier))) - { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF top dict"); - return false; + PrivateDict *pd = c->start_embed (); + if (unlikely (!pd)) return false; + c->push (); + cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints); + /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */ + if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link))) + { + unsigned fd = plan.fdmap[i]; + plan.fontdicts_mod[fd].privateDictInfo.size = c->length (); + plan.fontdicts_mod[fd].privateDictInfo.link = c->pop_pack (); + } + else + { + c->pop_discard (); + return false; + } } } - /* String INDEX */ + if (!acc.is_CID ()) + plan.info.privateDictInfo = plan.fontdicts_mod[0].privateDictInfo; + + /* CharStrings */ { - assert (plan.offsets.stringIndexInfo.offset == (unsigned) (c.head - c.start)); - CFF1StringIndex *dest = c.start_embed (); - if (unlikely (dest == nullptr)) return false; - if (unlikely (!dest->serialize (&c, *acc.stringIndex, plan.offsets.stringIndexInfo.offSize, plan.sidmap))) + CFF1CharStrings *cs = c->start_embed (); + if (unlikely (!cs)) return false; + c->push (); + if (likely (cs->serialize (c, plan.subset_charstrings))) + plan.info.char_strings_link = c->pop_pack (); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF string INDEX"); + c->pop_discard (); return false; } } - /* global subrs */ + /* FDArray (FD Index) */ + if (acc.fdArray != &Null (CFF1FDArray)) { - assert (plan.offsets.globalSubrsInfo.offset != 0); - assert (plan.offsets.globalSubrsInfo.offset == (unsigned) (c.head - c.start)); - - CFF1Subrs *dest = c.start_embed (); - if (unlikely (dest == nullptr)) return false; - if (unlikely (!dest->serialize (&c, plan.offsets.globalSubrsInfo.offSize, plan.subset_globalsubrs))) + CFF1FDArray *fda = c->start_embed (); + if (unlikely (!fda)) return false; + c->push (); + cff1_font_dict_op_serializer_t fontSzr; + auto it = + hb_zip (+ hb_iter (plan.fontdicts_mod), + hb_iter (plan.fontdicts_mod)); + if (likely (fda->serialize (c, it, fontSzr))) + plan.info.fd_array_link = c->pop_pack (false); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize global subroutines"); + c->pop_discard (); return false; } } - /* Encoding */ - if (plan.subset_encoding) + /* FDSelect */ + if (acc.fdSelect != &Null (CFF1FDSelect)) { - assert (plan.offsets.encodingOffset == (unsigned) (c.head - c.start)); - Encoding *dest = c.start_embed (); - if (unlikely (dest == nullptr)) return false; - if (unlikely (!dest->serialize (&c, - plan.subset_enc_format, - plan.subset_enc_num_codes, - plan.subset_enc_code_ranges, - plan.subset_enc_supp_codes))) + c->push (); + if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *acc.fdSelect, acc.fdCount, + plan.subset_fdselect_format, plan.info.fd_select.size, + plan.subset_fdselect_ranges))) + plan.info.fd_select.link = c->pop_pack (); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize Encoding"); + c->pop_discard (); return false; } } @@ -954,129 +803,120 @@ static inline bool _write_cff1 (const cff_subset_plan &plan, /* Charset */ if (plan.subset_charset) { - assert (plan.offsets.charsetInfo.offset == (unsigned) (c.head - c.start)); - Charset *dest = c.start_embed (); - if (unlikely (dest == nullptr)) return false; - if (unlikely (!dest->serialize (&c, - plan.subset_charset_format, - plan.num_glyphs, - plan.subset_charset_ranges))) + Charset *dest = c->start_embed (); + if (unlikely (!dest)) return false; + c->push (); + if (likely (dest->serialize (c, + plan.subset_charset_format, + plan.num_glyphs, + plan.subset_charset_ranges))) + plan.info.charset_link = c->pop_pack (); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize Charset"); + c->pop_discard (); return false; } } - /* FDSelect */ - if (acc.fdSelect != &Null(CFF1FDSelect)) + /* Encoding */ + if (plan.subset_encoding) { - assert (plan.offsets.FDSelectInfo.offset == (unsigned) (c.head - c.start)); - - if (unlikely (!hb_serialize_cff_fdselect (&c, glyphs.length, *acc.fdSelect, acc.fdCount, - plan.subset_fdselect_format, plan.offsets.FDSelectInfo.size, - plan.subset_fdselect_ranges))) + Encoding *dest = c->start_embed (); + if (unlikely (!dest)) return false; + c->push (); + if (likely (dest->serialize (c, + plan.subset_enc_format, + plan.subset_enc_num_codes, + plan.subset_enc_code_ranges, + plan.subset_enc_supp_codes))) + plan.info.encoding_link = c->pop_pack (); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF subset FDSelect"); + c->pop_discard (); return false; } } - /* FDArray (FD Index) */ - if (acc.fdArray != &Null(CFF1FDArray)) + /* global subrs */ { - assert (plan.offsets.FDArrayInfo.offset == (unsigned) (c.head - c.start)); - CFF1FDArray *fda = c.start_embed (); - if (unlikely (fda == nullptr)) return false; - cff1_font_dict_op_serializer_t fontSzr; - if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayInfo.offSize, - plan.fontdicts_mod, - fontSzr))) + c->push (); + CFF1Subrs *dest = c->start_embed (); + if (unlikely (!dest)) return false; + if (likely (dest->serialize (c, plan.subset_globalsubrs))) + c->pop_pack (); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF FDArray"); + c->pop_discard (); return false; } } - /* CharStrings */ + /* String INDEX */ { - assert (plan.offsets.charStringsInfo.offset == (unsigned) (c.head - c.start)); - CFF1CharStrings *cs = c.start_embed (); - if (unlikely (cs == nullptr)) return false; - if (unlikely (!cs->serialize (&c, plan.offsets.charStringsInfo.offSize, plan.subset_charstrings))) + CFF1StringIndex *dest = c->start_embed (); + if (unlikely (!dest)) return false; + c->push (); + if (likely (dest->serialize (c, *acc.stringIndex, plan.sidmap))) + c->pop_pack (); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF CharStrings"); + c->pop_discard (); return false; } } - /* private dicts & local subrs */ - assert (plan.offsets.privateDictInfo.offset == (unsigned) (c.head - c.start)); - for (unsigned int i = 0; i < acc.privateDicts.length; i++) + OT::cff1 *cff = c->allocate_min (); + if (unlikely (!cff)) + return false; + + /* header */ + cff->version.major = 0x01; + cff->version.minor = 0x00; + cff->nameIndex = cff->min_size; + cff->offSize = 4; /* unused? */ + + /* name INDEX */ + if (unlikely (!(*acc.nameIndex).copy (c))) return false; + + /* top dict INDEX */ { - if (plan.fdmap.includes (i)) + /* serialize singleton TopDict */ + TopDict *top = c->start_embed (); + if (!top) return false; + c->push (); + cff1_top_dict_op_serializer_t topSzr; + unsigned top_size = 0; + top_dict_modifiers_t modifier (plan.info, plan.topDictModSIDs); + if (likely (top->serialize (c, plan.topdict_mod, topSzr, modifier))) { - PrivateDict *pd = c.start_embed (); - if (unlikely (pd == nullptr)) return false; - unsigned int priv_size = plan.fontdicts_mod[plan.fdmap[i]].privateDictInfo.size; - bool result; - cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints); - /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */ - unsigned int subroffset = (plan.offsets.localSubrsInfos[i].size > 0)? priv_size: 0; - result = pd->serialize (&c, acc.privateDicts[i], privSzr, subroffset); - if (unlikely (!result)) - { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i); - return false; - } - if (plan.offsets.localSubrsInfos[i].size > 0) - { - CFF1Subrs *dest = c.start_embed (); - if (unlikely (dest == nullptr)) return false; - if (unlikely (!dest->serialize (&c, plan.offsets.localSubrsInfos[i].offSize, plan.subset_localsubrs[i]))) - { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize local subroutines"); - return false; - } - } + top_size = c->length (); + c->pop_pack (false); } + else + { + c->pop_discard (); + return false; + } + /* serialize INDEX header for above */ + CFF1Index *dest = c->start_embed (); + if (!dest) return false; + return dest->serialize_header (c, hb_iter (hb_array_t (&top_size, 1))); } - - assert (c.head == c.end); - c.end_serialize (); - - return true; } static bool _hb_subset_cff1 (const OT::cff1::accelerator_subset_t &acc, - const char *data, - hb_subset_plan_t *plan, - hb_blob_t **prime /* OUT */) + hb_subset_context_t *c) { cff_subset_plan cff_plan; - if (unlikely (!cff_plan.create (acc, plan))) + if (unlikely (!cff_plan.create (acc, c->plan))) { DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff subsetting plan."); return false; } - unsigned int cff_prime_size = cff_plan.get_final_size (); - char *cff_prime_data = (char *) calloc (1, cff_prime_size); - - if (unlikely (!_write_cff1 (cff_plan, acc, plan->glyphs, - cff_prime_size, cff_prime_data))) { - DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff."); - free (cff_prime_data); - return false; - } - - *prime = hb_blob_create (cff_prime_data, - cff_prime_size, - HB_MEMORY_MODE_READONLY, - cff_prime_data, - free); - return true; + return _serialize_cff1 (c->serializer, cff_plan, acc, c->plan->num_output_glyphs ()); } /** @@ -1086,18 +926,15 @@ _hb_subset_cff1 (const OT::cff1::accelerator_subset_t &acc, * Return value: subsetted cff table. **/ bool -hb_subset_cff1 (hb_subset_plan_t *plan, - hb_blob_t **prime /* OUT */) +hb_subset_cff1 (hb_subset_context_t *c) { - hb_blob_t *cff_blob = hb_sanitize_context_t().reference_table (plan->source); - const char *data = hb_blob_get_data(cff_blob, nullptr); - OT::cff1::accelerator_subset_t acc; - acc.init(plan->source); - bool result = likely (acc.is_valid ()) && - _hb_subset_cff1 (acc, data, plan, prime); - hb_blob_destroy (cff_blob); + acc.init (c->plan->source); + bool result = likely (acc.is_valid ()) && _hb_subset_cff1 (acc, c); acc.fini (); return result; } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.hh index 33a66380a39..aaf5def1ed5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.hh @@ -32,7 +32,6 @@ #include "hb-subset-plan.hh" HB_INTERNAL bool -hb_subset_cff1 (hb_subset_plan_t *plan, - hb_blob_t **cff_prime /* OUT */); +hb_subset_cff1 (hb_subset_context_t *c); #endif /* HB_SUBSET_CFF1_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.cc index ec69ed64894..6f1b4a7f6d9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.cc @@ -24,6 +24,10 @@ * Adobe Author(s): Michiharu Ariza */ +#include "hb.hh" + +#ifndef HB_NO_SUBSET_CFF + #include "hb-open-type.hh" #include "hb-ot-cff2-table.hh" #include "hb-set.h" @@ -34,43 +38,31 @@ using namespace CFF; -struct cff2_sub_table_offsets_t : cff_sub_table_offsets_t +struct cff2_sub_table_info_t : cff_sub_table_info_t { - cff2_sub_table_offsets_t () - : cff_sub_table_offsets_t (), - varStoreOffset (0) + cff2_sub_table_info_t () + : cff_sub_table_info_t (), + var_store_link (0) {} - unsigned int varStoreOffset; + objidx_t var_store_link; }; struct cff2_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<> { bool serialize (hb_serialize_context_t *c, const op_str_t &opstr, - const cff2_sub_table_offsets_t &offsets) const + const cff2_sub_table_info_t &info) const { TRACE_SERIALIZE (this); switch (opstr.op) { case OpCode_vstore: - return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.varStoreOffset)); + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.var_store_link)); default: - return_trace (cff_top_dict_op_serializer_t<>::serialize (c, opstr, offsets)); - } - } - - unsigned int calculate_serialized_size (const op_str_t &opstr) const - { - switch (opstr.op) - { - case OpCode_vstore: - return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op); - - default: - return cff_top_dict_op_serializer_t<>::calculate_serialized_size (opstr); + return_trace (cff_top_dict_op_serializer_t<>::serialize (c, opstr, info)); } } }; @@ -183,7 +175,7 @@ struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_tset_parsed (); - env.returnFromSubr (); + env.return_from_subr (); param.set_current_str (env, false); break; @@ -213,9 +205,9 @@ struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_tadd_call_op (op, str_ref, env.context.subr_num); - hb_set_add (closure, env.context.subr_num); + closure->add (env.context.subr_num); param.set_current_str (env, true); } @@ -225,7 +217,10 @@ struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t { - static void finalize_parsed_str (cff2_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring) + cff2_subr_subsetter_t (const OT::cff2::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_) + : subr_subsetter_t (acc_, plan_) {} + + static void complete_parsed_str (cff2_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring) { /* vsindex is inserted at the beginning of the charstring as necessary */ if (env.seen_vsindex ()) @@ -239,9 +234,9 @@ struct cff2_subr_subsetter_t : subr_subsetter_tcount; drop_hints = plan->drop_hints; desubroutinize = plan->desubroutinize; - /* CFF2 header */ - final_size += OT::cff2::static_size; - - /* top dict */ - { - cff2_top_dict_op_serializer_t topSzr; - offsets.topDictInfo.size = TopDict::calculate_serialized_size (acc.topDict, topSzr); - final_size += offsets.topDictInfo.size; - } - if (desubroutinize) { /* Flatten global & local subrs */ subr_flattener_t - flattener(acc, plan->glyphs, plan->drop_hints); + flattener(acc, plan); if (!flattener.flatten (subset_charstrings)) return false; - - /* no global/local subroutines */ - offsets.globalSubrsInfo.size = CFF2Subrs::calculate_serialized_size (1, 0, 0); } else { + cff2_subr_subsetter_t subr_subsetter (acc, plan); + /* Subset subrs: collect used subroutines, leaving all unused ones behind */ - if (!subr_subsetter.subset (acc, plan->glyphs, plan->drop_hints)) + if (!subr_subsetter.subset ()) return false; /* encode charstrings, global subrs, local subrs with new subroutine numbers */ - if (!subr_subsetter.encode_charstrings (acc, plan->glyphs, subset_charstrings)) + if (!subr_subsetter.encode_charstrings (subset_charstrings)) return false; if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs)) return false; - /* global subrs */ - unsigned int dataSize = subset_globalsubrs.total_size (); - offsets.globalSubrsInfo.offSize = calcOffSize (dataSize); - offsets.globalSubrsInfo.size = CFF2Subrs::calculate_serialized_size (offsets.globalSubrsInfo.offSize, subset_globalsubrs.length, dataSize); - /* local subrs */ - if (!offsets.localSubrsInfos.resize (orig_fdcount)) - return false; if (!subset_localsubrs.resize (orig_fdcount)) return false; for (unsigned int fd = 0; fd < orig_fdcount; fd++) { subset_localsubrs[fd].init (); - offsets.localSubrsInfos[fd].init (); - if (fdmap.includes (fd)) - { - if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) - return false; - - unsigned int dataSize = subset_localsubrs[fd].total_size (); - if (dataSize > 0) - { - offsets.localSubrsInfos[fd].offset = final_size; - offsets.localSubrsInfos[fd].offSize = calcOffSize (dataSize); - offsets.localSubrsInfos[fd].size = CFF2Subrs::calculate_serialized_size (offsets.localSubrsInfos[fd].offSize, subset_localsubrs[fd].length, dataSize); - } - } + if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) + return false; } } - /* global subrs */ - offsets.globalSubrsInfo.offset = final_size; - final_size += offsets.globalSubrsInfo.size; - - /* variation store */ - if (acc.varStore != &Null(CFF2VariationStore)) - { - offsets.varStoreOffset = final_size; - final_size += acc.varStore->get_size (); - } - /* FDSelect */ - if (acc.fdSelect != &Null(CFF2FDSelect)) + if (acc.fdSelect != &Null (CFF2FDSelect)) { - offsets.FDSelectInfo.offset = final_size; - if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs, - orig_fdcount, - *(const FDSelect *)acc.fdSelect, - subset_fdcount, - offsets.FDSelectInfo.size, - subset_fdselect_format, - subset_fdselect_ranges, - fdmap))) + if (unlikely (!hb_plan_subset_cff_fdselect (plan, + orig_fdcount, + *(const FDSelect *)acc.fdSelect, + subset_fdcount, + subset_fdselect_size, + subset_fdselect_format, + subset_fdselect_ranges, + fdmap))) return false; - - final_size += offsets.FDSelectInfo.size; } else fdmap.identity (1); - /* FDArray (FDIndex) */ - { - offsets.FDArrayInfo.offset = final_size; - cff_font_dict_op_serializer_t fontSzr; - unsigned int dictsSize = 0; - for (unsigned int i = 0; i < acc.fontDicts.length; i++) - if (fdmap.includes (i)) - dictsSize += FontDict::calculate_serialized_size (acc.fontDicts[i], fontSzr); - - offsets.FDArrayInfo.offSize = calcOffSize (dictsSize); - final_size += CFF2Index::calculate_serialized_size (offsets.FDArrayInfo.offSize, subset_fdcount, dictsSize); - } - - /* CharStrings */ - { - offsets.charStringsInfo.offset = final_size; - unsigned int dataSize = subset_charstrings.total_size (); - offsets.charStringsInfo.offSize = calcOffSize (dataSize); - final_size += CFF2CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.length, dataSize); - } - - /* private dicts & local subrs */ - offsets.privateDictsOffset = final_size; - for (unsigned int i = 0; i < orig_fdcount; i++) - { - if (fdmap.includes (i)) - { - bool has_localsubrs = offsets.localSubrsInfos[i].size > 0; - cff_private_dict_op_serializer_t privSzr (desubroutinize, drop_hints); - unsigned int priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr, has_localsubrs); - table_info_t privInfo = { final_size, priv_size, 0 }; - privateDictInfos.push (privInfo); - final_size += privInfo.size; - - if (!plan->desubroutinize && has_localsubrs) - { - offsets.localSubrsInfos[i].offset = final_size; - final_size += offsets.localSubrsInfos[i].size; - } - } - } - return true; } - unsigned int get_final_size () const { return final_size; } - - unsigned int final_size; - cff2_sub_table_offsets_t offsets; + cff2_sub_table_info_t info; unsigned int orig_fdcount; unsigned int subset_fdcount; + unsigned int subset_fdselect_size; unsigned int subset_fdselect_format; hb_vector_t subset_fdselect_ranges; - remap_t fdmap; + hb_inc_bimap_t fdmap; str_buff_vec_t subset_charstrings; str_buff_vec_t subset_globalsubrs; hb_vector_t subset_localsubrs; - hb_vector_t privateDictInfos; bool drop_hints; bool desubroutinize; - cff2_subr_subsetter_t subr_subsetter; }; -static inline bool _write_cff2 (const cff2_subset_plan &plan, - const OT::cff2::accelerator_subset_t &acc, - const hb_vector_t& glyphs, - unsigned int dest_sz, - void *dest) +static bool _serialize_cff2 (hb_serialize_context_t *c, + cff2_subset_plan &plan, + const OT::cff2::accelerator_subset_t &acc, + unsigned int num_glyphs) { - hb_serialize_context_t c (dest, dest_sz); - - OT::cff2 *cff2 = c.start_serialize (); - if (unlikely (!c.extend_min (*cff2))) - return false; - - /* header */ - cff2->version.major.set (0x02); - cff2->version.minor.set (0x00); - cff2->topDict.set (OT::cff2::static_size); + /* private dicts & local subrs */ + hb_vector_t private_dict_infos; + if (unlikely (!private_dict_infos.resize (plan.subset_fdcount))) return false; - /* top dict */ + for (int i = (int)acc.privateDicts.length; --i >= 0 ;) { - assert (cff2->topDict == (unsigned) (c.head - c.start)); - cff2->topDictSize.set (plan.offsets.topDictInfo.size); - TopDict &dict = cff2 + cff2->topDict; - cff2_top_dict_op_serializer_t topSzr; - if (unlikely (!dict.serialize (&c, acc.topDict, topSzr, plan.offsets))) + if (plan.fdmap.has (i)) { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 top dict"); - return false; - } - } + objidx_t subrs_link = 0; - /* global subrs */ - { - assert (cff2->topDict + plan.offsets.topDictInfo.size == (unsigned) (c.head - c.start)); - CFF2Subrs *dest = c.start_embed (); - if (unlikely (dest == nullptr)) return false; - if (unlikely (!dest->serialize (&c, plan.offsets.globalSubrsInfo.offSize, plan.subset_globalsubrs))) - { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize global subroutines"); - return false; + if (plan.subset_localsubrs[i].length > 0) + { + CFF2Subrs *dest = c->start_embed (); + if (unlikely (!dest)) return false; + c->push (); + if (likely (dest->serialize (c, plan.subset_localsubrs[i]))) + subrs_link = c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + PrivateDict *pd = c->start_embed (); + if (unlikely (!pd)) return false; + c->push (); + cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints); + if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link))) + { + unsigned fd = plan.fdmap[i]; + private_dict_infos[fd].size = c->length (); + private_dict_infos[fd].link = c->pop_pack (); + } + else + { + c->pop_discard (); + return false; + } } } - /* variation store */ - if (acc.varStore != &Null(CFF2VariationStore)) + /* CharStrings */ { - assert (plan.offsets.varStoreOffset == (unsigned) (c.head - c.start)); - CFF2VariationStore *dest = c.start_embed (); - if (unlikely (!dest->serialize (&c, acc.varStore))) + CFF2CharStrings *cs = c->start_embed (); + if (unlikely (!cs)) return false; + c->push (); + if (likely (cs->serialize (c, plan.subset_charstrings))) + plan.info.char_strings_link = c->pop_pack (); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 Variation Store"); + c->pop_discard (); return false; } } /* FDSelect */ - if (acc.fdSelect != &Null(CFF2FDSelect)) + if (acc.fdSelect != &Null (CFF2FDSelect)) { - assert (plan.offsets.FDSelectInfo.offset == (unsigned) (c.head - c.start)); - - if (unlikely (!hb_serialize_cff_fdselect (&c, glyphs.length, *(const FDSelect *)acc.fdSelect, acc.fdArray->count, - plan.subset_fdselect_format, plan.offsets.FDSelectInfo.size, - plan.subset_fdselect_ranges))) + c->push (); + if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *(const FDSelect *)acc.fdSelect, plan.orig_fdcount, + plan.subset_fdselect_format, plan.subset_fdselect_size, + plan.subset_fdselect_ranges))) + plan.info.fd_select.link = c->pop_pack (); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 subset FDSelect"); + c->pop_discard (); return false; } } /* FDArray (FD Index) */ { - assert (plan.offsets.FDArrayInfo.offset == (unsigned) (c.head - c.start)); - CFF2FDArray *fda = c.start_embed (); - if (unlikely (fda == nullptr)) return false; - cff_font_dict_op_serializer_t fontSzr; - if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayInfo.offSize, - acc.fontDicts, plan.subset_fdcount, plan.fdmap, - fontSzr, plan.privateDictInfos))) - { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 FDArray"); - return false; - } + c->push (); + CFF2FDArray *fda = c->start_embed (); + if (unlikely (!fda)) return false; + cff_font_dict_op_serializer_t fontSzr; + auto it = + + hb_zip (+ hb_iter (acc.fontDicts) + | hb_filter ([&] (const cff2_font_dict_values_t &_) + { return plan.fdmap.has (&_ - &acc.fontDicts[0]); }), + hb_iter (private_dict_infos)) + ; + if (unlikely (!fda->serialize (c, it, fontSzr))) return false; + plan.info.fd_array_link = c->pop_pack (); } - /* CharStrings */ + /* variation store */ + if (acc.varStore != &Null (CFF2VariationStore)) { - assert (plan.offsets.charStringsInfo.offset == (unsigned) (c.head - c.start)); - CFF2CharStrings *cs = c.start_embed (); - if (unlikely (cs == nullptr)) return false; - if (unlikely (!cs->serialize (&c, plan.offsets.charStringsInfo.offSize, plan.subset_charstrings))) - { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 CharStrings"); - return false; - } + c->push (); + CFF2VariationStore *dest = c->start_embed (); + if (unlikely (!dest || !dest->serialize (c, acc.varStore))) return false; + plan.info.var_store_link = c->pop_pack (); } - /* private dicts & local subrs */ - assert (plan.offsets.privateDictsOffset == (unsigned) (c.head - c.start)); - for (unsigned int i = 0; i < acc.privateDicts.length; i++) + OT::cff2 *cff2 = c->allocate_min (); + if (unlikely (!cff2)) return false; + + /* header */ + cff2->version.major = 0x02; + cff2->version.minor = 0x00; + cff2->topDict = OT::cff2::static_size; + + /* top dict */ { - if (plan.fdmap.includes (i)) - { - PrivateDict *pd = c.start_embed (); - if (unlikely (pd == nullptr)) return false; - unsigned int priv_size = plan.privateDictInfos[plan.fdmap[i]].size; - bool result; - cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints); - /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */ - unsigned int subroffset = (plan.offsets.localSubrsInfos[i].size > 0)? priv_size: 0; - result = pd->serialize (&c, acc.privateDicts[i], privSzr, subroffset); - if (unlikely (!result)) - { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i); - return false; - } - if (plan.offsets.localSubrsInfos[i].size > 0) - { - CFF2Subrs *dest = c.start_embed (); - if (unlikely (dest == nullptr)) return false; - if (unlikely (!dest->serialize (&c, plan.offsets.localSubrsInfos[i].offSize, plan.subset_localsubrs[i]))) - { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize local subroutines"); - return false; - } - } - } + TopDict &dict = cff2 + cff2->topDict; + cff2_top_dict_op_serializer_t topSzr; + if (unlikely (!dict.serialize (c, acc.topDict, topSzr, plan.info))) return false; + cff2->topDictSize = c->head - (const char *)&dict; } - assert (c.head == c.end); - c.end_serialize (); - - return true; + /* global subrs */ + { + CFF2Subrs *dest = c->start_embed (); + if (unlikely (!dest)) return false; + return dest->serialize (c, plan.subset_globalsubrs); + } } static bool _hb_subset_cff2 (const OT::cff2::accelerator_subset_t &acc, - const char *data, - hb_subset_plan_t *plan, - hb_blob_t **prime /* OUT */) + hb_subset_context_t *c) { cff2_subset_plan cff2_plan; - if (unlikely (!cff2_plan.create (acc, plan))) - { - DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff2 subsetting plan."); - return false; - } - - unsigned int cff2_prime_size = cff2_plan.get_final_size (); - char *cff2_prime_data = (char *) calloc (1, cff2_prime_size); - - if (unlikely (!_write_cff2 (cff2_plan, acc, plan->glyphs, - cff2_prime_size, cff2_prime_data))) { - DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff2."); - free (cff2_prime_data); - return false; - } - - *prime = hb_blob_create (cff2_prime_data, - cff2_prime_size, - HB_MEMORY_MODE_READONLY, - cff2_prime_data, - free); - return true; + if (unlikely (!cff2_plan.create (acc, c->plan))) return false; + return _serialize_cff2 (c->serializer, cff2_plan, acc, c->plan->num_output_glyphs ()); } /** * hb_subset_cff2: - * Subsets the CFF2 table according to a provided plan. - * - * Return value: subsetted cff2 table. + * Subsets the CFF2 table according to a provided subset context. **/ bool -hb_subset_cff2 (hb_subset_plan_t *plan, - hb_blob_t **prime /* OUT */) +hb_subset_cff2 (hb_subset_context_t *c) { - hb_blob_t *cff2_blob = hb_sanitize_context_t().reference_table (plan->source); - const char *data = hb_blob_get_data(cff2_blob, nullptr); - OT::cff2::accelerator_subset_t acc; - acc.init(plan->source); - bool result = likely (acc.is_valid ()) && - _hb_subset_cff2 (acc, data, plan, prime); - - hb_blob_destroy (cff2_blob); + acc.init (c->plan->source); + bool result = likely (acc.is_valid ()) && _hb_subset_cff2 (acc, c); acc.fini (); return result; } + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.hh index baac1a9be2f..f10556ddd74 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.hh @@ -32,7 +32,6 @@ #include "hb-subset-plan.hh" HB_INTERNAL bool -hb_subset_cff2 (hb_subset_plan_t *plan, - hb_blob_t **cff2_prime /* OUT */); +hb_subset_cff2 (hb_subset_context_t *c); #endif /* HB_SUBSET_CFF2_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-glyf.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-glyf.cc deleted file mode 100644 index 03c39a6dbbe..00000000000 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-glyf.cc +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright © 2018 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Garret Rieger, Roderick Sheeter - */ - -#include "hb-open-type.hh" -#include "hb-ot-glyf-table.hh" -#include "hb-set.h" -#include "hb-subset-glyf.hh" - -static bool -_calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf, - hb_vector_t &glyph_ids, - hb_bool_t drop_hints, - bool *use_short_loca /* OUT */, - unsigned int *glyf_size /* OUT */, - unsigned int *loca_size /* OUT */, - hb_vector_t *instruction_ranges /* OUT */) -{ - unsigned int total = 0; - for (unsigned int i = 0; i < glyph_ids.length; i++) - { - hb_codepoint_t next_glyph = glyph_ids[i]; - if (!instruction_ranges->resize (instruction_ranges->length + 2)) - { - DEBUG_MSG(SUBSET, nullptr, "Failed to resize instruction_ranges."); - return false; - } - unsigned int *instruction_start = &(*instruction_ranges)[instruction_ranges->length - 2]; - *instruction_start = 0; - unsigned int *instruction_end = &(*instruction_ranges)[instruction_ranges->length - 1]; - *instruction_end = 0; - - unsigned int start_offset, end_offset; - if (unlikely (!(glyf.get_offsets (next_glyph, &start_offset, &end_offset) && - glyf.remove_padding (start_offset, &end_offset)))) - { - DEBUG_MSG(SUBSET, nullptr, "Invalid gid %d", next_glyph); - continue; - } - if (end_offset - start_offset < OT::glyf::GlyphHeader::static_size) - continue; /* 0-length glyph */ - - if (drop_hints) - { - if (unlikely (!glyf.get_instruction_offsets (start_offset, end_offset, - instruction_start, instruction_end))) - { - DEBUG_MSG(SUBSET, nullptr, "Unable to get instruction offsets for %d", next_glyph); - return false; - } - } - - total += end_offset - start_offset - (*instruction_end - *instruction_start); - /* round2 so short loca will work */ - total += total % 2; - } - - *glyf_size = total; - *use_short_loca = (total <= 131070); - *loca_size = (glyph_ids.length + 1) - * (*use_short_loca ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32)); - - DEBUG_MSG(SUBSET, nullptr, "preparing to subset glyf: final size %d, loca size %d, using %s loca", - total, - *loca_size, - *use_short_loca ? "short" : "long"); - return true; -} - -static bool -_write_loca_entry (unsigned int id, - unsigned int offset, - bool is_short, - void *loca_prime, - unsigned int loca_size) -{ - unsigned int entry_size = is_short ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32); - if ((id + 1) * entry_size <= loca_size) - { - if (is_short) { - ((OT::HBUINT16*) loca_prime) [id].set (offset / 2); - } else { - ((OT::HBUINT32*) loca_prime) [id].set (offset); - } - return true; - } - - // Offset was not written because the write is out of bounds. - DEBUG_MSG(SUBSET, - nullptr, - "WARNING: Attempted to write an out of bounds loca entry at index %d. Loca size is %d.", - id, - loca_size); - return false; -} - -static void -_update_components (hb_subset_plan_t * plan, - char * glyph_start, - unsigned int length) -{ - OT::glyf::CompositeGlyphHeader::Iterator iterator; - if (OT::glyf::CompositeGlyphHeader::get_iterator (glyph_start, - length, - &iterator)) - { - do - { - hb_codepoint_t new_gid; - if (!plan->new_gid_for_old_gid (iterator.current->glyphIndex, - &new_gid)) - continue; - - ((OT::glyf::CompositeGlyphHeader *) iterator.current)->glyphIndex.set (new_gid); - } while (iterator.move_to_next ()); - } -} - -static bool _remove_composite_instruction_flag (char *glyf_prime, unsigned int length) -{ - /* remove WE_HAVE_INSTRUCTIONS from flags in dest */ - OT::glyf::CompositeGlyphHeader::Iterator composite_it; - if (unlikely (!OT::glyf::CompositeGlyphHeader::get_iterator (glyf_prime, length, &composite_it))) return false; - const OT::glyf::CompositeGlyphHeader *glyph; - do { - glyph = composite_it.current; - OT::HBUINT16 *flags = const_cast (&glyph->flags); - flags->set ( (uint16_t) *flags & ~OT::glyf::CompositeGlyphHeader::WE_HAVE_INSTRUCTIONS); - } while (composite_it.move_to_next ()); - return true; -} - -static bool -_write_glyf_and_loca_prime (hb_subset_plan_t *plan, - const OT::glyf::accelerator_t &glyf, - const char *glyf_data, - bool use_short_loca, - hb_vector_t &instruction_ranges, - unsigned int glyf_prime_size, - char *glyf_prime_data /* OUT */, - unsigned int loca_prime_size, - char *loca_prime_data /* OUT */) -{ - hb_vector_t &glyph_ids = plan->glyphs; - char *glyf_prime_data_next = glyf_prime_data; - - bool success = true; - for (unsigned int i = 0; i < glyph_ids.length; i++) - { - unsigned int start_offset, end_offset; - if (unlikely (!(glyf.get_offsets (glyph_ids[i], &start_offset, &end_offset) && - glyf.remove_padding (start_offset, &end_offset)))) - end_offset = start_offset = 0; - - unsigned int instruction_start = instruction_ranges[i * 2]; - unsigned int instruction_end = instruction_ranges[i * 2 + 1]; - - int length = end_offset - start_offset - (instruction_end - instruction_start); - - if (glyf_prime_data_next + length > glyf_prime_data + glyf_prime_size) - { - DEBUG_MSG(SUBSET, - nullptr, - "WARNING: Attempted to write an out of bounds glyph entry for gid %d (length %d)", - i, length); - return false; - } - - if (instruction_start == instruction_end) - memcpy (glyf_prime_data_next, glyf_data + start_offset, length); - else - { - memcpy (glyf_prime_data_next, glyf_data + start_offset, instruction_start - start_offset); - memcpy (glyf_prime_data_next + instruction_start - start_offset, glyf_data + instruction_end, end_offset - instruction_end); - /* if the instructions end at the end this was a composite glyph, else simple */ - if (instruction_end == end_offset) - { - if (unlikely (!_remove_composite_instruction_flag (glyf_prime_data_next, length))) return false; - } - else - /* zero instruction length, which is just before instruction_start */ - memset (glyf_prime_data_next + instruction_start - start_offset - 2, 0, 2); - } - - success = success && _write_loca_entry (i, - glyf_prime_data_next - glyf_prime_data, - use_short_loca, - loca_prime_data, - loca_prime_size); - _update_components (plan, glyf_prime_data_next, length); - - // TODO: don't align to two bytes if using long loca. - glyf_prime_data_next += length + (length % 2); // Align to 2 bytes for short loca. - } - - success = success && _write_loca_entry (glyph_ids.length, - glyf_prime_data_next - glyf_prime_data, - use_short_loca, - loca_prime_data, - loca_prime_size); - return success; -} - -static bool -_hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf, - const char *glyf_data, - hb_subset_plan_t *plan, - bool *use_short_loca, - hb_blob_t **glyf_prime /* OUT */, - hb_blob_t **loca_prime /* OUT */) -{ - // TODO(grieger): Sanity check allocation size for the new table. - hb_vector_t &glyphs_to_retain = plan->glyphs; - - unsigned int glyf_prime_size; - unsigned int loca_prime_size; - hb_vector_t instruction_ranges; - instruction_ranges.init (); - - if (unlikely (!_calculate_glyf_and_loca_prime_size (glyf, - glyphs_to_retain, - plan->drop_hints, - use_short_loca, - &glyf_prime_size, - &loca_prime_size, - &instruction_ranges))) { - instruction_ranges.fini (); - return false; - } - - char *glyf_prime_data = (char *) calloc (1, glyf_prime_size); - char *loca_prime_data = (char *) calloc (1, loca_prime_size); - if (unlikely (!_write_glyf_and_loca_prime (plan, glyf, glyf_data, - *use_short_loca, - instruction_ranges, - glyf_prime_size, glyf_prime_data, - loca_prime_size, loca_prime_data))) { - free (glyf_prime_data); - free (loca_prime_data); - instruction_ranges.fini (); - return false; - } - instruction_ranges.fini (); - - *glyf_prime = hb_blob_create (glyf_prime_data, - glyf_prime_size, - HB_MEMORY_MODE_READONLY, - glyf_prime_data, - free); - *loca_prime = hb_blob_create (loca_prime_data, - loca_prime_size, - HB_MEMORY_MODE_READONLY, - loca_prime_data, - free); - return true; -} - -/** - * hb_subset_glyf: - * Subsets the glyph table according to a provided plan. - * - * Return value: subsetted glyf table. - * - * Since: 1.7.5 - **/ -bool -hb_subset_glyf_and_loca (hb_subset_plan_t *plan, - bool *use_short_loca, /* OUT */ - hb_blob_t **glyf_prime, /* OUT */ - hb_blob_t **loca_prime /* OUT */) -{ - hb_blob_t *glyf_blob = hb_sanitize_context_t ().reference_table (plan->source); - const char *glyf_data = hb_blob_get_data (glyf_blob, nullptr); - - OT::glyf::accelerator_t glyf; - glyf.init (plan->source); - bool result = _hb_subset_glyf_and_loca (glyf, - glyf_data, - plan, - use_short_loca, - glyf_prime, - loca_prime); - - hb_blob_destroy (glyf_blob); - glyf.fini (); - - return result; -} diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-input.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-input.cc index 3feeb5c7592..087981b5243 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-input.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-input.cc @@ -44,7 +44,45 @@ hb_subset_input_create_or_fail () input->unicodes = hb_set_create (); input->glyphs = hb_set_create (); - input->drop_layout = true; + input->name_ids = hb_set_create (); + hb_set_add_range (input->name_ids, 0, 6); + input->name_languages = hb_set_create (); + hb_set_add (input->name_languages, 0x0409); + input->drop_tables = hb_set_create (); + input->drop_hints = false; + input->desubroutinize = false; + input->retain_gids = false; + input->name_legacy = false; + + hb_tag_t default_drop_tables[] = { + // Layout disabled by default + HB_TAG ('G', 'S', 'U', 'B'), + HB_TAG ('G', 'P', 'O', 'S'), + HB_TAG ('G', 'D', 'E', 'F'), + HB_TAG ('m', 'o', 'r', 'x'), + HB_TAG ('m', 'o', 'r', 't'), + HB_TAG ('k', 'e', 'r', 'x'), + HB_TAG ('k', 'e', 'r', 'n'), + + // Copied from fontTools: + HB_TAG ('B', 'A', 'S', 'E'), + HB_TAG ('J', 'S', 'T', 'F'), + HB_TAG ('D', 'S', 'I', 'G'), + HB_TAG ('E', 'B', 'D', 'T'), + HB_TAG ('E', 'B', 'L', 'C'), + HB_TAG ('E', 'B', 'S', 'C'), + HB_TAG ('S', 'V', 'G', ' '), + HB_TAG ('P', 'C', 'L', 'T'), + HB_TAG ('L', 'T', 'S', 'H'), + // Graphite tables + HB_TAG ('F', 'e', 'a', 't'), + HB_TAG ('G', 'l', 'a', 't'), + HB_TAG ('G', 'l', 'o', 'c'), + HB_TAG ('S', 'i', 'l', 'f'), + HB_TAG ('S', 'i', 'l', 'l'), + }; + + input->drop_tables->add_array (default_drop_tables, ARRAY_LENGTH (default_drop_tables)); return input; } @@ -78,6 +116,9 @@ hb_subset_input_destroy (hb_subset_input_t *subset_input) hb_set_destroy (subset_input->unicodes); hb_set_destroy (subset_input->glyphs); + hb_set_destroy (subset_input->name_ids); + hb_set_destroy (subset_input->name_languages); + hb_set_destroy (subset_input->drop_tables); free (subset_input); } @@ -106,6 +147,24 @@ hb_subset_input_glyph_set (hb_subset_input_t *subset_input) return subset_input->glyphs; } +HB_EXTERN hb_set_t * +hb_subset_input_nameid_set (hb_subset_input_t *subset_input) +{ + return subset_input->name_ids; +} + +HB_EXTERN hb_set_t * +hb_subset_input_namelangid_set (hb_subset_input_t *subset_input) +{ + return subset_input->name_languages; +} + +HB_EXTERN hb_set_t * +hb_subset_input_drop_tables_set (hb_subset_input_t *subset_input) +{ + return subset_input->drop_tables; +} + HB_EXTERN void hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input, hb_bool_t drop_hints) @@ -120,27 +179,51 @@ hb_subset_input_get_drop_hints (hb_subset_input_t *subset_input) } HB_EXTERN void -hb_subset_input_set_drop_layout (hb_subset_input_t *subset_input, - hb_bool_t drop_layout) +hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input, + hb_bool_t desubroutinize) { - subset_input->drop_layout = drop_layout; + subset_input->desubroutinize = desubroutinize; } HB_EXTERN hb_bool_t -hb_subset_input_get_drop_layout (hb_subset_input_t *subset_input) +hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input) { - return subset_input->drop_layout; + return subset_input->desubroutinize; } +/** + * hb_subset_input_set_retain_gids: + * @subset_input: a subset_input. + * @retain_gids: If true the subsetter will not renumber glyph ids. + * Since: 2.4.0 + **/ HB_EXTERN void -hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input, - hb_bool_t desubroutinize) +hb_subset_input_set_retain_gids (hb_subset_input_t *subset_input, + hb_bool_t retain_gids) { - subset_input->desubroutinize = desubroutinize; + subset_input->retain_gids = retain_gids; } +/** + * hb_subset_input_get_retain_gids: + * Returns: value of retain_gids. + * Since: 2.4.0 + **/ HB_EXTERN hb_bool_t -hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input) +hb_subset_input_get_retain_gids (hb_subset_input_t *subset_input) { - return subset_input->desubroutinize; + return subset_input->retain_gids; +} + +HB_EXTERN void +hb_subset_input_set_name_legacy (hb_subset_input_t *subset_input, + hb_bool_t name_legacy) +{ + subset_input->name_legacy = name_legacy; +} + +HB_EXTERN hb_bool_t +hb_subset_input_get_name_legacy (hb_subset_input_t *subset_input) +{ + return subset_input->name_legacy; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-input.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-input.hh index 8dad94f584e..0aeb96695b7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-input.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-input.hh @@ -40,15 +40,19 @@ struct hb_subset_input_t hb_set_t *unicodes; hb_set_t *glyphs; - - bool drop_hints : 1; - bool drop_layout : 1; - bool desubroutinize : 1; + hb_set_t *name_ids; + hb_set_t *name_languages; + hb_set_t *drop_tables; + + bool drop_hints; + bool desubroutinize; + bool retain_gids; + bool name_legacy; /* TODO * * features * lookups - * nameIDs + * name_ids * ... */ }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc index 69b432872da..2f2f343ae2b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc @@ -30,44 +30,47 @@ #include "hb-ot-cmap-table.hh" #include "hb-ot-glyf-table.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-ot-layout-gpos-table.hh" +#include "hb-ot-layout-gsub-table.hh" #include "hb-ot-cff1-table.hh" +#include "hb-ot-color-colr-table.hh" +#include "hb-ot-var-fvar-table.hh" +#include "hb-ot-stat-table.hh" -static void -_add_gid_and_children (const OT::glyf::accelerator_t &glyf, - hb_codepoint_t gid, - hb_set_t *gids_to_retain) -{ - if (hb_set_has (gids_to_retain, gid)) - // Already visited this gid, ignore. - return; - - hb_set_add (gids_to_retain, gid); - OT::glyf::CompositeGlyphHeader::Iterator composite; - if (glyf.get_composite (gid, &composite)) - { - do - { - _add_gid_and_children (glyf, (hb_codepoint_t) composite.current->glyphIndex, gids_to_retain); - } while (composite.move_to_next()); - } -} - -static void +#ifndef HB_NO_SUBSET_CFF +static inline void _add_cff_seac_components (const OT::cff1::accelerator_t &cff, - hb_codepoint_t gid, - hb_set_t *gids_to_retain) + hb_codepoint_t gid, + hb_set_t *gids_to_retain) { hb_codepoint_t base_gid, accent_gid; if (cff.get_seac_components (gid, &base_gid, &accent_gid)) { - hb_set_add (gids_to_retain, base_gid); - hb_set_add (gids_to_retain, accent_gid); + gids_to_retain->add (base_gid); + gids_to_retain->add (accent_gid); } } +#endif +#ifndef HB_NO_SUBSET_LAYOUT static void -_gsub_closure (hb_face_t *face, hb_set_t *gids_to_retain) +_remap_indexes (const hb_set_t *indexes, + hb_map_t *mapping /* OUT */) +{ + unsigned count = indexes->get_population (); + + for (auto _ : + hb_zip (indexes->iter (), hb_range (count))) + mapping->set (_.first, _.second); + +} + +static inline void +_gsub_closure_glyphs_lookups_features (hb_face_t *face, + hb_set_t *gids_to_retain, + hb_map_t *gsub_lookups, + hb_map_t *gsub_features) { hb_set_t lookup_indices; hb_ot_layout_collect_lookups (face, @@ -79,9 +82,88 @@ _gsub_closure (hb_face_t *face, hb_set_t *gids_to_retain) hb_ot_layout_lookups_substitute_closure (face, &lookup_indices, gids_to_retain); + hb_blob_ptr_t gsub = hb_sanitize_context_t ().reference_table (face); + gsub->closure_lookups (face, + gids_to_retain, + &lookup_indices); + _remap_indexes (&lookup_indices, gsub_lookups); + + //closure features + hb_set_t feature_indices; + gsub->closure_features (gsub_lookups, &feature_indices); + _remap_indexes (&feature_indices, gsub_features); + gsub.destroy (); } -static void +static inline void +_gpos_closure_lookups_features (hb_face_t *face, + const hb_set_t *gids_to_retain, + hb_map_t *gpos_lookups, + hb_map_t *gpos_features) +{ + hb_set_t lookup_indices; + hb_ot_layout_collect_lookups (face, + HB_OT_TAG_GPOS, + nullptr, + nullptr, + nullptr, + &lookup_indices); + hb_blob_ptr_t gpos = hb_sanitize_context_t ().reference_table (face); + gpos->closure_lookups (face, + gids_to_retain, + &lookup_indices); + _remap_indexes (&lookup_indices, gpos_lookups); + + //closure features + hb_set_t feature_indices; + gpos->closure_features (gpos_lookups, &feature_indices); + _remap_indexes (&feature_indices, gpos_features); + gpos.destroy (); +} +#endif + +#ifndef HB_NO_VAR +static inline void + _collect_layout_variation_indices (hb_face_t *face, + const hb_set_t *glyphset, + const hb_map_t *gpos_lookups, + hb_set_t *layout_variation_indices, + hb_map_t *layout_variation_idx_map) +{ + hb_blob_ptr_t gdef = hb_sanitize_context_t ().reference_table (face); + hb_blob_ptr_t gpos = hb_sanitize_context_t ().reference_table (face); + + if (!gdef->has_data ()) + { + gdef.destroy (); + gpos.destroy (); + return; + } + OT::hb_collect_variation_indices_context_t c (layout_variation_indices, glyphset, gpos_lookups); + gdef->collect_variation_indices (&c); + + if (hb_ot_layout_has_positioning (face)) + gpos->collect_variation_indices (&c); + + gdef->remap_layout_variation_indices (layout_variation_indices, layout_variation_idx_map); + + gdef.destroy (); + gpos.destroy (); +} +#endif + +static inline void +_cmap_closure (hb_face_t *face, + const hb_set_t *unicodes, + hb_set_t *glyphset) +{ + OT::cmap::accelerator_t cmap; + cmap.init (face); + cmap.table->closure_glyphs (unicodes, glyphset); + cmap.fini (); +} + +static inline void _remove_invalid_gids (hb_set_t *glyphs, unsigned int num_glyphs) { @@ -93,23 +175,29 @@ _remove_invalid_gids (hb_set_t *glyphs, } } -static hb_set_t * -_populate_gids_to_retain (hb_face_t *face, +static void +_populate_gids_to_retain (hb_subset_plan_t* plan, const hb_set_t *unicodes, + const hb_set_t *input_glyphs_to_retain, bool close_over_gsub, - hb_set_t *unicodes_to_retain, - hb_map_t *codepoint_to_glyph, - hb_vector_t *glyphs) + bool close_over_gpos, + bool close_over_gdef) { OT::cmap::accelerator_t cmap; OT::glyf::accelerator_t glyf; +#ifndef HB_NO_SUBSET_CFF OT::cff1::accelerator_t cff; - cmap.init (face); - glyf.init (face); - cff.init (face); +#endif + OT::COLR::accelerator_t colr; + cmap.init (plan->source); + glyf.init (plan->source); +#ifndef HB_NO_SUBSET_CFF + cff.init (plan->source); +#endif + colr.init (plan->source); - hb_set_t *initial_gids_to_retain = hb_set_create (); - initial_gids_to_retain->add (0); // Not-def + plan->_glyphset_gsub->add (0); // Not-def + hb_set_union (plan->_glyphset_gsub, input_glyphs_to_retain); hb_codepoint_t cp = HB_SET_VALUE_INVALID; while (unicodes->next (&cp)) @@ -120,48 +208,96 @@ _populate_gids_to_retain (hb_face_t *face, DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp); continue; } - unicodes_to_retain->add (cp); - codepoint_to_glyph->set (cp, gid); - initial_gids_to_retain->add (gid); + plan->unicodes->add (cp); + plan->codepoint_to_glyph->set (cp, gid); + plan->_glyphset_gsub->add (gid); } + _cmap_closure (plan->source, plan->unicodes, plan->_glyphset_gsub); + +#ifndef HB_NO_SUBSET_LAYOUT if (close_over_gsub) - // Add all glyphs needed for GSUB substitutions. - _gsub_closure (face, initial_gids_to_retain); + // closure all glyphs/lookups/features needed for GSUB substitutions. + _gsub_closure_glyphs_lookups_features (plan->source, plan->_glyphset_gsub, plan->gsub_lookups, plan->gsub_features); + + if (close_over_gpos) + _gpos_closure_lookups_features (plan->source, plan->_glyphset_gsub, plan->gpos_lookups, plan->gpos_features); +#endif + _remove_invalid_gids (plan->_glyphset_gsub, plan->source->get_num_glyphs ()); // Populate a full set of glyphs to retain by adding all referenced // composite glyphs. hb_codepoint_t gid = HB_SET_VALUE_INVALID; - hb_set_t *all_gids_to_retain = hb_set_create (); - while (initial_gids_to_retain->next (&gid)) + while (plan->_glyphset_gsub->next (&gid)) { - _add_gid_and_children (glyf, gid, all_gids_to_retain); + glyf.add_gid_and_children (gid, plan->_glyphset); +#ifndef HB_NO_SUBSET_CFF if (cff.is_valid ()) - _add_cff_seac_components (cff, gid, all_gids_to_retain); + _add_cff_seac_components (cff, gid, plan->_glyphset); +#endif + if (colr.is_valid ()) + colr.closure_glyphs (gid, plan->_glyphset); } - hb_set_destroy (initial_gids_to_retain); - _remove_invalid_gids (all_gids_to_retain, face->get_num_glyphs ()); + _remove_invalid_gids (plan->_glyphset, plan->source->get_num_glyphs ()); - glyphs->alloc (all_gids_to_retain->get_population ()); - gid = HB_SET_VALUE_INVALID; - while (all_gids_to_retain->next (&gid)) - glyphs->push (gid); +#ifndef HB_NO_VAR + if (close_over_gdef) + _collect_layout_variation_indices (plan->source, plan->_glyphset, plan->gpos_lookups, plan->layout_variation_indices, plan->layout_variation_idx_map); +#endif +#ifndef HB_NO_SUBSET_CFF cff.fini (); +#endif glyf.fini (); cmap.fini (); - - return all_gids_to_retain; } static void -_create_old_gid_to_new_gid_map (const hb_vector_t &glyphs, - hb_map_t *glyph_map) +_create_old_gid_to_new_gid_map (const hb_face_t *face, + bool retain_gids, + const hb_set_t *all_gids_to_retain, + hb_map_t *glyph_map, /* OUT */ + hb_map_t *reverse_glyph_map, /* OUT */ + unsigned int *num_glyphs /* OUT */) { - for (unsigned int i = 0; i < glyphs.length; i++) { - glyph_map->set (glyphs[i], i); + if (!retain_gids) + { + + hb_enumerate (hb_iter (all_gids_to_retain), (hb_codepoint_t) 0) + | hb_sink (reverse_glyph_map) + ; + *num_glyphs = reverse_glyph_map->get_population (); + } else { + + hb_iter (all_gids_to_retain) + | hb_map ([] (hb_codepoint_t _) { + return hb_pair_t (_, _); + }) + | hb_sink (reverse_glyph_map) + ; + + unsigned max_glyph = + + hb_iter (all_gids_to_retain) + | hb_reduce (hb_max, 0u) + ; + *num_glyphs = max_glyph + 1; } + + + reverse_glyph_map->iter () + | hb_map (&hb_pair_t::reverse) + | hb_sink (glyph_map) + ; +} + +static void +_nameid_closure (hb_face_t *face, + hb_set_t *nameids) +{ +#ifndef HB_NO_STYLE + face->table.STAT->collect_name_ids (nameids); +#endif +#ifndef HB_NO_VAR + face->table.fvar->collect_name_ids (nameids); +#endif } /** @@ -175,28 +311,52 @@ _create_old_gid_to_new_gid_map (const hb_vector_t &glyphs, * Since: 1.7.5 **/ hb_subset_plan_t * -hb_subset_plan_create (hb_face_t *face, - hb_subset_input_t *input) +hb_subset_plan_create (hb_face_t *face, + hb_subset_input_t *input) { - hb_subset_plan_t *plan = hb_object_create (); + hb_subset_plan_t *plan; + if (unlikely (!(plan = hb_object_create ()))) + return const_cast (&Null (hb_subset_plan_t)); + plan->successful = true; plan->drop_hints = input->drop_hints; - plan->drop_layout = input->drop_layout; plan->desubroutinize = input->desubroutinize; - plan->unicodes = hb_set_create(); - plan->glyphs.init(); + plan->retain_gids = input->retain_gids; + plan->name_legacy = input->name_legacy; + plan->unicodes = hb_set_create (); + plan->name_ids = hb_set_reference (input->name_ids); + _nameid_closure (face, plan->name_ids); + plan->name_languages = hb_set_reference (input->name_languages); + plan->glyphs_requested = hb_set_reference (input->glyphs); + plan->drop_tables = hb_set_reference (input->drop_tables); plan->source = hb_face_reference (face); plan->dest = hb_face_builder_create (); - plan->codepoint_to_glyph = hb_map_create(); - plan->glyph_map = hb_map_create(); - plan->glyphset = _populate_gids_to_retain (face, - input->unicodes, - !plan->drop_layout, - plan->unicodes, - plan->codepoint_to_glyph, - &plan->glyphs); - _create_old_gid_to_new_gid_map (plan->glyphs, - plan->glyph_map); + + plan->_glyphset = hb_set_create (); + plan->_glyphset_gsub = hb_set_create (); + plan->codepoint_to_glyph = hb_map_create (); + plan->glyph_map = hb_map_create (); + plan->reverse_glyph_map = hb_map_create (); + plan->gsub_lookups = hb_map_create (); + plan->gpos_lookups = hb_map_create (); + plan->gsub_features = hb_map_create (); + plan->gpos_features = hb_map_create (); + plan->layout_variation_indices = hb_set_create (); + plan->layout_variation_idx_map = hb_map_create (); + + _populate_gids_to_retain (plan, + input->unicodes, + input->glyphs, + !input->drop_tables->has (HB_OT_TAG_GSUB), + !input->drop_tables->has (HB_OT_TAG_GPOS), + !input->drop_tables->has (HB_OT_TAG_GDEF)); + + _create_old_gid_to_new_gid_map (face, + input->retain_gids, + plan->_glyphset, + plan->glyph_map, + plan->reverse_glyph_map, + &plan->_num_output_glyphs); return plan; } @@ -212,12 +372,24 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan) if (!hb_object_destroy (plan)) return; hb_set_destroy (plan->unicodes); - plan->glyphs.fini (); + hb_set_destroy (plan->name_ids); + hb_set_destroy (plan->name_languages); + hb_set_destroy (plan->glyphs_requested); + hb_set_destroy (plan->drop_tables); hb_face_destroy (plan->source); hb_face_destroy (plan->dest); hb_map_destroy (plan->codepoint_to_glyph); hb_map_destroy (plan->glyph_map); - hb_set_destroy (plan->glyphset); + hb_map_destroy (plan->reverse_glyph_map); + hb_set_destroy (plan->_glyphset); + hb_set_destroy (plan->_glyphset_gsub); + hb_map_destroy (plan->gsub_lookups); + hb_map_destroy (plan->gpos_lookups); + hb_map_destroy (plan->gsub_features); + hb_map_destroy (plan->gpos_features); + hb_set_destroy (plan->layout_variation_indices); + hb_map_destroy (plan->layout_variation_idx_map); + free (plan); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh index 83dd370f6a7..b029548e0dd 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh @@ -33,30 +33,111 @@ #include "hb-subset-input.hh" #include "hb-map.hh" +#include "hb-set.hh" struct hb_subset_plan_t { hb_object_header_t header; + bool successful : 1; bool drop_hints : 1; - bool drop_layout : 1; bool desubroutinize : 1; + bool retain_gids : 1; + bool name_legacy : 1; // For each cp that we'd like to retain maps to the corresponding gid. hb_set_t *unicodes; - hb_vector_t glyphs; - hb_set_t *glyphset; + // name_ids we would like to retain + hb_set_t *name_ids; + // name_languages we would like to retain + hb_set_t *name_languages; + + //glyph ids requested to retain + hb_set_t *glyphs_requested; + + // Tables which should be dropped. + hb_set_t *drop_tables; + + // The glyph subset hb_map_t *codepoint_to_glyph; + + // Old -> New glyph id mapping hb_map_t *glyph_map; + hb_map_t *reverse_glyph_map; // Plan is only good for a specific source/dest so keep them with it hb_face_t *source; hb_face_t *dest; - bool new_gid_for_codepoint (hb_codepoint_t codepoint, - hb_codepoint_t *new_gid) const + unsigned int _num_output_glyphs; + hb_set_t *_glyphset; + hb_set_t *_glyphset_gsub; + + //active lookups we'd like to retain + hb_map_t *gsub_lookups; + hb_map_t *gpos_lookups; + + //active features we'd like to retain + hb_map_t *gsub_features; + hb_map_t *gpos_features; + + //The set of layout item variation store delta set indices to be retained + hb_set_t *layout_variation_indices; + //Old -> New layout item variation store delta set index mapping + hb_map_t *layout_variation_idx_map; + + public: + + bool in_error () const { return !successful; } + + bool check_success(bool success) + { + successful = (successful && success); + return successful; + } + + /* + * The set of input glyph ids which will be retained in the subset. + * Does NOT include ids kept due to retain_gids. You probably want to use + * glyph_map/reverse_glyph_map. + */ + inline const hb_set_t * + glyphset () const + { + return _glyphset; + } + + /* + * The set of input glyph ids which will be retained in the subset. + */ + inline const hb_set_t * + glyphset_gsub () const + { + return _glyphset_gsub; + } + + /* + * The total number of output glyphs in the final subset. + */ + inline unsigned int + num_output_glyphs () const + { + return _num_output_glyphs; + } + + /* + * Given an output gid , returns true if that glyph id is an empty + * glyph (ie. it's a gid that we are dropping all data for). + */ + inline bool is_empty_glyph (hb_codepoint_t gid) const + { + return !_glyphset->has (gid); + } + + inline bool new_gid_for_codepoint (hb_codepoint_t codepoint, + hb_codepoint_t *new_gid) const { hb_codepoint_t old_gid = codepoint_to_glyph->get (codepoint); if (old_gid == HB_MAP_VALUE_INVALID) @@ -65,8 +146,8 @@ struct hb_subset_plan_t return new_gid_for_old_gid (old_gid, new_gid); } - bool new_gid_for_old_gid (hb_codepoint_t old_gid, - hb_codepoint_t *new_gid) const + inline bool new_gid_for_old_gid (hb_codepoint_t old_gid, + hb_codepoint_t *new_gid) const { hb_codepoint_t gid = glyph_map->get (old_gid); if (gid == HB_MAP_VALUE_INVALID) @@ -76,7 +157,18 @@ struct hb_subset_plan_t return true; } - bool + inline bool old_gid_for_new_gid (hb_codepoint_t new_gid, + hb_codepoint_t *old_gid) const + { + hb_codepoint_t gid = reverse_glyph_map->get (new_gid); + if (gid == HB_MAP_VALUE_INVALID) + return false; + + *old_gid = gid; + return true; + } + + inline bool add_table (hb_tag_t tag, hb_blob_t *contents) { diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset.cc index 65841672ad6..43afc30b9ac 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset.cc @@ -28,7 +28,6 @@ #include "hb-open-type.hh" #include "hb-subset.hh" -#include "hb-subset-glyf.hh" #include "hb-open-file.hh" #include "hb-ot-cmap-table.hh" @@ -38,218 +37,194 @@ #include "hb-ot-hhea-table.hh" #include "hb-ot-hmtx-table.hh" #include "hb-ot-maxp-table.hh" +#include "hb-ot-color-sbix-table.hh" +#include "hb-ot-color-colr-table.hh" #include "hb-ot-os2-table.hh" #include "hb-ot-post-table.hh" #include "hb-ot-cff1-table.hh" #include "hb-ot-cff2-table.hh" #include "hb-ot-vorg-table.hh" +#include "hb-ot-name-table.hh" +#include "hb-ot-color-cbdt-table.hh" #include "hb-ot-layout-gsub-table.hh" #include "hb-ot-layout-gpos-table.hh" +#include "hb-ot-var-gvar-table.hh" +#include "hb-ot-var-hvar-table.hh" -static unsigned int -_plan_estimate_subset_table_size (hb_subset_plan_t *plan, - unsigned int table_len) +static unsigned +_plan_estimate_subset_table_size (hb_subset_plan_t *plan, unsigned table_len) { - unsigned int src_glyphs = plan->source->get_num_glyphs (); - unsigned int dst_glyphs = plan->glyphset->get_population (); + unsigned src_glyphs = plan->source->get_num_glyphs (); + unsigned dst_glyphs = plan->glyphset ()->get_population (); if (unlikely (!src_glyphs)) return 512 + table_len; - return 512 + (unsigned int) (table_len * sqrt ((double) dst_glyphs / src_glyphs)); + return 512 + (unsigned) (table_len * sqrt ((double) dst_glyphs / src_glyphs)); } template static bool -_subset2 (hb_subset_plan_t *plan) +_subset (hb_subset_plan_t *plan) { + bool result = false; hb_blob_t *source_blob = hb_sanitize_context_t ().reference_table (plan->source); const TableType *table = source_blob->as (); hb_tag_t tag = TableType::tableTag; - hb_bool_t result = false; if (source_blob->data) { hb_vector_t buf; - unsigned int buf_size = _plan_estimate_subset_table_size (plan, source_blob->length); - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c initial estimated table size: %u bytes.", HB_UNTAG (tag), buf_size); + /* TODO Not all tables are glyph-related. 'name' table size for example should not be + * affected by number of glyphs. Accommodate that. */ + unsigned buf_size = _plan_estimate_subset_table_size (plan, source_blob->length); + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c initial estimated table size: %u bytes.", HB_UNTAG (tag), buf_size); if (unlikely (!buf.alloc (buf_size))) { - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c failed to allocate %u bytes.", HB_UNTAG (tag), buf_size); + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to allocate %u bytes.", HB_UNTAG (tag), buf_size); + hb_blob_destroy (source_blob); return false; } retry: hb_serialize_context_t serializer ((void *) buf, buf_size); - hb_subset_context_t c (plan, &serializer); - result = table->subset (&c); - if (serializer.in_error ()) + serializer.start_serialize (); + hb_subset_context_t c (source_blob, plan, &serializer, tag); + bool needed = table->subset (&c); + if (serializer.ran_out_of_room) { buf_size += (buf_size >> 1) + 32; - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.", HB_UNTAG (tag), buf_size); + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.", HB_UNTAG (tag), buf_size); if (unlikely (!buf.alloc (buf_size))) { - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.", HB_UNTAG (tag), buf_size); + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.", HB_UNTAG (tag), buf_size); + hb_blob_destroy (source_blob); return false; } goto retry; } + serializer.end_serialize (); + + result = !serializer.in_error (); + if (result) { - hb_blob_t *dest_blob = serializer.copy_blob (); - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c final subset table size: %u bytes.", HB_UNTAG (tag), dest_blob->length); - result = c.plan->add_table (tag, dest_blob); - hb_blob_destroy (dest_blob); - } - else - { - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset table subsetted to empty.", HB_UNTAG (tag)); - result = true; + if (needed) + { + hb_blob_t *dest_blob = serializer.copy_blob (); + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c final subset table size: %u bytes.", HB_UNTAG (tag), dest_blob->length); + result = c.plan->add_table (tag, dest_blob); + hb_blob_destroy (dest_blob); + } + else + { + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset table subsetted to empty.", HB_UNTAG (tag)); + } } } else - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag)); + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag)); hb_blob_destroy (source_blob); - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset %s", HB_UNTAG (tag), result ? "success" : "FAILED!"); + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset %s", HB_UNTAG (tag), result ? "success" : "FAILED!"); return result; } -template static bool -_subset (hb_subset_plan_t *plan) +_is_table_present (hb_face_t *source, hb_tag_t tag) { - hb_blob_t *source_blob = hb_sanitize_context_t ().reference_table (plan->source); - const TableType *table = source_blob->as (); - - hb_tag_t tag = TableType::tableTag; - hb_bool_t result = false; - if (source_blob->data) - result = table->subset (plan); - else - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag)); - - hb_blob_destroy (source_blob); - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset %s", HB_UNTAG (tag), result ? "success" : "FAILED!"); - return result; + hb_tag_t table_tags[32]; + unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags); + while ((hb_face_get_table_tags (source, offset, &num_tables, table_tags), num_tables)) + { + for (unsigned i = 0; i < num_tables; ++i) + if (table_tags[i] == tag) + return true; + offset += num_tables; + } + return false; } - static bool -_subset_table (hb_subset_plan_t *plan, - hb_tag_t tag) +_should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag) { - DEBUG_MSG(SUBSET, nullptr, "begin subset %c%c%c%c", HB_UNTAG (tag)); - bool result = true; - switch (tag) { - case HB_OT_TAG_glyf: - result = _subset (plan); - break; - case HB_OT_TAG_hdmx: - result = _subset (plan); - break; - case HB_OT_TAG_head: - // TODO that won't work well if there is no glyf - DEBUG_MSG(SUBSET, nullptr, "skip head, handled by glyf"); - result = true; - break; - case HB_OT_TAG_hhea: - DEBUG_MSG(SUBSET, nullptr, "skip hhea handled by hmtx"); - return true; - case HB_OT_TAG_hmtx: - result = _subset (plan); - break; - case HB_OT_TAG_vhea: - DEBUG_MSG(SUBSET, nullptr, "skip vhea handled by vmtx"); - return true; - case HB_OT_TAG_vmtx: - result = _subset (plan); - break; - case HB_OT_TAG_maxp: - result = _subset (plan); - break; - case HB_OT_TAG_loca: - DEBUG_MSG(SUBSET, nullptr, "skip loca handled by glyf"); - return true; - case HB_OT_TAG_cmap: - result = _subset (plan); - break; - case HB_OT_TAG_OS2: - result = _subset (plan); - break; - case HB_OT_TAG_post: - result = _subset (plan); - break; - case HB_OT_TAG_cff1: - result = _subset (plan); - break; - case HB_OT_TAG_cff2: - result = _subset (plan); - break; - case HB_OT_TAG_VORG: - result = _subset (plan); - break; - case HB_OT_TAG_GDEF: - result = _subset2 (plan); - break; - case HB_OT_TAG_GSUB: - result = _subset2 (plan); - break; - case HB_OT_TAG_GPOS: - result = _subset2 (plan); - break; + if (plan->drop_tables->has (tag)) + return true; - default: - hb_blob_t *source_table = hb_face_reference_table (plan->source, tag); - if (likely (source_table)) - result = plan->add_table (tag, source_table); - else - result = false; - hb_blob_destroy (source_table); - break; + switch (tag) + { + case HB_TAG ('c','v','a','r'): /* hint table, fallthrough */ + case HB_TAG ('c','v','t',' '): /* hint table, fallthrough */ + case HB_TAG ('f','p','g','m'): /* hint table, fallthrough */ + case HB_TAG ('p','r','e','p'): /* hint table, fallthrough */ + case HB_TAG ('h','d','m','x'): /* hint table, fallthrough */ + case HB_TAG ('V','D','M','X'): /* hint table, fallthrough */ + return plan->drop_hints; + +#ifdef HB_NO_SUBSET_LAYOUT + // Drop Layout Tables if requested. + case HB_OT_TAG_GDEF: + case HB_OT_TAG_GPOS: + case HB_OT_TAG_GSUB: + case HB_TAG ('m','o','r','x'): + case HB_TAG ('m','o','r','t'): + case HB_TAG ('k','e','r','x'): + case HB_TAG ('k','e','r','n'): + return true; +#endif + + default: + return false; } - DEBUG_MSG(SUBSET, nullptr, "subset %c%c%c%c %s", HB_UNTAG (tag), result ? "ok" : "FAILED"); - return result; } static bool -_should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag) +_subset_table (hb_subset_plan_t *plan, hb_tag_t tag) { - switch (tag) { - case HB_TAG ('c', 'v', 'a', 'r'): /* hint table, fallthrough */ - case HB_TAG ('c', 'v', 't', ' '): /* hint table, fallthrough */ - case HB_TAG ('f', 'p', 'g', 'm'): /* hint table, fallthrough */ - case HB_TAG ('p', 'r', 'e', 'p'): /* hint table, fallthrough */ - case HB_TAG ('h', 'd', 'm', 'x'): /* hint table, fallthrough */ - case HB_TAG ('V', 'D', 'M', 'X'): /* hint table, fallthrough */ - return plan->drop_hints; - // Drop Layout Tables if requested. - case HB_OT_TAG_GDEF: - case HB_OT_TAG_GPOS: - case HB_OT_TAG_GSUB: - return plan->drop_layout; - // Drop these tables below by default, list pulled - // from fontTools: - case HB_TAG ('B', 'A', 'S', 'E'): - case HB_TAG ('J', 'S', 'T', 'F'): - case HB_TAG ('D', 'S', 'I', 'G'): - case HB_TAG ('E', 'B', 'D', 'T'): - case HB_TAG ('E', 'B', 'L', 'C'): - case HB_TAG ('E', 'B', 'S', 'C'): - case HB_TAG ('S', 'V', 'G', ' '): - case HB_TAG ('P', 'C', 'L', 'T'): - case HB_TAG ('L', 'T', 'S', 'H'): - // Graphite tables: - case HB_TAG ('F', 'e', 'a', 't'): - case HB_TAG ('G', 'l', 'a', 't'): - case HB_TAG ('G', 'l', 'o', 'c'): - case HB_TAG ('S', 'i', 'l', 'f'): - case HB_TAG ('S', 'i', 'l', 'l'): - // Colour - case HB_TAG ('s', 'b', 'i', 'x'): - return true; - default: - return false; + DEBUG_MSG (SUBSET, nullptr, "subset %c%c%c%c", HB_UNTAG (tag)); + switch (tag) + { + case HB_OT_TAG_glyf: return _subset (plan); + case HB_OT_TAG_hdmx: return _subset (plan); + case HB_OT_TAG_name: return _subset (plan); + case HB_OT_TAG_head: + if (_is_table_present (plan->source, HB_OT_TAG_glyf) && !_should_drop_table (plan, HB_OT_TAG_glyf)) + return true; /* skip head, handled by glyf */ + return _subset (plan); + case HB_OT_TAG_hhea: return true; /* skip hhea, handled by hmtx */ + case HB_OT_TAG_hmtx: return _subset (plan); + case HB_OT_TAG_vhea: return true; /* skip vhea, handled by vmtx */ + case HB_OT_TAG_vmtx: return _subset (plan); + case HB_OT_TAG_maxp: return _subset (plan); + case HB_OT_TAG_sbix: return _subset (plan); + case HB_OT_TAG_loca: return true; /* skip loca, handled by glyf */ + case HB_OT_TAG_cmap: return _subset (plan); + case HB_OT_TAG_OS2 : return _subset (plan); + case HB_OT_TAG_post: return _subset (plan); + case HB_OT_TAG_COLR: return _subset (plan); + case HB_OT_TAG_CBLC: return _subset (plan); + case HB_OT_TAG_CBDT: return true; /* skip CBDT, handled by CBLC */ + +#ifndef HB_NO_SUBSET_CFF + case HB_OT_TAG_cff1: return _subset (plan); + case HB_OT_TAG_cff2: return _subset (plan); + case HB_OT_TAG_VORG: return _subset (plan); +#endif + +#ifndef HB_NO_SUBSET_LAYOUT + case HB_OT_TAG_GDEF: return _subset (plan); + case HB_OT_TAG_GSUB: return _subset (plan); + case HB_OT_TAG_GPOS: return _subset (plan); + case HB_OT_TAG_gvar: return _subset (plan); + case HB_OT_TAG_HVAR: return _subset (plan); + case HB_OT_TAG_VVAR: return _subset (plan); +#endif + + default: + hb_blob_t *source_table = hb_face_reference_table (plan->source, tag); + bool result = plan->add_table (tag, source_table); + hb_blob_destroy (source_table); + return result; } } @@ -261,33 +236,34 @@ _should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag) * Subsets a font according to provided input. **/ hb_face_t * -hb_subset (hb_face_t *source, - hb_subset_input_t *input) +hb_subset (hb_face_t *source, hb_subset_input_t *input) { if (unlikely (!input || !source)) return hb_face_get_empty (); hb_subset_plan_t *plan = hb_subset_plan_create (source, input); + if (unlikely (plan->in_error ())) + return hb_face_get_empty (); - hb_tag_t table_tags[32]; - unsigned int offset = 0, count; + hb_set_t tags_set; bool success = true; - do { - count = ARRAY_LENGTH (table_tags); - hb_face_get_table_tags (source, offset, &count, table_tags); - for (unsigned int i = 0; i < count; i++) + hb_tag_t table_tags[32]; + unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags); + while ((hb_face_get_table_tags (source, offset, &num_tables, table_tags), num_tables)) + { + for (unsigned i = 0; i < num_tables; ++i) { hb_tag_t tag = table_tags[i]; - if (_should_drop_table (plan, tag)) - { - DEBUG_MSG(SUBSET, nullptr, "drop %c%c%c%c", HB_UNTAG (tag)); - continue; - } - success = success && _subset_table (plan, tag); + if (_should_drop_table (plan, tag) && !tags_set.has (tag)) continue; + tags_set.add (tag); + success = _subset_table (plan, tag); + if (unlikely (!success)) goto end; } - offset += count; - } while (success && count == ARRAY_LENGTH (table_tags)); + offset += num_tables; + } +end: hb_face_t *result = success ? hb_face_reference (plan->dest) : hb_face_get_empty (); + hb_subset_plan_destroy (plan); return result; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset.h b/src/java.desktop/share/native/libharfbuzz/hb-subset.h index 75cc00aa39d..3deab75e2e2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset.h @@ -54,6 +54,15 @@ hb_subset_input_unicode_set (hb_subset_input_t *subset_input); HB_EXTERN hb_set_t * hb_subset_input_glyph_set (hb_subset_input_t *subset_input); +HB_EXTERN hb_set_t * +hb_subset_input_nameid_set (hb_subset_input_t *subset_input); + +HB_EXTERN hb_set_t * +hb_subset_input_namelangid_set (hb_subset_input_t *subset_input); + +HB_EXTERN hb_set_t * +hb_subset_input_drop_tables_set (hb_subset_input_t *subset_input); + HB_EXTERN void hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input, hb_bool_t drop_hints); @@ -61,16 +70,22 @@ HB_EXTERN hb_bool_t hb_subset_input_get_drop_hints (hb_subset_input_t *subset_input); HB_EXTERN void -hb_subset_input_set_drop_layout (hb_subset_input_t *subset_input, - hb_bool_t drop_layout); +hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input, + hb_bool_t desubroutinize); HB_EXTERN hb_bool_t -hb_subset_input_get_drop_layout (hb_subset_input_t *subset_input); +hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input); HB_EXTERN void -hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input, - hb_bool_t desubroutinize); +hb_subset_input_set_retain_gids (hb_subset_input_t *subset_input, + hb_bool_t retain_gids); HB_EXTERN hb_bool_t -hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input); +hb_subset_input_get_retain_gids (hb_subset_input_t *subset_input); + +HB_EXTERN void +hb_subset_input_set_name_legacy (hb_subset_input_t *subset_input, + hb_bool_t name_legacy); +HB_EXTERN hb_bool_t +hb_subset_input_get_name_legacy (hb_subset_input_t *subset_input); /* hb_subset () */ HB_EXTERN hb_face_t * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset.hh index d4d72092257..dbbe45131d2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset.hh @@ -40,19 +40,33 @@ struct hb_subset_context_t : hb_dispatch_context_t { const char *get_name () { return "SUBSET"; } - template - bool dispatch (const T &obj) { return obj.subset (this); } - static bool default_return_value () { return true; } + static return_t default_return_value () { return true; } + private: + template auto + _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN + ( obj.subset (this, hb_forward (ds)...) ) + template auto + _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN + ( obj.dispatch (this, hb_forward (ds)...) ) + public: + template auto + dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN + ( _dispatch (obj, hb_prioritize, hb_forward (ds)...) ) + + hb_blob_t *source_blob; hb_subset_plan_t *plan; hb_serialize_context_t *serializer; - unsigned int debug_depth; + hb_tag_t table_tag; - hb_subset_context_t (hb_subset_plan_t *plan_, - hb_serialize_context_t *serializer_) : + hb_subset_context_t (hb_blob_t *source_blob_, + hb_subset_plan_t *plan_, + hb_serialize_context_t *serializer_, + hb_tag_t table_tag_) : + source_blob (source_blob_), plan (plan_), serializer (serializer_), - debug_depth (0) {} + table_tag (table_tag_) {} }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh new file mode 100644 index 00000000000..88623db3387 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh @@ -0,0 +1,6780 @@ +/* == Start of generated table == */ +/* + * The following table is generated by running: + * + * ./gen-ucd-table.py ucd.nounihan.grouped.xml + * + * on file with this description: Unicode 13.0.0 + */ + +#ifndef HB_UCD_TABLE_HH +#define HB_UCD_TABLE_HH + +#include "hb.hh" + +static const hb_script_t +_hb_ucd_sc_map[157] = +{ + HB_SCRIPT_COMMON, HB_SCRIPT_INHERITED, + HB_SCRIPT_UNKNOWN, HB_SCRIPT_ARABIC, + HB_SCRIPT_ARMENIAN, HB_SCRIPT_BENGALI, + HB_SCRIPT_CYRILLIC, HB_SCRIPT_DEVANAGARI, + HB_SCRIPT_GEORGIAN, HB_SCRIPT_GREEK, + HB_SCRIPT_GUJARATI, HB_SCRIPT_GURMUKHI, + HB_SCRIPT_HANGUL, HB_SCRIPT_HAN, + HB_SCRIPT_HEBREW, HB_SCRIPT_HIRAGANA, + HB_SCRIPT_KANNADA, HB_SCRIPT_KATAKANA, + HB_SCRIPT_LAO, HB_SCRIPT_LATIN, + HB_SCRIPT_MALAYALAM, HB_SCRIPT_ORIYA, + HB_SCRIPT_TAMIL, HB_SCRIPT_TELUGU, + HB_SCRIPT_THAI, HB_SCRIPT_TIBETAN, + HB_SCRIPT_BOPOMOFO, HB_SCRIPT_BRAILLE, + HB_SCRIPT_CANADIAN_SYLLABICS, HB_SCRIPT_CHEROKEE, + HB_SCRIPT_ETHIOPIC, HB_SCRIPT_KHMER, + HB_SCRIPT_MONGOLIAN, HB_SCRIPT_MYANMAR, + HB_SCRIPT_OGHAM, HB_SCRIPT_RUNIC, + HB_SCRIPT_SINHALA, HB_SCRIPT_SYRIAC, + HB_SCRIPT_THAANA, HB_SCRIPT_YI, + HB_SCRIPT_DESERET, HB_SCRIPT_GOTHIC, + HB_SCRIPT_OLD_ITALIC, HB_SCRIPT_BUHID, + HB_SCRIPT_HANUNOO, HB_SCRIPT_TAGALOG, + HB_SCRIPT_TAGBANWA, HB_SCRIPT_CYPRIOT, + HB_SCRIPT_LIMBU, HB_SCRIPT_LINEAR_B, + HB_SCRIPT_OSMANYA, HB_SCRIPT_SHAVIAN, + HB_SCRIPT_TAI_LE, HB_SCRIPT_UGARITIC, + HB_SCRIPT_BUGINESE, HB_SCRIPT_COPTIC, + HB_SCRIPT_GLAGOLITIC, HB_SCRIPT_KHAROSHTHI, + HB_SCRIPT_NEW_TAI_LUE, HB_SCRIPT_OLD_PERSIAN, + HB_SCRIPT_SYLOTI_NAGRI, HB_SCRIPT_TIFINAGH, + HB_SCRIPT_BALINESE, HB_SCRIPT_CUNEIFORM, + HB_SCRIPT_NKO, HB_SCRIPT_PHAGS_PA, + HB_SCRIPT_PHOENICIAN, HB_SCRIPT_CARIAN, + HB_SCRIPT_CHAM, HB_SCRIPT_KAYAH_LI, + HB_SCRIPT_LEPCHA, HB_SCRIPT_LYCIAN, + HB_SCRIPT_LYDIAN, HB_SCRIPT_OL_CHIKI, + HB_SCRIPT_REJANG, HB_SCRIPT_SAURASHTRA, + HB_SCRIPT_SUNDANESE, HB_SCRIPT_VAI, + HB_SCRIPT_AVESTAN, HB_SCRIPT_BAMUM, + HB_SCRIPT_EGYPTIAN_HIEROGLYPHS, HB_SCRIPT_IMPERIAL_ARAMAIC, + HB_SCRIPT_INSCRIPTIONAL_PAHLAVI, HB_SCRIPT_INSCRIPTIONAL_PARTHIAN, + HB_SCRIPT_JAVANESE, HB_SCRIPT_KAITHI, + HB_SCRIPT_LISU, HB_SCRIPT_MEETEI_MAYEK, + HB_SCRIPT_OLD_SOUTH_ARABIAN, HB_SCRIPT_OLD_TURKIC, + HB_SCRIPT_SAMARITAN, HB_SCRIPT_TAI_THAM, + HB_SCRIPT_TAI_VIET, HB_SCRIPT_BATAK, + HB_SCRIPT_BRAHMI, HB_SCRIPT_MANDAIC, + HB_SCRIPT_CHAKMA, HB_SCRIPT_MEROITIC_CURSIVE, + HB_SCRIPT_MEROITIC_HIEROGLYPHS, HB_SCRIPT_MIAO, + HB_SCRIPT_SHARADA, HB_SCRIPT_SORA_SOMPENG, + HB_SCRIPT_TAKRI, HB_SCRIPT_BASSA_VAH, + HB_SCRIPT_CAUCASIAN_ALBANIAN, HB_SCRIPT_DUPLOYAN, + HB_SCRIPT_ELBASAN, HB_SCRIPT_GRANTHA, + HB_SCRIPT_KHOJKI, HB_SCRIPT_KHUDAWADI, + HB_SCRIPT_LINEAR_A, HB_SCRIPT_MAHAJANI, + HB_SCRIPT_MANICHAEAN, HB_SCRIPT_MENDE_KIKAKUI, + HB_SCRIPT_MODI, HB_SCRIPT_MRO, + HB_SCRIPT_NABATAEAN, HB_SCRIPT_OLD_NORTH_ARABIAN, + HB_SCRIPT_OLD_PERMIC, HB_SCRIPT_PAHAWH_HMONG, + HB_SCRIPT_PALMYRENE, HB_SCRIPT_PAU_CIN_HAU, + HB_SCRIPT_PSALTER_PAHLAVI, HB_SCRIPT_SIDDHAM, + HB_SCRIPT_TIRHUTA, HB_SCRIPT_WARANG_CITI, + HB_SCRIPT_AHOM, HB_SCRIPT_ANATOLIAN_HIEROGLYPHS, + HB_SCRIPT_HATRAN, HB_SCRIPT_MULTANI, + HB_SCRIPT_OLD_HUNGARIAN, HB_SCRIPT_SIGNWRITING, + HB_SCRIPT_ADLAM, HB_SCRIPT_BHAIKSUKI, + HB_SCRIPT_MARCHEN, HB_SCRIPT_OSAGE, + HB_SCRIPT_TANGUT, HB_SCRIPT_NEWA, + HB_SCRIPT_MASARAM_GONDI, HB_SCRIPT_NUSHU, + HB_SCRIPT_SOYOMBO, HB_SCRIPT_ZANABAZAR_SQUARE, + HB_SCRIPT_DOGRA, HB_SCRIPT_GUNJALA_GONDI, + HB_SCRIPT_HANIFI_ROHINGYA, HB_SCRIPT_MAKASAR, + HB_SCRIPT_MEDEFAIDRIN, HB_SCRIPT_OLD_SOGDIAN, + HB_SCRIPT_SOGDIAN, HB_SCRIPT_ELYMAIC, + HB_SCRIPT_NANDINAGARI, HB_SCRIPT_NYIAKENG_PUACHUE_HMONG, + HB_SCRIPT_WANCHO, HB_SCRIPT_CHORASMIAN, + HB_SCRIPT_DIVES_AKURU, HB_SCRIPT_KHITAN_SMALL_SCRIPT, + HB_SCRIPT_YEZIDI, +}; +static const uint16_t +_hb_ucd_dm1_p0_map[825] = +{ + 0x003Bu, 0x004Bu, 0x0060u, 0x00B4u, 0x00B7u, 0x00C5u, 0x02B9u, 0x0300u, + 0x0301u, 0x0313u, 0x0385u, 0x0386u, 0x0388u, 0x0389u, 0x038Au, 0x038Cu, + 0x038Eu, 0x038Fu, 0x0390u, 0x03A9u, 0x03ACu, 0x03ADu, 0x03AEu, 0x03AFu, + 0x03B0u, 0x03B9u, 0x03CCu, 0x03CDu, 0x03CEu, 0x2002u, 0x2003u, 0x3008u, + 0x3009u, 0x349Eu, 0x34B9u, 0x34BBu, 0x34DFu, 0x3515u, 0x36EEu, 0x36FCu, + 0x3781u, 0x382Fu, 0x3862u, 0x387Cu, 0x38C7u, 0x38E3u, 0x391Cu, 0x393Au, + 0x3A2Eu, 0x3A6Cu, 0x3AE4u, 0x3B08u, 0x3B19u, 0x3B49u, 0x3B9Du, 0x3C18u, + 0x3C4Eu, 0x3D33u, 0x3D96u, 0x3EACu, 0x3EB8u, 0x3F1Bu, 0x3FFCu, 0x4008u, + 0x4018u, 0x4039u, 0x4046u, 0x4096u, 0x40E3u, 0x412Fu, 0x4202u, 0x4227u, + 0x42A0u, 0x4301u, 0x4334u, 0x4359u, 0x43D5u, 0x43D9u, 0x440Bu, 0x446Bu, + 0x452Bu, 0x455Du, 0x4561u, 0x456Bu, 0x45D7u, 0x45F9u, 0x4635u, 0x46BEu, + 0x46C7u, 0x4995u, 0x49E6u, 0x4A6Eu, 0x4A76u, 0x4AB2u, 0x4B33u, 0x4BCEu, + 0x4CCEu, 0x4CEDu, 0x4CF8u, 0x4D56u, 0x4E0Du, 0x4E26u, 0x4E32u, 0x4E38u, + 0x4E39u, 0x4E3Du, 0x4E41u, 0x4E82u, 0x4E86u, 0x4EAEu, 0x4EC0u, 0x4ECCu, + 0x4EE4u, 0x4F60u, 0x4F80u, 0x4F86u, 0x4F8Bu, 0x4FAEu, 0x4FBBu, 0x4FBFu, + 0x5002u, 0x502Bu, 0x507Au, 0x5099u, 0x50CFu, 0x50DAu, 0x50E7u, 0x5140u, + 0x5145u, 0x514Du, 0x5154u, 0x5164u, 0x5167u, 0x5168u, 0x5169u, 0x516Du, + 0x5177u, 0x5180u, 0x518Du, 0x5192u, 0x5195u, 0x5197u, 0x51A4u, 0x51ACu, + 0x51B5u, 0x51B7u, 0x51C9u, 0x51CCu, 0x51DCu, 0x51DEu, 0x51F5u, 0x5203u, + 0x5207u, 0x5217u, 0x5229u, 0x523Au, 0x523Bu, 0x5246u, 0x5272u, 0x5277u, + 0x5289u, 0x529Bu, 0x52A3u, 0x52B3u, 0x52C7u, 0x52C9u, 0x52D2u, 0x52DEu, + 0x52E4u, 0x52F5u, 0x52FAu, 0x5305u, 0x5306u, 0x5317u, 0x533Fu, 0x5349u, + 0x5351u, 0x535Au, 0x5373u, 0x5375u, 0x537Du, 0x537Fu, 0x53C3u, 0x53CAu, + 0x53DFu, 0x53E5u, 0x53EBu, 0x53F1u, 0x5406u, 0x540Fu, 0x541Du, 0x5438u, + 0x5442u, 0x5448u, 0x5468u, 0x549Eu, 0x54A2u, 0x54BDu, 0x54F6u, 0x5510u, + 0x5553u, 0x5555u, 0x5563u, 0x5584u, 0x5587u, 0x5599u, 0x559Du, 0x55ABu, + 0x55B3u, 0x55C0u, 0x55C2u, 0x55E2u, 0x5606u, 0x5651u, 0x5668u, 0x5674u, + 0x56F9u, 0x5716u, 0x5717u, 0x578Bu, 0x57CEu, 0x57F4u, 0x580Du, 0x5831u, + 0x5832u, 0x5840u, 0x585Au, 0x585Eu, 0x58A8u, 0x58ACu, 0x58B3u, 0x58D8u, + 0x58DFu, 0x58EEu, 0x58F2u, 0x58F7u, 0x5906u, 0x591Au, 0x5922u, 0x5944u, + 0x5948u, 0x5951u, 0x5954u, 0x5962u, 0x5973u, 0x59D8u, 0x59ECu, 0x5A1Bu, + 0x5A27u, 0x5A62u, 0x5A66u, 0x5AB5u, 0x5B08u, 0x5B28u, 0x5B3Eu, 0x5B85u, + 0x5BC3u, 0x5BD8u, 0x5BE7u, 0x5BEEu, 0x5BF3u, 0x5BFFu, 0x5C06u, 0x5C22u, + 0x5C3Fu, 0x5C60u, 0x5C62u, 0x5C64u, 0x5C65u, 0x5C6Eu, 0x5C8Du, 0x5CC0u, + 0x5D19u, 0x5D43u, 0x5D50u, 0x5D6Bu, 0x5D6Eu, 0x5D7Cu, 0x5DB2u, 0x5DBAu, + 0x5DE1u, 0x5DE2u, 0x5DFDu, 0x5E28u, 0x5E3Du, 0x5E69u, 0x5E74u, 0x5EA6u, + 0x5EB0u, 0x5EB3u, 0x5EB6u, 0x5EC9u, 0x5ECAu, 0x5ED2u, 0x5ED3u, 0x5ED9u, + 0x5EECu, 0x5EFEu, 0x5F04u, 0x5F22u, 0x5F53u, 0x5F62u, 0x5F69u, 0x5F6Bu, + 0x5F8Bu, 0x5F9Au, 0x5FA9u, 0x5FADu, 0x5FCDu, 0x5FD7u, 0x5FF5u, 0x5FF9u, + 0x6012u, 0x601Cu, 0x6075u, 0x6081u, 0x6094u, 0x60C7u, 0x60D8u, 0x60E1u, + 0x6108u, 0x6144u, 0x6148u, 0x614Cu, 0x614Eu, 0x6160u, 0x6168u, 0x617Au, + 0x618Eu, 0x6190u, 0x61A4u, 0x61AFu, 0x61B2u, 0x61DEu, 0x61F2u, 0x61F6u, + 0x6200u, 0x6210u, 0x621Bu, 0x622Eu, 0x6234u, 0x625Du, 0x62B1u, 0x62C9u, + 0x62CFu, 0x62D3u, 0x62D4u, 0x62FCu, 0x62FEu, 0x633Du, 0x6350u, 0x6368u, + 0x637Bu, 0x6383u, 0x63A0u, 0x63A9u, 0x63C4u, 0x63C5u, 0x63E4u, 0x641Cu, + 0x6422u, 0x6452u, 0x6469u, 0x6477u, 0x647Eu, 0x649Au, 0x649Du, 0x64C4u, + 0x654Fu, 0x6556u, 0x656Cu, 0x6578u, 0x6599u, 0x65C5u, 0x65E2u, 0x65E3u, + 0x6613u, 0x6649u, 0x6674u, 0x6688u, 0x6691u, 0x669Cu, 0x66B4u, 0x66C6u, + 0x66F4u, 0x66F8u, 0x6700u, 0x6717u, 0x671Bu, 0x6721u, 0x674Eu, 0x6753u, + 0x6756u, 0x675Eu, 0x677Bu, 0x6785u, 0x6797u, 0x67F3u, 0x67FAu, 0x6817u, + 0x681Fu, 0x6852u, 0x6881u, 0x6885u, 0x688Eu, 0x68A8u, 0x6914u, 0x6942u, + 0x69A3u, 0x69EAu, 0x6A02u, 0x6A13u, 0x6AA8u, 0x6AD3u, 0x6ADBu, 0x6B04u, + 0x6B21u, 0x6B54u, 0x6B72u, 0x6B77u, 0x6B79u, 0x6B9Fu, 0x6BAEu, 0x6BBAu, + 0x6BBBu, 0x6C4Eu, 0x6C67u, 0x6C88u, 0x6CBFu, 0x6CCCu, 0x6CCDu, 0x6CE5u, + 0x6D16u, 0x6D1Bu, 0x6D1Eu, 0x6D34u, 0x6D3Eu, 0x6D41u, 0x6D69u, 0x6D6Au, + 0x6D77u, 0x6D78u, 0x6D85u, 0x6DCBu, 0x6DDAu, 0x6DEAu, 0x6DF9u, 0x6E1Au, + 0x6E2Fu, 0x6E6Eu, 0x6E9Cu, 0x6EBAu, 0x6EC7u, 0x6ECBu, 0x6ED1u, 0x6EDBu, + 0x6F0Fu, 0x6F22u, 0x6F23u, 0x6F6Eu, 0x6FC6u, 0x6FEBu, 0x6FFEu, 0x701Bu, + 0x701Eu, 0x7039u, 0x704Au, 0x7070u, 0x7077u, 0x707Du, 0x7099u, 0x70ADu, + 0x70C8u, 0x70D9u, 0x7145u, 0x7149u, 0x716Eu, 0x719Cu, 0x71CEu, 0x71D0u, + 0x7210u, 0x721Bu, 0x7228u, 0x722Bu, 0x7235u, 0x7250u, 0x7262u, 0x7280u, + 0x7295u, 0x72AFu, 0x72C0u, 0x72FCu, 0x732Au, 0x7375u, 0x737Au, 0x7387u, + 0x738Bu, 0x73A5u, 0x73B2u, 0x73DEu, 0x7406u, 0x7409u, 0x7422u, 0x7447u, + 0x745Cu, 0x7469u, 0x7471u, 0x7485u, 0x7489u, 0x7498u, 0x74CAu, 0x7506u, + 0x7524u, 0x753Bu, 0x753Eu, 0x7559u, 0x7565u, 0x7570u, 0x75E2u, 0x7610u, + 0x761Du, 0x761Fu, 0x7642u, 0x7669u, 0x76CAu, 0x76DBu, 0x76E7u, 0x76F4u, + 0x7701u, 0x771Eu, 0x771Fu, 0x7740u, 0x774Au, 0x778Bu, 0x77A7u, 0x784Eu, + 0x786Bu, 0x788Cu, 0x7891u, 0x78CAu, 0x78CCu, 0x78FBu, 0x792Au, 0x793Cu, + 0x793Eu, 0x7948u, 0x7949u, 0x7950u, 0x7956u, 0x795Du, 0x795Eu, 0x7965u, + 0x797Fu, 0x798Du, 0x798Eu, 0x798Fu, 0x79AEu, 0x79CAu, 0x79EBu, 0x7A1Cu, + 0x7A40u, 0x7A4Au, 0x7A4Fu, 0x7A81u, 0x7AB1u, 0x7ACBu, 0x7AEEu, 0x7B20u, + 0x7BC0u, 0x7BC6u, 0x7BC9u, 0x7C3Eu, 0x7C60u, 0x7C7Bu, 0x7C92u, 0x7CBEu, + 0x7CD2u, 0x7CD6u, 0x7CE3u, 0x7CE7u, 0x7CE8u, 0x7D00u, 0x7D10u, 0x7D22u, + 0x7D2Fu, 0x7D5Bu, 0x7D63u, 0x7DA0u, 0x7DBEu, 0x7DC7u, 0x7DF4u, 0x7E02u, + 0x7E09u, 0x7E37u, 0x7E41u, 0x7E45u, 0x7F3Eu, 0x7F72u, 0x7F79u, 0x7F7Au, + 0x7F85u, 0x7F95u, 0x7F9Au, 0x7FBDu, 0x7FFAu, 0x8001u, 0x8005u, 0x8046u, + 0x8060u, 0x806Fu, 0x8070u, 0x807Eu, 0x808Bu, 0x80ADu, 0x80B2u, 0x8103u, + 0x813Eu, 0x81D8u, 0x81E8u, 0x81EDu, 0x8201u, 0x8204u, 0x8218u, 0x826Fu, + 0x8279u, 0x828Bu, 0x8291u, 0x829Du, 0x82B1u, 0x82B3u, 0x82BDu, 0x82E5u, + 0x82E6u, 0x831Du, 0x8323u, 0x8336u, 0x8352u, 0x8353u, 0x8363u, 0x83ADu, + 0x83BDu, 0x83C9u, 0x83CAu, 0x83CCu, 0x83DCu, 0x83E7u, 0x83EFu, 0x83F1u, + 0x843Du, 0x8449u, 0x8457u, 0x84EEu, 0x84F1u, 0x84F3u, 0x84FCu, 0x8516u, + 0x8564u, 0x85CDu, 0x85FAu, 0x8606u, 0x8612u, 0x862Du, 0x863Fu, 0x8650u, + 0x865Cu, 0x8667u, 0x8669u, 0x8688u, 0x86A9u, 0x86E2u, 0x870Eu, 0x8728u, + 0x876Bu, 0x8779u, 0x8786u, 0x87BAu, 0x87E1u, 0x8801u, 0x881Fu, 0x884Cu, + 0x8860u, 0x8863u, 0x88C2u, 0x88CFu, 0x88D7u, 0x88DEu, 0x88E1u, 0x88F8u, + 0x88FAu, 0x8910u, 0x8941u, 0x8964u, 0x8986u, 0x898Bu, 0x8996u, 0x8AA0u, + 0x8AAAu, 0x8ABFu, 0x8ACBu, 0x8AD2u, 0x8AD6u, 0x8AEDu, 0x8AF8u, 0x8AFEu, + 0x8B01u, 0x8B39u, 0x8B58u, 0x8B80u, 0x8B8Au, 0x8C48u, 0x8C55u, 0x8CABu, + 0x8CC1u, 0x8CC2u, 0x8CC8u, 0x8CD3u, 0x8D08u, 0x8D1Bu, 0x8D77u, 0x8DBCu, + 0x8DCBu, 0x8DEFu, 0x8DF0u, 0x8ECAu, 0x8ED4u, 0x8F26u, 0x8F2Au, 0x8F38u, + 0x8F3Bu, 0x8F62u, 0x8F9Eu, 0x8FB0u, 0x8FB6u, 0x9023u, 0x9038u, 0x9072u, + 0x907Cu, 0x908Fu, 0x9094u, 0x90CEu, 0x90DEu, 0x90F1u, 0x90FDu, 0x9111u, + 0x911Bu, 0x916Au, 0x9199u, 0x91B4u, 0x91CCu, 0x91CFu, 0x91D1u, 0x9234u, + 0x9238u, 0x9276u, 0x927Cu, 0x92D7u, 0x92D8u, 0x9304u, 0x934Au, 0x93F9u, + 0x9415u, 0x958Bu, 0x95ADu, 0x95B7u, 0x962Eu, 0x964Bu, 0x964Du, 0x9675u, + 0x9678u, 0x967Cu, 0x9686u, 0x96A3u, 0x96B7u, 0x96B8u, 0x96C3u, 0x96E2u, + 0x96E3u, 0x96F6u, 0x96F7u, 0x9723u, 0x9732u, 0x9748u, 0x9756u, 0x97DBu, + 0x97E0u, 0x97FFu, 0x980Bu, 0x9818u, 0x9829u, 0x983Bu, 0x985Eu, 0x98E2u, + 0x98EFu, 0x98FCu, 0x9928u, 0x9929u, 0x99A7u, 0x99C2u, 0x99F1u, 0x99FEu, + 0x9A6Au, 0x9B12u, 0x9B6Fu, 0x9C40u, 0x9C57u, 0x9CFDu, 0x9D67u, 0x9DB4u, + 0x9DFAu, 0x9E1Eu, 0x9E7Fu, 0x9E97u, 0x9E9Fu, 0x9EBBu, 0x9ECEu, 0x9EF9u, + 0x9EFEu, 0x9F05u, 0x9F0Fu, 0x9F16u, 0x9F3Bu, 0x9F43u, 0x9F8Du, 0x9F8Eu, + 0x9F9Cu, +}; +static const uint16_t +_hb_ucd_dm1_p2_map[110] = +{ + 0x0122u, 0x051Cu, 0x0525u, 0x054Bu, 0x063Au, 0x0804u, 0x08DEu, 0x0A2Cu, + 0x0B63u, 0x14E4u, 0x16A8u, 0x16EAu, 0x19C8u, 0x1B18u, 0x1D0Bu, 0x1DE4u, + 0x1DE6u, 0x2183u, 0x219Fu, 0x2331u, 0x26D4u, 0x2844u, 0x284Au, 0x2B0Cu, + 0x2BF1u, 0x300Au, 0x32B8u, 0x335Fu, 0x3393u, 0x339Cu, 0x33C3u, 0x33D5u, + 0x346Du, 0x36A3u, 0x38A7u, 0x3A8Du, 0x3AFAu, 0x3CBCu, 0x3D1Eu, 0x3ED1u, + 0x3F5Eu, 0x3F8Eu, 0x4263u, 0x42EEu, 0x43ABu, 0x4608u, 0x4735u, 0x4814u, + 0x4C36u, 0x4C92u, 0x4FA1u, 0x4FB8u, 0x5044u, 0x50F2u, 0x50F3u, 0x5119u, + 0x5133u, 0x5249u, 0x541Du, 0x5626u, 0x569Au, 0x56C5u, 0x597Cu, 0x5AA7u, + 0x5BABu, 0x5C80u, 0x5CD0u, 0x5F86u, 0x61DAu, 0x6228u, 0x6247u, 0x62D9u, + 0x633Eu, 0x64DAu, 0x6523u, 0x65A8u, 0x67A7u, 0x67B5u, 0x6B3Cu, 0x6C36u, + 0x6CD5u, 0x6D6Bu, 0x6F2Cu, 0x6FB1u, 0x70D2u, 0x73CAu, 0x7667u, 0x78AEu, + 0x7966u, 0x7CA8u, 0x7ED3u, 0x7F2Fu, 0x85D2u, 0x85EDu, 0x872Eu, 0x8BFAu, + 0x8D77u, 0x9145u, 0x91DFu, 0x921Au, 0x940Au, 0x9496u, 0x95B6u, 0x9B30u, + 0xA0CEu, 0xA105u, 0xA20Eu, 0xA291u, 0xA392u, 0xA600u, +}; +static const uint32_t +_hb_ucd_dm2_u32_map[638] = +{ + HB_CODEPOINT_ENCODE3_11_7_14 (0x003Cu, 0x0338u, 0x226Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x003Du, 0x0338u, 0x2260u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x003Eu, 0x0338u, 0x226Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0300u, 0x00C0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0301u, 0x00C1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0302u, 0x00C2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0303u, 0x00C3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0304u, 0x0100u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0306u, 0x0102u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0307u, 0x0226u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0308u, 0x00C4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0309u, 0x1EA2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Au, 0x00C5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Cu, 0x01CDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Fu, 0x0200u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0311u, 0x0202u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0323u, 0x1EA0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0325u, 0x1E00u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0328u, 0x0104u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0307u, 0x1E02u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0323u, 0x1E04u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0331u, 0x1E06u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0301u, 0x0106u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0302u, 0x0108u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0307u, 0x010Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x030Cu, 0x010Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0327u, 0x00C7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0307u, 0x1E0Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x030Cu, 0x010Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0323u, 0x1E0Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0327u, 0x1E10u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x032Du, 0x1E12u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0331u, 0x1E0Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0300u, 0x00C8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0301u, 0x00C9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0302u, 0x00CAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0303u, 0x1EBCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0304u, 0x0112u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0306u, 0x0114u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0307u, 0x0116u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0308u, 0x00CBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0309u, 0x1EBAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x030Cu, 0x011Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x030Fu, 0x0204u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0311u, 0x0206u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0323u, 0x1EB8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0327u, 0x0228u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0328u, 0x0118u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x032Du, 0x1E18u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0330u, 0x1E1Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0046u, 0x0307u, 0x1E1Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0301u, 0x01F4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0302u, 0x011Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0304u, 0x1E20u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0306u, 0x011Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0307u, 0x0120u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x030Cu, 0x01E6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0327u, 0x0122u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0302u, 0x0124u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0307u, 0x1E22u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0308u, 0x1E26u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x030Cu, 0x021Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0323u, 0x1E24u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0327u, 0x1E28u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x032Eu, 0x1E2Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0300u, 0x00CCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0301u, 0x00CDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0302u, 0x00CEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0303u, 0x0128u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0304u, 0x012Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0306u, 0x012Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0307u, 0x0130u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0308u, 0x00CFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0309u, 0x1EC8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x030Cu, 0x01CFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x030Fu, 0x0208u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0311u, 0x020Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0323u, 0x1ECAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0328u, 0x012Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0330u, 0x1E2Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Au, 0x0302u, 0x0134u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0301u, 0x1E30u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x030Cu, 0x01E8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0323u, 0x1E32u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0327u, 0x0136u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0331u, 0x1E34u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0301u, 0x0139u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x030Cu, 0x013Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0323u, 0x1E36u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0327u, 0x013Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x032Du, 0x1E3Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0331u, 0x1E3Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0301u, 0x1E3Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0307u, 0x1E40u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0323u, 0x1E42u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0300u, 0x01F8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0301u, 0x0143u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0303u, 0x00D1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0307u, 0x1E44u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x030Cu, 0x0147u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0323u, 0x1E46u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0327u, 0x0145u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x032Du, 0x1E4Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0331u, 0x1E48u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0300u, 0x00D2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0301u, 0x00D3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0302u, 0x00D4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0303u, 0x00D5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0304u, 0x014Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0306u, 0x014Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0307u, 0x022Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0308u, 0x00D6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0309u, 0x1ECEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Bu, 0x0150u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Cu, 0x01D1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Fu, 0x020Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0311u, 0x020Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x031Bu, 0x01A0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0323u, 0x1ECCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0328u, 0x01EAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0050u, 0x0301u, 0x1E54u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0050u, 0x0307u, 0x1E56u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0301u, 0x0154u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0307u, 0x1E58u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x030Cu, 0x0158u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x030Fu, 0x0210u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0311u, 0x0212u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0323u, 0x1E5Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0327u, 0x0156u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0331u, 0x1E5Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0301u, 0x015Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0302u, 0x015Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0307u, 0x1E60u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x030Cu, 0x0160u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0323u, 0x1E62u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0326u, 0x0218u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0327u, 0x015Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0307u, 0x1E6Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x030Cu, 0x0164u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0323u, 0x1E6Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0326u, 0x021Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0327u, 0x0162u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x032Du, 0x1E70u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0331u, 0x1E6Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0300u, 0x00D9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0301u, 0x00DAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0302u, 0x00DBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0303u, 0x0168u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0304u, 0x016Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0306u, 0x016Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0308u, 0x00DCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0309u, 0x1EE6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Au, 0x016Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Bu, 0x0170u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Cu, 0x01D3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Fu, 0x0214u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0311u, 0x0216u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x031Bu, 0x01AFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0323u, 0x1EE4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0324u, 0x1E72u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0328u, 0x0172u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x032Du, 0x1E76u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0330u, 0x1E74u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0056u, 0x0303u, 0x1E7Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0056u, 0x0323u, 0x1E7Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0300u, 0x1E80u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0301u, 0x1E82u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0302u, 0x0174u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0307u, 0x1E86u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0308u, 0x1E84u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0323u, 0x1E88u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0058u, 0x0307u, 0x1E8Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0058u, 0x0308u, 0x1E8Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0300u, 0x1EF2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0301u, 0x00DDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0302u, 0x0176u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0303u, 0x1EF8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0304u, 0x0232u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0307u, 0x1E8Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0308u, 0x0178u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0309u, 0x1EF6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0323u, 0x1EF4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0301u, 0x0179u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0302u, 0x1E90u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0307u, 0x017Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x030Cu, 0x017Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0323u, 0x1E92u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0331u, 0x1E94u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0300u, 0x00E0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0301u, 0x00E1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0302u, 0x00E2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0303u, 0x00E3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0304u, 0x0101u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0306u, 0x0103u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0307u, 0x0227u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0308u, 0x00E4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0309u, 0x1EA3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Au, 0x00E5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Cu, 0x01CEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Fu, 0x0201u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0311u, 0x0203u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0323u, 0x1EA1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0325u, 0x1E01u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0328u, 0x0105u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0307u, 0x1E03u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0323u, 0x1E05u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0331u, 0x1E07u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0301u, 0x0107u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0302u, 0x0109u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0307u, 0x010Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x030Cu, 0x010Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0327u, 0x00E7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0307u, 0x1E0Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x030Cu, 0x010Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0323u, 0x1E0Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0327u, 0x1E11u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x032Du, 0x1E13u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0331u, 0x1E0Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0300u, 0x00E8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0301u, 0x00E9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0302u, 0x00EAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0303u, 0x1EBDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0304u, 0x0113u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0306u, 0x0115u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0307u, 0x0117u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0308u, 0x00EBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0309u, 0x1EBBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x030Cu, 0x011Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x030Fu, 0x0205u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0311u, 0x0207u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0323u, 0x1EB9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0327u, 0x0229u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0328u, 0x0119u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x032Du, 0x1E19u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0330u, 0x1E1Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0066u, 0x0307u, 0x1E1Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0301u, 0x01F5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0302u, 0x011Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0304u, 0x1E21u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0306u, 0x011Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0307u, 0x0121u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x030Cu, 0x01E7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0327u, 0x0123u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0302u, 0x0125u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0307u, 0x1E23u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0308u, 0x1E27u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x030Cu, 0x021Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0323u, 0x1E25u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0327u, 0x1E29u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x032Eu, 0x1E2Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0331u, 0x1E96u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0300u, 0x00ECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0301u, 0x00EDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0302u, 0x00EEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0303u, 0x0129u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0304u, 0x012Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0306u, 0x012Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0308u, 0x00EFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0309u, 0x1EC9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x030Cu, 0x01D0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x030Fu, 0x0209u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0311u, 0x020Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0323u, 0x1ECBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0328u, 0x012Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0330u, 0x1E2Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Au, 0x0302u, 0x0135u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Au, 0x030Cu, 0x01F0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0301u, 0x1E31u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x030Cu, 0x01E9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0323u, 0x1E33u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0327u, 0x0137u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0331u, 0x1E35u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0301u, 0x013Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x030Cu, 0x013Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0323u, 0x1E37u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0327u, 0x013Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x032Du, 0x1E3Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0331u, 0x1E3Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0301u, 0x1E3Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0307u, 0x1E41u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0323u, 0x1E43u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0300u, 0x01F9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0301u, 0x0144u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0303u, 0x00F1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0307u, 0x1E45u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x030Cu, 0x0148u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0323u, 0x1E47u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0327u, 0x0146u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x032Du, 0x1E4Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0331u, 0x1E49u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0300u, 0x00F2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0301u, 0x00F3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0302u, 0x00F4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0303u, 0x00F5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0304u, 0x014Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0306u, 0x014Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0307u, 0x022Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0308u, 0x00F6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0309u, 0x1ECFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Bu, 0x0151u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Cu, 0x01D2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Fu, 0x020Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0311u, 0x020Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x031Bu, 0x01A1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0323u, 0x1ECDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0328u, 0x01EBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0070u, 0x0301u, 0x1E55u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0070u, 0x0307u, 0x1E57u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0301u, 0x0155u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0307u, 0x1E59u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x030Cu, 0x0159u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x030Fu, 0x0211u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0311u, 0x0213u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0323u, 0x1E5Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0327u, 0x0157u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0331u, 0x1E5Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0301u, 0x015Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0302u, 0x015Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0307u, 0x1E61u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x030Cu, 0x0161u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0323u, 0x1E63u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0326u, 0x0219u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0327u, 0x015Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0307u, 0x1E6Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0308u, 0x1E97u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x030Cu, 0x0165u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0323u, 0x1E6Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0326u, 0x021Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0327u, 0x0163u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x032Du, 0x1E71u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0331u, 0x1E6Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0300u, 0x00F9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0301u, 0x00FAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0302u, 0x00FBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0303u, 0x0169u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0304u, 0x016Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0306u, 0x016Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0308u, 0x00FCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0309u, 0x1EE7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Au, 0x016Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Bu, 0x0171u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Cu, 0x01D4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Fu, 0x0215u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0311u, 0x0217u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x031Bu, 0x01B0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0323u, 0x1EE5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0324u, 0x1E73u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0328u, 0x0173u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x032Du, 0x1E77u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0330u, 0x1E75u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0076u, 0x0303u, 0x1E7Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0076u, 0x0323u, 0x1E7Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0300u, 0x1E81u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0301u, 0x1E83u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0302u, 0x0175u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0307u, 0x1E87u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0308u, 0x1E85u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x030Au, 0x1E98u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0323u, 0x1E89u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0078u, 0x0307u, 0x1E8Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0078u, 0x0308u, 0x1E8Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0300u, 0x1EF3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0301u, 0x00FDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0302u, 0x0177u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0303u, 0x1EF9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0304u, 0x0233u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0307u, 0x1E8Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0308u, 0x00FFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0309u, 0x1EF7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x030Au, 0x1E99u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0323u, 0x1EF5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0301u, 0x017Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0302u, 0x1E91u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0307u, 0x017Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x030Cu, 0x017Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0323u, 0x1E93u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0331u, 0x1E95u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0300u, 0x1FEDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0301u, 0x0385u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0342u, 0x1FC1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0300u, 0x1EA6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0301u, 0x1EA4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0303u, 0x1EAAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0309u, 0x1EA8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C4u, 0x0304u, 0x01DEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C5u, 0x0301u, 0x01FAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C6u, 0x0301u, 0x01FCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C6u, 0x0304u, 0x01E2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C7u, 0x0301u, 0x1E08u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0300u, 0x1EC0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0301u, 0x1EBEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0303u, 0x1EC4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0309u, 0x1EC2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CFu, 0x0301u, 0x1E2Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0300u, 0x1ED2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0301u, 0x1ED0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0303u, 0x1ED6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0309u, 0x1ED4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0301u, 0x1E4Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0304u, 0x022Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0308u, 0x1E4Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D6u, 0x0304u, 0x022Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D8u, 0x0301u, 0x01FEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0300u, 0x01DBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0301u, 0x01D7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0304u, 0x01D5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x030Cu, 0x01D9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0300u, 0x1EA7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0301u, 0x1EA5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0303u, 0x1EABu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0309u, 0x1EA9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E4u, 0x0304u, 0x01DFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E5u, 0x0301u, 0x01FBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E6u, 0x0301u, 0x01FDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E6u, 0x0304u, 0x01E3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E7u, 0x0301u, 0x1E09u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0300u, 0x1EC1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0301u, 0x1EBFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0303u, 0x1EC5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0309u, 0x1EC3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EFu, 0x0301u, 0x1E2Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0300u, 0x1ED3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0301u, 0x1ED1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0303u, 0x1ED7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0309u, 0x1ED5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0301u, 0x1E4Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0304u, 0x022Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0308u, 0x1E4Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F6u, 0x0304u, 0x022Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F8u, 0x0301u, 0x01FFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0300u, 0x01DCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0301u, 0x01D8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0304u, 0x01D6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x030Cu, 0x01DAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0300u, 0x1EB0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0301u, 0x1EAEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0303u, 0x1EB4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0309u, 0x1EB2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0300u, 0x1EB1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0301u, 0x1EAFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0303u, 0x1EB5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0309u, 0x1EB3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0112u, 0x0300u, 0x1E14u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0112u, 0x0301u, 0x1E16u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0113u, 0x0300u, 0x1E15u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0113u, 0x0301u, 0x1E17u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014Cu, 0x0300u, 0x1E50u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014Cu, 0x0301u, 0x1E52u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014Du, 0x0300u, 0x1E51u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014Du, 0x0301u, 0x1E53u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x015Au, 0x0307u, 0x1E64u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x015Bu, 0x0307u, 0x1E65u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0160u, 0x0307u, 0x1E66u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0161u, 0x0307u, 0x1E67u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0168u, 0x0301u, 0x1E78u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0169u, 0x0301u, 0x1E79u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x016Au, 0x0308u, 0x1E7Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x016Bu, 0x0308u, 0x1E7Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x017Fu, 0x0307u, 0x1E9Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0300u, 0x1EDCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0301u, 0x1EDAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0303u, 0x1EE0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0309u, 0x1EDEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0323u, 0x1EE2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0300u, 0x1EDDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0301u, 0x1EDBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0303u, 0x1EE1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0309u, 0x1EDFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0323u, 0x1EE3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0300u, 0x1EEAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0301u, 0x1EE8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0303u, 0x1EEEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0309u, 0x1EECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0323u, 0x1EF0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0300u, 0x1EEBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0301u, 0x1EE9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0303u, 0x1EEFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0309u, 0x1EEDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0323u, 0x1EF1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B7u, 0x030Cu, 0x01EEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01EAu, 0x0304u, 0x01ECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01EBu, 0x0304u, 0x01EDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0226u, 0x0304u, 0x01E0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0227u, 0x0304u, 0x01E1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0228u, 0x0306u, 0x1E1Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0229u, 0x0306u, 0x1E1Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x022Eu, 0x0304u, 0x0230u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x022Fu, 0x0304u, 0x0231u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0292u, 0x030Cu, 0x01EFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0308u, 0x0301u, 0x0000u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0300u, 0x1FBAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0301u, 0x0386u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0304u, 0x1FB9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0306u, 0x1FB8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0313u, 0x1F08u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0314u, 0x1F09u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0345u, 0x1FBCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0300u, 0x1FC8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0301u, 0x0388u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0313u, 0x1F18u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0314u, 0x1F19u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0300u, 0x1FCAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0301u, 0x0389u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0313u, 0x1F28u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0314u, 0x1F29u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0345u, 0x1FCCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0300u, 0x1FDAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0301u, 0x038Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0304u, 0x1FD9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0306u, 0x1FD8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0308u, 0x03AAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0313u, 0x1F38u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0314u, 0x1F39u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0300u, 0x1FF8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0301u, 0x038Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0313u, 0x1F48u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0314u, 0x1F49u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A1u, 0x0314u, 0x1FECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0300u, 0x1FEAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0301u, 0x038Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0304u, 0x1FE9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0306u, 0x1FE8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0308u, 0x03ABu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0314u, 0x1F59u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0300u, 0x1FFAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0301u, 0x038Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0313u, 0x1F68u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0314u, 0x1F69u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0345u, 0x1FFCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03ACu, 0x0345u, 0x1FB4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03AEu, 0x0345u, 0x1FC4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0300u, 0x1F70u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0301u, 0x03ACu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0304u, 0x1FB1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0306u, 0x1FB0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0313u, 0x1F00u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0314u, 0x1F01u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0342u, 0x1FB6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0345u, 0x1FB3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0300u, 0x1F72u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0301u, 0x03ADu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0313u, 0x1F10u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0314u, 0x1F11u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0300u, 0x1F74u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0301u, 0x03AEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0313u, 0x1F20u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0314u, 0x1F21u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0342u, 0x1FC6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0345u, 0x1FC3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0300u, 0x1F76u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0301u, 0x03AFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0304u, 0x1FD1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0306u, 0x1FD0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0308u, 0x03CAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0313u, 0x1F30u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0314u, 0x1F31u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0342u, 0x1FD6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0300u, 0x1F78u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0301u, 0x03CCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0313u, 0x1F40u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0314u, 0x1F41u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C1u, 0x0313u, 0x1FE4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C1u, 0x0314u, 0x1FE5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0300u, 0x1F7Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0301u, 0x03CDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0304u, 0x1FE1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0306u, 0x1FE0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0308u, 0x03CBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0313u, 0x1F50u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0314u, 0x1F51u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0342u, 0x1FE6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0300u, 0x1F7Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0301u, 0x03CEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0313u, 0x1F60u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0314u, 0x1F61u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0342u, 0x1FF6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0345u, 0x1FF3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0300u, 0x1FD2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0301u, 0x0390u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0342u, 0x1FD7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0300u, 0x1FE2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0301u, 0x03B0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0342u, 0x1FE7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CEu, 0x0345u, 0x1FF4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03D2u, 0x0301u, 0x03D3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03D2u, 0x0308u, 0x03D4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0406u, 0x0308u, 0x0407u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0410u, 0x0306u, 0x04D0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0410u, 0x0308u, 0x04D2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0413u, 0x0301u, 0x0403u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0300u, 0x0400u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0306u, 0x04D6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0308u, 0x0401u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0416u, 0x0306u, 0x04C1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0416u, 0x0308u, 0x04DCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0417u, 0x0308u, 0x04DEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0300u, 0x040Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0304u, 0x04E2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0306u, 0x0419u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0308u, 0x04E4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x041Au, 0x0301u, 0x040Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x041Eu, 0x0308u, 0x04E6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0304u, 0x04EEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0306u, 0x040Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0308u, 0x04F0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x030Bu, 0x04F2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0427u, 0x0308u, 0x04F4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x042Bu, 0x0308u, 0x04F8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x042Du, 0x0308u, 0x04ECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0430u, 0x0306u, 0x04D1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0430u, 0x0308u, 0x04D3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0433u, 0x0301u, 0x0453u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0300u, 0x0450u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0306u, 0x04D7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0308u, 0x0451u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0436u, 0x0306u, 0x04C2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0436u, 0x0308u, 0x04DDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0437u, 0x0308u, 0x04DFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0300u, 0x045Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0304u, 0x04E3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0306u, 0x0439u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0308u, 0x04E5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x043Au, 0x0301u, 0x045Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x043Eu, 0x0308u, 0x04E7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0304u, 0x04EFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0306u, 0x045Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0308u, 0x04F1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x030Bu, 0x04F3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0447u, 0x0308u, 0x04F5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x044Bu, 0x0308u, 0x04F9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x044Du, 0x0308u, 0x04EDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0456u, 0x0308u, 0x0457u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0474u, 0x030Fu, 0x0476u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0475u, 0x030Fu, 0x0477u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04D8u, 0x0308u, 0x04DAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04D9u, 0x0308u, 0x04DBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04E8u, 0x0308u, 0x04EAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04E9u, 0x0308u, 0x04EBu), +}; +static const uint64_t +_hb_ucd_dm2_u64_map[388] = +{ + HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05B7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05B8u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D1u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D1u, 0x05BFu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D2u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D3u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D4u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D5u, 0x05B9u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D5u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D6u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D8u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D9u, 0x05B4u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D9u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05DAu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05DBu, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05DBu, 0x05BFu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05DCu, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05DEu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E0u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E1u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E3u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E4u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E4u, 0x05BFu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E6u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E7u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E8u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05C1u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05C2u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05EAu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05F2u, 0x05B7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0627u, 0x0653u, 0x0622u), HB_CODEPOINT_ENCODE3 (0x0627u, 0x0654u, 0x0623u), + HB_CODEPOINT_ENCODE3 (0x0627u, 0x0655u, 0x0625u), HB_CODEPOINT_ENCODE3 (0x0648u, 0x0654u, 0x0624u), + HB_CODEPOINT_ENCODE3 (0x064Au, 0x0654u, 0x0626u), HB_CODEPOINT_ENCODE3 (0x06C1u, 0x0654u, 0x06C2u), + HB_CODEPOINT_ENCODE3 (0x06D2u, 0x0654u, 0x06D3u), HB_CODEPOINT_ENCODE3 (0x06D5u, 0x0654u, 0x06C0u), + HB_CODEPOINT_ENCODE3 (0x0915u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0916u, 0x093Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0917u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x091Cu, 0x093Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0921u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0922u, 0x093Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0928u, 0x093Cu, 0x0929u), HB_CODEPOINT_ENCODE3 (0x092Bu, 0x093Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x092Fu, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0930u, 0x093Cu, 0x0931u), + HB_CODEPOINT_ENCODE3 (0x0933u, 0x093Cu, 0x0934u), HB_CODEPOINT_ENCODE3 (0x09A1u, 0x09BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x09A2u, 0x09BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x09AFu, 0x09BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x09C7u, 0x09BEu, 0x09CBu), HB_CODEPOINT_ENCODE3 (0x09C7u, 0x09D7u, 0x09CCu), + HB_CODEPOINT_ENCODE3 (0x0A16u, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A17u, 0x0A3Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0A1Cu, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A2Bu, 0x0A3Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0A32u, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A38u, 0x0A3Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0B21u, 0x0B3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0B22u, 0x0B3Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B3Eu, 0x0B4Bu), HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B56u, 0x0B48u), + HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B57u, 0x0B4Cu), HB_CODEPOINT_ENCODE3 (0x0B92u, 0x0BD7u, 0x0B94u), + HB_CODEPOINT_ENCODE3 (0x0BC6u, 0x0BBEu, 0x0BCAu), HB_CODEPOINT_ENCODE3 (0x0BC6u, 0x0BD7u, 0x0BCCu), + HB_CODEPOINT_ENCODE3 (0x0BC7u, 0x0BBEu, 0x0BCBu), HB_CODEPOINT_ENCODE3 (0x0C46u, 0x0C56u, 0x0C48u), + HB_CODEPOINT_ENCODE3 (0x0CBFu, 0x0CD5u, 0x0CC0u), HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CC2u, 0x0CCAu), + HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CD5u, 0x0CC7u), HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CD6u, 0x0CC8u), + HB_CODEPOINT_ENCODE3 (0x0CCAu, 0x0CD5u, 0x0CCBu), HB_CODEPOINT_ENCODE3 (0x0D46u, 0x0D3Eu, 0x0D4Au), + HB_CODEPOINT_ENCODE3 (0x0D46u, 0x0D57u, 0x0D4Cu), HB_CODEPOINT_ENCODE3 (0x0D47u, 0x0D3Eu, 0x0D4Bu), + HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DCAu, 0x0DDAu), HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DCFu, 0x0DDCu), + HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DDFu, 0x0DDEu), HB_CODEPOINT_ENCODE3 (0x0DDCu, 0x0DCAu, 0x0DDDu), + HB_CODEPOINT_ENCODE3 (0x0F40u, 0x0FB5u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F42u, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F4Cu, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F51u, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F56u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F5Bu, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F72u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F74u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F80u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F90u, 0x0FB5u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F92u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F9Cu, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0FA1u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0FA6u, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0FABu, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0FB2u, 0x0F80u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0FB3u, 0x0F80u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1025u, 0x102Eu, 0x1026u), + HB_CODEPOINT_ENCODE3 (0x1B05u, 0x1B35u, 0x1B06u), HB_CODEPOINT_ENCODE3 (0x1B07u, 0x1B35u, 0x1B08u), + HB_CODEPOINT_ENCODE3 (0x1B09u, 0x1B35u, 0x1B0Au), HB_CODEPOINT_ENCODE3 (0x1B0Bu, 0x1B35u, 0x1B0Cu), + HB_CODEPOINT_ENCODE3 (0x1B0Du, 0x1B35u, 0x1B0Eu), HB_CODEPOINT_ENCODE3 (0x1B11u, 0x1B35u, 0x1B12u), + HB_CODEPOINT_ENCODE3 (0x1B3Au, 0x1B35u, 0x1B3Bu), HB_CODEPOINT_ENCODE3 (0x1B3Cu, 0x1B35u, 0x1B3Du), + HB_CODEPOINT_ENCODE3 (0x1B3Eu, 0x1B35u, 0x1B40u), HB_CODEPOINT_ENCODE3 (0x1B3Fu, 0x1B35u, 0x1B41u), + HB_CODEPOINT_ENCODE3 (0x1B42u, 0x1B35u, 0x1B43u), HB_CODEPOINT_ENCODE3 (0x1E36u, 0x0304u, 0x1E38u), + HB_CODEPOINT_ENCODE3 (0x1E37u, 0x0304u, 0x1E39u), HB_CODEPOINT_ENCODE3 (0x1E5Au, 0x0304u, 0x1E5Cu), + HB_CODEPOINT_ENCODE3 (0x1E5Bu, 0x0304u, 0x1E5Du), HB_CODEPOINT_ENCODE3 (0x1E62u, 0x0307u, 0x1E68u), + HB_CODEPOINT_ENCODE3 (0x1E63u, 0x0307u, 0x1E69u), HB_CODEPOINT_ENCODE3 (0x1EA0u, 0x0302u, 0x1EACu), + HB_CODEPOINT_ENCODE3 (0x1EA0u, 0x0306u, 0x1EB6u), HB_CODEPOINT_ENCODE3 (0x1EA1u, 0x0302u, 0x1EADu), + HB_CODEPOINT_ENCODE3 (0x1EA1u, 0x0306u, 0x1EB7u), HB_CODEPOINT_ENCODE3 (0x1EB8u, 0x0302u, 0x1EC6u), + HB_CODEPOINT_ENCODE3 (0x1EB9u, 0x0302u, 0x1EC7u), HB_CODEPOINT_ENCODE3 (0x1ECCu, 0x0302u, 0x1ED8u), + HB_CODEPOINT_ENCODE3 (0x1ECDu, 0x0302u, 0x1ED9u), HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0300u, 0x1F02u), + HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0301u, 0x1F04u), HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0342u, 0x1F06u), + HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0345u, 0x1F80u), HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0300u, 0x1F03u), + HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0301u, 0x1F05u), HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0342u, 0x1F07u), + HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0345u, 0x1F81u), HB_CODEPOINT_ENCODE3 (0x1F02u, 0x0345u, 0x1F82u), + HB_CODEPOINT_ENCODE3 (0x1F03u, 0x0345u, 0x1F83u), HB_CODEPOINT_ENCODE3 (0x1F04u, 0x0345u, 0x1F84u), + HB_CODEPOINT_ENCODE3 (0x1F05u, 0x0345u, 0x1F85u), HB_CODEPOINT_ENCODE3 (0x1F06u, 0x0345u, 0x1F86u), + HB_CODEPOINT_ENCODE3 (0x1F07u, 0x0345u, 0x1F87u), HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0300u, 0x1F0Au), + HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0301u, 0x1F0Cu), HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0342u, 0x1F0Eu), + HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0345u, 0x1F88u), HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0300u, 0x1F0Bu), + HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0301u, 0x1F0Du), HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0342u, 0x1F0Fu), + HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0345u, 0x1F89u), HB_CODEPOINT_ENCODE3 (0x1F0Au, 0x0345u, 0x1F8Au), + HB_CODEPOINT_ENCODE3 (0x1F0Bu, 0x0345u, 0x1F8Bu), HB_CODEPOINT_ENCODE3 (0x1F0Cu, 0x0345u, 0x1F8Cu), + HB_CODEPOINT_ENCODE3 (0x1F0Du, 0x0345u, 0x1F8Du), HB_CODEPOINT_ENCODE3 (0x1F0Eu, 0x0345u, 0x1F8Eu), + HB_CODEPOINT_ENCODE3 (0x1F0Fu, 0x0345u, 0x1F8Fu), HB_CODEPOINT_ENCODE3 (0x1F10u, 0x0300u, 0x1F12u), + HB_CODEPOINT_ENCODE3 (0x1F10u, 0x0301u, 0x1F14u), HB_CODEPOINT_ENCODE3 (0x1F11u, 0x0300u, 0x1F13u), + HB_CODEPOINT_ENCODE3 (0x1F11u, 0x0301u, 0x1F15u), HB_CODEPOINT_ENCODE3 (0x1F18u, 0x0300u, 0x1F1Au), + HB_CODEPOINT_ENCODE3 (0x1F18u, 0x0301u, 0x1F1Cu), HB_CODEPOINT_ENCODE3 (0x1F19u, 0x0300u, 0x1F1Bu), + HB_CODEPOINT_ENCODE3 (0x1F19u, 0x0301u, 0x1F1Du), HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0300u, 0x1F22u), + HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0301u, 0x1F24u), HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0342u, 0x1F26u), + HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0345u, 0x1F90u), HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0300u, 0x1F23u), + HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0301u, 0x1F25u), HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0342u, 0x1F27u), + HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0345u, 0x1F91u), HB_CODEPOINT_ENCODE3 (0x1F22u, 0x0345u, 0x1F92u), + HB_CODEPOINT_ENCODE3 (0x1F23u, 0x0345u, 0x1F93u), HB_CODEPOINT_ENCODE3 (0x1F24u, 0x0345u, 0x1F94u), + HB_CODEPOINT_ENCODE3 (0x1F25u, 0x0345u, 0x1F95u), HB_CODEPOINT_ENCODE3 (0x1F26u, 0x0345u, 0x1F96u), + HB_CODEPOINT_ENCODE3 (0x1F27u, 0x0345u, 0x1F97u), HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0300u, 0x1F2Au), + HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0301u, 0x1F2Cu), HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0342u, 0x1F2Eu), + HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0345u, 0x1F98u), HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0300u, 0x1F2Bu), + HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0301u, 0x1F2Du), HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0342u, 0x1F2Fu), + HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0345u, 0x1F99u), HB_CODEPOINT_ENCODE3 (0x1F2Au, 0x0345u, 0x1F9Au), + HB_CODEPOINT_ENCODE3 (0x1F2Bu, 0x0345u, 0x1F9Bu), HB_CODEPOINT_ENCODE3 (0x1F2Cu, 0x0345u, 0x1F9Cu), + HB_CODEPOINT_ENCODE3 (0x1F2Du, 0x0345u, 0x1F9Du), HB_CODEPOINT_ENCODE3 (0x1F2Eu, 0x0345u, 0x1F9Eu), + HB_CODEPOINT_ENCODE3 (0x1F2Fu, 0x0345u, 0x1F9Fu), HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0300u, 0x1F32u), + HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0301u, 0x1F34u), HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0342u, 0x1F36u), + HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0300u, 0x1F33u), HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0301u, 0x1F35u), + HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0342u, 0x1F37u), HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0300u, 0x1F3Au), + HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0301u, 0x1F3Cu), HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0342u, 0x1F3Eu), + HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0300u, 0x1F3Bu), HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0301u, 0x1F3Du), + HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0342u, 0x1F3Fu), HB_CODEPOINT_ENCODE3 (0x1F40u, 0x0300u, 0x1F42u), + HB_CODEPOINT_ENCODE3 (0x1F40u, 0x0301u, 0x1F44u), HB_CODEPOINT_ENCODE3 (0x1F41u, 0x0300u, 0x1F43u), + HB_CODEPOINT_ENCODE3 (0x1F41u, 0x0301u, 0x1F45u), HB_CODEPOINT_ENCODE3 (0x1F48u, 0x0300u, 0x1F4Au), + HB_CODEPOINT_ENCODE3 (0x1F48u, 0x0301u, 0x1F4Cu), HB_CODEPOINT_ENCODE3 (0x1F49u, 0x0300u, 0x1F4Bu), + HB_CODEPOINT_ENCODE3 (0x1F49u, 0x0301u, 0x1F4Du), HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0300u, 0x1F52u), + HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0301u, 0x1F54u), HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0342u, 0x1F56u), + HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0300u, 0x1F53u), HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0301u, 0x1F55u), + HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0342u, 0x1F57u), HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0300u, 0x1F5Bu), + HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0301u, 0x1F5Du), HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0342u, 0x1F5Fu), + HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0300u, 0x1F62u), HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0301u, 0x1F64u), + HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0342u, 0x1F66u), HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0345u, 0x1FA0u), + HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0300u, 0x1F63u), HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0301u, 0x1F65u), + HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0342u, 0x1F67u), HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0345u, 0x1FA1u), + HB_CODEPOINT_ENCODE3 (0x1F62u, 0x0345u, 0x1FA2u), HB_CODEPOINT_ENCODE3 (0x1F63u, 0x0345u, 0x1FA3u), + HB_CODEPOINT_ENCODE3 (0x1F64u, 0x0345u, 0x1FA4u), HB_CODEPOINT_ENCODE3 (0x1F65u, 0x0345u, 0x1FA5u), + HB_CODEPOINT_ENCODE3 (0x1F66u, 0x0345u, 0x1FA6u), HB_CODEPOINT_ENCODE3 (0x1F67u, 0x0345u, 0x1FA7u), + HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0300u, 0x1F6Au), HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0301u, 0x1F6Cu), + HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0342u, 0x1F6Eu), HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0345u, 0x1FA8u), + HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0300u, 0x1F6Bu), HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0301u, 0x1F6Du), + HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0342u, 0x1F6Fu), HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0345u, 0x1FA9u), + HB_CODEPOINT_ENCODE3 (0x1F6Au, 0x0345u, 0x1FAAu), HB_CODEPOINT_ENCODE3 (0x1F6Bu, 0x0345u, 0x1FABu), + HB_CODEPOINT_ENCODE3 (0x1F6Cu, 0x0345u, 0x1FACu), HB_CODEPOINT_ENCODE3 (0x1F6Du, 0x0345u, 0x1FADu), + HB_CODEPOINT_ENCODE3 (0x1F6Eu, 0x0345u, 0x1FAEu), HB_CODEPOINT_ENCODE3 (0x1F6Fu, 0x0345u, 0x1FAFu), + HB_CODEPOINT_ENCODE3 (0x1F70u, 0x0345u, 0x1FB2u), HB_CODEPOINT_ENCODE3 (0x1F74u, 0x0345u, 0x1FC2u), + HB_CODEPOINT_ENCODE3 (0x1F7Cu, 0x0345u, 0x1FF2u), HB_CODEPOINT_ENCODE3 (0x1FB6u, 0x0345u, 0x1FB7u), + HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0300u, 0x1FCDu), HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0301u, 0x1FCEu), + HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0342u, 0x1FCFu), HB_CODEPOINT_ENCODE3 (0x1FC6u, 0x0345u, 0x1FC7u), + HB_CODEPOINT_ENCODE3 (0x1FF6u, 0x0345u, 0x1FF7u), HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0300u, 0x1FDDu), + HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0301u, 0x1FDEu), HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0342u, 0x1FDFu), + HB_CODEPOINT_ENCODE3 (0x2190u, 0x0338u, 0x219Au), HB_CODEPOINT_ENCODE3 (0x2192u, 0x0338u, 0x219Bu), + HB_CODEPOINT_ENCODE3 (0x2194u, 0x0338u, 0x21AEu), HB_CODEPOINT_ENCODE3 (0x21D0u, 0x0338u, 0x21CDu), + HB_CODEPOINT_ENCODE3 (0x21D2u, 0x0338u, 0x21CFu), HB_CODEPOINT_ENCODE3 (0x21D4u, 0x0338u, 0x21CEu), + HB_CODEPOINT_ENCODE3 (0x2203u, 0x0338u, 0x2204u), HB_CODEPOINT_ENCODE3 (0x2208u, 0x0338u, 0x2209u), + HB_CODEPOINT_ENCODE3 (0x220Bu, 0x0338u, 0x220Cu), HB_CODEPOINT_ENCODE3 (0x2223u, 0x0338u, 0x2224u), + HB_CODEPOINT_ENCODE3 (0x2225u, 0x0338u, 0x2226u), HB_CODEPOINT_ENCODE3 (0x223Cu, 0x0338u, 0x2241u), + HB_CODEPOINT_ENCODE3 (0x2243u, 0x0338u, 0x2244u), HB_CODEPOINT_ENCODE3 (0x2245u, 0x0338u, 0x2247u), + HB_CODEPOINT_ENCODE3 (0x2248u, 0x0338u, 0x2249u), HB_CODEPOINT_ENCODE3 (0x224Du, 0x0338u, 0x226Du), + HB_CODEPOINT_ENCODE3 (0x2261u, 0x0338u, 0x2262u), HB_CODEPOINT_ENCODE3 (0x2264u, 0x0338u, 0x2270u), + HB_CODEPOINT_ENCODE3 (0x2265u, 0x0338u, 0x2271u), HB_CODEPOINT_ENCODE3 (0x2272u, 0x0338u, 0x2274u), + HB_CODEPOINT_ENCODE3 (0x2273u, 0x0338u, 0x2275u), HB_CODEPOINT_ENCODE3 (0x2276u, 0x0338u, 0x2278u), + HB_CODEPOINT_ENCODE3 (0x2277u, 0x0338u, 0x2279u), HB_CODEPOINT_ENCODE3 (0x227Au, 0x0338u, 0x2280u), + HB_CODEPOINT_ENCODE3 (0x227Bu, 0x0338u, 0x2281u), HB_CODEPOINT_ENCODE3 (0x227Cu, 0x0338u, 0x22E0u), + HB_CODEPOINT_ENCODE3 (0x227Du, 0x0338u, 0x22E1u), HB_CODEPOINT_ENCODE3 (0x2282u, 0x0338u, 0x2284u), + HB_CODEPOINT_ENCODE3 (0x2283u, 0x0338u, 0x2285u), HB_CODEPOINT_ENCODE3 (0x2286u, 0x0338u, 0x2288u), + HB_CODEPOINT_ENCODE3 (0x2287u, 0x0338u, 0x2289u), HB_CODEPOINT_ENCODE3 (0x2291u, 0x0338u, 0x22E2u), + HB_CODEPOINT_ENCODE3 (0x2292u, 0x0338u, 0x22E3u), HB_CODEPOINT_ENCODE3 (0x22A2u, 0x0338u, 0x22ACu), + HB_CODEPOINT_ENCODE3 (0x22A8u, 0x0338u, 0x22ADu), HB_CODEPOINT_ENCODE3 (0x22A9u, 0x0338u, 0x22AEu), + HB_CODEPOINT_ENCODE3 (0x22ABu, 0x0338u, 0x22AFu), HB_CODEPOINT_ENCODE3 (0x22B2u, 0x0338u, 0x22EAu), + HB_CODEPOINT_ENCODE3 (0x22B3u, 0x0338u, 0x22EBu), HB_CODEPOINT_ENCODE3 (0x22B4u, 0x0338u, 0x22ECu), + HB_CODEPOINT_ENCODE3 (0x22B5u, 0x0338u, 0x22EDu), HB_CODEPOINT_ENCODE3 (0x2ADDu, 0x0338u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x3046u, 0x3099u, 0x3094u), HB_CODEPOINT_ENCODE3 (0x304Bu, 0x3099u, 0x304Cu), + HB_CODEPOINT_ENCODE3 (0x304Du, 0x3099u, 0x304Eu), HB_CODEPOINT_ENCODE3 (0x304Fu, 0x3099u, 0x3050u), + HB_CODEPOINT_ENCODE3 (0x3051u, 0x3099u, 0x3052u), HB_CODEPOINT_ENCODE3 (0x3053u, 0x3099u, 0x3054u), + HB_CODEPOINT_ENCODE3 (0x3055u, 0x3099u, 0x3056u), HB_CODEPOINT_ENCODE3 (0x3057u, 0x3099u, 0x3058u), + HB_CODEPOINT_ENCODE3 (0x3059u, 0x3099u, 0x305Au), HB_CODEPOINT_ENCODE3 (0x305Bu, 0x3099u, 0x305Cu), + HB_CODEPOINT_ENCODE3 (0x305Du, 0x3099u, 0x305Eu), HB_CODEPOINT_ENCODE3 (0x305Fu, 0x3099u, 0x3060u), + HB_CODEPOINT_ENCODE3 (0x3061u, 0x3099u, 0x3062u), HB_CODEPOINT_ENCODE3 (0x3064u, 0x3099u, 0x3065u), + HB_CODEPOINT_ENCODE3 (0x3066u, 0x3099u, 0x3067u), HB_CODEPOINT_ENCODE3 (0x3068u, 0x3099u, 0x3069u), + HB_CODEPOINT_ENCODE3 (0x306Fu, 0x3099u, 0x3070u), HB_CODEPOINT_ENCODE3 (0x306Fu, 0x309Au, 0x3071u), + HB_CODEPOINT_ENCODE3 (0x3072u, 0x3099u, 0x3073u), HB_CODEPOINT_ENCODE3 (0x3072u, 0x309Au, 0x3074u), + HB_CODEPOINT_ENCODE3 (0x3075u, 0x3099u, 0x3076u), HB_CODEPOINT_ENCODE3 (0x3075u, 0x309Au, 0x3077u), + HB_CODEPOINT_ENCODE3 (0x3078u, 0x3099u, 0x3079u), HB_CODEPOINT_ENCODE3 (0x3078u, 0x309Au, 0x307Au), + HB_CODEPOINT_ENCODE3 (0x307Bu, 0x3099u, 0x307Cu), HB_CODEPOINT_ENCODE3 (0x307Bu, 0x309Au, 0x307Du), + HB_CODEPOINT_ENCODE3 (0x309Du, 0x3099u, 0x309Eu), HB_CODEPOINT_ENCODE3 (0x30A6u, 0x3099u, 0x30F4u), + HB_CODEPOINT_ENCODE3 (0x30ABu, 0x3099u, 0x30ACu), HB_CODEPOINT_ENCODE3 (0x30ADu, 0x3099u, 0x30AEu), + HB_CODEPOINT_ENCODE3 (0x30AFu, 0x3099u, 0x30B0u), HB_CODEPOINT_ENCODE3 (0x30B1u, 0x3099u, 0x30B2u), + HB_CODEPOINT_ENCODE3 (0x30B3u, 0x3099u, 0x30B4u), HB_CODEPOINT_ENCODE3 (0x30B5u, 0x3099u, 0x30B6u), + HB_CODEPOINT_ENCODE3 (0x30B7u, 0x3099u, 0x30B8u), HB_CODEPOINT_ENCODE3 (0x30B9u, 0x3099u, 0x30BAu), + HB_CODEPOINT_ENCODE3 (0x30BBu, 0x3099u, 0x30BCu), HB_CODEPOINT_ENCODE3 (0x30BDu, 0x3099u, 0x30BEu), + HB_CODEPOINT_ENCODE3 (0x30BFu, 0x3099u, 0x30C0u), HB_CODEPOINT_ENCODE3 (0x30C1u, 0x3099u, 0x30C2u), + HB_CODEPOINT_ENCODE3 (0x30C4u, 0x3099u, 0x30C5u), HB_CODEPOINT_ENCODE3 (0x30C6u, 0x3099u, 0x30C7u), + HB_CODEPOINT_ENCODE3 (0x30C8u, 0x3099u, 0x30C9u), HB_CODEPOINT_ENCODE3 (0x30CFu, 0x3099u, 0x30D0u), + HB_CODEPOINT_ENCODE3 (0x30CFu, 0x309Au, 0x30D1u), HB_CODEPOINT_ENCODE3 (0x30D2u, 0x3099u, 0x30D3u), + HB_CODEPOINT_ENCODE3 (0x30D2u, 0x309Au, 0x30D4u), HB_CODEPOINT_ENCODE3 (0x30D5u, 0x3099u, 0x30D6u), + HB_CODEPOINT_ENCODE3 (0x30D5u, 0x309Au, 0x30D7u), HB_CODEPOINT_ENCODE3 (0x30D8u, 0x3099u, 0x30D9u), + HB_CODEPOINT_ENCODE3 (0x30D8u, 0x309Au, 0x30DAu), HB_CODEPOINT_ENCODE3 (0x30DBu, 0x3099u, 0x30DCu), + HB_CODEPOINT_ENCODE3 (0x30DBu, 0x309Au, 0x30DDu), HB_CODEPOINT_ENCODE3 (0x30EFu, 0x3099u, 0x30F7u), + HB_CODEPOINT_ENCODE3 (0x30F0u, 0x3099u, 0x30F8u), HB_CODEPOINT_ENCODE3 (0x30F1u, 0x3099u, 0x30F9u), + HB_CODEPOINT_ENCODE3 (0x30F2u, 0x3099u, 0x30FAu), HB_CODEPOINT_ENCODE3 (0x30FDu, 0x3099u, 0x30FEu), + HB_CODEPOINT_ENCODE3 (0xFB49u, 0x05C1u, 0x0000u), HB_CODEPOINT_ENCODE3 (0xFB49u, 0x05C2u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x11099u, 0x110BAu, 0x1109Au),HB_CODEPOINT_ENCODE3 (0x1109Bu, 0x110BAu, 0x1109Cu), + HB_CODEPOINT_ENCODE3 (0x110A5u, 0x110BAu, 0x110ABu),HB_CODEPOINT_ENCODE3 (0x11131u, 0x11127u, 0x1112Eu), + HB_CODEPOINT_ENCODE3 (0x11132u, 0x11127u, 0x1112Fu),HB_CODEPOINT_ENCODE3 (0x11347u, 0x1133Eu, 0x1134Bu), + HB_CODEPOINT_ENCODE3 (0x11347u, 0x11357u, 0x1134Cu),HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114B0u, 0x114BCu), + HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114BAu, 0x114BBu),HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114BDu, 0x114BEu), + HB_CODEPOINT_ENCODE3 (0x115B8u, 0x115AFu, 0x115BAu),HB_CODEPOINT_ENCODE3 (0x115B9u, 0x115AFu, 0x115BBu), + HB_CODEPOINT_ENCODE3 (0x11935u, 0x11930u, 0x11938u), HB_CODEPOINT_ENCODE3 (0x1D157u, 0x1D165u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D158u, 0x1D165u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D16Eu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D16Fu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D170u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D171u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D172u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D1B9u, 0x1D165u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BAu, 0x1D165u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D1BBu, 0x1D16Eu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BBu, 0x1D16Fu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D1BCu, 0x1D16Eu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BCu, 0x1D16Fu, 0x0000u), +}; + +#ifndef HB_OPTIMIZE_SIZE + +static const uint8_t +_hb_ucd_u8[32480] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 27, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 28, + 29, 26, 30, 31, 32, 33, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 34, 35, 35, 35, 35, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 26, 57, 58, 59, 59, 59, 59, 59, 26, 26, 60, 59, 59, 59, 59, 59, + 59, 59, 26, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 26, 62, 59, 63, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 64, 26, 26, 65, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 66, 67, 59, 59, 59, 59, 68, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 69, 70, 71, 72, 73, 74, 59, 59, + 75, 76, 59, 59, 77, 59, 78, 79, 80, 81, 73, 82, 83, 84, 59, 59, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 85, 26, 26, 26, 26, 26, 26, 26, 86, 87, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 88, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 89, 59, 59, 59, 59, 59, 59, 26, 90, 59, 59, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 91, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 92, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 93, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 94, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 29, 21, 21, 21, 23, 21, 21, 21, 22, 18, 21, 25, 21, 17, 21, 21, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 25, 25, 25, 21, + 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 22, 21, 18, 24, 16, + 24, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 22, 25, 18, 25, 0, + 29, 21, 23, 23, 23, 23, 26, 21, 24, 26, 7, 20, 25, 1, 26, 24, + 26, 25, 15, 15, 24, 5, 21, 21, 24, 15, 7, 19, 15, 15, 15, 21, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 25, 9, 9, 9, 9, 9, 9, 9, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 25, 5, 5, 5, 5, 5, 5, 5, 5, + 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, + 9, 5, 9, 5, 9, 5, 9, 5, 5, 9, 5, 9, 5, 9, 5, 9, + 5, 9, 5, 9, 5, 9, 5, 9, 5, 5, 9, 5, 9, 5, 9, 5, + 9, 5, 9, 5, 9, 5, 9, 5, 9, 9, 5, 9, 5, 9, 5, 5, + 5, 9, 9, 5, 9, 5, 9, 9, 5, 9, 9, 9, 5, 5, 9, 9, + 9, 9, 5, 9, 9, 5, 9, 9, 9, 5, 5, 5, 9, 9, 5, 9, + 9, 5, 9, 5, 9, 5, 9, 9, 5, 9, 5, 5, 9, 5, 9, 9, + 5, 9, 9, 9, 5, 9, 5, 9, 9, 5, 5, 7, 9, 5, 5, 5, + 7, 7, 7, 7, 9, 8, 5, 9, 8, 5, 9, 8, 5, 9, 5, 9, + 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 5, 9, 5, + 5, 9, 8, 5, 9, 5, 9, 9, 9, 5, 9, 5, 9, 5, 9, 5, + 9, 5, 9, 5, 5, 5, 5, 5, 5, 5, 9, 9, 5, 9, 9, 5, + 5, 9, 5, 9, 9, 9, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, + 5, 5, 5, 5, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 24, 24, 24, 24, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 6, 6, 6, 6, 6, 24, 24, 24, 24, 24, 24, 24, 6, 24, 6, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 9, 5, 9, 5, 6, 24, 9, 5, 2, 2, 6, 5, 5, 5, 21, 9, + 2, 2, 2, 2, 24, 24, 9, 21, 9, 9, 9, 2, 9, 2, 9, 9, + 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 9, + 5, 5, 9, 9, 9, 5, 5, 5, 9, 5, 9, 5, 9, 5, 9, 5, + 5, 5, 5, 5, 9, 5, 25, 9, 5, 9, 9, 5, 5, 9, 9, 9, + 9, 5, 26, 12, 12, 12, 12, 12, 11, 11, 9, 5, 9, 5, 9, 5, + 9, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 5, + 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 2, 2, 6, 21, 21, 21, 21, 21, 21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 21, 17, 2, 2, 26, 26, 23, + 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 17, 12, + 21, 12, 12, 21, 12, 12, 21, 12, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 7, + 7, 7, 7, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 25, 25, 25, 21, 21, 23, 21, 21, 26, 26, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 21, 1, 2, 21, 21, + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 21, 21, 7, 7, + 12, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 21, 7, 12, 12, 12, 12, 12, 12, 12, 1, 26, 12, + 12, 12, 12, 12, 12, 6, 6, 12, 12, 26, 12, 12, 12, 12, 7, 7, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 7, 7, 7, 26, 26, 7, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, 1, + 7, 12, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 6, 6, 26, 21, 21, 21, 6, 2, 2, 12, 23, 23, + 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 6, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 6, 12, 12, 12, 6, 12, 12, 12, 12, 12, 2, 2, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 2, 2, 21, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 1, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 10, 12, 7, 10, 10, + 10, 12, 12, 12, 12, 12, 12, 12, 12, 10, 10, 10, 10, 12, 10, 10, + 7, 12, 12, 12, 12, 12, 12, 12, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 12, 12, 21, 21, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 21, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 12, 10, 10, 2, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 7, + 7, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, + 7, 2, 7, 2, 2, 2, 7, 7, 7, 7, 2, 2, 12, 7, 10, 10, + 10, 12, 12, 12, 12, 2, 2, 10, 10, 2, 2, 10, 10, 12, 7, 2, + 2, 2, 2, 2, 2, 2, 2, 10, 2, 2, 2, 2, 7, 7, 2, 7, + 7, 7, 12, 12, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 7, 7, 23, 23, 15, 15, 15, 15, 15, 15, 26, 23, 7, 21, 12, 2, + 2, 12, 12, 10, 2, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 7, + 7, 2, 7, 7, 2, 7, 7, 2, 7, 7, 2, 2, 12, 2, 10, 10, + 10, 12, 12, 2, 2, 2, 2, 12, 12, 2, 2, 12, 12, 12, 2, 2, + 2, 12, 2, 2, 2, 2, 2, 2, 2, 7, 7, 7, 7, 2, 7, 2, + 2, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 12, 12, 7, 7, 7, 12, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 12, 12, 10, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, + 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 2, 2, 12, 7, 10, 10, + 10, 12, 12, 12, 12, 12, 2, 12, 12, 10, 2, 10, 10, 12, 2, 2, + 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 21, 23, 2, 2, 2, 2, 2, 2, 2, 7, 12, 12, 12, 12, 12, 12, + 2, 12, 10, 10, 2, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 7, + 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 2, 2, 12, 7, 10, 12, + 10, 12, 12, 12, 12, 2, 2, 10, 10, 2, 2, 10, 10, 12, 2, 2, + 2, 2, 2, 2, 2, 12, 12, 10, 2, 2, 2, 2, 7, 7, 2, 7, + 26, 7, 15, 15, 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 12, 7, 2, 7, 7, 7, 7, 7, 7, 2, 2, 2, 7, 7, + 7, 2, 7, 7, 7, 7, 2, 2, 2, 7, 7, 2, 7, 2, 7, 7, + 2, 2, 2, 7, 7, 2, 2, 2, 7, 7, 7, 2, 2, 2, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 10, 10, + 12, 10, 10, 2, 2, 2, 10, 10, 10, 2, 10, 10, 10, 12, 2, 2, + 7, 2, 2, 2, 2, 2, 2, 10, 2, 2, 2, 2, 2, 2, 2, 2, + 15, 15, 15, 26, 26, 26, 26, 26, 26, 23, 26, 2, 2, 2, 2, 2, + 12, 10, 10, 10, 12, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, + 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 7, 12, 12, + 12, 10, 10, 10, 10, 2, 12, 12, 12, 2, 12, 12, 12, 12, 2, 2, + 2, 2, 2, 2, 2, 12, 12, 2, 7, 7, 7, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 21, 15, 15, 15, 15, 15, 15, 15, 26, + 7, 12, 10, 10, 21, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, + 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 2, 2, 12, 7, 10, 12, + 10, 10, 10, 10, 10, 2, 12, 10, 10, 2, 10, 10, 12, 12, 2, 2, + 2, 2, 2, 2, 2, 10, 10, 2, 2, 2, 2, 2, 2, 2, 7, 2, + 2, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 12, 12, 10, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 7, 10, 10, + 10, 12, 12, 12, 12, 2, 10, 10, 10, 2, 10, 10, 10, 12, 7, 26, + 2, 2, 2, 2, 7, 7, 7, 10, 15, 15, 15, 15, 15, 15, 15, 7, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 7, 7, 7, 7, 7, 7, + 2, 12, 10, 10, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 7, 7, 7, 7, 7, 7, + 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 12, 2, 2, 2, 2, 10, + 10, 10, 12, 12, 12, 2, 12, 2, 10, 10, 10, 10, 10, 10, 10, 10, + 2, 2, 10, 10, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 12, 7, 7, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, 2, 23, + 7, 7, 7, 7, 7, 7, 6, 12, 12, 12, 12, 12, 12, 12, 12, 21, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 2, 2, 2, 2, + 2, 7, 7, 2, 7, 2, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, + 7, 7, 7, 7, 2, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 12, 7, 7, 12, 12, 12, 12, 12, 12, 12, 12, 12, 7, 2, 2, + 7, 7, 7, 7, 7, 2, 6, 2, 12, 12, 12, 12, 12, 12, 2, 2, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 7, 7, 7, 7, + 7, 26, 26, 26, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 26, 21, 26, 26, 26, 12, 12, 26, 26, 26, 26, 26, 26, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 26, 12, 26, 12, 26, 12, 22, 18, 22, 18, 10, 10, + 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, + 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 10, + 12, 12, 12, 12, 12, 21, 12, 12, 7, 7, 7, 7, 7, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 2, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 26, 26, + 26, 26, 26, 26, 26, 26, 12, 26, 26, 26, 26, 26, 26, 2, 26, 26, + 21, 21, 21, 21, 21, 26, 26, 26, 26, 21, 21, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10, 10, 12, 12, 12, + 12, 10, 12, 12, 12, 12, 12, 12, 10, 12, 12, 10, 10, 12, 12, 7, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 21, 21, 21, 21, + 7, 7, 7, 7, 7, 7, 10, 10, 12, 12, 7, 7, 7, 7, 12, 12, + 12, 7, 10, 10, 10, 7, 7, 10, 10, 10, 10, 10, 10, 10, 7, 7, + 7, 12, 12, 12, 12, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 12, 10, 10, 12, 12, 10, 10, 10, 10, 10, 10, 12, 7, 10, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 10, 10, 12, 26, 26, + 9, 9, 9, 9, 9, 9, 2, 9, 2, 2, 2, 2, 2, 9, 2, 2, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 21, 6, 5, 5, 5, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 2, 7, 2, 7, 7, 7, 7, 2, 2, + 7, 2, 7, 7, 7, 7, 2, 2, 7, 7, 7, 7, 7, 7, 7, 2, + 7, 2, 7, 7, 7, 7, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 12, 12, 12, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2, 2, + 9, 9, 9, 9, 9, 9, 2, 2, 5, 5, 5, 5, 5, 5, 2, 2, + 17, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 26, 21, 7, + 29, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 22, 18, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 21, 21, 21, 14, 14, + 14, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, + 7, 7, 12, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 12, 12, 12, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 2, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 12, 12, 10, 12, 12, 12, 12, 12, 12, 12, 10, 10, + 10, 10, 10, 10, 10, 10, 12, 10, 10, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 21, 21, 21, 6, 21, 21, 21, 23, 7, 12, 2, 2, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 2, 2, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, + 21, 21, 21, 21, 21, 21, 17, 21, 21, 21, 21, 12, 12, 12, 1, 2, + 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 12, 12, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 7, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, + 12, 12, 12, 10, 10, 10, 10, 12, 12, 10, 10, 10, 2, 2, 2, 2, + 10, 10, 12, 10, 10, 10, 10, 10, 10, 12, 12, 12, 2, 2, 2, 2, + 26, 2, 2, 2, 21, 21, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, + 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 15, 2, 2, 2, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 7, 7, 7, 7, 7, 7, 7, 12, 12, 10, 10, 12, 2, 2, 21, 21, + 7, 7, 7, 7, 7, 10, 12, 10, 12, 12, 12, 12, 12, 12, 12, 2, + 12, 10, 12, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 10, 10, 10, + 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 12, + 21, 21, 21, 21, 21, 21, 21, 6, 21, 21, 21, 21, 21, 21, 2, 2, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 11, 12, + 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 12, 12, 12, 12, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 12, 10, 12, 12, 12, 12, 12, 10, 12, 10, 10, 10, + 10, 10, 12, 10, 10, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, + 21, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, + 12, 12, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 10, 12, 12, 12, 12, 10, 10, 12, 12, 10, 12, 12, 12, 7, 7, + 7, 7, 7, 7, 7, 7, 12, 10, 12, 12, 10, 10, 10, 12, 10, 12, + 12, 12, 10, 10, 2, 2, 2, 2, 2, 2, 2, 2, 21, 21, 21, 21, + 7, 7, 7, 7, 10, 10, 10, 10, 10, 10, 10, 10, 12, 12, 12, 12, + 12, 12, 12, 12, 10, 10, 12, 12, 2, 2, 2, 21, 21, 21, 21, 21, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 21, 21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 2, 2, 9, 9, 9, + 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, + 12, 12, 12, 21, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 10, 12, 12, 12, 12, 12, 12, 12, 7, 7, 7, 7, 12, 7, 7, + 7, 7, 7, 7, 12, 7, 7, 10, 12, 12, 7, 2, 2, 2, 2, 2, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 12, 12, 12, 12, 12, + 9, 5, 9, 5, 9, 5, 5, 5, 5, 5, 5, 5, 5, 5, 9, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 9, 9, 9, 9, 9, 9, 9, 9, + 5, 5, 5, 5, 5, 5, 2, 2, 9, 9, 9, 9, 9, 9, 2, 2, + 5, 5, 5, 5, 5, 5, 5, 5, 2, 9, 2, 9, 2, 9, 2, 9, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, + 5, 5, 5, 5, 5, 5, 5, 5, 8, 8, 8, 8, 8, 8, 8, 8, + 5, 5, 5, 5, 5, 2, 5, 5, 9, 9, 9, 9, 8, 24, 5, 24, + 24, 24, 5, 5, 5, 2, 5, 5, 9, 9, 9, 9, 8, 24, 24, 24, + 5, 5, 5, 5, 2, 2, 5, 5, 9, 9, 9, 9, 2, 24, 24, 24, + 5, 5, 5, 5, 5, 5, 5, 5, 9, 9, 9, 9, 9, 24, 24, 24, + 2, 2, 5, 5, 5, 2, 5, 5, 9, 9, 9, 9, 8, 24, 24, 2, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 1, 1, 1, 1, 1, + 17, 17, 17, 17, 17, 17, 21, 21, 20, 19, 22, 20, 20, 19, 22, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 27, 28, 1, 1, 1, 1, 1, 29, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 20, 19, 21, 21, 21, 21, 16, + 16, 21, 21, 21, 25, 22, 18, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 25, 21, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 29, + 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 15, 6, 2, 2, 15, 15, 15, 15, 15, 15, 25, 25, 25, 22, 18, 6, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 25, 25, 25, 22, 18, 2, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, + 11, 12, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 26, 26, 9, 26, 26, 26, 26, 9, 26, 26, 5, 9, 9, 9, 5, 5, + 9, 9, 9, 5, 26, 9, 26, 26, 25, 9, 9, 9, 9, 9, 26, 26, + 26, 26, 26, 26, 9, 26, 9, 26, 9, 26, 9, 9, 9, 9, 26, 5, + 9, 9, 9, 9, 5, 7, 7, 7, 7, 5, 26, 26, 5, 5, 9, 9, + 25, 25, 25, 25, 25, 9, 5, 5, 5, 5, 26, 25, 26, 26, 5, 26, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 9, 5, 14, 14, 14, 14, 15, 26, 26, 2, 2, 2, 2, + 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 25, 25, 26, 26, 26, 26, + 25, 26, 26, 25, 26, 26, 25, 26, 26, 26, 26, 26, 26, 26, 25, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, + 26, 26, 25, 26, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 22, 18, 22, 18, 26, 26, 26, 26, + 25, 25, 26, 26, 26, 26, 26, 26, 26, 22, 18, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25, + 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 15, 15, 15, 15, 15, 15, + 26, 26, 26, 26, 26, 26, 26, 25, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 22, 18, 22, 18, 22, 18, 22, 18, + 22, 18, 22, 18, 22, 18, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 25, 25, 25, 25, 25, 22, 18, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 22, 18, 22, 18, 22, 18, 22, 18, 22, 18, + 25, 25, 25, 22, 18, 22, 18, 22, 18, 22, 18, 22, 18, 22, 18, 22, + 18, 22, 18, 22, 18, 22, 18, 22, 18, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 22, 18, 22, 18, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 22, 18, 25, 25, + 25, 25, 25, 25, 25, 26, 26, 25, 25, 25, 25, 25, 25, 26, 26, 26, + 26, 26, 26, 26, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 2, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, + 9, 5, 9, 9, 9, 5, 5, 9, 5, 9, 5, 9, 5, 9, 9, 9, + 9, 5, 9, 5, 5, 9, 5, 5, 5, 5, 5, 5, 6, 6, 9, 9, + 9, 5, 9, 5, 5, 26, 26, 26, 26, 26, 26, 9, 5, 9, 5, 12, + 12, 12, 9, 5, 2, 2, 2, 2, 2, 21, 21, 21, 21, 15, 21, 21, + 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 2, 2, 5, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 6, + 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 12, + 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 2, + 21, 21, 20, 19, 20, 19, 21, 21, 21, 20, 19, 21, 20, 19, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 17, 21, 21, 17, 21, 20, 19, 21, 21, + 20, 19, 22, 18, 22, 18, 22, 18, 22, 18, 21, 21, 21, 21, 21, 6, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 17, 17, 21, 21, 21, 21, + 17, 21, 22, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 26, 26, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, + 29, 21, 21, 21, 26, 6, 7, 14, 22, 18, 22, 18, 22, 18, 22, 18, + 22, 18, 26, 26, 22, 18, 22, 18, 22, 18, 22, 18, 17, 22, 18, 18, + 26, 14, 14, 14, 14, 14, 14, 14, 14, 14, 12, 12, 12, 12, 10, 10, + 17, 6, 6, 6, 6, 6, 26, 26, 14, 14, 14, 6, 7, 21, 26, 26, + 7, 7, 7, 7, 7, 7, 7, 2, 2, 12, 12, 24, 24, 6, 6, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 21, 6, 6, 6, 7, + 2, 2, 2, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 26, 26, 15, 15, 15, 15, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 15, 15, 15, 15, 15, 15, 15, 15, + 26, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 21, 21, 21, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 7, 7, 2, 2, 2, 2, + 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 7, 12, + 11, 11, 11, 21, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 21, 6, + 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 6, 6, 12, 12, + 7, 7, 7, 7, 7, 7, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 12, 12, 21, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, + 24, 24, 24, 24, 24, 24, 24, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 24, 24, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, + 5, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, + 6, 5, 5, 5, 5, 5, 5, 5, 5, 9, 5, 9, 5, 9, 9, 5, + 9, 5, 9, 5, 9, 5, 9, 5, 6, 24, 24, 9, 5, 9, 5, 7, + 9, 5, 9, 5, 5, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, + 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 9, 9, 9, 9, 5, + 9, 9, 9, 9, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, + 2, 2, 9, 5, 9, 9, 9, 9, 5, 9, 5, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 9, 5, 7, 6, 6, 5, 7, 7, 7, 7, 7, + 7, 7, 12, 7, 7, 7, 12, 7, 7, 7, 7, 12, 7, 7, 7, 7, + 7, 7, 7, 10, 10, 12, 12, 10, 26, 26, 26, 26, 12, 2, 2, 2, + 15, 15, 15, 15, 15, 15, 26, 26, 23, 26, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, + 10, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 21, 21, + 12, 12, 7, 7, 7, 7, 7, 7, 21, 21, 21, 7, 21, 7, 7, 12, + 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, 12, 12, 21, 21, + 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 10, 10, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 21, + 7, 7, 7, 12, 10, 10, 12, 12, 12, 12, 10, 10, 12, 12, 10, 10, + 10, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, 6, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 21, 21, + 7, 7, 7, 7, 7, 12, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 7, 7, 7, 7, 7, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, 10, + 10, 12, 12, 10, 10, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 12, 7, 7, 7, 7, 7, 7, 7, 7, 12, 10, 2, 2, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 21, 21, 21, 21, + 6, 7, 7, 7, 7, 7, 7, 26, 26, 26, 7, 10, 12, 10, 7, 7, + 12, 7, 12, 12, 12, 7, 7, 12, 12, 7, 7, 7, 7, 7, 12, 12, + 7, 12, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7, 7, 6, 21, 21, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10, 12, 12, 10, 10, + 21, 21, 7, 6, 6, 10, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 7, 7, 7, 7, 7, 7, 2, 2, 7, 7, 7, 7, 7, 7, 2, + 2, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 24, 6, 6, 6, 6, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 24, 24, 2, 2, 2, 2, + 7, 7, 7, 10, 10, 12, 10, 10, 12, 10, 10, 21, 10, 12, 2, 2, + 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 7, 7, 7, 7, 7, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 7, 12, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 25, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 2, 7, 2, + 7, 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 18, 22, + 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 23, 26, 2, 2, + 21, 21, 21, 21, 21, 21, 21, 22, 18, 21, 2, 2, 2, 2, 2, 2, + 21, 17, 17, 16, 16, 22, 18, 22, 18, 22, 18, 22, 18, 22, 18, 22, + 18, 22, 18, 22, 18, 21, 21, 22, 18, 21, 21, 21, 21, 16, 16, 16, + 21, 21, 21, 2, 21, 21, 21, 21, 17, 22, 18, 22, 18, 22, 18, 21, + 21, 21, 25, 17, 25, 25, 25, 2, 21, 23, 21, 21, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 1, + 2, 21, 21, 21, 23, 21, 21, 21, 22, 18, 21, 25, 21, 17, 21, 21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 22, 25, 18, 25, 22, + 18, 21, 22, 18, 21, 21, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, + 2, 2, 7, 7, 7, 7, 7, 7, 2, 2, 7, 7, 7, 7, 7, 7, + 2, 2, 7, 7, 7, 7, 7, 7, 2, 2, 7, 7, 7, 2, 2, 2, + 23, 23, 25, 24, 26, 23, 23, 2, 26, 25, 25, 25, 25, 26, 26, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 26, 26, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 2, 7, + 21, 21, 21, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 2, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 14, 14, 14, 14, 14, 15, 15, 15, 15, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 15, 15, 26, 26, 26, 2, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, + 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 12, 2, 2, + 12, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2, 2, + 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7, 7, 7, + 7, 14, 7, 7, 7, 7, 7, 7, 7, 7, 14, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 21, + 7, 7, 7, 7, 2, 2, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, + 21, 14, 14, 14, 14, 14, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 9, 9, 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, 5, 5, 5, 5, + 9, 9, 9, 9, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, + 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 21, + 7, 7, 7, 7, 7, 7, 2, 2, 7, 2, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 2, 7, 7, 2, 2, 2, 7, 2, 2, 7, + 7, 7, 7, 7, 7, 7, 2, 21, 15, 15, 15, 15, 15, 15, 15, 15, + 7, 7, 7, 7, 7, 7, 7, 26, 26, 15, 15, 15, 15, 15, 15, 15, + 2, 2, 2, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 7, 7, 7, 2, 7, 7, 2, 2, 2, 2, 2, 15, 15, 15, 15, 15, + 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 2, 2, 2, 21, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 21, + 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 15, 15, 7, 7, + 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 7, 12, 12, 12, 2, 12, 12, 2, 2, 2, 2, 2, 12, 12, 12, 12, + 7, 7, 7, 7, 2, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 2, 2, 12, 12, 12, 2, 2, 2, 2, 12, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 21, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 15, + 7, 7, 7, 7, 7, 7, 7, 7, 26, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 12, 12, 2, 2, 2, 2, 15, 15, 15, 15, 15, + 21, 21, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 2, 2, 2, 21, 21, 21, 21, 21, 21, 21, + 7, 7, 7, 7, 7, 7, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, + 7, 7, 7, 2, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, + 7, 7, 2, 2, 2, 2, 2, 2, 2, 21, 21, 21, 21, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, 15, + 9, 9, 9, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, + 7, 7, 7, 7, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 12, 12, 17, 2, 2, + 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 15, 15, 15, 15, 15, 15, 15, 7, 2, 2, 2, 2, 2, 2, 2, 2, + 12, 15, 15, 15, 15, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2, 2, + 10, 12, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 21, 21, 21, 21, 21, 21, 21, 2, 2, + 15, 15, 15, 15, 15, 15, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 12, + 10, 10, 10, 12, 12, 12, 12, 10, 10, 12, 12, 21, 21, 1, 21, 21, + 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, + 12, 12, 12, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 10, 12, 12, 12, + 12, 12, 12, 12, 12, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 21, 21, 21, 21, 7, 10, 10, 7, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 12, 21, 21, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 10, + 10, 7, 7, 7, 7, 21, 21, 21, 21, 12, 12, 12, 12, 21, 10, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 7, 21, 7, 21, 21, 21, + 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 12, + 12, 12, 10, 10, 12, 10, 12, 12, 21, 21, 21, 21, 21, 21, 12, 2, + 7, 7, 7, 7, 7, 7, 7, 2, 7, 2, 7, 7, 7, 7, 2, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 21, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, + 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, 2, 2, + 12, 12, 10, 10, 2, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 7, + 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 2, 12, 12, 7, 10, 10, + 12, 10, 10, 10, 10, 2, 2, 10, 10, 2, 2, 10, 10, 10, 2, 2, + 7, 2, 2, 2, 2, 2, 2, 10, 2, 2, 2, 2, 2, 7, 7, 7, + 7, 7, 10, 10, 2, 2, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, + 12, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, + 10, 10, 12, 12, 12, 10, 12, 7, 7, 7, 7, 21, 21, 21, 21, 21, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 2, 21, 12, 7, + 10, 10, 10, 12, 12, 12, 12, 12, 12, 10, 12, 10, 10, 10, 10, 12, + 12, 10, 12, 12, 7, 7, 21, 7, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10, + 10, 10, 12, 12, 12, 12, 2, 2, 10, 10, 10, 10, 12, 12, 10, 12, + 12, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 7, 7, 7, 7, 12, 12, 2, 2, + 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 10, 10, 12, 10, 12, + 12, 21, 21, 21, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 10, 12, 10, 10, + 12, 12, 12, 12, 12, 12, 10, 12, 7, 2, 2, 2, 2, 2, 2, 2, + 10, 10, 12, 12, 12, 12, 10, 12, 12, 12, 12, 12, 2, 2, 2, 2, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 15, 15, 21, 21, 21, 26, + 12, 12, 12, 12, 12, 12, 12, 12, 10, 12, 12, 21, 2, 2, 2, 2, + 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7, + 7, 7, 7, 7, 7, 7, 7, 2, 2, 7, 2, 2, 7, 7, 7, 7, + 7, 7, 7, 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, + 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 2, 12, 12, 10, 12, 7, + 10, 7, 10, 12, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 7, 7, 7, 7, 7, 7, + 7, 10, 10, 10, 12, 12, 12, 12, 2, 2, 12, 12, 10, 10, 10, 10, + 12, 7, 21, 7, 10, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 7, 7, 7, 7, 7, + 7, 7, 7, 12, 12, 12, 12, 12, 12, 10, 7, 12, 12, 12, 12, 21, + 21, 21, 21, 21, 21, 21, 21, 12, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 12, 12, 12, 12, 12, 12, 10, 10, 12, 12, 12, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 10, 12, 12, 21, 21, 21, 7, 21, 21, + 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 12, 12, 12, 12, 12, 12, 12, 2, 12, 12, 12, 12, 12, 12, 10, 12, + 7, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 21, 21, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 2, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 2, 10, 12, 12, 12, 12, 12, 12, + 12, 10, 12, 12, 10, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, + 7, 12, 12, 12, 12, 12, 12, 2, 2, 2, 12, 2, 12, 12, 2, 12, + 12, 12, 12, 12, 12, 12, 7, 12, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10, 2, + 12, 12, 2, 10, 10, 12, 10, 12, 7, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 12, 12, 10, 10, 21, 21, 2, 2, 2, 2, 2, 2, 2, + 15, 15, 15, 15, 15, 26, 26, 26, 26, 26, 26, 26, 26, 23, 23, 23, + 23, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 21, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, + 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, + 12, 12, 12, 12, 12, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 12, 12, 12, 12, 12, 12, 12, 21, 21, 21, 21, 21, 26, 26, 26, 26, + 6, 6, 6, 6, 21, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 15, 15, 15, 15, 15, + 15, 15, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 7, 7, 7, + 15, 15, 15, 15, 15, 15, 15, 21, 21, 21, 21, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 12, + 7, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 2, 2, 2, 2, 2, 2, 2, 12, + 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 21, 6, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 10, 10, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 26, 12, 12, 21, + 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 26, 2, 2, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 10, 10, 12, 12, 12, 26, 26, 26, 10, 10, 10, + 10, 10, 10, 1, 1, 1, 1, 1, 1, 1, 1, 12, 12, 12, 12, 12, + 12, 12, 12, 26, 26, 12, 12, 12, 12, 12, 12, 12, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 12, 12, 12, 12, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 12, 12, 12, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 5, + 5, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 9, 9, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 9, 2, 9, 9, + 2, 2, 9, 2, 2, 9, 9, 2, 2, 9, 9, 9, 9, 2, 9, 9, + 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, 2, 5, 2, 5, 5, 5, + 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 9, 9, 2, 9, 9, 9, 9, 2, 2, 9, 9, 9, + 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, 2, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 9, 9, 2, 9, 9, 9, 9, 2, + 9, 9, 9, 9, 9, 2, 9, 2, 2, 2, 9, 9, 9, 9, 9, 9, + 9, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 2, 2, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 25, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 25, 5, 5, 5, 5, + 5, 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 25, 5, 5, 5, 5, 5, 5, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 25, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 25, + 5, 5, 5, 5, 5, 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 25, 5, 5, 5, 5, 5, 5, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 25, 5, 5, 5, 5, 5, 5, 9, 5, 2, 2, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 12, 12, 12, 12, 12, 12, 12, 26, 26, 26, 26, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 26, 26, 26, + 26, 26, 26, 26, 26, 12, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 12, 26, 26, 21, 21, 21, 21, 21, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 2, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 12, 12, 12, 12, 12, + 12, 12, 2, 12, 12, 2, 12, 12, 12, 12, 12, 2, 2, 2, 2, 2, + 12, 12, 12, 12, 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 2, 2, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 7, 26, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 2, 23, + 7, 7, 7, 7, 7, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 5, 5, 5, 5, 12, 12, 12, 12, 12, 12, 12, 6, 2, 2, 2, 2, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 15, 15, 15, + 23, 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, + 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 2, 7, 7, 2, 7, 2, 2, 7, 2, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 2, 7, 7, 7, 7, 2, 7, 2, 7, 2, 2, 2, 2, + 2, 2, 7, 2, 2, 2, 2, 7, 2, 7, 2, 7, 2, 7, 7, 7, + 2, 7, 7, 2, 7, 2, 2, 7, 2, 7, 2, 7, 2, 7, 2, 7, + 2, 7, 7, 2, 7, 2, 2, 7, 7, 7, 7, 2, 7, 7, 7, 7, + 7, 7, 7, 2, 7, 7, 7, 7, 2, 7, 7, 7, 7, 2, 7, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, + 2, 7, 7, 7, 2, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, + 25, 25, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, + 2, 2, 2, 2, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 24, 24, 24, 24, 24, + 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 26, 26, 26, + 26, 26, 26, 26, 26, 2, 2, 2, 26, 26, 26, 2, 2, 2, 2, 2, + 26, 26, 26, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 12, 12, 13, 14, 12, 15, 16, 17, 18, 19, 20, + 21, 22, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 24, 25, + 0, 26, 27, 0, 28, 29, 30, 31, 32, 33, 0, 34, 0, 0, 0, 0, + 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 37, 38, 0, 0, 0, 0, + 39, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, + 43, 44, 45, 46, 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 50, 0, 0, 0, + 0, 0, 0, 51, 0, 52, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 54, 55, 0, 0, 0, 0, 56, 0, 0, 57, 58, 0, + 59, 60, 61, 62, 63, 64, 65, 0, 66, 67, 0, 68, 69, 70, 71, 0, + 60, 0, 72, 73, 74, 75, 0, 0, 69, 0, 76, 77, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 78, 79, 0, 0, 0, 0, 0, 0, 0, 0, 80, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 82, 83, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 85, 0, 79, 0, 0, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 87, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 1, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 14, 15, 16, 17, 18, 19, 20, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 21, 0, 0, 0, 0, 0, 22, 23, 24, + 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 27, + 28, 29, 0, 0, 0, 0, 30, 0, 0, 0, 31, 32, 33, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 13, 35, 36, 0, 0, 26, 37, 38, 39, 0, 0, 0, 0, 0, 40, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 1, + 42, 43, 44, 45, 0, 0, 0, 0, 0, 0, 0, 46, 0, 47, 48, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 47, 0, 0, + 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 46, 0, 47, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 50, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 47, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 0, 54, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 0, 56, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 58, 59, 0, 0, 0, 0, + 0, 0, 60, 61, 62, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 65, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, + 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 68, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 71, 72, 0, 0, 0, 0, 0, 0, 0, 0, + 73, 0, 66, 74, 0, 0, 0, 0, 0, 0, 75, 76, 72, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 67, 0, 0, 0, + 0, 77, 78, 0, 0, 0, 0, 0, 0, 79, 0, 0, 0, 0, 0, 0, + 80, 0, 79, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 82, + 83, 84, 85, 86, 0, 0, 0, 0, 0, 0, 0, 0, 87, 88, 89, 1, + 1, 1, 90, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 93, + 94, 95, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 71, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 71,100,101, 0, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 86, 0,102, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, + 1, 1, 86, 0, 0, 0, 0, 0, 0,103, 0, 0, 0, 0,104, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105, 0, 73, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,106,107,108, 0, 0, 0, + 0, 0,102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 47, 0, 0, 0, 0, 0,109, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,110,111, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 26,112, 0,113, 0, 0, 0, 0, 0,114, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 115, 0, 0, 0, 0, 0, 0, 0,100, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,117,118, 72, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,102, 0, 0, 0, + 0, 0, 0, 97, 0, 0, 0, 0, 0, 0, 0,119, 0, 0, 0, 0, + 0, 0, 0, 0,112, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, + 0, 0,105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73,120, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,121, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,122, 0, 0, 0, 0, 0, 0, 0, 0, 0,123, 0, 47, 0, 0, + 26,124,124, 0, 0, 0, 0, 0, 0, 0, 0, 0,125, 0, 0, 49, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,126, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97,127, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,129,105, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 97, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,130, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,131, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,132, 0, 0, 0, 0, 0, 0, 0,133, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,134, 0, 0, 0, 0,135, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 136,137,138,139,140,141, 0, 0, 0,142, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,143, 0, 0, 0, + 0, 0, 0, 0,133, 1, 1,144,145,112, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,146, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,100,147, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,230,230,230,230, + 230,230,230,230,230,230,230,230,230,232,220,220,220,220,232,216, + 220,220,220,220,220,202,202,220,220,220,220,202,202,220,220,220, + 220,220,220,220,220,220,220,220, 1, 1, 1, 1, 1,220,220,220, + 220,230,230,230,230,230,230,230,230,240,230,220,220,220,230,230, + 230,220,220, 0,230,230,230,220,220,220,220,230,232,220,220,230, + 233,234,234,233,234,234,233,230,230,230,230,230, 0, 0, 0,230, + 230,230,230,230, 0,220,230,230,230,230,220,230,230,230,222,220, + 230,230,230,230,230,230,220,220,220,220,220,220,230,230,220,230, + 230,222,228,230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, + 21, 22, 0, 23, 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, + 0, 0, 0, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34,230, + 230,220,220,230,230,230,230,230,220,230,230,220, 35, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,230,230,230,230,230,230, + 230, 0, 0,230,230,230,230,220,230, 0, 0,230,230, 0,220,230, + 230,220, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0,230,220,230,230, + 220,230,230,220,220,220,230,220,220,230,220,230,230,230,220,230, + 220,230,220,230,220,230,230, 0, 0, 0, 0, 0,230,230,220,230, + 0, 0, 0, 0, 0, 0, 0, 0, 0,220, 0, 0,230,230, 0,230, + 230,230,230,230,230,230,230,230, 0,230,230,230, 0,230,230,230, + 230,230, 0, 0, 0,220,220,220, 0, 0, 0, 0, 0, 0, 0,220, + 230,230,230,230,230,230, 0,220,230,230,220,230,230,220,230,230, + 230,220,220,220, 27, 28, 29,230,230,230,220,230,230,220,220,230, + 230,230,230,230, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, + 0, 9, 0, 0, 0,230,220,230,230, 0, 0, 0, 0, 0, 0, 0, + 0, 0,230, 0, 0, 0, 0, 0, 0, 84, 91, 0, 0, 0, 0, 9, + 9, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0,103,103, 9, 0, + 0, 0, 0, 0,107,107,107,107, 0, 0, 0, 0,118,118, 9, 0, + 0, 0, 0, 0,122,122,122,122, 0, 0, 0, 0,220,220, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,220, 0,220, 0,216, 0, 0, + 0, 0, 0, 0, 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130, + 130,130, 0, 0,130, 0,230,230, 9, 0,230,230, 0, 0, 0, 0, + 0, 0,220, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 9, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,230,230,230, 0, 0, 0, 0, + 9, 0, 0, 0, 0, 0, 0, 0, 0,230, 0, 0, 0,228, 0, 0, + 0, 0, 0, 0, 0,222,230,220, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,230,220, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 0, 0, 0, 0,230,230,230,230,230, 0, 0,220,230,230,230,230, + 230,220,220,220,220,220,220,230,230,220, 0,220, 0, 0, 0,230, + 220,230,230,230,230,230,230,230, 0, 0, 0, 0, 0, 0, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0,230,230,230, 0, + 1,220,220,220,220,220,230,230,220,220,220,220,230, 0, 1, 1, + 1, 1, 1, 1, 1, 0, 0, 0, 0,220, 0, 0, 0, 0, 0, 0, + 230, 0, 0, 0,230,230, 0, 0, 0, 0, 0, 0,230,230,220,230, + 230,230,230,230,230,230,220,230,230,234,214,220,202,230,230,230, + 230,230,230,230,230,230,230,230,230,230,232,228,228,220, 0,230, + 233,220,230,220,230,230, 1, 1,230,230,230,230, 1, 1, 1,230, + 230, 0, 0, 0, 0,230, 0, 0, 0, 1, 1,230,220,230, 1, 1, + 220,220,220,220,230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 9, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, + 0, 0, 0, 0, 0, 0, 0, 0,230,230,230,230,230,230,230,230, + 230,230, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0,220, + 220,220, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 7, + 0, 0, 0, 0,230, 0,230,230,220, 0, 0,230,230, 0, 0, 0, + 0, 0,230,230, 0,230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 26, 0,230,230,230,230,230,230,230,220,220,220,220,220, + 220,220,230,230,230,230,230, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,220, 0,230,230, 1,220, 0, 0, 0, 0, 9, 0, 0, 0, 0, + 0,230,220, 0, 0, 0, 0,230,230, 0, 0, 0, 0, 0, 0, 0, + 0, 0,220,220,230,230,230,220,230,220,220,220, 0, 9, 7, 0, + 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 0, 0, 0, 0, 0, 7, + 7, 0, 0, 0,230,230,230,230,230, 0, 0, 0, 0, 0, 9, 0, + 0, 0, 7, 0, 0, 0, 9, 7, 0, 0, 0, 0, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 7, 0, 0, 0, 0, + 0, 9, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, + 9, 9, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,230,230,230,230, + 230,230,230, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0,216,216, 1, 1, 1, 0, 0, + 0,226,216,216,216,216,216, 0, 0, 0, 0, 0, 0, 0, 0,220, + 220,220,220,220,220,220,220, 0, 0,230,230,230,230,230,220,220, + 0, 0, 0, 0, 0, 0,230,230,230,230, 0, 0, 0, 0,230,230, + 230, 0, 0, 0,230, 0, 0,230,230,230,230,230,230,230, 0,230, + 230, 0,230,230,220,220,220,220,220,220,220, 0,230,230, 7, 0, + 0, 0, 0, 0, 16, 17, 17, 17, 17, 17, 17, 33, 17, 17, 17, 19, + 17, 17, 17, 17, 20,101, 17,113,129,169, 17, 27, 28, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17,237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 0, 0, + 0, 0, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, + 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 7, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 10, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 11, 12, 0, 13, 0, 14, 15, 16, 0, 0, + 0, 0, 0, 1, 17, 18, 0, 19, 7, 1, 0, 0, 0, 20, 20, 7, + 20, 20, 20, 20, 20, 20, 20, 8, 21, 0, 22, 0, 7, 23, 24, 0, + 20, 20, 25, 0, 0, 0, 26, 27, 1, 7, 20, 20, 20, 20, 20, 1, + 28, 29, 30, 31, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, + 20, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 21, 32, 4, 0, 10, 0, 33, 7, 20, 20, 20, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 34, 34, 35, 36, 34, 37, 0, 38, 1, 20, 20, + 0, 0, 39, 0, 1, 1, 0, 8, 21, 1, 20, 0, 0, 0, 1, 0, + 0, 40, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 21, + 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 26, 34, 34, 34, 34, 34, 34, 34, 34, 34, 21, 7, 20, 41, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 21, 0, 42, 43, 44, 0, 45, + 0, 8, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 46, 7, 1, 10, 1, 0, 0, 0, 1, 20, 20, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 20, 20, 1, 20, 20, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 21, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, + 0, 0, 3, 4, 0, 0, 0, 0, 0, 0, 3, 47, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 0, 0, + 0, 0, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 17, 19, 20, 21, 22, 23, 24, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 27, 28, 28, 29, 30, 31, 32, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 35, 35, 35, 35, 35, 59, 59, 60, 35, + 35, 35, 35, 35, 35, 35, 61, 62, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 63, 64, 35, 65, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 67, 66, 68, 69, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 70, 71, 35, 35, + 35, 35, 72, 35, 35, 35, 35, 35, 35, 35, 35, 35, 73, 74, 75, 76, + 77, 78, 35, 35, 79, 80, 35, 35, 81, 35, 82, 83, 84, 85, 17, 86, + 87, 88, 35, 35, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 89, 25, 25, 25, 25, 25, 25, 25, 90, + 91, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 92, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 93, 35, 35, 35, 35, 35, 35, + 25, 94, 35, 35, 25, 25, 25, 25, 25, 25, 25, 25, 25, 95, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 19, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, + 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, + 26, 26, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, 2, 2, + 9, 9, 9, 9, 0, 9, 2, 2, 2, 2, 9, 0, 9, 0, 9, 9, + 9, 2, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 2, 2, 4, 4, 4, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 2, + 2, 2, 2, 2, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 2, 2, 2, 2, 14, 14, 14, 14, 14, 14, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, + 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 0, 3, 2, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 0, 3, 3, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 2, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 2, 2, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 2, 2, 64, 64, 64, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 2, 2, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 2, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 2, 2, 95, 2, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, + 5, 5, 5, 2, 2, 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, + 5, 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 5, 5, 5, 5, + 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 2, + 2, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 5, 2, 2, + 2, 2, 5, 5, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 2, 2, 11, 11, 11, 2, 11, 11, 11, 11, 11, + 11, 2, 2, 2, 2, 11, 11, 2, 2, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, + 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, + 2, 2, 11, 2, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, + 2, 11, 11, 11, 2, 2, 2, 11, 2, 2, 2, 2, 2, 2, 2, 11, + 11, 11, 11, 2, 11, 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 2, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 2, 10, 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, + 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, 10, + 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, + 2, 10, 10, 10, 2, 2, 10, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 2, 2, 2, 2, 2, 2, 2, 10, + 10, 10, 10, 10, 10, 10, 2, 21, 21, 21, 2, 21, 21, 21, 21, 21, + 21, 21, 21, 2, 2, 21, 21, 2, 2, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, + 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 2, 21, 21, 21, 21, 21, + 2, 2, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, + 2, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 21, 21, 21, 2, 2, + 2, 2, 21, 21, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, + 22, 2, 2, 2, 22, 22, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, + 22, 2, 22, 2, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, 22, 22, + 22, 2, 2, 2, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 2, 2, 2, 2, 22, 22, 22, 22, 22, 2, 2, 2, 22, 22, 22, 2, + 22, 22, 22, 22, 2, 2, 22, 2, 2, 2, 2, 2, 2, 22, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 2, 2, 2, 2, 2, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 2, 23, 23, 23, 2, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 2, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 2, 2, 2, 23, 23, 23, 23, 23, 23, 23, 23, 2, 23, 23, 23, 2, + 23, 23, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 23, 2, 23, 23, + 23, 2, 2, 2, 2, 2, 23, 23, 23, 23, 2, 2, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 2, 16, 16, 16, 2, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 16, + 2, 2, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 2, + 16, 16, 16, 16, 2, 2, 2, 2, 2, 2, 2, 16, 16, 2, 2, 2, + 2, 2, 2, 2, 16, 2, 16, 16, 16, 16, 2, 2, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 2, 16, 16, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 2, 20, 20, 20, 2, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 2, 20, 20, 20, 2, + 20, 20, 20, 20, 20, 20, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 2, 2, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 2, 36, 36, 36, 2, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 2, 36, 2, 2, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, + 36, 2, 2, 2, 2, 36, 36, 36, 36, 36, 36, 2, 36, 2, 36, 36, + 36, 36, 36, 36, 36, 36, 2, 2, 2, 2, 2, 2, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 2, 2, 36, 36, 36, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 2, 2, 2, 2, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 2, 2, 2, 2, 2, 18, 18, 2, 18, 2, 18, 18, 18, 18, + 18, 2, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 2, 2, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, + 18, 18, 18, 18, 2, 2, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 2, 2, 18, 18, 18, 18, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 2, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 2, 2, 2, 2, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 2, 25, 25, 25, 25, 25, 25, 25, 0, 0, 0, 0, 25, + 25, 2, 2, 2, 2, 2, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 2, 8, 2, 2, + 2, 2, 2, 8, 2, 2, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 0, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 2, + 30, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, 30, 30, 2, 30, 2, + 30, 30, 30, 30, 2, 2, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, + 30, 30, 30, 30, 30, 2, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 2, 2, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 2, 2, 2, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 2, 2, 2, 2, 2, 2, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 2, 2, 29, 29, + 29, 29, 29, 29, 2, 2, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 2, 2, 2, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 0, 0, 0, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 2, + 2, 2, 2, 2, 2, 2, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 2, 45, 45, 45, 45, 45, 45, 45, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 2, 46, 46, 46, 2, 46, 46, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 2, 2, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 2, 2, 2, 2, 2, 2, 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 2, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 2, 2, 2, 2, 2, 2, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2, + 2, 2, 2, 2, 2, 2, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 2, 2, 2, 2, 2, 28, 28, 28, 28, 28, 28, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 2, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 2, 2, 2, 2, 48, 2, 2, 2, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 2, 2, 52, 52, 52, 52, 52, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 2, 2, 2, 2, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 2, 2, 2, 2, 2, 2, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 2, 2, 2, 58, 58, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 2, 2, 54, 54, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 2, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 2, 2, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 2, 2, 2, 2, 2, 2, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 2, 2, 2, 2, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 2, 2, 2, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 2, 2, 2, 2, 2, 2, + 2, 2, 93, 93, 93, 93, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 2, 2, + 2, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, + 2, 2, 2, 2, 2, 2, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 2, 2, 8, 8, 8, 76, 76, 76, 76, 76, 76, 76, 76, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, + 0, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, + 9, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 9, 9, 9, 9, + 9, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 6, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 2, 2, 9, 9, + 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, 9, 9, 9, 9, 2, 9, + 2, 9, 2, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, + 9, 9, 2, 9, 9, 9, 2, 2, 9, 9, 9, 2, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 19, 2, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 2, 2, 2, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 19, 19, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 19, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, + 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 2, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 2, 2, 2, 2, 2, 55, + 55, 55, 55, 55, 55, 55, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 2, 2, + 2, 2, 2, 2, 2, 61, 61, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 61, 30, 30, 30, 30, 30, 30, 30, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, + 30, 30, 30, 30, 30, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 13, 0, 13, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 1, 1, 1, 1, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 13, 13, + 13, 13, 0, 0, 0, 0, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, + 1, 0, 0, 15, 15, 15, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 0, 0, 17, 17, 17, 2, 2, 2, 2, 2, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 2, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 0, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, + 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 2, 2, 2, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 2, 2, 2, 39, 39, 39, 39, 39, 39, 39, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 2, 2, 2, 2, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, + 0, 19, 19, 19, 19, 19, 2, 2, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 2, 2, + 2, 2, 2, 2, 2, 2, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 2, 2, 2, 2, + 2, 2, 2, 2, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 2, 2, 2, 2, 2, 2, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 0, 69, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 74, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 2, 2, 2, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 2, 0, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 2, 2, 2, 2, 84, 84, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 2, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 2, 2, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 2, 2, 68, 68, 68, 68, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 92, 92, 92, 92, 92, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 30, 30, 30, 30, 30, 30, 2, 2, 30, + 30, 30, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, 30, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 19, 19, 19, 19, + 0, 0, 2, 2, 2, 2, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 2, 2, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, + 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 2, 2, + 2, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 14, 14, + 14, 14, 14, 2, 14, 2, 14, 14, 2, 14, 14, 2, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 0, 0, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6, 6, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 0, 0, 2, 2, 12, 12, 12, 12, 12, 12, 2, 2, + 12, 12, 12, 12, 12, 12, 2, 2, 12, 12, 12, 12, 12, 12, 2, 2, + 12, 12, 12, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, + 0, 0, 0, 0, 2, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 2, 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 2, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 2, 2, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 2, 2, 2, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 2, 2, 2, 2, 2,118,118,118,118,118,118,118,118,118,118, + 118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118, + 118, 2, 2, 2, 2, 2, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2, 2, 2, 2, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 2, 2, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 2, 2, 2, 2, 2, 2,135,135,135,135,135,135,135,135,135,135, + 135,135,135,135,135,135,135,135,135,135, 2, 2, 2, 2,135,135, + 135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135, + 135,135, 2, 2, 2, 2,106,106,106,106,106,106,106,106,106,106, + 106,106,106,106,106,106,106,106,106,106,106,106,106,106, 2, 2, + 2, 2, 2, 2, 2, 2,104,104,104,104,104,104,104,104,104,104, + 104,104,104,104,104,104,104,104,104,104, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2,104,110,110,110,110,110,110,110,110,110,110, + 110,110,110,110,110,110,110,110,110,110,110,110,110, 2, 2, 2, + 2, 2, 2, 2, 2, 2,110,110,110,110,110,110, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,110,110,110,110,110,110,110,110, 2, 2, + 2, 2, 2, 2, 2, 2, 47, 47, 47, 47, 47, 47, 2, 2, 47, 2, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 2, 47, 47, 2, + 2, 2, 47, 2, 2, 47, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2, 81, 81, 81, + 81, 81, 81, 81, 81, 81,120,120,120,120,120,120,120,120,120,120, + 120,120,120,120,120,120,116,116,116,116,116,116,116,116,116,116, + 116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116, + 116,116,116,116,116, 2, 2, 2, 2, 2, 2, 2, 2,116,116,116, + 116,116,116,116,116,116,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128, 2,128,128, 2, 2, 2, 2, + 2,128,128,128,128,128, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 2, 2, 2, 66, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 2, 2, 2, 2, 2, 72, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 2, 2, + 2, 2, 97, 97, 97, 97, 2, 2, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 57, 57, 57, 57, 2, 57, 57, 2, 2, 2, + 2, 2, 57, 57, 57, 57, 57, 57, 57, 57, 2, 57, 57, 57, 2, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 2, 2, 57, 57, + 57, 2, 2, 2, 2, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 2, + 2, 2, 2, 2, 2, 2, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88,117,117,117,117,117,117,117,117,117,117, + 117,117,117,117,117,117,112,112,112,112,112,112,112,112,112,112, + 112,112,112,112,112,112,112,112,112,112,112,112,112, 2, 2, 2, + 2,112,112,112,112,112,112,112,112,112,112,112,112, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 2, 2, 2, 78, + 78, 78, 78, 78, 78, 78, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 2, 2, 83, 83, + 83, 83, 83, 83, 83, 83, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, 82, 2, 2, 2, 2, 2, 82, 82, + 82, 82, 82, 82, 82, 82,122,122,122,122,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122, 2, 2, 2, 2, 2, 2, 2,122, + 122,122,122, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,122, + 122,122,122,122,122,122, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 2, + 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,130,130,130,130, + 130,130,130,130,130,130,130,130,130, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,130,130,130, 2, 2, 2, 2, 2, 2, 2, + 130,130,130,130,130,130,144,144,144,144,144,144,144,144,144,144, + 144,144,144,144,144,144,144,144,144,144,144,144,144,144, 2, 2, + 2, 2, 2, 2, 2, 2,144,144,144,144,144,144,144,144,144,144, + 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 2,156,156,156,156,156,156,156,156,156,156, + 156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156, + 2,156,156,156, 2, 2,156,156, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,147,147,147,147,147,147,147,147,147,147, + 147,147,147,147,147,147,147,147,147,147,147,147,147,147, 2, 2, + 2, 2, 2, 2, 2, 2,148,148,148,148,148,148,148,148,148,148, + 148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148, + 2, 2, 2, 2, 2, 2,153,153,153,153,153,153,153,153,153,153, + 153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153, + 153,153, 2, 2, 2, 2,149,149,149,149,149,149,149,149,149,149, + 149,149,149,149,149,149,149,149,149,149,149,149,149, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 94, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 85, 2, 2,101,101,101,101,101,101,101,101,101,101, + 101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, 2, + 2, 2, 2, 2, 2, 2,101,101,101,101,101,101,101,101,101,101, + 2, 2, 2, 2, 2, 2, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 2, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 2, 2, + 2, 2, 2, 2, 2, 2,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111, 2, 2, 2, + 2, 2, 2, 2, 2, 2,100,100,100,100,100,100,100,100,100,100, + 100,100,100,100,100,100, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,108,108,108,108,108,108,108,108,108,108, + 108,108,108,108,108,108,108,108, 2,108,108,108,108,108,108,108, + 108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108, + 108,108,108,108,108, 2,129,129,129,129,129,129,129, 2,129, 2, + 129,129,129,129, 2,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129, 2,129,129,129,129,129,129,129,129,129,129,129, + 2, 2, 2, 2, 2, 2,109,109,109,109,109,109,109,109,109,109, + 109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + 109, 2, 2, 2, 2, 2,109,109,109,109,109,109,109,109,109,109, + 2, 2, 2, 2, 2, 2,107,107,107,107, 2,107,107,107,107,107, + 107,107,107, 2, 2,107,107, 2, 2,107,107,107,107,107,107,107, + 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107, 2, + 107,107,107,107,107,107,107, 2,107,107, 2,107,107,107,107,107, + 2, 1,107,107,107,107,107,107,107,107,107, 2, 2,107,107, 2, + 2,107,107,107, 2, 2,107, 2, 2, 2, 2, 2, 2,107, 2, 2, + 2, 2, 2,107,107,107,107,107,107,107, 2, 2,107,107,107,107, + 107,107,107, 2, 2, 2,107,107,107,107,107, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,137,137,137,137,137,137,137,137,137,137, + 137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137, + 137,137, 2,137,137,137,137,137, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,124,124,124,124,124,124,124,124,124,124, + 124,124,124,124,124,124,124,124,124,124,124,124,124,124, 2, 2, + 2, 2, 2, 2, 2, 2,124,124,124,124,124,124,124,124,124,124, + 2, 2, 2, 2, 2, 2,123,123,123,123,123,123,123,123,123,123, + 123,123,123,123,123,123,123,123,123,123,123,123, 2, 2,123,123, + 123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, + 123,123,123,123, 2, 2,114,114,114,114,114,114,114,114,114,114, + 114,114,114,114,114,114,114,114,114,114,114, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,114,114,114,114,114,114,114,114,114,114, + 2, 2, 2, 2, 2, 2, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 2, 2, 2,102,102,102,102,102,102,102,102,102,102, + 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102, 2, + 2, 2, 2, 2, 2, 2,102,102,102,102,102,102,102,102,102,102, + 2, 2, 2, 2, 2, 2,126,126,126,126,126,126,126,126,126,126, + 126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126, + 126, 2, 2,126,126,126,126,126,126,126,126,126,126,126,126,126, + 126,126, 2, 2, 2, 2,142,142,142,142,142,142,142,142,142,142, + 142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142, + 142,142, 2, 2, 2, 2,125,125,125,125,125,125,125,125,125,125, + 125,125,125,125,125,125,125,125,125, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2,125,154,154,154,154,154,154,154, 2, 2,154, + 2, 2,154,154,154,154,154,154,154,154, 2,154,154, 2,154,154, + 154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154, + 154,154,154,154,154,154,154,154,154,154,154,154, 2,154,154, 2, + 2,154,154,154,154,154,154,154,154,154,154,154,154, 2, 2, 2, + 2, 2, 2, 2, 2, 2,154,154,154,154,154,154,154,154,154,154, + 2, 2, 2, 2, 2, 2,150,150,150,150,150,150,150,150, 2, 2, + 150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, + 150,150,150,150,150,150,150,150,150,150,150, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,141,141,141,141,141,141,141,141,141,141, + 141,141,141,141,141,141,141,141,141,141,141,141,141,141, 2, 2, + 2, 2, 2, 2, 2, 2,140,140,140,140,140,140,140,140,140,140, + 140,140,140,140,140,140,140,140,140, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,121,121,121,121,121,121,121,121,121,121, + 121,121,121,121,121,121,121,121,121,121,121,121,121,121,121, 2, + 2, 2, 2, 2, 2, 2,133,133,133,133,133,133,133,133,133, 2, + 133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133, + 133,133,133,133,133,133,133,133,133,133,133,133,133, 2,133,133, + 133,133,133,133,133,133,133,133,133,133,133,133, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,133,133,133,133,133,133,133,133,133,133, + 133,133,133, 2, 2, 2,134,134,134,134,134,134,134,134,134,134, + 134,134,134,134,134,134, 2, 2,134,134,134,134,134,134,134,134, + 134,134,134,134,134,134,134,134,134,134,134,134,134,134, 2,134, + 134,134,134,134,134,134,134,134,134,134,134,134,134, 2, 2, 2, + 2, 2, 2, 2, 2, 2,138,138,138,138,138,138,138, 2,138,138, + 2,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138, + 138,138,138,138,138,138,138,138,138,138,138,138,138, 2, 2, 2, + 138, 2,138,138, 2,138,138,138,138,138,138,138,138,138, 2, 2, + 2, 2, 2, 2, 2, 2,138,138,138,138,138,138,138,138,138,138, + 2, 2, 2, 2, 2, 2,143,143,143,143,143,143, 2,143,143, 2, + 143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143, + 143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143, + 143,143,143,143,143, 2,143,143, 2,143,143,143,143,143,143, 2, + 2, 2, 2, 2, 2, 2,143,143,143,143,143,143,143,143,143,143, + 2, 2, 2, 2, 2, 2,145,145,145,145,145,145,145,145,145,145, + 145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, 2, + 2, 2, 2, 2, 2, 2, 86, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 22, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 2, 63, 63, 63, 63, 63, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 2, 80, 80, 80, 80, 80, 80, 80, 80, 80, 2, + 2, 2, 2, 2, 2, 2,127,127,127,127,127,127,127,127,127,127, + 127,127,127,127,127,127,127,127,127,127,127,127,127, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 79, 79, 79, 79, 79, 79, 79, 79, 79, 2, + 2, 2, 2, 2, 2, 2,115,115,115,115,115,115,115,115,115,115, + 115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, + 115,115,115,115,115, 2,115,115,115,115,115,115,115,115,115,115, + 2, 2, 2, 2,115,115,103,103,103,103,103,103,103,103,103,103, + 103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103, + 103,103,103,103, 2, 2,103,103,103,103,103,103, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,119,119,119,119,119,119,119,119,119,119, + 119,119,119,119,119,119,119,119,119,119,119,119, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,119,119,119,119,119,119,119,119,119,119, + 2,119,119,119,119,119,119,119, 2,119,119,119,119,119,119,119, + 119,119,119,119,119,119,119,119,119,119,119,119,119,119, 2, 2, + 2, 2, 2,119,119,119,146,146,146,146,146,146,146,146,146,146, + 146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, + 146, 2, 2, 2, 2, 2, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 2, 2, 2, 2, 99, 99, 99, 99, 99, 99, 99, 99, 99, 2, 2, + 2, 2, 2, 2, 2, 99,136,139, 0, 0,155, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 13, 13, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,136,136,136,136,136,136,136,136,136,136, + 136,136,136,136,136,136,136,136,136,136,136,136,136,136, 2, 2, + 2, 2, 2, 2, 2, 2,155,155,155,155,155,155,155,155,155,155, + 155,155,155,155,155,155,155,155,155,155,155,155, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,136,136,136,136,136,136,136,136,136, 2, + 2, 2, 2, 2, 2, 2, 17, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 2, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 17, 17, 17, 17, 2, 2, + 2, 2, 2, 2, 2, 2,139,139,139,139,139,139,139,139,139,139, + 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, + 139,139, 2, 2, 2, 2,105,105,105,105,105,105,105,105,105,105, + 105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105, + 105, 2, 2, 2, 2, 2,105,105,105,105,105,105,105,105,105,105, + 105,105,105, 2, 2, 2,105,105,105,105,105,105,105,105,105, 2, + 2, 2, 2, 2, 2, 2,105,105,105,105,105,105,105,105,105,105, + 2, 2,105,105,105,105, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + 2, 2, 2, 2, 2, 2, 9, 9, 9, 9, 9, 9, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, 2, 2, 0, 2, 2, 0, 0, 2, 2, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 2, 0, 0,131,131,131,131,131,131,131,131,131,131, + 131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131, + 131,131, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2,131,131,131,131,131, 2,131,131,131,131,131,131,131,131,131, + 131,131,131,131,131,131, 56, 56, 56, 56, 56, 56, 56, 2, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 2, + 2, 56, 56, 56, 56, 56, 56, 56, 2, 56, 56, 2, 56, 56, 56, 56, + 56, 2, 2, 2, 2, 2,151,151,151,151,151,151,151,151,151,151, + 151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151, + 151,151,151, 2, 2, 2,151,151,151,151,151,151,151,151,151,151, + 151,151,151,151, 2, 2,151,151,151,151,151,151,151,151,151,151, + 2, 2, 2, 2,151,151,152,152,152,152,152,152,152,152,152,152, + 152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, + 2, 2, 2, 2, 2,152,113,113,113,113,113,113,113,113,113,113, + 113,113,113,113,113,113,113,113,113,113,113, 2, 2,113,113,113, + 113,113,113,113,113,113,113,113,113,113,113,113,113, 2, 2, 2, + 2, 2, 2, 2, 2, 2,132,132,132,132,132,132,132,132,132,132, + 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132, + 132,132, 2, 2, 2, 2,132,132,132,132,132,132,132,132,132,132, + 2, 2, 2, 2,132,132, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 2, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 2, 3, 3, 2, 3, 2, 2, 3, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 2, 3, + 2, 3, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 3, 2, 3, + 2, 3, 2, 3, 3, 3, 2, 3, 3, 2, 3, 2, 2, 3, 2, 3, + 2, 3, 2, 3, 2, 3, 2, 3, 3, 2, 3, 2, 2, 3, 3, 3, + 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 2, 3, + 3, 3, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, 2, 3, 3, 3, 3, 3, + 2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 15, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, + 0, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 13, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 2, 3, 4, 5, 6, 0, + 0, 0, 0, 7, 8, 9, 10, 11, 0, 12, 0, 0, 0, 0, 13, 0, + 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 15, 16, 0, 17, 18, 19, + 0, 0, 0, 20, 21, 22, 0, 23, 0, 24, 0, 25, 0, 26, 0, 0, + 0, 0, 0, 27, 28, 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 41, 0, 42, 43, 44, 45, + 46, 47, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 50, 51, 52, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 64, 0, 0, 0, 0, 0, + 0, 0, 0, 65, 0, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 70, 71, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 72, 73, 74, 75, 76, 77, 78, 79, 80, 0, +}; +static const uint16_t +_hb_ucd_u16[11328] = +{ + 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, + 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, + 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, + 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, + 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, + 13, 13, 13, 42, 9, 43, 11, 11, 44, 45, 32, 46, 47, 48, 49, 50, + 51, 52, 48, 48, 53, 32, 54, 55, 48, 48, 48, 48, 48, 56, 57, 58, + 59, 60, 48, 32, 61, 48, 48, 48, 48, 48, 62, 63, 64, 48, 65, 66, + 48, 67, 68, 69, 48, 70, 71, 72, 72, 72, 48, 73, 74, 75, 76, 32, + 77, 48, 48, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 84, 85, 92, 93, 94, 95, 96, 97, 98, 85, 99, 100, 101, 89, 102, + 103, 84, 85, 104, 105, 106, 89, 107, 108, 109, 110, 111, 112, 113, 95, 114, + 115, 116, 85, 117, 118, 119, 89, 120, 121, 116, 85, 122, 123, 124, 89, 125, + 126, 116, 48, 127, 128, 129, 89, 130, 131, 132, 48, 133, 134, 135, 95, 136, + 137, 48, 48, 138, 139, 140, 72, 72, 141, 48, 142, 143, 144, 145, 72, 72, + 146, 147, 148, 149, 150, 48, 151, 152, 153, 154, 32, 155, 156, 157, 72, 72, + 48, 48, 158, 159, 160, 161, 162, 163, 164, 165, 9, 9, 166, 11, 11, 167, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 168, 169, 48, 48, 168, 48, 48, 170, 171, 172, 48, 48, + 48, 171, 48, 48, 48, 173, 174, 175, 48, 176, 9, 9, 9, 9, 9, 177, + 178, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 179, 48, 180, 181, 48, 48, 48, 48, 182, 183, + 184, 185, 48, 186, 48, 187, 184, 188, 48, 48, 48, 189, 190, 191, 192, 193, + 194, 192, 48, 48, 195, 48, 48, 196, 197, 48, 198, 48, 48, 48, 48, 199, + 48, 200, 201, 202, 203, 48, 204, 205, 48, 48, 206, 48, 207, 208, 209, 209, + 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 216, 72, 72, 72, + 217, 48, 48, 218, 219, 160, 220, 221, 222, 48, 223, 64, 48, 48, 224, 225, + 48, 48, 226, 227, 228, 64, 48, 229, 230, 9, 9, 231, 232, 233, 234, 235, + 11, 11, 236, 27, 27, 27, 237, 238, 11, 239, 27, 27, 32, 32, 32, 240, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 241, 13, 13, 13, 13, 13, 13, + 242, 243, 242, 242, 243, 244, 242, 245, 246, 246, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 262, 72, 263, 264, 216, + 265, 266, 267, 268, 269, 270, 271, 271, 272, 273, 274, 209, 275, 276, 209, 277, + 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 279, 209, 280, 209, 209, 209, 209, 281, 209, 282, 278, 283, 209, 284, 285, 209, + 209, 209, 286, 72, 287, 72, 270, 270, 270, 288, 209, 209, 209, 209, 289, 270, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 290, 291, 209, 209, 292, + 209, 209, 209, 209, 209, 209, 293, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 294, 295, 270, 296, 209, 209, 297, 278, 298, 278, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 278, 278, 278, 278, 278, 278, 278, 278, 299, 300, 278, 278, 278, 301, 278, 302, + 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 209, 209, 209, 278, 303, 209, 209, 304, 209, 305, 209, 209, 209, 209, 209, 209, + 9, 9, 306, 11, 11, 307, 308, 309, 13, 13, 13, 13, 13, 13, 310, 311, + 11, 11, 312, 48, 48, 48, 313, 314, 48, 315, 316, 316, 316, 316, 32, 32, + 317, 318, 319, 320, 321, 322, 72, 72, 209, 323, 209, 209, 209, 209, 209, 324, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 325, 72, 326, + 327, 328, 329, 330, 137, 48, 48, 48, 48, 331, 178, 48, 48, 48, 48, 332, + 333, 48, 48, 137, 48, 48, 48, 48, 200, 334, 48, 48, 209, 209, 324, 48, + 209, 335, 336, 209, 337, 338, 209, 209, 336, 209, 209, 338, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 209, 209, 209, 209, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 151, + 48, 339, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 286, 48, 48, 229, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 340, 48, 341, 72, 13, 13, 342, 343, 13, 344, 48, 48, 48, 48, 345, 346, + 31, 347, 348, 349, 13, 13, 13, 350, 351, 352, 353, 354, 355, 72, 72, 356, + 357, 48, 358, 359, 48, 48, 48, 360, 361, 48, 48, 362, 363, 192, 32, 364, + 64, 48, 365, 48, 366, 367, 48, 151, 77, 48, 48, 368, 369, 370, 371, 372, + 48, 48, 373, 374, 375, 376, 48, 377, 48, 48, 48, 378, 379, 380, 381, 382, + 383, 384, 316, 11, 11, 385, 386, 11, 11, 11, 11, 11, 48, 48, 387, 192, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 388, 48, 389, 48, 48, 206, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 207, 72, 72, + 392, 393, 394, 395, 396, 48, 48, 48, 48, 48, 48, 397, 398, 399, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 400, 72, 48, 48, 48, 48, 401, 48, 48, 74, 72, 72, 402, + 32, 403, 32, 404, 405, 406, 407, 73, 48, 48, 48, 48, 48, 48, 48, 408, + 409, 2, 3, 4, 5, 410, 411, 412, 48, 413, 48, 200, 414, 415, 416, 417, + 418, 48, 172, 419, 204, 204, 72, 72, 48, 48, 48, 48, 48, 48, 48, 71, + 420, 270, 270, 421, 271, 271, 271, 422, 423, 424, 425, 72, 72, 209, 209, 426, + 72, 72, 72, 72, 72, 72, 72, 72, 48, 151, 48, 48, 48, 101, 427, 428, + 48, 48, 429, 48, 430, 48, 48, 431, 48, 432, 48, 48, 433, 434, 72, 72, + 9, 9, 435, 11, 11, 48, 48, 48, 48, 204, 192, 9, 9, 436, 11, 437, + 48, 48, 74, 48, 48, 48, 438, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 315, 48, 199, 74, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 439, 48, 48, 440, 48, 441, 48, 442, 48, 200, 443, 72, 72, 72, 48, 444, + 48, 445, 48, 446, 72, 72, 72, 72, 48, 48, 48, 447, 270, 448, 270, 270, + 449, 450, 48, 451, 452, 453, 48, 454, 48, 455, 72, 72, 456, 48, 457, 458, + 48, 48, 48, 459, 48, 460, 48, 461, 48, 462, 463, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 196, 72, 72, 72, 9, 9, 9, 464, 11, 11, 11, 465, + 48, 48, 466, 192, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 270, 467, 48, 48, 468, 469, 72, 72, 72, 72, + 48, 455, 470, 48, 62, 471, 72, 72, 72, 72, 72, 48, 472, 72, 48, 315, + 473, 48, 48, 474, 475, 448, 476, 477, 222, 48, 48, 478, 479, 48, 196, 192, + 480, 48, 481, 482, 483, 48, 48, 484, 222, 48, 48, 485, 486, 487, 488, 489, + 48, 98, 490, 491, 72, 72, 72, 72, 492, 493, 494, 48, 48, 495, 496, 192, + 497, 84, 85, 498, 499, 500, 501, 502, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 503, 504, 505, 469, 72, 48, 48, 48, 506, 507, 192, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 48, 48, 508, 509, 510, 511, 72, 72, + 48, 48, 48, 512, 513, 192, 514, 72, 48, 48, 515, 516, 192, 72, 72, 72, + 48, 173, 517, 518, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 490, 519, 72, 72, 72, 72, 72, 72, 9, 9, 11, 11, 148, 520, + 521, 522, 48, 523, 524, 192, 72, 72, 72, 72, 525, 48, 48, 526, 527, 72, + 528, 48, 48, 529, 530, 531, 48, 48, 532, 533, 534, 72, 48, 48, 48, 196, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 85, 48, 508, 535, 536, 148, 175, 537, 48, 538, 539, 540, 72, 72, 72, 72, + 541, 48, 48, 542, 543, 192, 544, 48, 545, 546, 192, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 48, 547, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 101, 270, 548, 549, 550, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 207, 72, 72, 72, 72, 72, 72, + 271, 271, 271, 271, 271, 271, 551, 552, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 388, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 200, 553, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 315, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 196, 48, 200, 370, 72, 72, 72, 72, 72, 72, 48, 204, 554, + 48, 48, 48, 555, 556, 557, 558, 559, 48, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 9, 9, 11, 11, 270, 560, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 561, 562, 563, 563, 564, 565, 72, 72, 72, 72, 566, 567, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 74, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 199, 72, 72, + 196, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 200, 72, 72, 72, 568, 569, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 206, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 48, 48, 71, 151, 196, 570, 571, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 325, + 209, 209, 572, 209, 209, 209, 573, 574, 575, 209, 576, 209, 209, 209, 577, 72, + 209, 209, 209, 209, 578, 72, 72, 72, 72, 72, 72, 72, 72, 72, 270, 579, + 209, 209, 209, 209, 209, 286, 270, 452, 72, 72, 72, 72, 72, 72, 72, 72, + 9, 580, 11, 581, 582, 583, 242, 9, 584, 585, 586, 587, 588, 9, 580, 11, + 589, 590, 11, 591, 592, 593, 594, 9, 595, 11, 9, 580, 11, 581, 582, 11, + 242, 9, 584, 594, 9, 595, 11, 9, 580, 11, 596, 9, 597, 598, 599, 600, + 11, 601, 9, 602, 603, 604, 605, 11, 606, 9, 607, 11, 608, 609, 609, 609, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 32, 32, 32, 610, 32, 32, 611, 612, 613, 614, 45, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 615, 616, 617, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 151, 618, 619, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 48, 48, 620, 621, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 622, 623, 72, 72, + 9, 9, 584, 11, 624, 370, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 488, 270, 270, 625, 626, 72, 72, 72, 72, + 488, 270, 627, 628, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 629, 48, 630, 631, 632, 633, 634, 635, 636, 206, 637, 206, 72, 72, 72, 638, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 209, 209, 326, 209, 209, 209, 209, 209, 209, 324, 335, 639, 639, 639, 209, 325, + 640, 209, 209, 209, 209, 209, 209, 209, 209, 209, 641, 72, 72, 72, 642, 209, + 643, 209, 209, 326, 577, 644, 325, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 645, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 646, 424, 424, + 209, 209, 209, 209, 209, 209, 209, 324, 209, 209, 209, 209, 209, 577, 326, 72, + 326, 209, 209, 209, 646, 176, 209, 209, 646, 209, 641, 644, 72, 72, 72, 72, + 209, 209, 209, 209, 209, 209, 209, 647, 209, 209, 209, 209, 648, 209, 209, 209, + 209, 209, 209, 209, 209, 324, 641, 649, 286, 209, 577, 286, 643, 286, 72, 72, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 650, 209, 209, 287, 72, 72, 192, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 204, 72, 72, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 205, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 469, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 101, 72, + 48, 204, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 651, 72, 652, 652, 652, 652, 652, 652, 72, 72, 72, 72, 72, 72, 72, 72, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 72, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 653, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 654, + 0, 0, 0, 0, 1, 2, 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 0, 0, 7, 0, + 8, 8, 8, 8, 8, 8, 8, 9, 10, 11, 12, 11, 11, 11, 13, 11, + 14, 14, 14, 14, 14, 14, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 16, 17, 18, 17, 17, 19, 20, 21, 21, 22, 21, 23, 24, + 25, 26, 27, 27, 28, 29, 27, 30, 27, 27, 27, 27, 27, 31, 27, 27, + 32, 33, 33, 33, 34, 27, 27, 27, 35, 35, 35, 36, 37, 37, 37, 38, + 39, 39, 40, 41, 42, 43, 44, 45, 45, 45, 27, 46, 47, 48, 49, 27, + 50, 50, 50, 50, 50, 51, 52, 50, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 111, 112, 113, 114, 111, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 124, 125, 124, 126, 45, 45, 127, 128, 129, 130, 131, 132, 45, 45, + 133, 133, 133, 133, 134, 133, 135, 136, 133, 134, 133, 137, 137, 138, 45, 45, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 140, 140, 141, 140, 140, 142, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 144, 144, 144, 144, 145, 146, 144, 144, 145, 144, 144, 147, 148, 149, 144, 144, + 144, 148, 144, 144, 144, 150, 144, 151, 144, 152, 153, 153, 153, 153, 153, 154, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 156, 157, 158, 158, 158, 158, 159, 160, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 169, 169, 169, 169, 170, 171, 171, + 172, 173, 174, 174, 174, 174, 174, 175, 174, 174, 176, 155, 155, 155, 155, 177, + 178, 179, 180, 180, 181, 182, 183, 184, 185, 185, 186, 185, 187, 188, 169, 169, + 189, 190, 191, 191, 191, 192, 191, 193, 194, 194, 195, 8, 196, 45, 45, 45, + 197, 197, 197, 197, 198, 197, 197, 199, 200, 200, 200, 200, 201, 201, 201, 202, + 203, 203, 203, 204, 205, 206, 206, 206, 207, 140, 140, 208, 209, 210, 211, 212, + 4, 4, 213, 4, 4, 214, 215, 216, 4, 4, 4, 217, 8, 8, 8, 218, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 11, 219, 11, 11, 219, 220, 11, 221, 11, 11, 11, 222, 222, 223, 11, 224, + 225, 0, 0, 0, 0, 0, 226, 227, 228, 229, 0, 0, 45, 8, 8, 196, + 0, 0, 230, 231, 232, 0, 4, 4, 233, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 234, 45, 235, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 237, 0, 238, 0, 0, 0, 0, 0, 0, + 239, 239, 240, 239, 239, 240, 4, 4, 241, 241, 241, 241, 241, 241, 241, 242, + 140, 140, 141, 243, 243, 243, 244, 245, 144, 246, 247, 247, 247, 247, 14, 14, + 0, 0, 0, 0, 0, 248, 45, 45, 249, 250, 249, 249, 249, 249, 249, 251, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 252, 45, 253, + 254, 0, 255, 256, 257, 258, 258, 258, 258, 259, 260, 261, 261, 261, 261, 262, + 263, 264, 264, 265, 143, 143, 143, 143, 266, 0, 264, 264, 0, 0, 267, 261, + 143, 266, 0, 0, 0, 0, 143, 268, 0, 0, 0, 0, 0, 261, 261, 269, + 261, 261, 261, 261, 261, 270, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 0, 0, 0, 0, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 271, + 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, + 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, + 272, 272, 272, 272, 272, 272, 272, 272, 273, 272, 272, 272, 274, 275, 275, 275, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, + 276, 276, 277, 45, 14, 14, 14, 14, 14, 14, 278, 278, 278, 278, 278, 279, + 0, 0, 280, 4, 4, 4, 4, 4, 281, 4, 4, 4, 282, 45, 45, 283, + 284, 284, 285, 286, 287, 287, 287, 288, 289, 289, 289, 289, 290, 291, 50, 50, + 292, 292, 293, 294, 294, 295, 143, 296, 297, 297, 297, 297, 298, 299, 139, 300, + 301, 301, 301, 302, 303, 304, 139, 139, 305, 305, 305, 305, 306, 307, 308, 309, + 310, 311, 247, 4, 4, 312, 313, 153, 153, 153, 153, 153, 308, 308, 314, 315, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 316, 143, 317, 143, 143, 318, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 319, 249, 249, 249, 249, 249, 249, 320, 45, 45, + 321, 322, 21, 323, 324, 27, 27, 27, 27, 27, 27, 27, 325, 48, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 326, 45, 27, 27, 27, 27, 327, 27, 27, 47, 45, 45, 328, + 8, 286, 329, 0, 0, 330, 331, 46, 27, 27, 27, 27, 27, 27, 27, 332, + 333, 0, 1, 2, 1, 2, 334, 260, 261, 335, 143, 266, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 344, 45, 45, 341, 341, 341, 341, 341, 341, 341, 345, + 346, 0, 0, 347, 11, 11, 11, 11, 348, 349, 350, 45, 45, 0, 0, 351, + 45, 45, 45, 45, 45, 45, 45, 45, 352, 353, 354, 354, 354, 355, 356, 253, + 357, 357, 358, 359, 360, 361, 361, 362, 363, 364, 365, 365, 366, 367, 45, 45, + 368, 368, 368, 368, 368, 369, 369, 369, 370, 371, 372, 373, 373, 374, 373, 375, + 376, 376, 377, 378, 378, 378, 379, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, + 380, 380, 380, 381, 380, 382, 383, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 384, 385, 385, 386, 387, 388, 389, 389, 390, 391, 392, 45, 45, 45, 393, 394, + 395, 396, 397, 398, 45, 45, 45, 45, 399, 399, 400, 401, 400, 402, 400, 400, + 403, 404, 405, 406, 407, 407, 408, 408, 409, 409, 45, 45, 410, 410, 411, 412, + 413, 413, 413, 414, 415, 416, 417, 418, 419, 420, 421, 45, 45, 45, 45, 45, + 422, 422, 422, 422, 423, 45, 45, 45, 424, 424, 424, 425, 424, 424, 424, 426, + 427, 427, 428, 429, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 27, 430, 431, 431, 432, 433, 45, 45, 45, 45, + 434, 434, 435, 436, 436, 437, 45, 45, 45, 45, 45, 438, 439, 45, 440, 441, + 442, 442, 442, 442, 443, 444, 442, 445, 446, 446, 446, 446, 447, 448, 449, 450, + 451, 451, 451, 452, 453, 454, 454, 455, 456, 456, 456, 456, 456, 456, 457, 458, + 459, 460, 459, 461, 45, 45, 45, 45, 462, 463, 464, 465, 465, 465, 466, 467, + 468, 469, 470, 471, 472, 473, 474, 475, 45, 45, 45, 45, 45, 45, 45, 45, + 476, 476, 476, 476, 476, 477, 478, 45, 479, 479, 479, 479, 480, 481, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 482, 482, 482, 483, 482, 484, 45, 45, + 485, 485, 485, 485, 486, 487, 488, 45, 489, 489, 489, 490, 491, 45, 45, 45, + 492, 493, 494, 492, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 495, 495, 495, 496, 45, 45, 45, 45, 45, 45, 497, 497, 497, 497, 497, 498, + 499, 500, 501, 502, 503, 504, 45, 45, 45, 45, 505, 506, 506, 505, 507, 45, + 508, 508, 508, 508, 509, 510, 510, 510, 510, 510, 511, 45, 512, 512, 512, 513, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 514, 515, 515, 516, 517, 515, 518, 519, 519, 520, 521, 522, 45, 45, 45, 45, + 523, 524, 524, 525, 526, 527, 528, 529, 530, 531, 532, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 533, 534, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 535, 536, 536, 536, 537, + 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, + 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, + 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, + 538, 538, 538, 538, 538, 538, 538, 538, 538, 539, 45, 45, 45, 45, 45, 45, + 538, 538, 538, 538, 538, 538, 540, 541, 538, 538, 538, 538, 538, 538, 538, 538, + 538, 538, 538, 538, 542, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 544, 545, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, + 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, + 546, 546, 546, 546, 547, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 278, 278, 278, 548, 549, 550, 551, 45, 45, 45, 45, 45, 45, 552, 553, 554, + 555, 555, 555, 555, 556, 557, 558, 559, 555, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 560, 560, 560, 560, 560, 561, 45, 45, 45, 45, 45, 45, + 562, 562, 562, 562, 563, 562, 562, 562, 564, 562, 45, 45, 45, 45, 565, 566, + 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, + 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, + 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, + 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 568, + 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, + 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, + 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 570, 45, 45, + 571, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 572, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, + 258, 573, 45, 45, 45, 574, 575, 576, 576, 576, 576, 576, 576, 576, 576, 576, + 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 577, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 578, 578, 578, 578, 578, 578, 579, 580, 581, 582, 267, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 583, + 0, 0, 584, 0, 0, 0, 585, 586, 587, 0, 588, 0, 0, 0, 589, 45, + 11, 11, 11, 11, 590, 45, 45, 45, 45, 45, 45, 45, 45, 45, 0, 267, + 0, 0, 0, 0, 0, 234, 0, 589, 45, 45, 45, 45, 45, 45, 45, 45, + 0, 0, 0, 0, 0, 226, 0, 0, 0, 591, 592, 593, 594, 0, 0, 0, + 595, 596, 0, 597, 598, 599, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 600, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 601, 0, 0, 0, + 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, + 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, + 602, 602, 602, 602, 602, 602, 602, 602, 603, 604, 605, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 606, 607, 608, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 609, 609, 610, 611, 612, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 613, 613, 613, 614, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 616, 617, 45, 45, + 618, 618, 618, 618, 619, 620, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 333, 0, 0, 0, 621, 45, 45, 45, 45, + 333, 0, 0, 622, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 623, 27, 624, 625, 626, 627, 628, 629, 630, 631, 632, 631, 45, 45, 45, 325, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 0, 0, 253, 0, 0, 0, 0, 0, 0, 267, 228, 333, 333, 333, 0, 583, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 622, 45, 45, 45, 633, 0, + 634, 0, 0, 253, 589, 635, 583, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 636, 349, 349, + 0, 0, 0, 0, 0, 0, 0, 267, 0, 0, 0, 0, 0, 589, 253, 45, + 253, 0, 0, 0, 636, 286, 0, 0, 636, 0, 622, 635, 45, 45, 45, 45, + 0, 0, 0, 0, 0, 0, 0, 637, 0, 0, 0, 0, 638, 0, 0, 0, + 0, 0, 0, 0, 0, 267, 622, 639, 234, 0, 589, 234, 248, 234, 45, 45, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 330, 0, 0, 235, 45, 45, 286, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 319, 45, 45, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 640, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 319, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 566, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 641, 45, + 249, 319, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 642, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 643, 45, 0, 0, 0, 0, 0, 0, 45, 45, 45, 45, 45, 45, 45, 45, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, + 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0, + 1124,1125,1126,1127,1131,1133, 0,1147,1154,1155,1156,1161,1187,1188,1189,1193, + 0,1219,1226,1227,1228,1229,1233, 0, 0,1267,1268,1269,1273,1298, 0,1303, + 943,1128, 944,1129, 954,1139, 958,1143, 959,1144, 960,1145, 961,1146, 964,1149, + 0, 0, 973,1158, 974,1159, 975,1160, 983,1168, 978,1163, 988,1173, 990,1175, + 991,1176, 993,1178, 994,1179, 0, 0,1004,1190,1005,1191,1006,1192,1014,1199, + 1007, 0, 0, 0,1016,1201,1020,1206, 0,1022,1208,1025,1211,1023,1209, 0, + 0, 0, 0,1032,1218,1037,1223,1035,1221, 0, 0, 0,1044,1230,1045,1231, + 1049,1235, 0, 0,1058,1244,1064,1250,1060,1246,1066,1252,1067,1253,1072,1258, + 1069,1255,1077,1264,1074,1261, 0, 0,1083,1270,1084,1271,1085,1272,1088,1275, + 1089,1276,1096,1283,1103,1290,1111,1299,1115,1118,1307,1120,1309,1121,1310, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1053,1239, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1093, + 1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 949,1134,1010, + 1195,1050,1236,1090,1277,1341,1368,1340,1367,1342,1369,1339,1366, 0,1320,1347, + 1418,1419,1323,1350, 0, 0, 992,1177,1018,1204,1055,1241,1416,1417,1415,1424, + 1202, 0, 0, 0, 987,1172, 0, 0,1031,1217,1321,1348,1322,1349,1338,1365, + 950,1135, 951,1136, 979,1164, 980,1165,1011,1196,1012,1197,1051,1237,1052,1238, + 1061,1247,1062,1248,1091,1278,1092,1279,1071,1257,1076,1263, 0, 0, 997,1182, + 0, 0, 0, 0, 0, 0, 945,1130, 982,1167,1337,1364,1335,1362,1046,1232, + 1422,1423,1113,1301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 0, 10,1425, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0,1314,1427, 5,1434,1438,1443, 0,1450, 0,1455,1461, + 1514, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1446,1458,1468,1476,1480,1486, + 1517, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1489,1503,1494,1500,1508, 0, + 0, 0, 0,1520,1521, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1526,1528, 0,1525, 0, 0, 0,1522, 0, 0, 0, 0,1536,1532,1539, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1534, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1556, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1548,1550, 0,1547, 0, 0, 0,1567, 0, 0, 0, 0,1558,1554,1561, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1568,1569, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1529,1551, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1523,1545,1524,1546, 0, 0,1527,1549, 0, 0,1570,1571,1530,1552,1531,1553, + 0, 0,1533,1555,1535,1557,1537,1559, 0, 0,1572,1573,1544,1566,1538,1560, + 1540,1562,1541,1563,1542,1564, 0, 0,1543,1565, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1606,1607,1609,1608,1610, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1613, 0,1611, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1620, 0, 0, 0, 0, 0, 0, + 0,1623, 0, 0,1624, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1614,1615,1616,1617,1618,1619,1621,1622, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1628,1629, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1625,1626, 0,1627, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1634, 0, 0,1635, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1630,1631,1632, 0, 0,1633, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1639, 0, 0,1638,1640, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1636,1637, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1641, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1642,1644,1643, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1645, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1646, 0, 0, 0, 0, 0, 0,1648,1649, 0,1647,1650, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1651,1653,1652, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1654, 0,1655,1657,1656, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1659, 0, 0, 0, 0, 0, 0, 0, 0, 0,1660, 0, 0, + 0, 0,1661, 0, 0, 0, 0,1662, 0, 0, 0, 0,1663, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1658, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1664, 0,1665,1673, 0,1674, 0, 0, 0, 0, 0, 0, 0, + 0,1666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1668, 0, 0, 0, 0, 0, 0, 0, 0, 0,1669, 0, 0, + 0, 0,1670, 0, 0, 0, 0,1671, 0, 0, 0, 0,1672, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1667, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1675, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1676, 0,1677, 0,1678, 0,1679, 0,1680, 0, + 0, 0,1681, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1682, 0,1683, 0, 0, + 1684,1685, 0,1686, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 953,1138, 955,1140, 956,1141, 957,1142,1324,1351, 963,1148, 965,1150, 968,1153, + 966,1151, 967,1152,1378,1380,1379,1381, 984,1169, 985,1170,1420,1421, 986,1171, + 989,1174, 995,1180, 998,1183, 996,1181, 999,1184,1000,1185,1015,1200,1329,1356, + 1017,1203,1019,1205,1021,1207,1024,1210,1687,1688,1027,1213,1026,1212,1028,1214, + 1029,1215,1030,1216,1034,1220,1036,1222,1039,1225,1038,1224,1334,1361,1336,1363, + 1382,1384,1383,1385,1056,1242,1057,1243,1059,1245,1063,1249,1689,1690,1065,1251, + 1068,1254,1070,1256,1386,1387,1388,1389,1691,1692,1073,1259,1075,1262,1079,1266, + 1078,1265,1095,1282,1098,1285,1097,1284,1390,1391,1392,1393,1099,1286,1100,1287, + 1101,1288,1102,1289,1105,1292,1104,1291,1106,1294,1107,1295,1108,1296,1114,1302, + 1119,1308,1122,1311,1123,1312,1186,1260,1293,1305, 0,1394, 0, 0, 0, 0, + 952,1137, 947,1132,1317,1344,1316,1343,1319,1346,1318,1345,1693,1695,1371,1375, + 1370,1374,1373,1377,1372,1376,1694,1696, 981,1166, 977,1162, 972,1157,1326,1353, + 1325,1352,1328,1355,1327,1354,1697,1698,1009,1194,1013,1198,1054,1240,1048,1234, + 1331,1358,1330,1357,1333,1360,1332,1359,1699,1700,1396,1401,1395,1400,1398,1403, + 1397,1402,1399,1404,1094,1281,1087,1274,1406,1411,1405,1410,1408,1413,1407,1412, + 1409,1414,1109,1297,1117,1306,1116,1304,1112,1300, 0, 0, 0, 0, 0, 0, + 1471,1472,1701,1705,1702,1706,1703,1707,1430,1431,1715,1719,1716,1720,1717,1721, + 1477,1478,1729,1731,1730,1732, 0, 0,1435,1436,1733,1735,1734,1736, 0, 0, + 1481,1482,1737,1741,1738,1742,1739,1743,1439,1440,1751,1755,1752,1756,1753,1757, + 1490,1491,1765,1768,1766,1769,1767,1770,1447,1448,1771,1774,1772,1775,1773,1776, + 1495,1496,1777,1779,1778,1780, 0, 0,1451,1452,1781,1783,1782,1784, 0, 0, + 1504,1505,1785,1788,1786,1789,1787,1790, 0,1459, 0,1791, 0,1792, 0,1793, + 1509,1510,1794,1798,1795,1799,1796,1800,1462,1463,1808,1812,1809,1813,1810,1814, + 1467, 21,1475, 22,1479, 23,1485, 24,1493, 27,1499, 28,1507, 29, 0, 0, + 1704,1708,1709,1710,1711,1712,1713,1714,1718,1722,1723,1724,1725,1726,1727,1728, + 1740,1744,1745,1746,1747,1748,1749,1750,1754,1758,1759,1760,1761,1762,1763,1764, + 1797,1801,1802,1803,1804,1805,1806,1807,1811,1815,1816,1817,1818,1819,1820,1821, + 1470,1469,1822,1474,1465, 0,1473,1825,1429,1428,1426, 12,1432, 0, 26, 0, + 0,1315,1823,1484,1466, 0,1483,1829,1433, 13,1437, 14,1441,1826,1827,1828, + 1488,1487,1513, 19, 0, 0,1492,1515,1445,1444,1442, 15, 0,1831,1832,1833, + 1502,1501,1516, 25,1497,1498,1506,1518,1457,1456,1454, 17,1453,1313, 11, 3, + 0, 0,1824,1512,1519, 0,1511,1830,1449, 16,1460, 18,1464, 4, 0, 0, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1834,1835, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1836, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1837,1839,1838, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1840, 0, 0, 0, 0,1841, 0, 0,1842, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1843, 0,1844, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1845, 0, 0,1846, 0, 0,1847, 0,1848, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 937, 0,1850, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1849, 936, 938, + 1851,1852, 0, 0,1853,1854, 0, 0,1855,1856, 0, 0, 0, 0, 0, 0, + 1857,1858, 0, 0,1861,1862, 0, 0,1863,1864, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1867,1868,1869,1870, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1859,1860,1865,1866, 0, 0, 0, 0, 0, 0,1871,1872,1873,1874, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1875, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1877, 0,1878, 0, + 1879, 0,1880, 0,1881, 0,1882, 0,1883, 0,1884, 0,1885, 0,1886, 0, + 1887, 0,1888, 0, 0,1889, 0,1890, 0,1891, 0, 0, 0, 0, 0, 0, + 1892,1893, 0,1894,1895, 0,1896,1897, 0,1898,1899, 0,1900,1901, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1876, 0, 0, 0, 0, 0, 0, 0, 0, 0,1902, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1904, 0,1905, 0, + 1906, 0,1907, 0,1908, 0,1909, 0,1910, 0,1911, 0,1912, 0,1913, 0, + 1914, 0,1915, 0, 0,1916, 0,1917, 0,1918, 0, 0, 0, 0, 0, 0, + 1919,1920, 0,1921,1922, 0,1923,1924, 0,1925,1926, 0,1927,1928, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1903, 0, 0,1929,1930,1931,1932, 0, 0, 0,1933, 0, + 710, 385, 724, 715, 455, 103, 186, 825, 825, 242, 751, 205, 241, 336, 524, 601, + 663, 676, 688, 738, 411, 434, 474, 500, 649, 746, 799, 108, 180, 416, 482, 662, + 810, 275, 462, 658, 692, 344, 618, 679, 293, 388, 440, 492, 740, 116, 146, 168, + 368, 414, 481, 527, 606, 660, 665, 722, 781, 803, 809, 538, 553, 588, 642, 758, + 811, 701, 233, 299, 573, 612, 487, 540, 714, 779, 232, 267, 412, 445, 457, 585, + 594, 766, 167, 613, 149, 148, 560, 589, 648, 768, 708, 345, 411, 704, 105, 259, + 313, 496, 518, 174, 542, 120, 307, 101, 430, 372, 584, 183, 228, 529, 650, 697, + 424, 732, 428, 349, 632, 355, 517, 110, 135, 147, 403, 580, 624, 700, 750, 170, + 193, 245, 297, 374, 463, 543, 763, 801, 812, 815, 162, 384, 420, 730, 287, 330, + 337, 366, 459, 476, 509, 558, 591, 610, 726, 652, 734, 759, 154, 163, 198, 473, + 683, 697, 292, 311, 353, 423, 572, 494, 113, 217, 259, 280, 314, 499, 506, 603, + 608, 752, 778, 782, 788, 117, 557, 748, 774, 320, 109, 126, 260, 265, 373, 411, + 479, 523, 655, 737, 823, 380, 765, 161, 395, 398, 438, 451, 502, 516, 537, 583, + 791, 136, 340, 769, 122, 273, 446, 727, 305, 322, 400, 496, 771, 155, 190, 269, + 377, 391, 406, 432, 501, 519, 599, 684, 687, 749, 776, 175, 452, 191, 480, 510, + 659, 772, 805, 813, 397, 444, 619, 566, 568, 575, 491, 471, 707, 111, 636, 156, + 153, 288, 346, 578, 256, 435, 383, 729, 680, 767, 694, 295, 128, 210, 0, 0, + 227, 0, 379, 0, 0, 150, 493, 525, 544, 551, 552, 556, 783, 576, 604, 0, + 661, 0, 703, 0, 0, 735, 743, 0, 0, 0, 793, 794, 795, 808, 741, 773, + 118, 127, 130, 166, 169, 177, 207, 213, 215, 226, 229, 268, 270, 317, 327, 329, + 335, 369, 375, 381, 404, 441, 448, 458, 477, 484, 503, 539, 545, 547, 546, 548, + 549, 550, 554, 555, 561, 564, 569, 591, 593, 595, 598, 607, 620, 625, 625, 651, + 690, 695, 705, 706, 716, 717, 733, 735, 777, 786, 790, 315, 869, 623, 0, 0, + 102, 145, 134, 115, 129, 138, 165, 171, 207, 202, 206, 212, 227, 231, 240, 243, + 250, 254, 294, 296, 303, 308, 319, 325, 321, 329, 326, 335, 341, 357, 360, 362, + 370, 379, 388, 389, 393, 421, 424, 438, 456, 454, 458, 465, 477, 535, 485, 490, + 493, 507, 512, 514, 521, 522, 525, 526, 528, 533, 532, 541, 565, 569, 574, 586, + 591, 597, 607, 637, 647, 674, 691, 693, 695, 698, 703, 699, 705, 704, 702, 706, + 709, 717, 728, 736, 747, 754, 770, 777, 783, 784, 786, 787, 790, 802, 825, 848, + 847, 857, 55, 65, 66, 883, 892, 916, 822, 824, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1586, 0,1605, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1602,1603,1934,1935,1574,1575, + 1576,1577,1579,1580,1581,1583,1584, 0,1585,1587,1588,1589,1591, 0,1592, 0, + 1593,1594, 0,1595,1596, 0,1598,1599,1600,1601,1604,1582,1578,1590,1597, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1936, 0,1937, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1938, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1939,1940, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1941,1942, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1944,1943, 0,1945, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1946,1947, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1948, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1949,1950, + 1951,1952,1953,1954,1955, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1956,1957,1958,1960,1959, + 1961, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 106, 104, 107, 826, 114, 118, 119, 121, 123, 124, 127, 125, 34, 830, 130, 131, + 132, 137, 827, 35, 133, 139, 829, 142, 143, 112, 144, 145, 924, 151, 152, 37, + 157, 158, 159, 160, 38, 165, 166, 169, 171, 172, 173, 174, 176, 177, 178, 179, + 181, 182, 182, 182, 833, 468, 184, 185, 834, 187, 188, 189, 196, 192, 194, 195, + 197, 199, 200, 201, 203, 204, 204, 206, 208, 209, 211, 218, 213, 219, 214, 216, + 153, 234, 221, 222, 223, 220, 225, 224, 230, 835, 235, 236, 237, 238, 239, 244, + 836, 837, 247, 248, 249, 246, 251, 39, 40, 253, 255, 255, 838, 257, 258, 259, + 261, 839, 262, 263, 301, 264, 41, 266, 270, 272, 271, 841, 274, 842, 277, 276, + 278, 281, 282, 42, 283, 284, 285, 286, 43, 843, 44, 289, 290, 291, 293, 934, + 298, 845, 845, 621, 300, 300, 45, 852, 894, 302, 304, 46, 306, 309, 310, 312, + 316, 48, 47, 317, 846, 318, 323, 324, 325, 324, 328, 329, 333, 331, 332, 334, + 335, 336, 338, 339, 342, 343, 347, 351, 849, 350, 348, 352, 354, 359, 850, 361, + 358, 356, 49, 363, 365, 367, 364, 50, 369, 371, 851, 376, 386, 378, 53, 381, + 52, 51, 140, 141, 387, 382, 614, 78, 388, 389, 390, 394, 392, 856, 54, 399, + 396, 402, 404, 858, 405, 401, 407, 55, 408, 409, 410, 413, 859, 415, 56, 417, + 860, 418, 57, 419, 422, 424, 425, 861, 840, 862, 426, 863, 429, 431, 427, 433, + 437, 441, 438, 439, 442, 443, 864, 436, 449, 450, 58, 454, 453, 865, 447, 460, + 866, 867, 461, 466, 465, 464, 59, 467, 470, 469, 472, 828, 475, 868, 478, 870, + 483, 485, 486, 871, 488, 489, 872, 873, 495, 497, 60, 498, 61, 61, 504, 505, + 507, 508, 511, 62, 513, 874, 515, 875, 518, 844, 520, 876, 877, 878, 63, 64, + 528, 880, 879, 881, 882, 530, 531, 531, 533, 66, 534, 67, 68, 884, 536, 538, + 541, 69, 885, 549, 886, 887, 556, 559, 70, 561, 562, 563, 888, 889, 889, 567, + 71, 890, 570, 571, 72, 891, 577, 73, 581, 579, 582, 893, 587, 74, 590, 592, + 596, 75, 895, 896, 76, 897, 600, 898, 602, 605, 607, 899, 900, 609, 901, 611, + 853, 77, 615, 616, 79, 617, 252, 902, 903, 854, 855, 621, 622, 731, 80, 627, + 626, 628, 164, 629, 630, 631, 633, 904, 632, 634, 639, 640, 635, 641, 646, 651, + 638, 643, 644, 645, 905, 907, 906, 81, 653, 654, 656, 911, 657, 908, 82, 83, + 909, 910, 84, 664, 665, 666, 667, 669, 668, 671, 670, 674, 672, 673, 675, 85, + 677, 678, 86, 681, 682, 912, 685, 686, 87, 689, 36, 913, 914, 88, 89, 696, + 702, 709, 711, 915, 712, 713, 718, 719, 917, 831, 721, 720, 723, 832, 725, 728, + 918, 919, 739, 742, 744, 920, 745, 753, 756, 757, 755, 760, 761, 921, 762, 90, + 764, 922, 91, 775, 279, 780, 923, 925, 92, 93, 785, 926, 94, 927, 787, 787, + 789, 928, 792, 95, 796, 797, 798, 800, 96, 929, 802, 804, 806, 97, 98, 807, + 930, 99, 931, 932, 933, 814, 100, 816, 817, 818, 819, 820, 821, 935, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; +static const int16_t +_hb_ucd_i16[196] = +{ + 0, 0, 0, 0, 1, -1, 0, 0, 2, 0, -2, 0, 0, 0, 0, 2, + 0, -2, 0, 0, 0, 0, 0, 16, 0, 0, 0, -16, 0, 0, 1, -1, + 0, 0, 0, 1, -1, 0, 0, 0, 0, 1, -1, 0, 3, 3, 3, -3, + -3, -3, 0, 0, 0, 2016, 0, 0, 0, 0, 0, 2527, 1923, 1914, 1918, 0, + 2250, 0, 0, 0, 0, 0, 0, 138, 0, 7, 0, 0, -7, 0, 0, 0, + 1, -1, 1, -1, -1, 1, -1, 0, 1824, 0, 0, 0, 0, 0, 2104, 0, + 2108, 2106, 0, 2106, 1316, 0, 0, 0, 0, 1, -1, 1, -1, -138, 0, 0, + 1, -1, 8, 8, 8, 0, 7, 7, 0, 0, -8, -8, -8, -7, -7, 0, + 1, -1, 0, 2,-1316, 1, -1, 0, -1, 1, -1, 1, -1, 3, 1, -1, + -3, 1, -1, 1, -1, 0, 0,-1914,-1918, 0, 0,-1923,-1824, 0, 0, 0, + 0,-2016, 0, 0, 1, -1, 0, 1, 0, 0,-2104, 0, 0, 0, 0,-2106, + -2108,-2106, 0, 0, 1, -1,-2250, 0, 0, 0,-2527, 0, 0, -2, 0, 1, + -1, 0, 1, -1, +}; + +static inline uint_fast8_t +_hb_ucd_gc (unsigned u) +{ + return u<1114110u?_hb_ucd_u8[2176+(((_hb_ucd_u16[((_hb_ucd_u8[u>>4>>5])<<5)+((u>>4)&31u)])<<4)+((u)&15u))]:2; +} +static inline uint_fast8_t +_hb_ucd_ccc (unsigned u) +{ + return u<125259u?_hb_ucd_u8[15060+(((_hb_ucd_u8[13636+(((_hb_ucd_u8[12656+(u>>3>>4)])<<4)+((u>>3)&15u))])<<3)+((u)&7u))]:0; +} +static inline unsigned +_hb_ucd_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline int_fast16_t +_hb_ucd_bmg (unsigned u) +{ + return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[16372+(((_hb_ucd_b4(16244+_hb_ucd_u8,u>>2>>6))<<6)+((u>>2)&63u))])<<2)+((u)&3u)]:0; +} +static inline uint_fast8_t +_hb_ucd_sc (unsigned u) +{ + return u<918000u?_hb_ucd_u8[19126+(((_hb_ucd_u16[3040+(((_hb_ucd_u8[17332+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:2; +} +static inline uint_fast16_t +_hb_ucd_dm (unsigned u) +{ + return u<195102u?_hb_ucd_u16[6144+(((_hb_ucd_u8[29430+(u>>6)])<<6)+((u)&63u))]:0; +} + + +#elif !defined(HB_NO_UCD_UNASSIGNED) + +static const uint8_t +_hb_ucd_u8[17508] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 9, 10, 11, 7, 7, 7, 7, 12, 13, 14, 14, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 21, 23, 21, 21, 21, 21, 24, 7, 7, + 25, 26, 21, 21, 21, 21, 27, 28, 21, 21, 29, 30, 31, 32, 33, 34, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 35, 7, 36, 37, 7, 38, 7, 7, 7, 39, 21, 40, + 7, 7, 41, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 42, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 43, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 44, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 34, 35, 36, 37, 38, 39, 34, 34, 34, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 64, 65, 66, 67, 68, 69, 70, 71, 69, 72, 73, + 69, 69, 64, 74, 64, 64, 75, 76, 77, 78, 79, 80, 81, 82, 69, 83, + 84, 85, 86, 87, 88, 89, 69, 69, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 90, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 91, + 92, 34, 34, 34, 34, 34, 34, 34, 34, 93, 34, 34, 94, 95, 96, 97, + 98, 99,100,101,102,103,104,105, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,106, + 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107, + 108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108, + 108,108, 34, 34,109,110,111,112, 34, 34,113,114,115,116,117,118, + 119,120,121,122,123,124,125,126,127,128,129,123, 34, 34,130,123, + 131,132,133,134,135,136,137,138,139,140,141,123,142,143,144,145, + 146,147,148,149,150,151,152,123,153,154,123,155,156,157,158,123, + 159,160,161,162,163,164,123,123,165,166,167,168,123,169,123,170, + 34, 34, 34, 34, 34, 34, 34,171,172, 34,173,123,123,123,123,123, + 123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, + 34, 34, 34, 34, 34, 34, 34, 34,174,123,123,123,123,123,123,123, + 123,123,123,123,123,123,123,123, 34, 34, 34, 34,175,123,123,123, + 34, 34, 34, 34,176,177,178,179,123,123,123,123,180,181,182,183, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,184, + 34, 34, 34, 34, 34, 34, 34, 34, 34,185,186,123,123,123,123,123, + 34, 34,187, 34, 34,188,123,123,123,123,123,123,123,123,123,123, + 123,123,123,123,123,123,123,123,189,190,123,123,123,123,123,123, + 69,191,192,193,194,195,196,123,197,198,199,200,201,202,203,204, + 69, 69, 69, 69,205,206,123,123,123,123,123,123,123,123,123,123, + 207,123,208,123,123,209,123,123,123,123,123,123,123,123,123,123, + 34,210,211,123,123,123,123,123,212,213,214,123,215,216,123,123, + 217,218,219,220,221,123, 69,222, 69, 69, 69, 69, 69,223,224,225, + 226,227,228,229,230,231, 69,232,123,123,123,123,123,123,123,123, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,233, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,234, 34, + 235, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,236, 34, 34, + 34, 34, 34, 34, 34, 34, 34,237,123,123,123,123,123,123,123,123, + 34, 34, 34, 34,238,123,123,123,123,123,123,123,123,123,123,123, + 34, 34, 34, 34, 34, 34,239,123,123,123,123,123,123,123,123,123, + 240,123,241,242,123,123,123,123,123,123,123,123,123,123,123,123, + 108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,243, + 108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,244, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, + 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, + 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, + 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, + 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, + 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, + 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, + 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, + 16, 16, 39, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 41, 41, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, + 40, 40, 42, 41, 41, 41, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, + 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 42, 32, 44, 45, 16, 10, + 44, 44, 41, 46, 11, 47, 47, 11, 34, 11, 11, 11, 11, 11, 11, 11, + 11, 48, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, + 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 49, 34, 32, 34, 11, + 32, 50, 43, 43, 51, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, + 48, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 47, 52, 2, 2, 2, + 16, 16, 16, 16, 53, 54, 55, 56, 57, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 58, 59, 60, 43, 59, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 62, + 36, 63, 64, 44, 44, 44, 44, 44, 65, 65, 65, 8, 9, 66, 2, 67, + 43, 43, 43, 43, 43, 60, 68, 2, 69, 36, 36, 36, 36, 70, 43, 43, + 7, 7, 7, 7, 7, 2, 2, 36, 71, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 72, 43, 43, 43, 73, 50, 43, 43, 74, 75, 76, 43, 43, 36, + 7, 7, 7, 7, 7, 36, 77, 78, 2, 2, 2, 2, 2, 2, 2, 79, + 70, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 80, 62, 36, + 36, 36, 36, 43, 43, 43, 43, 43, 71, 44, 44, 44, 44, 44, 44, 44, + 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 70, 43, 43, + 43, 43, 40, 21, 2, 81, 57, 20, 36, 36, 36, 43, 43, 75, 43, 43, + 43, 43, 75, 43, 75, 43, 43, 44, 2, 2, 2, 2, 2, 2, 2, 64, + 36, 36, 36, 36, 70, 43, 44, 64, 36, 36, 36, 36, 36, 61, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 36, 36, 61, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 44, 44, 44, 44, 44, 57, 43, 43, 43, 43, 43, 43, + 43, 82, 43, 43, 43, 43, 43, 43, 43, 83, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 83, 71, 84, 85, 43, 43, 43, 83, 84, 85, 84, + 70, 43, 43, 43, 36, 36, 36, 36, 36, 43, 2, 7, 7, 7, 7, 7, + 86, 36, 36, 36, 36, 36, 36, 36, 70, 84, 62, 36, 36, 36, 61, 62, + 61, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 36, 36, 36, + 61, 61, 44, 36, 36, 44, 71, 84, 85, 43, 80, 87, 88, 87, 85, 61, + 44, 44, 44, 87, 44, 44, 36, 62, 36, 43, 44, 7, 7, 7, 7, 7, + 36, 20, 27, 27, 27, 56, 63, 80, 57, 83, 62, 36, 36, 61, 44, 62, + 61, 36, 62, 61, 36, 44, 80, 84, 85, 80, 44, 57, 80, 57, 43, 44, + 57, 44, 44, 44, 62, 36, 61, 61, 44, 44, 44, 7, 7, 7, 7, 7, + 43, 36, 70, 64, 44, 44, 44, 44, 57, 83, 62, 36, 36, 36, 36, 62, + 36, 62, 36, 36, 36, 36, 36, 36, 61, 36, 62, 36, 36, 44, 71, 84, + 85, 43, 43, 57, 83, 87, 85, 44, 61, 44, 44, 44, 44, 44, 44, 44, + 66, 44, 44, 44, 62, 43, 43, 43, 57, 84, 62, 36, 36, 36, 61, 62, + 61, 36, 62, 36, 36, 44, 71, 85, 85, 43, 80, 87, 88, 87, 85, 44, + 44, 44, 57, 83, 44, 44, 36, 62, 78, 27, 27, 27, 44, 44, 44, 44, + 44, 71, 62, 36, 36, 61, 44, 36, 61, 36, 36, 44, 62, 61, 61, 36, + 44, 62, 61, 44, 36, 61, 44, 36, 36, 36, 36, 36, 36, 44, 44, 84, + 83, 88, 44, 84, 88, 84, 85, 44, 61, 44, 44, 87, 44, 44, 44, 44, + 27, 89, 67, 67, 56, 90, 44, 44, 83, 84, 71, 36, 36, 36, 61, 36, + 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 44, 62, 43, + 83, 84, 88, 43, 80, 43, 43, 44, 44, 44, 57, 80, 36, 61, 44, 44, + 44, 44, 44, 91, 27, 27, 27, 89, 70, 84, 72, 36, 36, 36, 61, 36, + 36, 36, 62, 36, 36, 44, 71, 85, 84, 84, 88, 83, 88, 84, 43, 44, + 44, 44, 87, 88, 44, 44, 44, 61, 62, 61, 44, 44, 44, 44, 44, 44, + 43, 84, 36, 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 70, 71, 84, + 85, 43, 80, 84, 88, 84, 85, 77, 44, 44, 36, 92, 27, 27, 27, 93, + 27, 27, 27, 27, 89, 36, 36, 36, 57, 84, 62, 36, 36, 36, 36, 36, + 36, 36, 36, 61, 44, 36, 36, 36, 36, 62, 36, 36, 36, 36, 62, 44, + 36, 36, 36, 61, 44, 80, 44, 87, 84, 43, 80, 80, 84, 84, 84, 84, + 44, 84, 64, 44, 44, 44, 44, 44, 62, 36, 36, 36, 36, 36, 36, 36, + 70, 36, 43, 43, 43, 80, 44, 94, 36, 36, 36, 75, 43, 43, 43, 60, + 7, 7, 7, 7, 7, 2, 44, 44, 62, 61, 61, 36, 36, 61, 36, 36, + 36, 36, 62, 62, 36, 36, 36, 36, 70, 36, 43, 43, 43, 43, 71, 44, + 36, 36, 61, 81, 43, 43, 43, 44, 7, 7, 7, 7, 7, 44, 36, 36, + 77, 67, 2, 2, 2, 2, 2, 2, 2, 95, 95, 67, 43, 67, 67, 67, + 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 50, 50, 50, 4, 4, 84, + 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, + 57, 43, 43, 43, 43, 43, 43, 83, 43, 43, 60, 43, 36, 36, 70, 43, + 43, 43, 43, 43, 57, 43, 43, 43, 43, 43, 43, 43, 43, 43, 80, 67, + 67, 67, 67, 76, 67, 67, 90, 67, 2, 2, 95, 67, 21, 64, 44, 44, + 36, 36, 36, 36, 36, 92, 85, 43, 83, 43, 43, 43, 85, 83, 85, 71, + 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 84, 43, 36, 36, 43, + 71, 84, 96, 92, 84, 84, 84, 36, 70, 43, 71, 36, 36, 36, 36, 36, + 36, 83, 85, 83, 84, 84, 85, 92, 7, 7, 7, 7, 7, 84, 85, 67, + 11, 11, 11, 48, 44, 44, 48, 44, 16, 16, 16, 16, 16, 53, 45, 16, + 36, 36, 36, 36, 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, + 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, 36, 36, 36, 36, + 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 57, 43, + 2, 2, 2, 2, 97, 27, 27, 27, 27, 27, 27, 27, 27, 27, 98, 44, + 67, 67, 67, 67, 67, 44, 44, 44, 11, 11, 11, 44, 16, 16, 16, 44, + 99, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 77, 72, + 100, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,101,102, 44, + 36, 36, 36, 36, 36, 63, 2,103,104, 36, 36, 36, 61, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 61, 36, 36, 43, 80, 44, 44, 44, 44, 44, + 36, 43, 60, 64, 44, 44, 44, 44, 36, 43, 44, 44, 44, 44, 44, 44, + 61, 43, 44, 44, 44, 44, 44, 44, 36, 36, 43, 85, 43, 43, 43, 84, + 84, 84, 84, 83, 85, 43, 43, 43, 43, 43, 2, 86, 2, 66, 70, 44, + 7, 7, 7, 7, 7, 44, 44, 44, 27, 27, 27, 27, 27, 44, 44, 44, + 2, 2, 2,105, 2, 59, 43, 68, 36,106, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 61, 44, 44, 44, 36, 36, 70, 71, 36, 36, 36, 36, + 36, 36, 36, 36, 70, 61, 44, 44, 36, 36, 36, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 61, 43, 83, 84, 85, 83, 84, 44, 44, + 84, 83, 84, 84, 85, 43, 44, 44, 90, 44, 2, 7, 7, 7, 7, 7, + 36, 36, 36, 36, 36, 36, 36, 44, 36, 36, 61, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 44, 44, 36, 36, 36, 36, 36, 44, 44, 44, + 7, 7, 7, 7, 7, 98, 44, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 36, 36, 36, 70, 83, 85, 44, 2, 36, 36, 92, 83, 43, 43, 43, 80, + 83, 83, 85, 43, 43, 43, 83, 84, 84, 85, 43, 43, 43, 43, 80, 57, + 2, 2, 2, 86, 2, 2, 2, 44, 43, 43, 43, 43, 43, 43, 43,107, + 80, 44, 44, 44, 44, 44, 44, 44, 43, 43, 96, 36, 36, 36, 36, 36, + 36, 36, 83, 43, 43, 83, 83, 84, 84, 83, 96, 36, 36, 36, 44, 44, + 95, 67, 67, 67, 67, 50, 43, 43, 43, 43, 67, 67, 67, 67, 90, 44, + 43, 96, 36, 36, 36, 36, 36, 36, 92, 43, 43, 84, 43, 85, 43, 36, + 36, 36, 36, 83, 43, 84, 85, 85, 43, 84, 44, 44, 44, 44, 2, 2, + 36, 36, 84, 84, 84, 84, 43, 43, 43, 43, 84, 43, 44, 91, 2, 2, + 7, 7, 7, 7, 7, 44, 62, 36, 36, 36, 36, 36, 40, 40, 40, 2, + 16, 16, 16, 16,108, 44, 44, 44, 11, 11, 11, 11, 11, 47, 48, 11, + 2, 2, 2, 2, 44, 44, 44, 44, 43, 60, 43, 43, 43, 43, 43, 43, + 83, 43, 43, 43, 71, 36, 70, 36, 36, 36, 71, 92, 43, 61, 44, 44, + 16, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 45, 16, 16, + 16, 16, 16, 16, 45, 16, 16, 16, 16, 16, 16, 16, 16,109, 40, 40, + 43, 43, 43, 43, 43, 57, 43, 43, 32, 32, 32, 16, 16, 16, 16, 32, + 16, 16, 16, 16, 11, 11, 11, 11, 16, 16, 16, 44, 11, 11, 11, 44, + 16, 16, 16, 16, 48, 48, 48, 48, 16, 16, 16, 16, 16, 16, 16, 44, + 16, 16, 16, 16,110,110,110,110, 16, 16,108, 16, 11, 11,111,112, + 41, 16,108, 16, 11, 11,111, 41, 16, 16, 44, 16, 11, 11,113, 41, + 16, 16, 16, 16, 11, 11,114, 41, 44, 16,108, 16, 11, 11,111,115, + 116,116,116,116,116,117, 65, 65,118,118,118, 2,119,120,119,120, + 2, 2, 2, 2,121, 65, 65,122, 2, 2, 2, 2,123,124, 2,125, + 126, 2,127,128, 2, 2, 2, 2, 2, 9,126, 2, 2, 2, 2,129, + 65, 65, 68, 65, 65, 65, 65, 65,130, 44, 27, 27, 27, 8,127,131, + 27, 27, 27, 27, 27, 8,127,102, 40, 40, 40, 40, 40, 40, 81, 44, + 20, 20, 20, 20, 20, 20, 20, 20, 43, 43, 43, 43, 43, 43,132, 51, + 107, 51,107, 43, 43, 43, 43, 43, 67,133, 67,134, 67, 34, 11, 16, + 11, 32,134, 67, 49, 11, 11, 67, 67, 67,133,133,133, 11, 11,135, + 11, 11, 35, 36, 39, 67, 16, 11, 8, 8, 49, 16, 16, 26, 67,136, + 27, 27, 27, 27, 27, 27, 27, 27,103,103,103,103,103,103,103,103, + 103,137,138,103,139, 67, 44, 44, 8, 8,140, 67, 67, 8, 67, 67, + 140, 26, 67,140, 67, 67, 67,140, 67, 67, 67, 67, 67, 67, 67, 8, + 67,140,140, 67, 67, 67, 67, 67, 67, 67, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 67, 67, 67, 67, 4, 4, 67, 67, + 8, 67, 67, 67,141,142, 67, 67, 67, 67, 67, 67, 67, 67,140, 67, + 67, 67, 67, 67, 67, 26, 8, 8, 8, 8, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 8, 8, 8, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 90, 44, 44, 44, 44, 67, 67, 67, 67, 67, 90, 44, 44, + 27, 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, + 67, 67, 67, 26, 67, 67, 67, 67, 26, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 8, 8, 8, 8, 67, 67, 67, 67, 67, 67, 67, 26, + 67, 67, 67, 67, 4, 4, 4, 4, 4, 4, 4, 27, 27, 27, 27, 27, + 27, 27, 67, 67, 67, 67, 67, 67, 8, 8,127,143, 8, 8, 8, 8, + 8, 8, 8, 4, 4, 4, 4, 4, 8,127,144,144,144,144,144,144, + 144,144,144,144,143, 8, 8, 8, 8, 8, 8, 8, 4, 4, 8, 8, + 8, 8, 8, 8, 8, 8, 4, 8, 8, 8,140, 26, 8, 8,140, 67, + 67, 67, 44, 67, 67, 67, 67, 67, 67, 67, 67, 55, 67, 67, 67, 67, + 11, 11, 11, 11, 11, 11, 11, 47, 16, 16, 16, 16, 16, 16, 16,108, + 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 40, 11, + 32, 32,136, 67, 67,134, 34,145, 43, 32, 44, 44, 91, 2, 97, 2, + 16, 16, 16,146, 44, 44,146, 44, 36, 36, 36, 36, 44, 44, 44, 52, + 64, 44, 44, 44, 44, 44, 44, 57, 36, 36, 36, 61, 44, 44, 44, 44, + 36, 36, 36, 61, 36, 36, 36, 61, 2,119,119, 2,123,124,119, 2, + 2, 2, 2, 6, 2,105,119, 2,119, 4, 4, 4, 4, 2, 2, 86, + 2, 2, 2, 2, 2,118, 2, 2,105,147, 2, 2, 2, 2, 2, 2, + 67, 64, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 67, 55, 67, 67, + 67, 67, 44, 44, 44, 44, 44, 44, 67, 67, 67, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 67, 44, 44, 1, 2,148,149, 4, 4, 4, 4, + 4, 67, 4, 4, 4, 4,150,151,152,103,103,103,103, 43, 43, 84, + 153, 40, 40, 67,103,154, 63, 67, 36, 36, 36, 61, 57,155,156, 69, + 36, 36, 36, 36, 36, 63, 40, 69, 44, 44, 62, 36, 36, 36, 36, 36, + 67, 27, 27, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 90, + 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, 27, + 157, 27, 27, 27, 27, 27, 27, 27, 36, 36,106, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36,158, 2, 7, 7, 7, 7, 7, 36, 44, 44, + 32, 32, 32, 32, 32, 32, 32, 70, 51,159, 43, 43, 43, 43, 43, 86, + 32, 32, 32, 32, 32, 32, 40, 43, 36, 36, 36,103,103,103,103,103, + 43, 2, 2, 2, 44, 44, 44, 44, 41, 41, 41,156, 40, 40, 40, 40, + 41, 32, 32, 32, 32, 32, 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, + 45, 16, 16, 16, 34, 34, 34, 32, 32, 32, 32, 32, 42,160, 34, 35, + 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, 32, + 11, 11, 32, 32, 32, 32, 32, 32, 44, 32, 11, 11, 34,108, 44, 44, + 44, 44, 48, 35, 40, 35, 36, 36, 36, 71, 36, 71, 36, 70, 36, 36, + 36, 92, 85, 83, 67, 67, 80, 44, 27, 27, 27, 67,161, 44, 44, 44, + 36, 36, 2, 2, 44, 44, 44, 44, 84, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 84, 84, 84, 84, 84, 84, 84, 84, 43, 44, 44, 44, 44, 2, + 43, 36, 36, 36, 2, 72, 72, 70, 36, 36, 36, 43, 43, 43, 43, 2, + 36, 36, 36, 70, 43, 43, 43, 43, 43, 84, 44, 44, 44, 44, 44, 91, + 36, 70, 84, 43, 43, 84, 43, 84,162, 2, 2, 2, 2, 2, 2, 52, + 7, 7, 7, 7, 7, 44, 44, 2, 36, 36, 70, 69, 36, 36, 36, 36, + 7, 7, 7, 7, 7, 36, 36, 61, 36, 36, 36, 36, 70, 43, 43, 83, + 85, 83, 85, 80, 44, 44, 44, 44, 36, 70, 36, 36, 36, 36, 83, 44, + 7, 7, 7, 7, 7, 44, 2, 2, 69, 36, 36, 77, 67, 92, 83, 36, + 71, 43, 71, 70, 71, 36, 36, 43, 70, 61, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 62,106, 2, 36, 36, 36, 36, 36, 92, 43, 84, + 2,106,163, 80, 44, 44, 44, 44, 62, 36, 36, 61, 62, 36, 36, 61, + 62, 36, 36, 61, 44, 44, 44, 44, 16, 16, 16, 16, 16,112, 40, 40, + 16, 16, 16, 16,109, 41, 44, 44, 36, 92, 85, 84, 83,162, 85, 44, + 36, 36, 44, 44, 44, 44, 44, 44, 36, 36, 36, 61, 44, 62, 36, 36, + 164,164,164,164,164,164,164,164,165,165,165,165,165,165,165,165, + 16, 16, 16,108, 44, 44, 44, 44, 44,146, 16, 16, 44, 44, 62, 71, + 36, 36, 36, 36,166, 36, 36, 36, 36, 36, 36, 61, 36, 36, 61, 61, + 36, 62, 61, 36, 36, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, + 41, 44, 44, 44, 44, 44, 44, 44, 44, 62, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36,144, 44, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36,161, 44, 2, 2, 2,167,128, 44, 44, 44, + 6,168,169,144,144,144,144,144,144,144,128,167,128, 2,125,170, + 2, 64, 2, 2,150,144,144,128, 2,171, 8,172, 66, 2, 44, 44, + 36, 36, 36, 36, 36, 36, 61, 79, 91, 2, 3, 2, 4, 5, 6, 2, + 16, 16, 16, 16, 16, 17, 18,127,128, 4, 2, 36, 36, 36, 36, 36, + 69, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, + 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 61, 44, + 20,173, 56,174, 26, 8,140, 90, 44, 44, 44, 44, 79, 65, 67, 44, + 36, 36, 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 61, 36, 62, + 2, 64, 44,175, 27, 27, 27, 27, 27, 27, 44, 55, 67, 67, 67, 67, + 103,103,139, 27, 89, 67, 67, 67, 67, 67, 67, 67, 67, 27, 67, 90, + 67, 67, 67, 67, 67, 67, 90, 44, 90, 44, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 67, 50, 44,176, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 44, 44, 27, 27, 44, 44, 44, 44, 62, 36, + 149, 36, 36, 36, 36,177, 44, 44, 36, 36, 36, 43, 43, 80, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 91, 36, 36, 44, 44, 36, 36, 36, 36, + 178,103,103, 44, 44, 44, 44, 44, 11, 11, 11, 11, 16, 16, 16, 16, + 11, 11, 44, 44, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 44, 44, + 36, 36, 44, 44, 44, 44, 44, 91, 36, 36, 36, 44, 61, 36, 36, 36, + 36, 36, 36, 62, 61, 44, 61, 62, 36, 36, 36, 91, 27, 27, 27, 27, + 36, 36, 36, 77,157, 27, 27, 27, 44, 44, 44,175, 27, 27, 27, 27, + 36, 61, 36, 44, 44,175, 27, 27, 36, 36, 36, 27, 27, 27, 44, 91, + 36, 36, 36, 36, 36, 44, 44, 91, 36, 36, 36, 36, 44, 44, 27, 36, + 44, 27, 27, 27, 27, 27, 27, 27, 70, 43, 57, 80, 44, 44, 43, 43, + 36, 36, 62, 36, 62, 36, 36, 36, 36, 36, 36, 44, 43, 80, 44, 57, + 27, 27, 27, 27, 98, 44, 44, 44, 2, 2, 2, 2, 64, 44, 44, 44, + 36, 36, 36, 36, 36, 36,179, 30, 36, 36, 36, 36, 36, 36,179, 27, + 36, 36, 36, 36, 78, 36, 36, 36, 36, 36, 70, 80, 44,175, 27, 27, + 2, 2, 2, 64, 44, 44, 44, 44, 36, 36, 36, 44, 91, 2, 2, 2, + 36, 36, 36, 44, 27, 27, 27, 27, 36, 61, 44, 44, 27, 27, 27, 27, + 36, 44, 44, 44, 91, 2, 64, 44, 44, 44, 44, 44,175, 27, 27, 27, + 11, 47, 44, 44, 44, 44, 44, 44, 16,108, 44, 44, 44, 27, 27, 27, + 36, 36, 43, 43, 44, 44, 44, 44, 27, 27, 27, 27, 27, 27, 27, 98, + 36, 36, 36, 36, 36, 57,180, 44, 36, 44, 44, 44, 44, 44, 44, 44, + 27, 27, 27, 93, 44, 44, 44, 44,176, 27, 30, 2, 2, 44, 44, 44, + 36, 36,179, 27, 27, 27, 44, 44, 85, 96, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 43, 60, 2, 2, 2, 44, + 27, 27, 27, 7, 7, 7, 7, 7, 44, 44, 44, 44, 44, 44, 44, 57, + 84, 85, 43, 83, 85, 60,181, 2, 2, 44, 44, 44, 44, 44, 79, 44, + 43, 71, 36, 36, 36, 36, 36, 36, 36, 36, 36, 70, 43, 43, 85, 43, + 43, 43, 80, 7, 7, 7, 7, 7, 2, 2, 92, 96, 44, 44, 44, 44, + 36, 70, 2, 61, 44, 44, 44, 44, 36, 92, 84, 43, 43, 43, 43, 83, + 96, 36, 63, 2, 59, 43, 60, 85, 7, 7, 7, 7, 7, 63, 63, 2, + 175, 27, 27, 27, 27, 27, 27, 27, 27, 27, 98, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 84, 85, 43, 84, 83, 43, 2, 2, 2, 80, + 36, 36, 36, 61, 61, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 62, + 36, 36, 36, 36, 63, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 70, + 84, 85, 43, 43, 43, 80, 44, 44, 43, 84, 62, 36, 36, 36, 61, 62, + 61, 36, 62, 36, 36, 57, 71, 84, 83, 84, 88, 87, 88, 87, 84, 44, + 61, 44, 44, 87, 44, 44, 62, 36, 36, 84, 44, 43, 43, 43, 80, 44, + 43, 43, 80, 44, 44, 44, 44, 44, 36, 36, 92, 84, 43, 43, 43, 43, + 84, 43, 83, 71, 36, 63, 2, 2, 7, 7, 7, 7, 7, 2, 91, 71, + 84, 85, 43, 43, 83, 83, 84, 85, 83, 43, 36, 72, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 92, 84, 43, 43, 44, 84, 84, 43, 85, + 60, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 43, 44, + 84, 85, 43, 43, 43, 83, 85, 85, 60, 2, 61, 44, 44, 44, 44, 44, + 2, 2, 2, 2, 2, 2, 64, 44, 36, 36, 36, 36, 36, 70, 85, 84, + 43, 43, 43, 85, 61, 44, 44, 44, 84, 43, 43, 85, 43, 43, 44, 44, + 7, 7, 7, 7, 7, 27, 2, 95, 43, 43, 43, 43, 85, 60, 44, 44, + 27, 98, 44, 44, 44, 44, 44, 62, 36, 36, 36, 61, 62, 44, 36, 36, + 36, 36, 62, 61, 36, 36, 36, 36, 84, 84, 84, 87, 88, 57, 83, 71, + 96, 85, 2, 64, 44, 44, 44, 44, 36, 36, 36, 36, 44, 36, 36, 36, + 92, 84, 43, 43, 44, 43, 84, 84, 71, 72, 88, 44, 44, 44, 44, 44, + 70, 43, 43, 43, 43, 71, 36, 36, 36, 70, 43, 43, 83, 70, 43, 60, + 2, 2, 2, 59, 44, 44, 44, 44, 70, 43, 43, 83, 85, 43, 36, 36, + 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 83, 43, 2, 72, 2, + 2, 64, 44, 44, 44, 44, 44, 44, 43, 43, 43, 80, 43, 43, 43, 85, + 63, 2, 2, 44, 44, 44, 44, 44, 2, 36, 36, 36, 36, 36, 36, 36, + 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 87, 43, 43, 43, + 83, 43, 85, 80, 44, 44, 44, 44, 36, 36, 36, 61, 36, 62, 36, 36, + 70, 43, 43, 80, 44, 80, 43, 57, 43, 43, 43, 70, 44, 44, 44, 44, + 36, 36, 36, 62, 61, 36, 36, 36, 36, 36, 36, 36, 36, 84, 84, 88, + 43, 87, 85, 85, 61, 44, 44, 44, 36, 70, 83,162, 64, 44, 44, 44, + 27, 27, 89, 67, 67, 67, 56, 20,161, 67, 67, 67, 67, 67, 67, 67, + 67, 44, 44, 44, 44, 44, 44, 91,103,103,103,103,103,103,103,177, + 2, 2, 64, 44, 44, 44, 44, 44, 65, 65, 65, 65, 68, 44, 44, 44, + 43, 43, 60, 44, 44, 44, 44, 44, 43, 43, 43, 60, 2, 2, 67, 67, + 40, 40, 95, 44, 44, 44, 44, 44, 7, 7, 7, 7, 7,175, 27, 27, + 27, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 44, 44, 62, 36, + 27, 27, 27, 30, 2, 64, 44, 44, 36, 36, 36, 36, 36, 61, 44, 57, + 92, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 44, 44, 44, 57, 43, 74, 40, 40, 40, 40, 40, 40, + 40, 86, 80, 44, 44, 44, 44, 44, 84, 44, 44, 44, 44, 44, 44, 44, + 36, 61, 44, 44, 44, 44, 44, 44, 44, 44, 36, 36, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 44, 50, 60, 65, 65, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 90, 55, 67, 67, 67, 67, 67,182, 85, 43, 67,182, 84, + 84,183, 65, 65, 65, 82, 43, 43, 43, 76, 50, 43, 43, 43, 67, 67, + 67, 67, 67, 67, 67, 43, 43, 67, 67, 67, 67, 67, 90, 44, 44, 44, + 67, 43, 76, 44, 44, 44, 44, 44, 27, 27, 44, 44, 44, 44, 44, 44, + 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 16, 16, 16,108, 16, 16, 16, 16, 16, + 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 47, 11, + 44, 47, 48, 47, 48, 11, 47, 11, 11, 11, 11, 16, 16,146,146, 16, + 16, 16,146, 16, 16, 16, 16, 16, 16, 16, 11, 48, 11, 47, 48, 11, + 11, 11, 47, 11, 11, 11, 47, 16, 16, 16, 16, 16, 11, 48, 11, 47, + 11, 11, 47, 47, 44, 11, 11, 11, 47, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, + 16, 16, 16, 44, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, + 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, + 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, + 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, + 16, 33, 16, 16, 16, 32, 44, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 43, 43, 43, 76, 67, 50, 43, 43, 43, 43, 43, 43, 43, 43, 76, 67, + 67, 67, 50, 67, 67, 67, 67, 67, 67, 67, 76, 21, 2, 2, 44, 44, + 44, 44, 44, 44, 44, 57, 43, 43, 43, 43, 43, 80, 43, 43, 43, 43, + 43, 43, 43, 43, 80, 57, 43, 43, 43, 57, 80, 43, 43, 80, 44, 44, + 43, 43, 43, 74, 40, 40, 40, 44, 7, 7, 7, 7, 7, 44, 44, 77, + 36, 36, 36, 36, 36, 36, 43, 43, 7, 7, 7, 7, 7, 44, 44, 94, + 36, 36, 61,175, 27, 27, 27, 27, 43, 43, 43, 80, 44, 44, 44, 44, + 16, 16, 43, 43, 43, 74, 44, 44, 27, 27, 27, 27, 27, 27,157, 27, + 184, 27, 98, 44, 44, 44, 44, 44, 27, 27, 27, 27, 27, 27, 27,157, + 27, 27, 27, 27, 27, 27, 27, 44, 36, 36, 62, 36, 36, 36, 36, 36, + 62, 61, 61, 62, 62, 36, 36, 36, 36, 61, 36, 36, 62, 62, 44, 44, + 44, 61, 44, 62, 62, 62, 62, 36, 62, 61, 61, 62, 62, 62, 62, 62, + 62, 61, 61, 62, 36, 61, 36, 36, 36, 61, 36, 36, 62, 36, 61, 61, + 36, 36, 36, 36, 36, 62, 36, 36, 62, 36, 62, 36, 36, 62, 36, 36, + 8, 44, 44, 44, 44, 44, 44, 44, 55, 67, 67, 67, 67, 67, 67, 67, + 27, 27, 27, 27, 27, 27, 89, 67, 67, 67, 67, 67, 67, 67, 67, 44, + 44, 44, 44, 67, 67, 67, 67, 67, 67, 90, 44, 44, 44, 44, 44, 44, + 67, 44, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 67, 25, 41, 41, + 67, 67, 67, 67, 44, 44, 44, 44, 67, 67, 67, 67, 90, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 55, 67, 67, 67, 90, 44, 67, 90, 44, 44, + 67, 90, 67, 67, 67, 67, 67, 67, 79, 44, 44, 44, 44, 44, 44, 44, + 65, 65, 65, 65, 65, 65, 65, 65,165,165,165,165,165,165,165, 44, + 165,165,165,165,165,165,165, 0, 0, 0, 29, 21, 21, 21, 23, 21, + 22, 18, 21, 25, 21, 17, 13, 13, 25, 25, 25, 21, 21, 9, 9, 9, + 9, 22, 21, 18, 24, 16, 24, 5, 5, 5, 5, 22, 25, 18, 25, 0, + 23, 23, 26, 21, 24, 26, 7, 20, 25, 1, 26, 24, 26, 25, 15, 15, + 24, 15, 7, 19, 15, 21, 9, 25, 9, 5, 5, 25, 5, 9, 5, 7, + 7, 7, 9, 8, 8, 5, 7, 5, 6, 6, 24, 24, 6, 24, 12, 12, + 2, 2, 6, 5, 9, 21, 9, 2, 2, 9, 25, 9, 26, 12, 11, 11, + 2, 6, 5, 21, 17, 2, 2, 26, 26, 23, 2, 12, 17, 12, 21, 12, + 12, 21, 7, 2, 2, 7, 7, 21, 21, 2, 1, 1, 21, 23, 26, 26, + 1, 2, 6, 7, 7, 12, 12, 7, 21, 7, 12, 1, 12, 6, 6, 12, + 12, 26, 7, 26, 26, 7, 2, 1, 12, 2, 6, 2, 1, 12, 12, 10, + 10, 10, 10, 12, 21, 6, 2, 10, 10, 2, 15, 26, 26, 2, 2, 21, + 7, 10, 15, 7, 2, 23, 21, 26, 10, 7, 21, 15, 15, 2, 17, 7, + 29, 7, 7, 22, 18, 2, 14, 14, 14, 7, 17, 21, 7, 6, 11, 12, + 5, 2, 5, 6, 8, 8, 8, 24, 5, 24, 2, 24, 9, 24, 24, 2, + 29, 29, 29, 1, 17, 17, 20, 19, 22, 20, 27, 28, 1, 29, 21, 20, + 19, 21, 21, 16, 16, 21, 25, 22, 18, 21, 21, 29, 15, 6, 18, 6, + 12, 11, 9, 26, 26, 9, 26, 5, 5, 26, 14, 9, 5, 14, 14, 15, + 25, 26, 26, 22, 18, 26, 18, 25, 18, 22, 5, 12, 2, 5, 22, 21, + 26, 6, 7, 14, 17, 22, 18, 18, 26, 14, 17, 6, 14, 6, 12, 24, + 24, 6, 26, 15, 6, 21, 11, 21, 24, 9, 23, 26, 10, 21, 6, 10, + 4, 4, 3, 3, 7, 25, 21, 22, 17, 16, 16, 22, 16, 16, 25, 17, + 25, 2, 25, 24, 23, 2, 2, 15, 12, 15, 14, 2, 21, 14, 7, 15, + 12, 17, 21, 1, 26, 10, 10, 1, 23, 15, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 0, 10, 11, 12, 13, 0, 14, 0, 0, 0, 0, 0, + 15, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 19, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, + 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 37, 38, 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, 41, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 0, + 9, 0, 10, 11, 0, 0, 12, 13, 14, 15, 16, 0, 0, 0, 0, 17, + 18, 19, 20, 0, 0, 0, 21, 22, 0, 23, 24, 0, 0, 23, 25, 26, + 0, 23, 25, 0, 0, 23, 25, 0, 0, 23, 25, 0, 0, 0, 25, 0, + 0, 0, 27, 0, 0, 23, 25, 0, 0, 28, 25, 0, 0, 0, 29, 0, + 0, 30, 31, 0, 0, 32, 33, 0, 34, 35, 0, 36, 37, 0, 38, 0, + 0, 39, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 42, 42, 0, 0, 0, 0, 43, 0, + 0, 0, 0, 0, 0, 44, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, + 46, 0, 0, 47, 0, 48, 49, 0, 0, 50, 51, 52, 0, 53, 0, 54, + 0, 55, 0, 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 0, 58, 59, + 0, 0, 0, 0, 0, 0, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, 0, 0, 0, 64, + 0, 65, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 67, 68, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, + 70, 71, 0, 0, 0, 0, 51, 72, 0, 73, 74, 0, 0, 75, 76, 0, + 0, 0, 0, 0, 0, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 25, + 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, + 0, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, + 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 83, 0, 0, 0, 0, + 84, 85, 0, 0, 0, 0, 0, 86, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 88, 0, 0, 0, 0, 89, 0, 0, 0, 0, 0, + 0, 0, 70, 63, 0, 90, 0, 0, 91, 92, 0, 75, 0, 0, 93, 0, + 0, 94, 0, 0, 0, 0, 0, 95, 0, 96, 25, 97, 0, 0, 0, 0, + 0, 0, 98, 0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 63,100, 0, + 0, 63, 0, 0, 0,101, 0, 0, 0,102, 0, 0, 0, 0, 0, 0, + 0, 90, 0, 0, 0, 0, 0, 0, 0,103,104, 0, 0, 0, 0, 76, + 0, 42,105, 0,106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 63, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,108, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,109, 0,110, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,111, + 0, 0, 0, 0,112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,113,114,115, 0, 0, + 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 117,118, 0, 0, 0, 0, 0, 0, 0,110, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,119, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,120, 0, 0, 0,121, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, + 5, 6, 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, + 18, 1, 1, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, 27, 28, + 29, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 31, 0, + 0, 0, 32, 33, 34, 35, 1, 36, 0, 0, 0, 0, 37, 0, 0, 0, + 0, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 43, 36, 44, 45, + 21, 45, 46, 0, 0, 0, 0, 0, 0, 0, 19, 1, 21, 0, 0, 47, + 0, 0, 0, 0, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, 52, 1, 1, 1, + 53, 21, 43, 54, 55, 21, 35, 1, 0, 0, 0, 0, 0, 0, 0, 56, + 0, 0, 0, 57, 58, 59, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 57, 0, 61, 0, 0, + 0, 0, 0, 0, 0, 0, 62, 63, 0, 0, 64, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 68, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 70, 71, 0, + 0, 0, 0, 0, 72, 73, 74, 75, 76, 77, 0, 0, 0, 0, 0, 0, + 0, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 80, 0, + 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, + 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 64, 0, 0, 81, + 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 0, + 0, 0, 0, 0, 0, 19, 84, 0, 63, 0, 0, 0, 0, 49, 1, 85, + 0, 0, 0, 0, 1, 54, 15, 86, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 56, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, + 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, 87, 0, 0, 0, 0, 0, + 0, 88, 0, 0, 87, 0, 0, 0, 0, 0, 0, 0, 0, 79, 0, 0, + 0, 0, 0, 0, 89, 9, 12, 4, 90, 8, 91, 47, 0, 59, 50, 0, + 21, 1, 21, 92, 93, 1, 1, 1, 1, 1, 1, 1, 1, 94, 95, 96, + 0, 0, 0, 0, 97, 1, 98, 59, 81, 99,100, 4, 59, 0, 0, 0, + 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,101,102, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,103, 0, 0, 0, 0, 19, 0, 1, 1, 50, + 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 50, 0, 0, 0, + 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, + 1, 1, 1, 1, 50, 0, 0, 0, 0, 0, 52, 69, 0, 0, 0, 0, + 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 0, 79, 0, 0, 0, + 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,104,105, 59, 38, + 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, + 0, 0, 0, 0, 0, 0, 0,106, 1, 14, 4, 12, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 0, 0, 38, 89, 0, + 0, 0, 0,107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,108, 62, + 0,109, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 19, 59, 0, 0, 0, 0, 0,110, 14, 54, 84, 0, 0, 0, + 0, 0, 0, 0, 0, 0,111, 0, 89, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 62, 63, 0, 0, 63, 0, 88, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,111, 0, 0, 0, 0,112, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 79, 56, 0, 38, 1, 59, 1, 59, 0, 0, + 64, 88, 0, 0, 0, 0, 0, 60,113, 0, 0, 0, 0, 0, 0, 0, + 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,113, 0, 0, + 0, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, + 79, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 57, 0, 88,114, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 8, 91, 0, 0, + 0, 0, 0, 0, 1, 89, 0, 0, 0, 0, 0, 0,115, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,116, 0,117,118,119,120, 0, 52, 4, + 121, 49, 23, 0, 0, 0, 0, 0, 0, 0, 38, 50, 0, 0, 0, 0, + 38, 59, 0, 0, 0, 0, 0, 0, 1, 89, 1, 1, 1, 1, 39, 1, + 48,104, 89, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 4,121, 0, 0, 0, 1,122, 0, 0, 0, 0, 0, + 0, 0, 0, 0,230,230,230,230,230,232,220,220,220,220,232,216, + 220,220,220,220,220,202,202,220,220,220,220,202,202,220,220,220, + 1, 1, 1, 1, 1,220,220,220,220,230,230,230,230,240,230,220, + 220,220,230,230,230,220,220, 0,230,230,230,220,220,220,220,230, + 232,220,220,230,233,234,234,233,234,234,233,230, 0, 0, 0,230, + 0,220,230,230,230,230,220,230,230,230,222,220,230,230,220,220, + 230,222,228,230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, + 21, 22, 0, 23, 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, + 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34,230,230,220,220,230, + 220,230,230,220, 35, 0, 0, 0, 0, 0,230,230,230, 0, 0,230, + 230, 0,220,230,230,220, 0, 0, 0, 36, 0, 0,230,220,230,230, + 220,220,230,220,220,230,220,230,220,230,230, 0, 0,220, 0, 0, + 230,230, 0,230, 0,230,230,230,230,230, 0, 0, 0,220,220,220, + 0, 0, 0,220,230,230, 0,220,230,220,220,220, 27, 28, 29,230, + 7, 0, 0, 0, 0, 9, 0, 0, 0,230,220,230,230, 0, 0, 0, + 0, 0,230, 0, 0, 84, 91, 0, 0, 0, 0, 9, 9, 0, 0, 0, + 0, 0, 9, 0,103,103, 9, 0,107,107,107,107,118,118, 9, 0, + 122,122,122,122,220,220, 0, 0, 0,220, 0,220, 0,216, 0, 0, + 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130,130,130, 0, 0, + 130, 0,230,230, 9, 0,230,230, 0, 0,220, 0, 0, 0, 0, 7, + 0, 9, 9, 0, 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220, + 220, 0, 0, 0,230, 0, 0,220,230,220, 0,220, 0, 0, 9, 9, + 0, 0, 7, 0,230,230,230, 0,230, 0, 1, 1, 1, 0, 0, 0, + 230,234,214,220,202,230,230,230,230,230,232,228,228,220, 0,230, + 233,220,230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230, + 220,230, 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, + 230, 0,230,230,220, 0, 0,230, 0, 0, 26, 0, 0,220, 0,230, + 230, 1,220, 0, 0,230,220, 0, 0, 0,220,220, 0, 9, 7, 0, + 0, 7, 9, 0, 0, 0, 9, 7, 9, 9, 0, 0, 6, 6, 0, 0, + 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, 0,226,216,216, + 216,216,216, 0,220,220,220, 0,230,230, 7, 0, 16, 17, 17, 17, + 17, 17, 17, 33, 17, 17, 17, 19, 17, 17, 17, 17, 20,101, 17,113, + 129,169, 17, 27, 28, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,237, 0, 1, 2, 2, + 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 6, 7, 8, + 9, 0, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 21, 22, 0, 0, 0, 0, + 23, 24, 25, 26, 0, 27, 0, 28, 29, 30, 31, 32, 0, 0, 0, 0, + 0, 0, 0, 33, 34, 35, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, 0, 1, 2, 39, 40, + 0, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 5, 0, + 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, + 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 10, + 0, 0, 0, 0, 0, 0, 11, 12, 0, 13, 0, 14, 15, 16, 0, 0, + 0, 0, 0, 1, 17, 18, 0, 19, 7, 1, 0, 0, 0, 20, 20, 7, + 20, 20, 20, 20, 20, 20, 20, 8, 21, 0, 22, 0, 7, 23, 24, 0, + 20, 20, 25, 0, 0, 0, 26, 27, 1, 7, 20, 20, 20, 20, 20, 1, + 28, 29, 30, 31, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 10, 0, + 0, 0, 0, 0, 0, 0, 20, 20, 20, 1, 0, 0, 8, 21, 32, 4, + 0, 10, 0, 33, 7, 20, 20, 20, 0, 0, 0, 0, 8, 34, 34, 35, + 36, 34, 37, 0, 38, 1, 20, 20, 0, 0, 39, 0, 1, 1, 0, 8, + 21, 1, 20, 0, 0, 0, 1, 0, 0, 40, 1, 1, 0, 0, 8, 21, + 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 26, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 21, 7, 20, 41, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 21, 0, 42, 43, 44, 0, 45, 0, 8, 21, 0, 0, 0, 0, 0, + 0, 0, 0, 46, 7, 1, 10, 1, 0, 0, 0, 1, 20, 20, 1, 0, + 0, 0, 0, 0, 0, 0, 20, 20, 1, 20, 20, 0, 0, 0, 0, 0, + 0, 0, 26, 21, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 47, 48, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, + 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 9, + 10, 11, 12, 12, 12, 12, 13, 14, 14, 14, 14, 15, 16, 17, 18, 19, + 20, 14, 21, 14, 22, 14, 14, 14, 14, 23, 24, 24, 25, 26, 14, 14, + 14, 14, 27, 28, 14, 14, 29, 30, 31, 32, 33, 34, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 35, 7, 36, 37, 7, 38, 7, 7, 7, 39, 14, 40, 7, 7, 41, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 42, 0, 0, 1, + 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 32, 33, 34, 35, 36, 37, 37, 37, 37, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 2, 2, 53, 54, 55, 56, + 57, 58, 59, 59, 59, 59, 60, 59, 59, 59, 59, 59, 59, 59, 61, 61, + 59, 59, 59, 59, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 59, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 79, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 80, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 82, 83, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 96, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 70, 70, 98, 99,100,101,102,102,103,104,105,106,107,108,109,110, + 111,112, 97,113,114,115,116,117,118, 97,119,119,120, 97,121,122, + 123,124,125,126,127,128,129,130,131, 97,132,133,134,135,136,137, + 138,139,140,141,142, 97,143,144, 97,145,146,147,148, 97,149,150, + 151,152,153,154, 97, 97,155,156,157,158, 97,159, 97,160,161,161, + 161,161,161,161,161,162,163,161,164, 97, 97, 97, 97, 97,165,165, + 165,165,165,165,165,165,166, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97,167,167,167,167,168, 97, 97, 97,169,169, + 169,169,170,171,172,173, 97, 97, 97, 97,174,175,176,177,178,178, + 178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178, + 178,178,178,178,178,178,178,178,178,178,178,178,178,179,178,178, + 178,178,178,178,180,180,180,181,182, 97, 97, 97, 97, 97,183,184, + 185,186,186,187, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97,188,189, 97, 97, 97, 97, 97, 97, 59,190, + 191,192,193,194,195, 97,196,197,198, 59, 59,199, 59,200,201,201, + 201,201,201,202, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,203, 97, + 204, 97, 97,205, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,206,207, + 208, 97, 97, 97, 97, 97,209,210,211, 97,212,213, 97, 97,214,215, + 59,216,217, 97, 59, 59, 59, 59, 59, 59, 59,218,219,220,221,222, + 223,224,225,226, 59,227, 97, 97, 97, 97, 97, 97, 97, 97, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,228, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,229, 70,230, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,231, 70, 70, 70, 70, + 70, 70, 70, 70, 70,232, 97, 97, 97, 97, 97, 97, 97, 97, 70, 70, + 70, 70,233, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 70, 70, + 70, 70, 70, 70,234, 97, 97, 97, 97, 97, 97, 97, 97, 97,235, 97, + 236,237, 0, 1, 2, 2, 0, 1, 2, 2, 2, 3, 4, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, 0, 0, + 19, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 0, 19, 0, + 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, + 26, 26, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, + 9, 9, 0, 9, 9, 9, 2, 2, 9, 9, 9, 9, 0, 9, 2, 2, + 2, 2, 9, 0, 9, 0, 9, 9, 9, 2, 9, 2, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 6, 2, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 2, 4, 4, 4, 2, 2, 4, 4, 4, 2, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 2, + 2, 2, 2, 2, 2, 2, 14, 14, 14, 2, 2, 2, 2, 14, 14, 14, + 14, 14, 14, 2, 2, 2, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, + 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 0, 3, 2, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 3, + 3, 3, 3, 3, 3, 3, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 2, 37, 37, 37, 37, 2, 2, 37, 37, 37, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 2, 2, 2, 2, 2, 2, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 2, 2, 64, 64, 64, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 2, 2, 90, 90, + 90, 90, 90, 90, 90, 2, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 2, 2, 95, 2, 37, 37, 37, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 2, 3, 3, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, + 0, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, + 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, 5, 5, + 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 2, + 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, + 5, 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 5, 5, 5, 5, + 2, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 2, 2, 2, + 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 5, 5, 2, 5, 5, 5, + 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 11, + 11, 11, 2, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, + 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, + 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, + 2, 2, 11, 2, 11, 11, 11, 2, 2, 11, 11, 11, 2, 2, 2, 11, + 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, 2, 11, 2, 2, 2, + 2, 2, 2, 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 2, 10, + 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, + 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, + 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, 10, + 2, 2, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 2, 2, 10, 2, + 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, + 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 2, 21, + 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, + 2, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, + 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 2, 21, 21, 21, 21, 21, + 2, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 2, 2, 2, 2, + 2, 2, 2, 21, 21, 21, 2, 2, 2, 2, 21, 21, 2, 21, 21, 21, + 21, 21, 2, 2, 21, 21, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, + 22, 2, 2, 2, 22, 22, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, + 22, 2, 22, 2, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 2, 2, 2, 2, 22, 22, 22, 2, + 2, 2, 2, 2, 2, 22, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, + 22, 2, 2, 2, 2, 2, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 2, 23, 23, 23, 2, 23, 23, 23, 23, 23, 23, 23, 23, + 2, 2, 2, 23, 23, 23, 23, 2, 23, 23, 23, 23, 2, 2, 2, 2, + 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 2, 2, 2, 2, 23, 23, + 23, 23, 2, 2, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 2, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 16, + 2, 2, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 2, 2, 2, 2, + 2, 2, 2, 16, 16, 2, 2, 2, 2, 2, 2, 2, 16, 2, 16, 16, + 16, 16, 2, 2, 16, 16, 2, 16, 16, 2, 2, 2, 2, 2, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 2, 20, 20, 20, 2, + 20, 20, 20, 20, 20, 20, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, + 20, 20, 2, 2, 20, 20, 2, 36, 36, 36, 2, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, + 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 2, 36, 2, 2, 2, 2, 36, 2, 2, 2, 2, 36, 36, 36, + 36, 36, 36, 2, 36, 2, 2, 2, 2, 2, 2, 2, 36, 36, 2, 2, + 36, 36, 36, 2, 2, 2, 2, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 2, 2, 2, 2, 0, 24, 24, + 24, 24, 2, 2, 2, 2, 2, 18, 18, 2, 18, 2, 18, 18, 18, 18, + 18, 2, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 2, 18, 2, 18, 18, 18, 18, 18, 18, 18, 2, 2, 18, 18, + 18, 18, 18, 2, 18, 2, 18, 18, 2, 2, 18, 18, 18, 18, 25, 25, + 25, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 2, 2, 2, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, + 25, 25, 25, 0, 0, 0, 0, 25, 25, 2, 2, 2, 2, 2, 33, 33, + 33, 33, 33, 33, 33, 33, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 2, 8, 2, 2, 2, 2, 2, 8, 2, 2, 8, 8, + 8, 0, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, + 30, 30, 30, 30, 30, 2, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, + 30, 30, 30, 2, 2, 2, 30, 30, 2, 2, 2, 2, 2, 2, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 2, 2, 28, 28, + 28, 28, 28, 28, 28, 28, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 2, 2, 2, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 0, 0, 0, 35, 35, 35, 2, 2, 2, 2, 2, 2, 2, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 2, 45, 45, 45, 45, + 45, 45, 45, 2, 2, 2, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 0, 0, 2, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 2, 2, 2, 2, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 2, 46, 46, 46, 2, 46, 46, 2, 2, 2, 2, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 2, 2, 31, 31, + 2, 2, 2, 2, 2, 2, 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 2, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 2, 2, 2, 2, 2, 2, 32, 2, 2, 2, 2, 2, 2, 2, 32, 32, + 32, 2, 2, 2, 2, 2, 28, 28, 28, 28, 28, 28, 2, 2, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 2, 48, 48, + 48, 48, 2, 2, 2, 2, 48, 2, 2, 2, 48, 48, 48, 48, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 2, 2, 52, 52, + 52, 52, 52, 2, 2, 2, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 2, 2, 2, 2, 58, 58, 2, 2, 2, 2, 2, 2, 58, 58, + 58, 2, 2, 2, 58, 58, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 2, 2, 54, 54, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 2, 91, 91, 91, 91, 91, 2, 2, 91, 91, 91, + 2, 2, 2, 2, 2, 2, 91, 91, 91, 91, 91, 91, 2, 2, 1, 2, + 2, 2, 2, 2, 2, 2, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 2, 2, 2, 2, 62, 62, 62, 62, 62, 2, 2, 2, 76, 76, + 76, 76, 76, 76, 76, 76, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 2, 2, 2, 2, 2, 2, 2, 2, 93, 93, 93, 93, 70, 70, + 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 70, 70, 70, 70, + 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 73, 73, 73, 73, 6, 2, + 2, 2, 2, 2, 2, 2, 8, 8, 8, 2, 2, 8, 8, 8, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, + 0, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, + 9, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, 9, + 19, 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 19, 6, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 1, 1, + 2, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 2, 2, 2, 9, + 2, 9, 2, 9, 2, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, + 9, 9, 2, 2, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 2, 2, + 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 2, 0, 0, + 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 19, + 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, + 0, 0, 0, 0, 0, 2, 19, 19, 19, 19, 19, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 9, 0, 0, 0, 19, 19, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 19, 0, 19, 0, 0, 0, 2, 2, 2, 2, 0, 0, + 0, 2, 2, 2, 2, 2, 27, 27, 27, 27, 27, 27, 27, 27, 0, 0, + 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 2, 55, 55, + 55, 55, 2, 2, 2, 2, 2, 55, 55, 55, 55, 55, 55, 55, 61, 61, + 61, 61, 61, 61, 61, 61, 2, 2, 2, 2, 2, 2, 2, 61, 61, 2, + 2, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 13, 13, + 13, 13, 13, 13, 2, 2, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 13, 0, 13, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 1, 1, 1, 1, 12, 12, 13, 13, 13, 13, 0, 0, 0, 0, 2, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 2, 2, 1, 1, 0, 0, 15, 15, 15, 0, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 0, 0, 17, 17, 17, 2, 2, 2, 2, 2, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 2, 12, 12, 12, 12, 12, 12, 12, 0, 17, 17, + 17, 17, 17, 17, 17, 0, 13, 13, 13, 13, 13, 2, 2, 2, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 2, 2, 2, 39, 39, + 39, 39, 39, 39, 39, 2, 86, 86, 86, 86, 86, 86, 86, 86, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 2, 2, 2, 2, 79, 79, + 79, 79, 79, 79, 79, 79, 0, 0, 19, 19, 19, 19, 19, 19, 0, 0, + 0, 19, 19, 19, 19, 19, 2, 2, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 19, 19, 19, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 2, 2, 2, 0, 0, + 2, 2, 2, 2, 2, 2, 65, 65, 65, 65, 65, 65, 65, 65, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 2, 2, 2, 2, + 2, 2, 2, 2, 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 0, 69, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 74, 12, 12, 12, 12, 12, 2, 2, 2, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 2, 0, 84, 84, + 2, 2, 2, 2, 84, 84, 33, 33, 33, 33, 33, 33, 33, 2, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 2, 68, 68, + 68, 68, 68, 68, 2, 2, 68, 68, 2, 2, 68, 68, 68, 68, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 2, 2, 2, 2, 2, 2, 2, + 2, 92, 92, 92, 92, 92, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 2, 2, 30, 30, 30, 30, 30, 30, 2, 19, 19, + 19, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 19, 19, 19, 19, + 0, 0, 2, 2, 2, 2, 87, 87, 87, 87, 87, 87, 2, 2, 87, 87, + 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, + 2, 12, 12, 12, 12, 12, 13, 13, 2, 2, 2, 2, 2, 2, 19, 19, + 19, 19, 19, 19, 19, 2, 2, 2, 2, 4, 4, 4, 4, 4, 2, 2, + 2, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 14, 14, + 14, 14, 14, 2, 14, 2, 14, 14, 2, 14, 14, 2, 14, 14, 3, 3, + 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 0, 0, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 1, 1, + 1, 1, 1, 1, 6, 6, 0, 0, 0, 2, 0, 0, 0, 0, 3, 3, + 3, 3, 3, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 2, 2, + 12, 12, 12, 12, 12, 12, 2, 2, 12, 12, 12, 2, 2, 2, 2, 0, + 0, 0, 0, 0, 2, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, + 49, 2, 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 2, 2, 49, 49, + 49, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, + 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 9, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 1, 2, 2, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 2, 2, 2, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 2, 2, 2, 2, 2, 2, 2, 1, 0, + 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 2, 2, 2, 2, 2, 2, 2, 2, 2, 42, 42, 42, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 2, 2, 2, 2, 2,118,118, + 118,118,118,118,118,118,118,118,118, 2, 2, 2, 2, 2, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 2, 53, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2, 2, 2, 2, 59, 59, + 59, 59, 59, 59, 2, 2, 40, 40, 40, 40, 40, 40, 40, 40, 51, 51, + 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 2, 2, 50, 50, 2, 2, 2, 2, 2, 2,135,135, + 135,135,135,135,135,135,135,135,135,135, 2, 2, 2, 2,106,106, + 106,106,106,106,106,106,104,104,104,104,104,104,104,104,104,104, + 104,104, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,104,110,110, + 110,110,110,110,110,110,110,110,110,110,110,110,110, 2,110,110, + 110,110,110,110, 2, 2, 47, 47, 47, 47, 47, 47, 2, 2, 47, 2, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 2, 47, 47, 2, 2, 2, 47, 2, 2, 47, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2, 81,120,120, + 120,120,120,120,120,120,116,116,116,116,116,116,116,116,116,116, + 116,116,116,116,116, 2, 2, 2, 2, 2, 2, 2, 2,116,128,128, + 128,128,128,128,128,128,128,128,128, 2,128,128, 2, 2, 2, 2, + 2,128,128,128,128,128, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 2, 2, 2, 66, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 2, 2, 2, 2, 2, 72, 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, + 97, 97, 97, 97, 97, 97, 2, 2, 2, 2, 97, 97, 97, 97, 2, 2, + 97, 97, 97, 97, 97, 97, 57, 57, 57, 57, 2, 57, 57, 2, 2, 2, + 2, 2, 57, 57, 57, 57, 57, 57, 57, 57, 2, 57, 57, 57, 2, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 2, 2, 57, 57, 57, 2, 2, 2, 2, 57, 57, 2, + 2, 2, 2, 2, 2, 2, 88, 88, 88, 88, 88, 88, 88, 88,117,117, + 117,117,117,117,117,117,112,112,112,112,112,112,112,112,112,112, + 112,112,112,112,112, 2, 2, 2, 2,112,112,112,112,112, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 2, 2, 2, 78, + 78, 78, 78, 78, 78, 78, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 2, 2, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 2, 2, 2, 2, 2,122,122,122,122,122,122,122,122,122,122, + 2, 2, 2, 2, 2, 2, 2,122,122,122,122, 2, 2, 2, 2,122, + 122,122,122,122,122,122, 89, 89, 89, 89, 89, 89, 89, 89, 89, 2, + 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,130,130,130,130, + 130, 2, 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,144,144, + 144,144,144,144,144,144,144,144, 2, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 2,156,156,156,156,156,156,156,156,156,156, + 2,156,156,156, 2, 2,156,156, 2, 2, 2, 2, 2, 2,147,147, + 147,147,147,147,147,147,148,148,148,148,148,148,148,148,148,148, + 2, 2, 2, 2, 2, 2,153,153,153,153,153,153,153,153,153,153, + 153,153, 2, 2, 2, 2,149,149,149,149,149,149,149,149,149,149, + 149,149,149,149,149, 2, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 94, 94, 94, 94, 2, 2, + 2, 2, 2, 2, 2, 94, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 85, 2, 2,101,101, + 101,101,101,101,101,101,101, 2, 2, 2, 2, 2, 2, 2,101,101, + 2, 2, 2, 2, 2, 2, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 2, 96, 96,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111, 2,100,100,100,100,100,100,100,100, 2, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2,108,108, + 108,108,108,108,108,108,108,108, 2,108,108,108,108,108,108,108, + 108,108,108,108,108, 2,129,129,129,129,129,129,129, 2,129, 2, + 129,129,129,129, 2,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129, 2,129,129,129, 2, 2, 2, 2, 2, 2,109,109, + 109,109,109,109,109,109,109,109,109, 2, 2, 2, 2, 2,109,109, + 2, 2, 2, 2, 2, 2,107,107,107,107, 2,107,107,107,107,107, + 107,107,107, 2, 2,107,107, 2, 2,107,107,107,107,107,107,107, + 107,107,107,107,107,107,107, 2,107,107,107,107,107,107,107, 2, + 107,107, 2,107,107,107,107,107, 2, 1,107,107,107,107,107, 2, + 2,107,107,107, 2, 2,107, 2, 2, 2, 2, 2, 2,107, 2, 2, + 2, 2, 2,107,107,107,107,107,107,107, 2, 2,107,107,107,107, + 107,107,107, 2, 2, 2,137,137,137,137,137,137,137,137,137,137, + 137,137, 2,137,137,137,137,137, 2, 2, 2, 2, 2, 2,124,124, + 124,124,124,124,124,124,124,124, 2, 2, 2, 2, 2, 2,123,123, + 123,123,123,123,123,123,123,123,123,123,123,123, 2, 2,114,114, + 114,114,114,114,114,114,114,114,114,114,114, 2, 2, 2,114,114, + 2, 2, 2, 2, 2, 2, 32, 32, 32, 32, 32, 2, 2, 2,102,102, + 102,102,102,102,102,102,102, 2, 2, 2, 2, 2, 2, 2,102,102, + 2, 2, 2, 2, 2, 2,126,126,126,126,126,126,126,126,126,126, + 126, 2, 2,126,126,126,126,126,126,126, 2, 2, 2, 2,142,142, + 142,142,142,142,142,142,142,142,142,142, 2, 2, 2, 2,125,125, + 125,125,125,125,125,125,125,125,125, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2,125,154,154,154,154,154,154,154, 2, 2,154, + 2, 2,154,154,154,154,154,154,154,154, 2,154,154, 2,154,154, + 154,154,154,154,154,154,154,154,154,154,154,154, 2,154,154, 2, + 2,154,154,154,154,154,154,154, 2, 2, 2, 2, 2, 2,150,150, + 150,150,150,150,150,150, 2, 2,150,150,150,150,150,150,150,150, + 150,150,150, 2, 2, 2,141,141,141,141,141,141,141,141,140,140, + 140,140,140,140,140,140,140,140,140, 2, 2, 2, 2, 2,121,121, + 121,121,121,121,121,121,121, 2, 2, 2, 2, 2, 2, 2,133,133, + 133,133,133,133,133,133,133, 2,133,133,133,133,133,133,133,133, + 133,133,133,133,133, 2,133,133,133,133,133,133, 2, 2,133,133, + 133,133,133, 2, 2, 2,134,134,134,134,134,134,134,134, 2, 2, + 134,134,134,134,134,134, 2,134,134,134,134,134,134,134,134,134, + 134,134,134,134,134, 2,138,138,138,138,138,138,138, 2,138,138, + 2,138,138,138,138,138,138,138,138,138,138,138,138,138, 2, 2, + 138, 2,138,138, 2,138,138,138, 2, 2, 2, 2, 2, 2,143,143, + 143,143,143,143, 2,143,143, 2,143,143,143,143,143,143,143,143, + 143,143,143,143,143,143,143,143,143,143,143,143,143, 2,143,143, + 2,143,143,143,143,143,143, 2, 2, 2, 2, 2, 2, 2,143,143, + 2, 2, 2, 2, 2, 2,145,145,145,145,145,145,145,145,145, 2, + 2, 2, 2, 2, 2, 2, 86, 2, 2, 2, 2, 2, 2, 2, 22, 22, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 22, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 2, 2, 2, 2, 2, 2, 63, 63, + 63, 63, 63, 63, 63, 2, 63, 63, 63, 63, 63, 2, 2, 2, 63, 63, + 63, 63, 2, 2, 2, 2, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 2, 80, 2, 2, 2, 2, 2, 2, 2,127,127, + 127,127,127,127,127,127,127,127,127,127,127,127,127, 2, 79, 2, + 2, 2, 2, 2, 2, 2,115,115,115,115,115,115,115,115,115,115, + 115,115,115,115,115, 2,115,115, 2, 2, 2, 2,115,115,103,103, + 103,103,103,103,103,103,103,103,103,103,103,103, 2, 2,119,119, + 119,119,119,119,119,119,119,119,119,119,119,119, 2, 2,119,119, + 2,119,119,119,119,119, 2, 2, 2, 2, 2,119,119,119,146,146, + 146,146,146,146,146,146,146,146,146, 2, 2, 2, 2, 2, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 2, 2, 2, 2, 99, 2, 2, + 2, 2, 2, 2, 2, 99,136,139, 0, 0,155, 2, 2, 2,136,136, + 136,136,136,136,136,136,155,155,155,155,155,155,155,155,155,155, + 155,155,155,155, 2, 2,136, 2, 2, 2, 2, 2, 2, 2, 17, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 17, 17, 17, 17,139,139,139,139,139,139,139,139,139,139, + 139,139, 2, 2, 2, 2,105,105,105,105,105,105,105,105,105,105, + 105, 2, 2, 2, 2, 2,105,105,105,105,105, 2, 2, 2,105, 2, + 2, 2, 2, 2, 2, 2,105,105, 2, 2,105,105,105,105, 0, 0, + 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, + 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0,131,131,131,131,131,131,131,131,131,131, + 131,131, 2, 2, 2, 2, 2, 2, 2,131,131,131,131,131, 2,131, + 131,131,131,131,131,131, 56, 2, 2, 56, 56, 56, 56, 56, 56, 56, + 2, 56, 56, 2, 56, 56, 56, 56, 56, 2, 2, 2, 2, 2,151,151, + 151,151,151,151,151,151,151,151,151,151,151, 2, 2, 2,151,151, + 151,151,151,151, 2, 2,151,151, 2, 2, 2, 2,151,151,152,152, + 152,152,152,152,152,152,152,152, 2, 2, 2, 2, 2,152,113,113, + 113,113,113,113,113,113,113,113,113,113,113, 2, 2,113,113,113, + 113,113,113,113,113, 2,132,132,132,132,132,132,132,132,132,132, + 132,132, 2, 2, 2, 2,132,132, 2, 2, 2, 2,132,132, 3, 3, + 3, 3, 2, 3, 3, 3, 2, 3, 3, 2, 3, 2, 2, 3, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 2, 3, + 2, 3, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 3, 2, 3, + 2, 3, 2, 3, 3, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, + 3, 3, 3, 2, 3, 2, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, + 2, 2, 2, 2, 0, 0, 15, 0, 0, 2, 2, 2, 2, 2, 13, 2, + 2, 2, 2, 2, 2, 2, 13, 13, 13, 2, 2, 2, 2, 2, 2, 0, + 2, 2, 2, 2, 2, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 9, 9, 9, 10, 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 16, 17, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 18, 19, + 20, 9, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 22, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 23, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, + 29, 30, 0, 0, 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, + 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 41, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, + 46, 47, 0, 0, 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, + 53, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, + 55, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, + 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, + 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, + 99,100,101,102,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0, + 107, 0, 0, 0,108, 0,109, 0,110, 0,111,112,113, 0,114, 0, + 0, 0,115, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,118,119,120,121, 0,122,123,124, + 125,126, 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,128,129,130,131,132,133,134,135,136,137,138,139, + 140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155, + 156,157, 0, 0, 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,162,163, 0, + 0, 0, 0, 0, 0, 0,164, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,165, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,166, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,167, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,168, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,169,170, 0, 0, 0, 0,171, + 172, 0, 0, 0,173,174,175,176,177,178,179,180,181,182,183,184, + 185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200, + 201,202,203,204,205,206, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, +}; +static const uint16_t +_hb_ucd_u16[9080] = +{ + 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, + 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, + 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, + 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, + 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, + 13, 13, 13, 42, 9, 43, 11, 11, 44, 45, 32, 46, 47, 48, 49, 50, + 51, 52, 48, 48, 53, 32, 54, 55, 48, 48, 48, 48, 48, 56, 57, 58, + 59, 60, 48, 32, 61, 48, 48, 48, 48, 48, 62, 63, 64, 48, 65, 66, + 48, 67, 68, 69, 48, 70, 71, 72, 72, 72, 48, 73, 74, 75, 76, 32, + 77, 48, 48, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 84, 85, 92, 93, 94, 95, 96, 97, 98, 85, 99, 100, 101, 89, 102, + 103, 84, 85, 104, 105, 106, 89, 107, 108, 109, 110, 111, 112, 113, 95, 114, + 115, 116, 85, 117, 118, 119, 89, 120, 121, 116, 85, 122, 123, 124, 89, 125, + 126, 116, 48, 127, 128, 129, 89, 130, 131, 132, 48, 133, 134, 135, 95, 136, + 137, 48, 48, 138, 139, 140, 72, 72, 141, 48, 142, 143, 144, 145, 72, 72, + 146, 147, 148, 149, 150, 48, 151, 152, 153, 154, 32, 155, 156, 157, 72, 72, + 48, 48, 158, 159, 160, 161, 162, 163, 164, 165, 9, 9, 166, 11, 11, 167, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 168, 169, 48, 48, + 168, 48, 48, 170, 171, 172, 48, 48, 48, 171, 48, 48, 48, 173, 174, 175, + 48, 176, 9, 9, 9, 9, 9, 177, 178, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 179, 48, 180, 181, 48, 48, 48, 48, 182, 183, + 184, 185, 48, 186, 48, 187, 184, 188, 48, 48, 48, 189, 190, 191, 192, 193, + 194, 192, 48, 48, 195, 48, 48, 196, 197, 48, 198, 48, 48, 48, 48, 199, + 48, 200, 201, 202, 203, 48, 204, 205, 48, 48, 206, 48, 207, 208, 209, 209, + 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 216, 72, 72, 72, + 217, 48, 48, 218, 219, 160, 220, 221, 222, 48, 223, 64, 48, 48, 224, 225, + 48, 48, 226, 227, 228, 64, 48, 229, 230, 9, 9, 231, 232, 233, 234, 235, + 11, 11, 236, 27, 27, 27, 237, 238, 11, 239, 27, 27, 32, 32, 32, 240, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 241, 13, 13, 13, 13, 13, 13, + 242, 243, 242, 242, 243, 244, 242, 245, 246, 246, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 262, 72, 263, 264, 216, + 265, 266, 267, 268, 269, 270, 271, 271, 272, 273, 274, 209, 275, 276, 209, 277, + 278, 278, 278, 278, 278, 278, 278, 278, 279, 209, 280, 209, 209, 209, 209, 281, + 209, 282, 278, 283, 209, 284, 285, 209, 209, 209, 286, 72, 287, 72, 270, 270, + 270, 288, 209, 209, 209, 209, 289, 270, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 290, 291, 209, 209, 292, 209, 209, 209, 209, 209, 209, 293, 209, + 209, 209, 209, 209, 209, 209, 294, 295, 270, 296, 209, 209, 297, 278, 298, 278, + 299, 300, 278, 278, 278, 301, 278, 302, 209, 209, 209, 278, 303, 209, 209, 304, + 209, 305, 209, 209, 209, 209, 209, 209, 9, 9, 306, 11, 11, 307, 308, 309, + 13, 13, 13, 13, 13, 13, 310, 311, 11, 11, 312, 48, 48, 48, 313, 314, + 48, 315, 316, 316, 316, 316, 32, 32, 317, 318, 319, 320, 321, 322, 72, 72, + 209, 323, 209, 209, 209, 209, 209, 324, 209, 209, 209, 209, 209, 325, 72, 326, + 327, 328, 329, 330, 137, 48, 48, 48, 48, 331, 178, 48, 48, 48, 48, 332, + 333, 48, 48, 137, 48, 48, 48, 48, 200, 334, 48, 48, 209, 209, 324, 48, + 209, 335, 336, 209, 337, 338, 209, 209, 336, 209, 209, 338, 209, 209, 209, 209, + 48, 48, 48, 48, 209, 209, 209, 209, 48, 48, 48, 48, 48, 48, 48, 151, + 48, 339, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 286, 48, 48, 229, + 340, 48, 341, 72, 13, 13, 342, 343, 13, 344, 48, 48, 48, 48, 345, 346, + 31, 347, 348, 349, 13, 13, 13, 350, 351, 352, 353, 354, 355, 72, 72, 356, + 357, 48, 358, 359, 48, 48, 48, 360, 361, 48, 48, 362, 363, 192, 32, 364, + 64, 48, 365, 48, 366, 367, 48, 151, 77, 48, 48, 368, 369, 370, 371, 372, + 48, 48, 373, 374, 375, 376, 48, 377, 48, 48, 48, 378, 379, 380, 381, 382, + 383, 384, 316, 11, 11, 385, 386, 11, 11, 11, 11, 11, 48, 48, 387, 192, + 48, 48, 388, 48, 389, 48, 48, 206, 390, 390, 390, 390, 390, 390, 390, 390, + 391, 391, 391, 391, 391, 391, 391, 391, 48, 48, 48, 48, 48, 48, 204, 48, + 48, 48, 48, 48, 48, 207, 72, 72, 392, 393, 394, 395, 396, 48, 48, 48, + 48, 48, 48, 397, 398, 399, 48, 48, 48, 48, 48, 400, 72, 48, 48, 48, + 48, 401, 48, 48, 74, 72, 72, 402, 32, 403, 32, 404, 405, 406, 407, 73, + 48, 48, 48, 48, 48, 48, 48, 408, 409, 2, 3, 4, 5, 410, 411, 412, + 48, 413, 48, 200, 414, 415, 416, 417, 418, 48, 172, 419, 204, 204, 72, 72, + 48, 48, 48, 48, 48, 48, 48, 71, 420, 270, 270, 421, 271, 271, 271, 422, + 423, 424, 425, 72, 72, 209, 209, 426, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 151, 48, 48, 48, 101, 427, 428, 48, 48, 429, 48, 430, 48, 48, 431, + 48, 432, 48, 48, 433, 434, 72, 72, 9, 9, 435, 11, 11, 48, 48, 48, + 48, 204, 192, 9, 9, 436, 11, 437, 48, 48, 74, 48, 48, 48, 438, 72, + 48, 48, 48, 315, 48, 199, 74, 72, 439, 48, 48, 440, 48, 441, 48, 442, + 48, 200, 443, 72, 72, 72, 48, 444, 48, 445, 48, 446, 72, 72, 72, 72, + 48, 48, 48, 447, 270, 448, 270, 270, 449, 450, 48, 451, 452, 453, 48, 454, + 48, 455, 72, 72, 456, 48, 457, 458, 48, 48, 48, 459, 48, 460, 48, 461, + 48, 462, 463, 72, 72, 72, 72, 72, 48, 48, 48, 48, 196, 72, 72, 72, + 9, 9, 9, 464, 11, 11, 11, 465, 48, 48, 466, 192, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 270, 467, 48, 48, 468, 469, 72, 72, 72, 72, + 48, 455, 470, 48, 62, 471, 72, 72, 72, 72, 72, 48, 472, 72, 48, 315, + 473, 48, 48, 474, 475, 448, 476, 477, 222, 48, 48, 478, 479, 48, 196, 192, + 480, 48, 481, 482, 483, 48, 48, 484, 222, 48, 48, 485, 486, 487, 488, 489, + 48, 98, 490, 491, 72, 72, 72, 72, 492, 493, 494, 48, 48, 495, 496, 192, + 497, 84, 85, 498, 499, 500, 501, 502, 48, 48, 48, 503, 504, 505, 469, 72, + 48, 48, 48, 506, 507, 192, 72, 72, 48, 48, 508, 509, 510, 511, 72, 72, + 48, 48, 48, 512, 513, 192, 514, 72, 48, 48, 515, 516, 192, 72, 72, 72, + 48, 173, 517, 518, 72, 72, 72, 72, 48, 48, 490, 519, 72, 72, 72, 72, + 72, 72, 9, 9, 11, 11, 148, 520, 521, 522, 48, 523, 524, 192, 72, 72, + 72, 72, 525, 48, 48, 526, 527, 72, 528, 48, 48, 529, 530, 531, 48, 48, + 532, 533, 534, 72, 48, 48, 48, 196, 85, 48, 508, 535, 536, 148, 175, 537, + 48, 538, 539, 540, 72, 72, 72, 72, 541, 48, 48, 542, 543, 192, 544, 48, + 545, 546, 192, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 48, 547, + 72, 72, 72, 101, 270, 548, 549, 550, 48, 207, 72, 72, 72, 72, 72, 72, + 271, 271, 271, 271, 271, 271, 551, 552, 48, 48, 48, 48, 388, 72, 72, 72, + 48, 48, 200, 553, 72, 72, 72, 72, 48, 48, 48, 48, 315, 72, 72, 72, + 48, 48, 48, 196, 48, 200, 370, 72, 72, 72, 72, 72, 72, 48, 204, 554, + 48, 48, 48, 555, 556, 557, 558, 559, 48, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 9, 9, 11, 11, 270, 560, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 561, 562, 563, 563, 564, 565, 72, 72, 72, 72, 566, 567, + 48, 48, 48, 48, 48, 48, 48, 74, 48, 48, 48, 48, 48, 199, 72, 72, + 196, 72, 72, 72, 72, 72, 72, 72, 48, 200, 72, 72, 72, 568, 569, 48, + 48, 48, 48, 48, 48, 48, 48, 206, 48, 48, 48, 48, 48, 48, 71, 151, + 196, 570, 571, 72, 72, 72, 72, 72, 209, 209, 209, 209, 209, 209, 209, 325, + 209, 209, 572, 209, 209, 209, 573, 574, 575, 209, 576, 209, 209, 209, 577, 72, + 209, 209, 209, 209, 578, 72, 72, 72, 72, 72, 72, 72, 72, 72, 270, 579, + 209, 209, 209, 209, 209, 286, 270, 452, 9, 580, 11, 581, 582, 583, 242, 9, + 584, 585, 586, 587, 588, 9, 580, 11, 589, 590, 11, 591, 592, 593, 594, 9, + 595, 11, 9, 580, 11, 581, 582, 11, 242, 9, 584, 594, 9, 595, 11, 9, + 580, 11, 596, 9, 597, 598, 599, 600, 11, 601, 9, 602, 603, 604, 605, 11, + 606, 9, 607, 11, 608, 609, 609, 609, 32, 32, 32, 610, 32, 32, 611, 612, + 613, 614, 45, 72, 72, 72, 72, 72, 615, 616, 617, 72, 72, 72, 72, 72, + 48, 48, 151, 618, 619, 72, 72, 72, 72, 72, 72, 72, 48, 48, 620, 621, + 48, 48, 48, 48, 622, 623, 72, 72, 9, 9, 584, 11, 624, 370, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 488, 270, 270, 625, 626, 72, 72, 72, 72, + 488, 270, 627, 628, 72, 72, 72, 72, 629, 48, 630, 631, 632, 633, 634, 635, + 636, 206, 637, 206, 72, 72, 72, 638, 209, 209, 326, 209, 209, 209, 209, 209, + 209, 324, 335, 639, 639, 639, 209, 325, 640, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 641, 72, 72, 72, 642, 209, 643, 209, 209, 326, 577, 644, 325, 72, + 209, 209, 209, 209, 209, 209, 209, 645, 209, 209, 209, 209, 209, 646, 424, 424, + 209, 209, 209, 209, 209, 209, 209, 324, 209, 209, 209, 209, 209, 577, 326, 72, + 326, 209, 209, 209, 646, 176, 209, 209, 646, 209, 641, 644, 72, 72, 72, 72, + 209, 209, 209, 209, 209, 209, 209, 647, 209, 209, 209, 209, 648, 209, 209, 209, + 209, 209, 209, 209, 209, 324, 641, 649, 286, 209, 577, 286, 643, 286, 72, 72, + 209, 650, 209, 209, 287, 72, 72, 192, 48, 48, 48, 48, 48, 204, 72, 72, + 48, 48, 48, 205, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, + 48, 48, 469, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 101, 72, + 48, 204, 72, 72, 72, 72, 72, 72, 48, 48, 48, 48, 71, 72, 72, 72, + 651, 72, 652, 652, 652, 652, 652, 652, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 72, 391, 391, 391, 391, 391, 391, 391, 653, + 391, 391, 391, 391, 391, 391, 391, 654, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 2, 3, 1, 2, 2, 3, 0, 0, 0, 0, 0, 4, 0, 4, + 2, 2, 5, 2, 2, 2, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, + 0, 0, 0, 0, 7, 8, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 10, 11, 12, 13, 14, 14, 15, 14, 14, 14, + 14, 14, 14, 14, 16, 17, 14, 14, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 19, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 20, 21, + 21, 21, 22, 20, 21, 21, 21, 21, 21, 23, 24, 25, 25, 25, 25, 25, + 25, 26, 25, 25, 25, 27, 28, 26, 29, 30, 31, 32, 31, 31, 31, 31, + 33, 34, 35, 31, 31, 31, 36, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 29, 31, 31, 31, 31, 37, 38, 37, 37, 37, 37, 37, 37, + 37, 39, 31, 31, 31, 31, 31, 31, 40, 40, 40, 40, 40, 40, 41, 26, + 42, 42, 42, 42, 42, 42, 42, 43, 44, 44, 44, 44, 44, 45, 44, 46, + 47, 47, 47, 48, 37, 49, 26, 26, 26, 26, 26, 26, 31, 31, 50, 31, + 31, 26, 51, 31, 52, 31, 31, 31, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 54, 53, 55, 53, 53, 53, 56, 57, 58, 59, 59, 60, 61, 62, + 57, 63, 64, 65, 66, 59, 59, 67, 68, 69, 70, 71, 71, 72, 73, 74, + 69, 75, 76, 77, 78, 71, 79, 26, 80, 81, 82, 83, 83, 84, 85, 86, + 81, 87, 88, 26, 89, 83, 90, 91, 92, 93, 94, 95, 95, 96, 97, 98, + 93, 99, 100, 101, 102, 95, 95, 26, 103, 104, 105, 106, 107, 104, 108, 109, + 104, 105, 110, 26, 111, 108, 108, 112, 113, 114, 115, 113, 113, 115, 113, 116, + 114, 117, 118, 119, 120, 113, 121, 113, 122, 123, 124, 122, 122, 124, 125, 126, + 123, 127, 128, 129, 130, 122, 131, 26, 132, 133, 134, 132, 132, 132, 132, 132, + 133, 134, 135, 132, 136, 132, 132, 132, 137, 138, 139, 140, 138, 138, 141, 142, + 139, 143, 144, 138, 145, 138, 146, 26, 147, 148, 148, 148, 148, 148, 148, 149, + 148, 148, 148, 150, 26, 26, 26, 26, 151, 152, 153, 153, 154, 153, 153, 155, + 156, 155, 153, 157, 26, 26, 26, 26, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 159, 158, 158, 158, 160, 159, 158, 158, 158, 158, 159, 158, 158, 158, 161, + 158, 161, 162, 163, 26, 26, 26, 26, 164, 164, 164, 164, 164, 164, 164, 164, + 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, + 166, 167, 165, 165, 165, 165, 165, 168, 169, 169, 169, 169, 169, 169, 169, 169, + 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, 170, 170, + 170, 171, 172, 171, 170, 170, 170, 170, 170, 171, 170, 170, 170, 170, 171, 172, + 171, 170, 172, 170, 170, 170, 170, 170, 170, 170, 171, 170, 170, 170, 170, 170, + 170, 170, 170, 173, 170, 170, 170, 174, 170, 170, 170, 175, 176, 176, 176, 176, + 176, 176, 176, 176, 176, 176, 177, 177, 178, 178, 178, 178, 178, 178, 178, 178, + 178, 178, 178, 178, 178, 178, 178, 178, 179, 179, 179, 180, 181, 181, 181, 181, + 181, 181, 181, 181, 181, 182, 181, 183, 184, 185, 186, 26, 187, 187, 188, 26, + 189, 189, 190, 26, 191, 192, 193, 26, 194, 194, 194, 194, 194, 194, 194, 194, + 194, 194, 194, 195, 194, 196, 194, 196, 197, 198, 199, 200, 199, 199, 199, 199, + 199, 199, 199, 199, 199, 199, 199, 201, 199, 199, 199, 199, 199, 202, 178, 178, + 178, 178, 178, 178, 178, 178, 203, 26, 204, 204, 204, 205, 204, 206, 204, 206, + 207, 204, 208, 208, 208, 209, 210, 26, 211, 211, 211, 211, 211, 212, 211, 211, + 211, 213, 211, 214, 194, 194, 194, 194, 215, 215, 215, 216, 217, 217, 217, 217, + 217, 217, 217, 218, 217, 217, 217, 219, 217, 220, 217, 220, 217, 221, 9, 9, + 222, 26, 26, 26, 26, 26, 26, 26, 223, 223, 223, 223, 223, 223, 223, 223, + 223, 224, 223, 223, 223, 223, 223, 225, 226, 226, 226, 226, 226, 226, 226, 226, + 227, 227, 227, 227, 227, 227, 228, 229, 230, 230, 230, 230, 230, 230, 230, 231, + 230, 232, 233, 233, 233, 233, 233, 233, 18, 234, 165, 165, 165, 165, 165, 235, + 226, 26, 236, 9, 237, 238, 239, 240, 2, 2, 2, 2, 241, 242, 2, 2, + 2, 2, 2, 243, 244, 245, 2, 246, 2, 2, 2, 2, 2, 2, 2, 247, + 9, 9, 9, 9, 9, 9, 9, 248, 14, 14, 249, 249, 14, 14, 14, 14, + 249, 249, 14, 250, 14, 14, 14, 249, 14, 14, 14, 14, 14, 14, 251, 14, + 251, 14, 252, 253, 14, 14, 254, 255, 0, 256, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 257, 0, 258, 259, 0, 260, 2, 261, 0, 0, 0, 0, + 26, 26, 9, 9, 9, 9, 222, 26, 0, 0, 0, 0, 262, 263, 4, 0, + 0, 264, 0, 0, 2, 2, 2, 2, 2, 265, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 260, 26, 26, 26, + 0, 266, 26, 26, 0, 0, 0, 0, 267, 267, 267, 267, 267, 267, 267, 267, + 267, 267, 267, 267, 267, 267, 267, 267, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, 269, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 270, 270, 270, 270, 270, 271, 270, 270, + 270, 270, 270, 271, 2, 2, 2, 2, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 272, 273, 165, 165, 165, 165, 166, 167, 274, 274, + 274, 274, 274, 274, 274, 275, 276, 275, 170, 170, 172, 26, 172, 172, 172, 172, + 172, 172, 172, 172, 18, 18, 18, 18, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 266, 26, 26, 26, 26, 26, 277, 277, 277, 278, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 279, 26, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 280, 26, 26, 26, 0, 281, 282, 0, 0, 0, 283, 284, 0, 285, + 286, 287, 287, 287, 287, 287, 287, 287, 287, 287, 288, 289, 290, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 292, 293, 294, 294, 294, 294, 294, 295, 169, + 169, 169, 169, 169, 169, 169, 169, 169, 169, 296, 0, 0, 294, 294, 294, 294, + 0, 0, 0, 0, 281, 26, 291, 291, 169, 169, 169, 296, 0, 0, 0, 0, + 0, 0, 0, 0, 169, 169, 169, 297, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 291, 291, 291, 291, 291, 298, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 0, 0, 0, 0, 0, 277, 277, 277, 277, 277, 277, 277, 277, + 0, 0, 0, 0, 0, 0, 0, 0, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 299, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300, 300, 301, 300, 300, 300, 300, 300, 300, + 302, 26, 303, 303, 303, 303, 303, 303, 304, 304, 304, 304, 304, 304, 304, 304, + 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 305, 26, 26, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 306, 306, 306, 306, + 306, 306, 306, 306, 306, 306, 306, 26, 0, 0, 0, 0, 307, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 308, 2, 2, 2, 2, 2, 2, + 309, 310, 26, 26, 26, 26, 311, 2, 312, 312, 312, 312, 312, 313, 0, 314, + 315, 315, 315, 315, 315, 315, 315, 26, 316, 316, 316, 316, 316, 316, 316, 316, + 317, 318, 316, 319, 53, 53, 53, 53, 320, 320, 320, 320, 320, 321, 322, 322, + 322, 322, 323, 324, 169, 169, 169, 325, 326, 326, 326, 326, 326, 326, 326, 326, + 326, 327, 326, 328, 164, 164, 164, 329, 330, 330, 330, 330, 330, 330, 331, 26, + 330, 332, 330, 333, 164, 164, 164, 164, 334, 334, 334, 334, 334, 334, 334, 334, + 335, 26, 26, 336, 337, 337, 338, 26, 339, 339, 339, 26, 172, 172, 2, 2, + 2, 2, 2, 340, 341, 342, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, + 337, 337, 337, 337, 337, 343, 337, 344, 169, 169, 169, 169, 345, 26, 169, 169, + 296, 346, 169, 169, 169, 169, 169, 345, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 280, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 347, 26, 26, 26, 26, 348, 26, 349, 350, 25, 25, 351, 352, + 353, 25, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 354, 26, 51, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 355, + 26, 26, 31, 31, 31, 31, 31, 31, 31, 31, 356, 31, 31, 31, 31, 31, + 31, 26, 26, 26, 26, 26, 31, 357, 9, 9, 0, 314, 9, 358, 0, 0, + 0, 0, 359, 0, 260, 281, 50, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 360, 361, 0, 0, 0, 1, 2, 2, 3, + 1, 2, 2, 3, 362, 291, 290, 291, 291, 291, 291, 363, 169, 169, 169, 296, + 364, 364, 364, 365, 260, 260, 26, 366, 367, 368, 367, 367, 369, 367, 367, 370, + 367, 371, 367, 371, 26, 26, 26, 26, 367, 367, 367, 367, 367, 367, 367, 367, + 367, 367, 367, 367, 367, 367, 367, 372, 373, 0, 0, 0, 0, 0, 374, 0, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 255, 0, 375, 376, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 0, 377, 378, 378, 378, 379, 380, 380, 380, 380, + 380, 380, 381, 26, 382, 0, 0, 281, 383, 383, 383, 383, 384, 385, 386, 386, + 386, 387, 388, 388, 388, 388, 388, 389, 390, 390, 390, 391, 392, 392, 392, 392, + 393, 392, 394, 26, 26, 26, 26, 26, 395, 395, 395, 395, 395, 395, 395, 395, + 395, 395, 396, 396, 396, 396, 396, 396, 397, 397, 397, 398, 397, 399, 400, 400, + 400, 400, 401, 400, 400, 400, 400, 401, 402, 402, 402, 402, 402, 26, 403, 403, + 403, 403, 403, 403, 404, 405, 26, 26, 406, 406, 406, 406, 406, 406, 406, 406, + 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 407, 26, + 406, 406, 408, 26, 406, 26, 26, 26, 409, 410, 411, 411, 411, 411, 412, 413, + 414, 414, 415, 414, 416, 416, 416, 416, 417, 417, 417, 418, 419, 417, 26, 26, + 26, 26, 26, 26, 420, 420, 421, 422, 423, 423, 423, 424, 425, 425, 425, 426, + 26, 26, 26, 26, 26, 26, 26, 26, 427, 427, 427, 427, 428, 428, 428, 429, + 428, 428, 430, 428, 428, 428, 428, 428, 431, 432, 433, 434, 435, 435, 436, 437, + 435, 438, 435, 438, 439, 439, 439, 439, 440, 440, 440, 440, 26, 26, 26, 26, + 441, 441, 441, 441, 442, 443, 442, 26, 444, 444, 444, 444, 444, 444, 445, 446, + 447, 447, 448, 447, 449, 449, 450, 449, 451, 451, 452, 453, 26, 454, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 455, 455, 455, 455, 455, 455, 455, 455, + 455, 456, 26, 26, 26, 26, 26, 26, 457, 457, 457, 457, 457, 457, 458, 26, + 457, 457, 457, 457, 457, 457, 458, 459, 460, 460, 460, 460, 460, 26, 460, 461, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 31, 31, 31, 462, 463, 463, 463, 463, 463, 464, 465, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 466, 466, 466, 466, 466, 26, 467, 467, + 467, 467, 467, 468, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 469, 469, + 469, 470, 26, 26, 471, 471, 472, 26, 473, 473, 473, 473, 473, 473, 473, 473, + 473, 474, 475, 473, 473, 473, 26, 476, 477, 477, 477, 477, 477, 477, 477, 477, + 478, 479, 480, 480, 480, 481, 480, 482, 483, 483, 483, 483, 483, 483, 484, 483, + 483, 26, 485, 485, 485, 485, 486, 26, 487, 487, 487, 487, 487, 487, 487, 487, + 487, 487, 487, 487, 488, 138, 489, 26, 490, 490, 491, 490, 490, 490, 490, 492, + 26, 26, 26, 26, 26, 26, 26, 26, 493, 494, 495, 496, 495, 497, 498, 498, + 498, 498, 498, 498, 498, 499, 498, 500, 501, 502, 503, 504, 504, 505, 506, 507, + 502, 508, 509, 510, 511, 512, 512, 26, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 514, 515, 26, 26, 26, 516, 516, 516, 516, 516, 516, 516, 516, + 516, 26, 516, 517, 26, 26, 26, 26, 518, 518, 518, 518, 518, 518, 519, 518, + 518, 518, 518, 519, 26, 26, 26, 26, 520, 520, 520, 520, 520, 520, 520, 520, + 521, 26, 520, 522, 199, 523, 26, 26, 524, 524, 524, 524, 524, 524, 524, 525, + 524, 526, 26, 26, 26, 26, 26, 26, 527, 527, 527, 528, 527, 529, 527, 527, + 26, 26, 26, 26, 26, 26, 26, 26, 530, 530, 530, 530, 530, 530, 530, 531, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 532, 532, 532, 532, + 532, 532, 532, 532, 532, 532, 533, 534, 535, 536, 537, 538, 538, 538, 539, 540, + 535, 26, 538, 541, 26, 26, 26, 26, 26, 26, 26, 26, 542, 543, 542, 542, + 542, 542, 542, 543, 544, 26, 26, 26, 545, 545, 545, 545, 545, 545, 545, 545, + 545, 26, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 547, 26, 26, 26, + 548, 548, 548, 548, 548, 548, 548, 549, 550, 551, 550, 550, 550, 550, 552, 550, + 553, 26, 550, 550, 550, 554, 555, 555, 555, 555, 556, 555, 555, 557, 558, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 559, 560, 561, 561, 561, 561, 559, 562, + 561, 26, 561, 563, 564, 565, 566, 566, 566, 567, 568, 569, 566, 570, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 571, 571, 571, 572, 26, 26, 26, 26, 26, 26, 573, 26, + 108, 108, 108, 108, 108, 108, 574, 575, 576, 576, 576, 576, 576, 576, 576, 576, + 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 577, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 576, 576, 576, 576, 576, 576, 576, 576, + 576, 576, 576, 576, 576, 578, 579, 26, 576, 576, 576, 576, 576, 576, 576, 576, + 580, 26, 26, 26, 26, 26, 26, 26, 581, 581, 581, 581, 581, 581, 581, 581, + 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 582, 581, 583, + 26, 26, 26, 26, 26, 26, 26, 26, 584, 584, 584, 584, 584, 584, 584, 584, + 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, + 585, 26, 26, 26, 26, 26, 26, 26, 306, 306, 306, 306, 306, 306, 306, 306, + 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 586, + 587, 587, 587, 588, 587, 589, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 590, 590, 590, 591, 591, 26, 592, 592, 592, 592, 592, 592, 592, 592, + 593, 26, 592, 594, 594, 592, 592, 595, 592, 592, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 597, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 598, 598, 598, 598, 598, 598, 598, 598, + 598, 599, 598, 598, 598, 598, 598, 598, 598, 600, 598, 598, 26, 26, 26, 26, + 26, 26, 26, 26, 601, 26, 347, 26, 602, 602, 602, 602, 602, 602, 602, 602, + 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, + 602, 602, 602, 602, 602, 602, 602, 26, 603, 603, 603, 603, 603, 603, 603, 603, + 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, + 603, 603, 604, 26, 26, 26, 26, 26, 602, 605, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 606, 287, 287, 287, 287, 287, 287, 287, + 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, + 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 288, 26, 26, 26, 26, + 26, 26, 607, 26, 608, 26, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, + 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, + 609, 609, 609, 609, 609, 609, 609, 610, 611, 611, 611, 611, 611, 611, 611, 611, + 611, 611, 611, 611, 611, 612, 611, 613, 611, 614, 611, 615, 281, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 616, 26, 0, 0, 0, 0, 260, 361, 0, 0, + 0, 0, 0, 0, 617, 618, 0, 619, 620, 621, 0, 0, 0, 622, 0, 0, + 0, 0, 0, 0, 0, 623, 26, 26, 14, 14, 14, 14, 14, 14, 14, 14, + 249, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 0, 0, 281, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 260, 26, 0, 0, 0, 623, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 257, 0, 0, 0, 0, 0, 0, 0, 0, 257, 624, 625, 0, 626, + 627, 0, 0, 0, 0, 0, 0, 0, 269, 628, 257, 257, 0, 0, 0, 629, + 630, 631, 632, 0, 0, 0, 0, 0, 0, 0, 0, 0, 616, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 268, 0, 0, 0, 0, 0, 0, 633, 633, 633, 633, 633, 633, 633, 633, + 633, 633, 633, 633, 633, 633, 633, 633, 633, 634, 26, 635, 636, 633, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 271, 270, 270, 637, 638, 639, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 640, 640, 640, 640, 640, 641, 640, 642, + 640, 643, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 644, 644, 644, 644, 644, 644, 644, 645, 646, 646, 646, 646, 646, 646, 646, 646, + 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, + 647, 646, 648, 26, 26, 26, 26, 26, 649, 649, 649, 649, 649, 649, 649, 649, + 649, 650, 649, 651, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 361, 0, 0, 0, 0, 0, 0, 0, 375, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 361, 0, 0, 0, 0, 0, 0, 616, + 26, 26, 26, 26, 26, 26, 26, 26, 652, 31, 31, 31, 653, 654, 655, 656, + 657, 658, 653, 659, 653, 655, 655, 660, 31, 661, 31, 662, 663, 661, 31, 662, + 26, 26, 26, 26, 26, 26, 354, 26, 0, 0, 0, 0, 0, 281, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 281, 26, 0, 260, 361, 0, + 361, 0, 361, 0, 0, 0, 616, 26, 0, 0, 0, 0, 0, 616, 26, 26, + 26, 26, 26, 26, 664, 0, 0, 0, 665, 26, 0, 0, 0, 0, 0, 281, + 0, 623, 314, 26, 616, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 0, 375, 0, 375, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 281, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 623, 0, 281, 26, 26, 0, 281, 0, 0, 0, 0, 0, 0, + 0, 26, 0, 314, 0, 0, 0, 0, 0, 26, 0, 0, 0, 616, 314, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 632, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 627, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 281, 26, 0, 616, 375, 266, 260, 26, 0, 0, 0, 623, 260, 26, + 266, 26, 260, 26, 26, 26, 26, 26, 0, 0, 359, 0, 0, 0, 0, 0, + 0, 266, 26, 26, 26, 26, 0, 314, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 280, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 299, 26, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 280, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 347, 26, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 666, 26, 26, 26, 277, 277, 277, 280, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 667, 26, 26, 26, 26, 26, 26, 668, 26, 26, 26, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, + 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, + 0,1080,1081,1082,1086,1110, 0, 0,1124,1125,1126,1127,1131,1133, 0,1147, + 1154,1155,1156,1161,1187,1188,1189,1193, 0,1219,1226,1227,1228,1229,1233, 0, + 0,1267,1268,1269,1273,1298, 0,1303, 943,1128, 944,1129, 954,1139, 958,1143, + 959,1144, 960,1145, 961,1146, 964,1149, 0, 0, 973,1158, 974,1159, 975,1160, + 983,1168, 978,1163, 988,1173, 990,1175, 991,1176, 993,1178, 994,1179, 0, 0, + 1004,1190,1005,1191,1006,1192,1014,1199,1007, 0, 0, 0,1016,1201,1020,1206, + 0,1022,1208,1025,1211,1023,1209, 0, 0, 0, 0,1032,1218,1037,1223,1035, + 1221, 0, 0, 0,1044,1230,1045,1231,1049,1235, 0, 0,1058,1244,1064,1250, + 1060,1246,1066,1252,1067,1253,1072,1258,1069,1255,1077,1264,1074,1261, 0, 0, + 1083,1270,1084,1271,1085,1272,1088,1275,1089,1276,1096,1283,1103,1290,1111,1299, + 1115,1118,1307,1120,1309,1121,1310, 0,1053,1239, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1093,1280, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 949,1134,1010,1195,1050,1236,1090,1277,1341,1368,1340, + 1367,1342,1369,1339,1366, 0,1320,1347,1418,1419,1323,1350, 0, 0, 992,1177, + 1018,1204,1055,1241,1416,1417,1415,1424,1202, 0, 0, 0, 987,1172, 0, 0, + 1031,1217,1321,1348,1322,1349,1338,1365, 950,1135, 951,1136, 979,1164, 980,1165, + 1011,1196,1012,1197,1051,1237,1052,1238,1061,1247,1062,1248,1091,1278,1092,1279, + 1071,1257,1076,1263, 0, 0, 997,1182, 0, 0, 0, 0, 0, 0, 945,1130, + 982,1167,1337,1364,1335,1362,1046,1232,1422,1423,1113,1301, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 10,1425, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,1314,1427, 5, + 1434,1438,1443, 0,1450, 0,1455,1461,1514, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1446,1458,1468,1476,1480,1486,1517, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1489,1503,1494,1500,1508, 0, 0, 0, 0,1520,1521, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1526,1528, 0,1525, 0, 0, 0,1522, + 0, 0, 0, 0,1536,1532,1539, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1534, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1556, 0, 0, 0, 0, 0, 0,1548,1550, 0,1547, 0, 0, 0,1567, + 0, 0, 0, 0,1558,1554,1561, 0, 0, 0, 0, 0, 0, 0,1568,1569, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1529,1551, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1523,1545,1524,1546, 0, 0,1527,1549, + 0, 0,1570,1571,1530,1552,1531,1553, 0, 0,1533,1555,1535,1557,1537,1559, + 0, 0,1572,1573,1544,1566,1538,1560,1540,1562,1541,1563,1542,1564, 0, 0, + 1543,1565, 0, 0, 0, 0, 0, 0, 0, 0,1606,1607,1609,1608,1610, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1613, 0,1611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1612, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1620, 0, 0, 0, 0, 0, 0, 0,1623, 0, 0,1624, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1614,1615,1616,1617,1618,1619,1621,1622, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1628,1629, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1625,1626, 0,1627, 0, 0, 0,1634, 0, 0,1635, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1630,1631,1632, 0, 0,1633, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1639, 0, 0,1638,1640, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1636,1637, 0, 0, 0, 0, 0, 0,1641, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1642,1644,1643, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1645, 0, 0, 0, 0, 0, 0, 0,1646, 0, 0, 0, 0, 0, 0,1648, + 1649, 0,1647,1650, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1651,1653,1652, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1654, 0,1655,1657,1656, 0, 0, 0, 0,1659, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1660, 0, 0, 0, 0,1661, 0, 0, 0, 0,1662, + 0, 0, 0, 0,1663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1658, 0, 0, 0, 0, 0, 0, 0, 0, 0,1664, 0,1665,1673, 0, + 1674, 0, 0, 0, 0, 0, 0, 0, 0,1666, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1668, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1669, 0, 0, 0, 0,1670, 0, 0, 0, 0,1671, + 0, 0, 0, 0,1672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1667, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1675, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1676, 0, + 1677, 0,1678, 0,1679, 0,1680, 0, 0, 0,1681, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1682, 0,1683, 0, 0,1684,1685, 0,1686, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 953,1138, 955,1140, 956,1141, 957,1142, + 1324,1351, 963,1148, 965,1150, 968,1153, 966,1151, 967,1152,1378,1380,1379,1381, + 984,1169, 985,1170,1420,1421, 986,1171, 989,1174, 995,1180, 998,1183, 996,1181, + 999,1184,1000,1185,1015,1200,1329,1356,1017,1203,1019,1205,1021,1207,1024,1210, + 1687,1688,1027,1213,1026,1212,1028,1214,1029,1215,1030,1216,1034,1220,1036,1222, + 1039,1225,1038,1224,1334,1361,1336,1363,1382,1384,1383,1385,1056,1242,1057,1243, + 1059,1245,1063,1249,1689,1690,1065,1251,1068,1254,1070,1256,1386,1387,1388,1389, + 1691,1692,1073,1259,1075,1262,1079,1266,1078,1265,1095,1282,1098,1285,1097,1284, + 1390,1391,1392,1393,1099,1286,1100,1287,1101,1288,1102,1289,1105,1292,1104,1291, + 1106,1294,1107,1295,1108,1296,1114,1302,1119,1308,1122,1311,1123,1312,1186,1260, + 1293,1305, 0,1394, 0, 0, 0, 0, 952,1137, 947,1132,1317,1344,1316,1343, + 1319,1346,1318,1345,1693,1695,1371,1375,1370,1374,1373,1377,1372,1376,1694,1696, + 981,1166, 977,1162, 972,1157,1326,1353,1325,1352,1328,1355,1327,1354,1697,1698, + 1009,1194,1013,1198,1054,1240,1048,1234,1331,1358,1330,1357,1333,1360,1332,1359, + 1699,1700,1396,1401,1395,1400,1398,1403,1397,1402,1399,1404,1094,1281,1087,1274, + 1406,1411,1405,1410,1408,1413,1407,1412,1409,1414,1109,1297,1117,1306,1116,1304, + 1112,1300, 0, 0, 0, 0, 0, 0,1471,1472,1701,1705,1702,1706,1703,1707, + 1430,1431,1715,1719,1716,1720,1717,1721,1477,1478,1729,1731,1730,1732, 0, 0, + 1435,1436,1733,1735,1734,1736, 0, 0,1481,1482,1737,1741,1738,1742,1739,1743, + 1439,1440,1751,1755,1752,1756,1753,1757,1490,1491,1765,1768,1766,1769,1767,1770, + 1447,1448,1771,1774,1772,1775,1773,1776,1495,1496,1777,1779,1778,1780, 0, 0, + 1451,1452,1781,1783,1782,1784, 0, 0,1504,1505,1785,1788,1786,1789,1787,1790, + 0,1459, 0,1791, 0,1792, 0,1793,1509,1510,1794,1798,1795,1799,1796,1800, + 1462,1463,1808,1812,1809,1813,1810,1814,1467, 21,1475, 22,1479, 23,1485, 24, + 1493, 27,1499, 28,1507, 29, 0, 0,1704,1708,1709,1710,1711,1712,1713,1714, + 1718,1722,1723,1724,1725,1726,1727,1728,1740,1744,1745,1746,1747,1748,1749,1750, + 1754,1758,1759,1760,1761,1762,1763,1764,1797,1801,1802,1803,1804,1805,1806,1807, + 1811,1815,1816,1817,1818,1819,1820,1821,1470,1469,1822,1474,1465, 0,1473,1825, + 1429,1428,1426, 12,1432, 0, 26, 0, 0,1315,1823,1484,1466, 0,1483,1829, + 1433, 13,1437, 14,1441,1826,1827,1828,1488,1487,1513, 19, 0, 0,1492,1515, + 1445,1444,1442, 15, 0,1831,1832,1833,1502,1501,1516, 25,1497,1498,1506,1518, + 1457,1456,1454, 17,1453,1313, 11, 3, 0, 0,1824,1512,1519, 0,1511,1830, + 1449, 16,1460, 18,1464, 4, 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, + 0, 0, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1834,1835, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1836, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1837,1839,1838, 0, 0, 0, 0,1840, 0, 0, 0, + 0,1841, 0, 0,1842, 0, 0, 0, 0, 0, 0, 0,1843, 0,1844, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1845, 0, 0,1846, 0, 0,1847, + 0,1848, 0, 0, 0, 0, 0, 0, 937, 0,1850, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1849, 936, 938,1851,1852, 0, 0,1853,1854, 0, 0, + 1855,1856, 0, 0, 0, 0, 0, 0,1857,1858, 0, 0,1861,1862, 0, 0, + 1863,1864, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1867,1868,1869,1870,1859,1860,1865,1866, 0, 0, 0, 0, + 0, 0,1871,1872,1873,1874, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 32, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1875, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1877, 0,1878, 0,1879, 0,1880, 0,1881, 0,1882, 0, + 1883, 0,1884, 0,1885, 0,1886, 0,1887, 0,1888, 0, 0,1889, 0,1890, + 0,1891, 0, 0, 0, 0, 0, 0,1892,1893, 0,1894,1895, 0,1896,1897, + 0,1898,1899, 0,1900,1901, 0, 0, 0, 0, 0, 0,1876, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1902, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1904, 0,1905, 0,1906, 0,1907, 0,1908, 0,1909, 0, + 1910, 0,1911, 0,1912, 0,1913, 0,1914, 0,1915, 0, 0,1916, 0,1917, + 0,1918, 0, 0, 0, 0, 0, 0,1919,1920, 0,1921,1922, 0,1923,1924, + 0,1925,1926, 0,1927,1928, 0, 0, 0, 0, 0, 0,1903, 0, 0,1929, + 1930,1931,1932, 0, 0, 0,1933, 0, 710, 385, 724, 715, 455, 103, 186, 825, + 825, 242, 751, 205, 241, 336, 524, 601, 663, 676, 688, 738, 411, 434, 474, 500, + 649, 746, 799, 108, 180, 416, 482, 662, 810, 275, 462, 658, 692, 344, 618, 679, + 293, 388, 440, 492, 740, 116, 146, 168, 368, 414, 481, 527, 606, 660, 665, 722, + 781, 803, 809, 538, 553, 588, 642, 758, 811, 701, 233, 299, 573, 612, 487, 540, + 714, 779, 232, 267, 412, 445, 457, 585, 594, 766, 167, 613, 149, 148, 560, 589, + 648, 768, 708, 345, 411, 704, 105, 259, 313, 496, 518, 174, 542, 120, 307, 101, + 430, 372, 584, 183, 228, 529, 650, 697, 424, 732, 428, 349, 632, 355, 517, 110, + 135, 147, 403, 580, 624, 700, 750, 170, 193, 245, 297, 374, 463, 543, 763, 801, + 812, 815, 162, 384, 420, 730, 287, 330, 337, 366, 459, 476, 509, 558, 591, 610, + 726, 652, 734, 759, 154, 163, 198, 473, 683, 697, 292, 311, 353, 423, 572, 494, + 113, 217, 259, 280, 314, 499, 506, 603, 608, 752, 778, 782, 788, 117, 557, 748, + 774, 320, 109, 126, 260, 265, 373, 411, 479, 523, 655, 737, 823, 380, 765, 161, + 395, 398, 438, 451, 502, 516, 537, 583, 791, 136, 340, 769, 122, 273, 446, 727, + 305, 322, 400, 496, 771, 155, 190, 269, 377, 391, 406, 432, 501, 519, 599, 684, + 687, 749, 776, 175, 452, 191, 480, 510, 659, 772, 805, 813, 397, 444, 619, 566, + 568, 575, 491, 471, 707, 111, 636, 156, 153, 288, 346, 578, 256, 435, 383, 729, + 680, 767, 694, 295, 128, 210, 0, 0, 227, 0, 379, 0, 0, 150, 493, 525, + 544, 551, 552, 556, 783, 576, 604, 0, 661, 0, 703, 0, 0, 735, 743, 0, + 0, 0, 793, 794, 795, 808, 741, 773, 118, 127, 130, 166, 169, 177, 207, 213, + 215, 226, 229, 268, 270, 317, 327, 329, 335, 369, 375, 381, 404, 441, 448, 458, + 477, 484, 503, 539, 545, 547, 546, 548, 549, 550, 554, 555, 561, 564, 569, 591, + 593, 595, 598, 607, 620, 625, 625, 651, 690, 695, 705, 706, 716, 717, 733, 735, + 777, 786, 790, 315, 869, 623, 0, 0, 102, 145, 134, 115, 129, 138, 165, 171, + 207, 202, 206, 212, 227, 231, 240, 243, 250, 254, 294, 296, 303, 308, 319, 325, + 321, 329, 326, 335, 341, 357, 360, 362, 370, 379, 388, 389, 393, 421, 424, 438, + 456, 454, 458, 465, 477, 535, 485, 490, 493, 507, 512, 514, 521, 522, 525, 526, + 528, 533, 532, 541, 565, 569, 574, 586, 591, 597, 607, 637, 647, 674, 691, 693, + 695, 698, 703, 699, 705, 704, 702, 706, 709, 717, 728, 736, 747, 754, 770, 777, + 783, 784, 786, 787, 790, 802, 825, 848, 847, 857, 55, 65, 66, 883, 892, 916, + 822, 824, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1586, 0,1605, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1602,1603,1934,1935,1574,1575,1576,1577,1579,1580,1581,1583,1584, 0, + 1585,1587,1588,1589,1591, 0,1592, 0,1593,1594, 0,1595,1596, 0,1598,1599, + 1600,1601,1604,1582,1578,1590,1597, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1936, 0,1937, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1938, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1939,1940, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1941,1942, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1944,1943, 0,1945, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1946,1947, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1949,1950,1951,1952,1953,1954,1955, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1956,1957,1958,1960,1959,1961, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 106, 104, 107, 826, 114, 118, 119, 121, + 123, 124, 127, 125, 34, 830, 130, 131, 132, 137, 827, 35, 133, 139, 829, 142, + 143, 112, 144, 145, 924, 151, 152, 37, 157, 158, 159, 160, 38, 165, 166, 169, + 171, 172, 173, 174, 176, 177, 178, 179, 181, 182, 182, 182, 833, 468, 184, 185, + 834, 187, 188, 189, 196, 192, 194, 195, 197, 199, 200, 201, 203, 204, 204, 206, + 208, 209, 211, 218, 213, 219, 214, 216, 153, 234, 221, 222, 223, 220, 225, 224, + 230, 835, 235, 236, 237, 238, 239, 244, 836, 837, 247, 248, 249, 246, 251, 39, + 40, 253, 255, 255, 838, 257, 258, 259, 261, 839, 262, 263, 301, 264, 41, 266, + 270, 272, 271, 841, 274, 842, 277, 276, 278, 281, 282, 42, 283, 284, 285, 286, + 43, 843, 44, 289, 290, 291, 293, 934, 298, 845, 845, 621, 300, 300, 45, 852, + 894, 302, 304, 46, 306, 309, 310, 312, 316, 48, 47, 317, 846, 318, 323, 324, + 325, 324, 328, 329, 333, 331, 332, 334, 335, 336, 338, 339, 342, 343, 347, 351, + 849, 350, 348, 352, 354, 359, 850, 361, 358, 356, 49, 363, 365, 367, 364, 50, + 369, 371, 851, 376, 386, 378, 53, 381, 52, 51, 140, 141, 387, 382, 614, 78, + 388, 389, 390, 394, 392, 856, 54, 399, 396, 402, 404, 858, 405, 401, 407, 55, + 408, 409, 410, 413, 859, 415, 56, 417, 860, 418, 57, 419, 422, 424, 425, 861, + 840, 862, 426, 863, 429, 431, 427, 433, 437, 441, 438, 439, 442, 443, 864, 436, + 449, 450, 58, 454, 453, 865, 447, 460, 866, 867, 461, 466, 465, 464, 59, 467, + 470, 469, 472, 828, 475, 868, 478, 870, 483, 485, 486, 871, 488, 489, 872, 873, + 495, 497, 60, 498, 61, 61, 504, 505, 507, 508, 511, 62, 513, 874, 515, 875, + 518, 844, 520, 876, 877, 878, 63, 64, 528, 880, 879, 881, 882, 530, 531, 531, + 533, 66, 534, 67, 68, 884, 536, 538, 541, 69, 885, 549, 886, 887, 556, 559, + 70, 561, 562, 563, 888, 889, 889, 567, 71, 890, 570, 571, 72, 891, 577, 73, + 581, 579, 582, 893, 587, 74, 590, 592, 596, 75, 895, 896, 76, 897, 600, 898, + 602, 605, 607, 899, 900, 609, 901, 611, 853, 77, 615, 616, 79, 617, 252, 902, + 903, 854, 855, 621, 622, 731, 80, 627, 626, 628, 164, 629, 630, 631, 633, 904, + 632, 634, 639, 640, 635, 641, 646, 651, 638, 643, 644, 645, 905, 907, 906, 81, + 653, 654, 656, 911, 657, 908, 82, 83, 909, 910, 84, 664, 665, 666, 667, 669, + 668, 671, 670, 674, 672, 673, 675, 85, 677, 678, 86, 681, 682, 912, 685, 686, + 87, 689, 36, 913, 914, 88, 89, 696, 702, 709, 711, 915, 712, 713, 718, 719, + 917, 831, 721, 720, 723, 832, 725, 728, 918, 919, 739, 742, 744, 920, 745, 753, + 756, 757, 755, 760, 761, 921, 762, 90, 764, 922, 91, 775, 279, 780, 923, 925, + 92, 93, 785, 926, 94, 927, 787, 787, 789, 928, 792, 95, 796, 797, 798, 800, + 96, 929, 802, 804, 806, 97, 98, 807, 930, 99, 931, 932, 933, 814, 100, 816, + 817, 818, 819, 820, 821, 935, 0, 0, +}; +static const int16_t +_hb_ucd_i16[196] = +{ + 0, 0, 0, 0, 1, -1, 0, 0, 2, 0, -2, 0, 0, 0, 0, 2, + 0, -2, 0, 0, 0, 0, 0, 16, 0, 0, 0, -16, 0, 0, 1, -1, + 0, 0, 0, 1, -1, 0, 0, 0, 0, 1, -1, 0, 3, 3, 3, -3, + -3, -3, 0, 0, 0, 2016, 0, 0, 0, 0, 0, 2527, 1923, 1914, 1918, 0, + 2250, 0, 0, 0, 0, 0, 0, 138, 0, 7, 0, 0, -7, 0, 0, 0, + 1, -1, 1, -1, -1, 1, -1, 0, 1824, 0, 0, 0, 0, 0, 2104, 0, + 2108, 2106, 0, 2106, 1316, 0, 0, 0, 0, 1, -1, 1, -1, -138, 0, 0, + 1, -1, 8, 8, 8, 0, 7, 7, 0, 0, -8, -8, -8, -7, -7, 0, + 1, -1, 0, 2,-1316, 1, -1, 0, -1, 1, -1, 1, -1, 3, 1, -1, + -3, 1, -1, 1, -1, 0, 0,-1914,-1918, 0, 0,-1923,-1824, 0, 0, 0, + 0,-2016, 0, 0, 1, -1, 0, 1, 0, 0,-2104, 0, 0, 0, 0,-2106, + -2108,-2106, 0, 0, 1, -1,-2250, 0, 0, 0,-2527, 0, 0, -2, 0, 1, + -1, 0, 1, -1, +}; + +static inline uint_fast8_t +_hb_ucd_gc (unsigned u) +{ + return u<1114110u?_hb_ucd_u8[6504+(((_hb_ucd_u8[1264+(((_hb_ucd_u16[((_hb_ucd_u8[544+(((_hb_ucd_u8[u>>1>>3>>3>>4])<<4)+((u>>1>>3>>3)&15u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; +} +static inline uint_fast8_t +_hb_ucd_ccc (unsigned u) +{ + return u<125259u?_hb_ucd_u8[8768+(((_hb_ucd_u8[7792+(((_hb_ucd_u8[7120+(((_hb_ucd_u8[6874+(u>>2>>3>>4)])<<4)+((u>>2>>3)&15u))])<<3)+((u>>2)&7u))])<<2)+((u)&3u))]:0; +} +static inline unsigned +_hb_ucd_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline int_fast16_t +_hb_ucd_bmg (unsigned u) +{ + return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9508+(((_hb_ucd_u8[9388+(((_hb_ucd_b4(9260+_hb_ucd_u8,u>>2>>3>>3))<<3)+((u>>2>>3)&7u))])<<3)+((u>>2)&7u))])<<2)+((u)&3u)]:0; +} +static inline uint_fast8_t +_hb_ucd_sc (unsigned u) +{ + return u<918000u?_hb_ucd_u8[10974+(((_hb_ucd_u16[1960+(((_hb_ucd_u8[10286+(((_hb_ucd_u8[9836+(u>>3>>4>>4)])<<4)+((u>>3>>4)&15u))])<<4)+((u>>3)&15u))])<<3)+((u)&7u))]:2; +} +static inline uint_fast16_t +_hb_ucd_dm (unsigned u) +{ + return u<195102u?_hb_ucd_u16[5768+(((_hb_ucd_u8[16708+(((_hb_ucd_u8[16326+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; +} + + +#else + +static const uint8_t +_hb_ucd_u8[13344] = +{ + 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 5, 5, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 5, 17, 15, 15, 18, 15, 19, 20, 21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 22, 23, + 5, 24, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 25, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 34, 34, 34, 35, 36, 37, 34, 34, 34, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 67, 68, 69, 67, 70, 71, + 67, 67, 62, 72, 62, 62, 73, 67, 74, 75, 76, 77, 78, 67, 67, 67, + 79, 80, 34, 81, 82, 83, 67, 67, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 84, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 85, 34, 34, 34, 34, 34, 34, 34, 34, 86, 34, 34, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 100,100, 34, 34, 34, 34,101,102, 34, 34,103,104,105,106,107,108, + 34, 34,109,110,111,112,113,114,115,116,117,111, 34, 34, 34,111, + 118,119,120,121,122,123,124,125, 34,126,127,111,128,129,130,131, + 132,133,134,135,136,137,138,111,139,140,111,141,142,143,144,111, + 145,146,147,148,149,150,111,111,151,152,153,154,111,155,111,156, + 34, 34, 34, 34, 34, 34, 34, 34,157, 34, 34,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 34, 34, 34, 34, 34, 34, 34, 34,158,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111, 34, 34, 34, 34, 34,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 34, 34, 34, 34,159,160,161, 34,111,111,111,111,162,163,164,165, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111, + 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111, 34,166,111,111,111,111,111,111, + 67, 67,167,168,169,128, 65,111,170,171,172,173,174,175,176,177, + 67, 67, 67, 67,178,179,111,111,111,111,111,111,111,111,111,111, + 180,111,181,111,111,182,111,111,111,111,111,111,111,111,111,111, + 34,183,184,111,111,111,111,111,128,185,186,111, 34,187,111,111, + 67, 67,188, 67, 67,111, 67,189, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67,190,111,111,111,111,111,111,111,111, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111,111, + 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 191,111,180,180,111,111,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, + 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, + 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, + 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, + 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, + 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, + 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, + 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, + 16, 16, 39, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 41, 41, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, + 40, 40, 42, 41, 41, 41, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, + 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 42, 32, 16, 44, 16, 10, + 41, 41, 41, 45, 11, 11, 11, 11, 34, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, + 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 46, 34, 32, 34, 11, + 32, 47, 43, 43, 48, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, + 11, 11, 11, 11, 49, 2, 2, 2, 16, 16, 16, 16, 50, 51, 52, 53, + 54, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 55, + 56, 57, 43, 56, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 58, 2, 2, 2, 2, 2, 2, 59, 59, 59, 8, 9, 60, 2, 61, + 43, 43, 43, 43, 43, 57, 59, 2, 62, 36, 36, 36, 36, 63, 43, 43, + 7, 7, 7, 7, 7, 2, 2, 36, 64, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 65, 43, 43, 43, 66, 47, 43, 43, 67, 68, 69, 43, 43, 36, + 7, 7, 7, 7, 7, 36, 70, 71, 2, 2, 2, 2, 2, 2, 2, 72, + 63, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 64, 36, + 36, 36, 36, 43, 43, 43, 43, 43, 7, 7, 7, 7, 7, 36, 36, 36, + 36, 36, 36, 36, 36, 63, 43, 43, 43, 43, 40, 21, 2, 40, 68, 20, + 36, 36, 36, 43, 43, 68, 43, 43, 43, 43, 68, 43, 68, 43, 43, 43, + 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 36, 36, 63, 43, 43, 2, + 36, 63, 43, 43, 43, 43, 43, 43, 43, 73, 43, 43, 43, 43, 43, 43, + 43, 74, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 74, 64, 75, + 76, 43, 43, 43, 74, 75, 76, 75, 63, 43, 43, 43, 36, 36, 36, 36, + 36, 43, 2, 7, 7, 7, 7, 7, 77, 36, 36, 36, 36, 36, 36, 36, + 63, 75, 78, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 75, + 76, 43, 43, 74, 75, 75, 76, 36, 36, 36, 36, 79, 75, 75, 36, 36, + 36, 43, 43, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 53, 58, 43, + 43, 74, 78, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 75, + 76, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 64, 36, 36, 36, + 36, 36, 36, 7, 7, 7, 7, 7, 43, 36, 63, 2, 2, 2, 2, 2, + 76, 43, 43, 43, 74, 75, 76, 43, 60, 20, 20, 20, 80, 43, 43, 43, + 43, 75, 78, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 76, + 76, 43, 43, 74, 75, 75, 76, 43, 43, 43, 43, 74, 75, 75, 36, 36, + 71, 27, 27, 27, 27, 27, 27, 27, 43, 64, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 75, 74, 75, 75, 75, 75, 75, 76, 43, + 36, 36, 36, 79, 75, 75, 75, 75, 75, 75, 75, 7, 7, 7, 7, 7, + 27, 81, 61, 61, 53, 61, 61, 61, 74, 75, 64, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 43, 74, 75, 75, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 36, 36, 36, 36, 7, 7, 7, 82, 27, 27, 27, 81, + 63, 75, 65, 36, 36, 36, 36, 36, 75, 75, 75, 74, 75, 75, 43, 43, + 43, 43, 74, 75, 75, 75, 75, 36, 83, 36, 36, 36, 36, 36, 36, 36, + 43, 75, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 63, 64, 75, + 76, 43, 43, 75, 75, 75, 76, 70, 61, 61, 36, 79, 27, 27, 27, 84, + 27, 27, 27, 27, 81, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 74, + 75, 43, 43, 43, 75, 75, 75, 75, 7, 75, 2, 2, 2, 2, 2, 2, + 63, 36, 43, 43, 43, 43, 43, 85, 36, 36, 36, 68, 43, 43, 43, 57, + 7, 7, 7, 7, 7, 2, 2, 2, 63, 36, 43, 43, 43, 43, 64, 36, + 36, 36, 36, 40, 43, 43, 43, 43, 7, 7, 7, 7, 7, 7, 36, 36, + 70, 61, 2, 2, 2, 2, 2, 2, 2, 86, 86, 61, 43, 61, 61, 61, + 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 47, 47, 47, 4, 4, 75, + 63, 43, 43, 43, 43, 43, 43, 74, 43, 43, 57, 43, 36, 36, 63, 43, + 43, 43, 43, 43, 43, 43, 43, 61, 61, 61, 61, 69, 61, 61, 61, 61, + 2, 2, 86, 61, 21, 2, 2, 2, 36, 36, 36, 36, 36, 79, 76, 43, + 74, 43, 43, 43, 76, 74, 76, 64, 36, 36, 36, 75, 43, 36, 36, 43, + 64, 75, 78, 79, 75, 75, 75, 36, 63, 43, 64, 36, 36, 36, 36, 36, + 36, 74, 76, 74, 75, 75, 76, 79, 7, 7, 7, 7, 7, 75, 76, 61, + 16, 16, 16, 16, 16, 50, 44, 16, 36, 36, 36, 36, 36, 36, 63, 43, + 2, 2, 2, 2, 87, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 61, 61, 61, 61, 61, 61, 61, 61, 11, 11, 11, 11, 16, 16, 16, 16, + 88, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 70, 65, + 89, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 90, 91, 91, + 36, 36, 36, 36, 36, 58, 2, 92, 93, 36, 36, 36, 36, 36, 36, 36, + 36, 43, 43, 43, 43, 43, 43, 43, 36, 43, 57, 2, 2, 2, 2, 2, + 36, 36, 43, 76, 43, 43, 43, 75, 75, 75, 75, 74, 76, 43, 43, 43, + 43, 43, 2, 77, 2, 60, 63, 43, 7, 7, 7, 7, 7, 7, 7, 7, + 2, 2, 2, 94, 2, 56, 43, 59, 36, 95, 36, 36, 36, 36, 36, 36, + 36, 36, 63, 64, 36, 36, 36, 36, 36, 36, 36, 36, 63, 36, 36, 36, + 43, 74, 75, 76, 74, 75, 75, 75, 75, 74, 75, 75, 76, 43, 43, 43, + 61, 61, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 27, 27, 61, + 36, 36, 36, 63, 74, 76, 43, 2, 36, 36, 79, 74, 43, 43, 43, 43, + 74, 74, 76, 43, 43, 43, 74, 75, 75, 76, 43, 43, 43, 43, 43, 43, + 2, 2, 2, 77, 2, 2, 2, 2, 43, 43, 43, 43, 43, 43, 43, 96, + 43, 43, 78, 36, 36, 36, 36, 36, 36, 36, 74, 43, 43, 74, 74, 75, + 75, 74, 78, 36, 36, 36, 36, 36, 86, 61, 61, 61, 61, 47, 43, 43, + 43, 43, 61, 61, 61, 61, 61, 61, 43, 78, 36, 36, 36, 36, 36, 36, + 79, 43, 43, 75, 43, 76, 43, 36, 36, 36, 36, 74, 43, 75, 76, 76, + 43, 75, 75, 75, 75, 75, 2, 2, 36, 36, 75, 75, 75, 75, 43, 43, + 43, 43, 75, 43, 43, 57, 2, 2, 7, 7, 7, 7, 7, 7, 83, 36, + 36, 36, 36, 36, 40, 40, 40, 2, 43, 57, 43, 43, 43, 43, 43, 43, + 74, 43, 43, 43, 64, 36, 63, 36, 36, 36, 64, 79, 43, 36, 36, 36, + 16, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 44, 16, 16, + 16, 16, 16, 16, 44, 16, 16, 16, 16, 16, 16, 16, 16, 97, 40, 40, + 32, 32, 32, 16, 16, 16, 16, 32, 16, 16, 16, 16, 11, 11, 11, 11, + 16, 16, 16, 16, 34, 11, 11, 11, 16, 16, 16, 16, 98, 98, 98, 98, + 16, 16, 16, 16, 11, 11, 99,100, 41, 16, 16, 16, 11, 11, 99, 41, + 16, 16, 16, 16, 11, 11,101, 41,102,102,102,102,102,103, 59, 59, + 51, 51, 51, 2,104,105,104,105, 2, 2, 2, 2,106, 59, 59,107, + 2, 2, 2, 2,108,109, 2,110,111, 2,112,113, 2, 2, 2, 2, + 2, 9,111, 2, 2, 2, 2,114, 59, 59, 59, 59, 59, 59, 59, 59, + 115, 40, 27, 27, 27, 8,112,116, 27, 27, 27, 27, 27, 8,112, 91, + 20, 20, 20, 20, 20, 20, 20, 20, 43, 43, 43, 43, 43, 43,117, 48, + 96, 48, 96, 43, 43, 43, 43, 43, 61,118, 61,119, 61, 34, 11, 16, + 11, 32,119, 61, 46, 11, 11, 61, 61, 61,118,118,118, 11, 11,120, + 11, 11, 35, 36, 39, 61, 16, 11, 8, 8, 46, 16, 16, 26, 61,121, + 92, 92, 92, 92, 92, 92, 92, 92, 92,122,123, 92,124, 61, 61, 61, + 8, 8,125, 61, 61, 8, 61, 61,125, 26, 61,125, 61, 61, 61,125, + 61, 61, 61, 61, 61, 61, 61, 8, 61,125,125, 61, 61, 61, 61, 61, + 61, 61, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 61, 61, 61, 61, 4, 4, 61, 61, 8, 61, 61, 61,126,127, 61, 61, + 61, 61, 61, 61, 61, 61,125, 61, 61, 61, 61, 61, 61, 26, 8, 8, + 8, 8, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 8, 8, + 8, 61, 61, 61, 61, 61, 61, 61, 27, 27, 27, 27, 27, 27, 61, 61, + 61, 61, 61, 61, 61, 27, 27, 27, 61, 61, 61, 26, 61, 61, 61, 61, + 26, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 8, 8, 8, 8, + 61, 61, 61, 61, 61, 61, 61, 26, 61, 61, 61, 61, 4, 4, 4, 4, + 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 61, 61, 61, 61, 61, 61, + 8, 8,112,128, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, + 8,112,129,129,129,129,129,129,129,129,129,129,128, 8, 8, 8, + 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, + 8, 8,125, 26, 8, 8,125, 61, 32, 11, 32, 34, 34, 34, 34, 11, + 32, 32, 34, 16, 16, 16, 40, 11, 32, 32,121, 61, 61,119, 34,130, + 43, 32, 16, 16, 50, 2, 87, 2, 36, 36, 36, 36, 36, 36, 36, 95, + 2, 2, 2, 2, 2, 2, 2, 56, 2,104,104, 2,108,109,104, 2, + 2, 2, 2, 6, 2, 94,104, 2,104, 4, 4, 4, 4, 2, 2, 77, + 2, 2, 2, 2, 2, 51, 2, 2, 94,131, 2, 2, 2, 2, 2, 2, + 61, 2, 2, 2, 2, 2, 2, 2, 1, 2,132,133, 4, 4, 4, 4, + 4, 61, 4, 4, 4, 4,134, 91,135, 92, 92, 92, 92, 43, 43, 75, + 136, 40, 40, 61, 92,137, 58, 61, 71, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 63,138,139, 62, 36, 36, 36, 36, 36, 58, 40, 62, + 61, 27, 27, 61, 61, 61, 61, 61, 27, 27, 27, 27, 27, 61, 61, 61, + 61, 61, 61, 61, 27, 27, 27, 27,140, 27, 27, 27, 27, 27, 27, 27, + 36, 36, 95, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,141, 2, + 32, 32, 32, 32, 32, 32, 32, 63, 48,142, 43, 43, 43, 43, 43, 77, + 32, 32, 32, 32, 32, 32, 40, 43, 36, 36, 36, 92, 92, 92, 92, 92, + 43, 2, 2, 2, 2, 2, 2, 2, 41, 41, 41,139, 40, 40, 40, 40, + 41, 32, 32, 32, 32, 32, 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, + 44, 16, 16, 16, 34, 34, 34, 32, 32, 32, 32, 32, 42,143, 34, 35, + 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, 32, + 11, 11, 32, 32, 32, 32, 32, 32, 16, 32, 11, 11, 34, 16, 16, 16, + 16, 16, 34, 35, 40, 35, 36, 36, 36, 64, 36, 64, 36, 63, 36, 36, + 36, 79, 76, 74, 61, 61, 43, 43, 27, 27, 27, 61,144, 61, 61, 61, + 36, 36, 2, 2, 2, 2, 2, 2, 75, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 75, 75, 75, 75, 75, 75, 75, 75, 43, 43, 43, 43, 43, 2, + 43, 36, 36, 36, 2, 65, 65, 63, 36, 36, 36, 43, 43, 43, 43, 2, + 36, 36, 36, 63, 43, 43, 43, 43, 43, 75, 75, 75, 75, 75, 75,145, + 36, 63, 75, 43, 43, 75, 43, 75,145, 2, 2, 2, 2, 2, 2, 77, + 7, 7, 7, 7, 7, 7, 7, 2, 36, 36, 63, 62, 36, 36, 36, 36, + 36, 36, 36, 36, 63, 43, 43, 74, 76, 74, 76, 43, 43, 43, 43, 43, + 36, 63, 36, 36, 36, 36, 74, 75, 7, 7, 7, 7, 7, 7, 2, 2, + 62, 36, 36, 70, 61, 79, 74, 36, 64, 43, 64, 63, 64, 36, 36, 43, + 36, 36, 36, 36, 36, 36, 95, 2, 36, 36, 36, 36, 36, 79, 43, 75, + 2, 95,146, 43, 43, 43, 43, 43, 16, 16, 16, 16, 16,100, 40, 40, + 16, 16, 16, 16, 97, 41, 41, 41, 36, 79, 76, 75, 74,145, 76, 43, + 147,147,147,147,147,147,147,147,148,148,148,148,148,148,148,148, + 16, 16, 16, 16, 16, 16, 35, 64, 36, 36, 36, 36,149, 36, 36, 36, + 36, 41, 41, 41, 41, 41, 41, 41, 41,150, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36,129,151,151,151,151,151,151,151,151, + 36, 36, 36, 36, 36, 36,144, 61, 2, 2, 2,152,113, 2, 2, 2, + 6,153,154,129,129,129,129,129,129,129,113,152,113, 2,110,155, + 2, 2, 2, 2,134,129,129,113, 2,156, 8, 8, 60, 2, 2, 2, + 36, 36, 36, 36, 36, 36, 36,157, 2, 2, 3, 2, 4, 5, 6, 2, + 16, 16, 16, 16, 16, 17, 18,112,113, 4, 2, 36, 36, 36, 36, 36, + 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, + 20,158, 53, 20, 26, 8,125, 61, 61, 61, 61, 61,159, 59, 61, 61, + 2, 2, 2, 87, 27, 27, 27, 27, 27, 27, 27, 81, 61, 61, 61, 61, + 92, 92,124, 27, 81, 61, 61, 61, 61, 61, 61, 61, 61, 27, 61, 61, + 61, 61, 61, 61, 61, 61, 47, 43,160,160,160,160,160,160,160,160, + 161, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 84, 36, + 133, 36, 36, 36, 36, 92, 92, 92, 36, 36, 36, 36, 36, 36, 36, 58, + 162, 92, 92, 92, 92, 92, 92, 92, 36, 36, 36, 58, 27, 27, 27, 27, + 36, 36, 36, 70,140, 27, 27, 27, 36, 36, 36,163, 27, 27, 27, 27, + 36, 36, 36, 36, 36,163, 27, 27, 36, 36, 36, 27, 27, 27, 27, 30, + 36, 36, 36, 36, 36, 36, 27, 36, 63, 43, 43, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36,163, 30, + 36, 36, 36, 36, 36, 36,163, 27, 36, 36, 36, 36, 71, 36, 36, 36, + 36, 36, 63, 43, 43,161, 27, 27, 36, 36, 36, 36, 58, 2, 2, 2, + 36, 36, 36, 36, 27, 27, 27, 27, 16, 16, 16, 16, 16, 27, 27, 27, + 36, 36, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 63,164, 51, + 27, 27, 27, 84, 36, 36, 36, 36,161, 27, 30, 2, 2, 2, 2, 2, + 36, 36,163, 27, 27, 27, 27, 27, 76, 78, 36, 36, 36, 36, 36, 36, + 43, 43, 43, 57, 2, 2, 2, 2, 2, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,165, + 75, 76, 43, 74, 76, 57, 72, 2, 2, 2, 2, 2, 2, 2, 72, 59, + 36, 36, 36, 63, 43, 43, 76, 43, 43, 43, 43, 7, 7, 7, 7, 7, + 2, 2, 79, 78, 36, 36, 36, 36, 36, 63, 2, 36, 36, 36, 36, 36, + 36, 79, 75, 43, 43, 43, 43, 74, 78, 36, 58, 2, 56, 43, 57, 76, + 7, 7, 7, 7, 7, 58, 58, 2, 87, 27, 27, 27, 27, 27, 27, 27, + 36, 36, 36, 36, 36, 36, 75, 76, 43, 75, 74, 43, 2, 2, 2, 43, + 36, 36, 36, 36, 36, 36, 36, 63, 74, 75, 75, 75, 75, 75, 75, 75, + 36, 36, 36, 79, 75, 75, 78, 36, 36, 75, 75, 43, 43, 43, 43, 43, + 36, 36, 79, 75, 43, 43, 43, 43, 75, 43, 74, 64, 36, 58, 2, 2, + 7, 7, 7, 7, 7, 2, 2, 64, 75, 76, 43, 43, 74, 74, 75, 76, + 74, 43, 36, 65, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 79, + 75, 43, 43, 43, 75, 75, 43, 76, 57, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 36, 36, 43, 43, 75, 76, 43, 43, 43, 74, 76, 76, + 57, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 63, 76, 75, + 43, 43, 43, 76, 36, 36, 36, 36, 75, 43, 43, 76, 43, 43, 43, 43, + 7, 7, 7, 7, 7, 27, 2, 86, 43, 43, 43, 43, 76, 57, 2, 2, + 27, 27, 27, 27, 27, 27, 27, 84, 75, 75, 75, 75, 75, 76, 74, 64, + 78, 76, 2, 2, 2, 2, 2, 2, 79, 75, 43, 43, 43, 43, 75, 75, + 64, 65, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 63, 43, 43, 43, 43, 64, 36, 36, 36, 63, 43, 43, 74, 63, 43, 57, + 2, 2, 2, 56, 43, 43, 43, 43, 63, 43, 43, 74, 76, 43, 36, 36, + 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 74, 43, 2, 65, 2, + 43, 43, 43, 43, 43, 43, 43, 76, 58, 2, 2, 2, 2, 2, 2, 2, + 2, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 74, 43, 43, 43, + 74, 43, 76, 43, 43, 43, 43, 43, 43, 43, 43, 63, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 75, 75, 75, 43, 74, 76, 76, 36, 36, 36, 36, + 36, 63, 74,145, 2, 2, 2, 2, 27, 27, 81, 61, 61, 61, 53, 20, + 144, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 21, + 43, 43, 57, 2, 2, 2, 2, 2, 43, 43, 43, 57, 2, 2, 61, 61, + 40, 40, 86, 61, 61, 61, 61, 61, 7, 7, 7, 7, 7,166, 27, 27, + 27, 84, 36, 36, 36, 36, 36, 36, 27, 27, 27, 30, 2, 2, 2, 2, + 79, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 76, + 43, 67, 40, 40, 40, 40, 40, 40, 40, 77, 43, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 47, 57, 61, 61,167, 76, 43, 61,167, 75, + 75,168, 59, 59, 59, 73, 43, 43, 43, 69, 47, 43, 43, 43, 61, 61, + 61, 61, 61, 61, 61, 43, 43, 61, 61, 43, 69, 61, 61, 61, 61, 61, + 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 16, 11, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, + 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, + 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, + 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, + 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 16, 7, + 43, 43, 43, 69, 61, 47, 43, 43, 43, 43, 43, 43, 43, 43, 69, 61, + 61, 61, 47, 61, 61, 61, 61, 61, 61, 61, 69, 21, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 56, 43, 43, 43, 43, 43, 67, 40, 40, 40, 40, + 7, 7, 7, 7, 7, 7, 7, 70, 36, 36, 36, 36, 36, 36, 43, 43, + 7, 7, 7, 7, 7, 7, 7,169, 16, 16, 43, 43, 43, 67, 40, 40, + 27, 27, 27, 27, 27, 27,140, 27,170, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27,140, 27, 27, 27, 27, 27, 27, 81, 61, + 61, 61, 61, 61, 61, 25, 41, 41, 0, 0, 29, 21, 21, 21, 23, 21, + 22, 18, 21, 25, 21, 17, 13, 13, 25, 25, 25, 21, 21, 9, 9, 9, + 9, 22, 21, 18, 24, 16, 24, 5, 5, 5, 5, 22, 25, 18, 25, 0, + 23, 23, 26, 21, 24, 26, 7, 20, 25, 1, 26, 24, 26, 25, 15, 15, + 24, 15, 7, 19, 15, 21, 9, 25, 9, 5, 5, 25, 5, 9, 5, 7, + 7, 7, 9, 8, 8, 5, 7, 5, 6, 6, 24, 24, 6, 24, 12, 12, + 6, 5, 9, 21, 25, 9, 26, 12, 11, 11, 9, 6, 5, 21, 17, 17, + 17, 26, 26, 23, 23, 12, 17, 12, 21, 12, 12, 21, 7, 21, 1, 1, + 21, 23, 26, 26, 6, 7, 7, 12, 12, 7, 21, 7, 12, 1, 12, 6, + 6, 12, 12, 26, 7, 26, 26, 7, 21, 1, 1, 12, 12, 10, 10, 10, + 10, 12, 21, 6, 10, 7, 7, 10, 23, 7, 15, 26, 13, 21, 13, 7, + 15, 7, 12, 23, 21, 26, 21, 15, 17, 7, 29, 7, 7, 22, 18, 18, + 14, 14, 14, 7, 17, 21, 7, 6, 11, 12, 5, 6, 8, 8, 8, 24, + 5, 24, 9, 24, 29, 29, 29, 1, 20, 19, 22, 20, 27, 28, 1, 29, + 21, 20, 19, 21, 21, 16, 16, 21, 25, 22, 18, 21, 21, 29, 15, 6, + 18, 6, 12, 11, 9, 26, 26, 9, 26, 5, 5, 26, 14, 9, 5, 14, + 14, 15, 25, 26, 26, 22, 18, 26, 18, 25, 18, 22, 5, 12, 22, 21, + 26, 6, 7, 14, 17, 22, 26, 14, 17, 6, 14, 6, 12, 24, 24, 6, + 26, 15, 6, 21, 11, 21, 24, 9, 23, 26, 10, 21, 6, 10, 4, 4, + 3, 3, 7, 25, 24, 7, 22, 22, 21, 22, 17, 16, 16, 22, 16, 16, + 25, 17, 7, 1, 25, 24, 26, 1, 2, 2, 12, 15, 21, 14, 7, 15, + 12, 17, 13, 12, 13, 15, 26, 10, 10, 1, 13, 23, 23, 15, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 0, 14, 0, + 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 21, 22, 23, + 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 34, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, 0, 0, 0, 39, 40, + 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 4, 5, 6, 7, 0, 8, + 9, 10, 0, 11, 12, 13, 0, 14, 15, 16, 15, 17, 15, 18, 15, 18, + 15, 18, 0, 18, 0, 19, 15, 18, 20, 18, 0, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 32, 0, 0, 0, 0, 0, 0, 33, 0, 0, 34, 0, 0, 35, 0, + 36, 0, 0, 0, 37, 38, 39, 40, 41, 42, 43, 44, 45, 0, 0, 46, + 0, 0, 0, 47, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 49, + 0, 50, 0, 51, 52, 0, 53, 0, 0, 0, 0, 0, 0, 54, 55, 56, + 0, 0, 0, 0, 57, 0, 0, 58, 59, 60, 61, 62, 0, 0, 63, 64, + 0, 0, 0, 65, 0, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 69, + 0, 70, 0, 0, 71, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, + 73, 0, 0, 0, 0, 0, 74, 0, 0, 75, 0, 0, 0, 76, 77, 0, + 78, 61, 0, 79, 80, 0, 0, 81, 82, 83, 0, 0, 0, 84, 0, 85, + 0, 0, 50, 86, 50, 0, 87, 0, 88, 0, 0, 0, 77, 0, 0, 0, + 89, 90, 0, 91, 92, 93, 94, 0, 0, 0, 0, 0, 50, 0, 0, 0, + 0, 95, 96, 0, 0, 0, 0, 97, 98, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 99, 0, 0,100, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,101,102, 0, 0,103, 0, 0, 0, 0, 0, 0,104, 0, 0, 0, + 98, 0, 0, 0, 0, 0, 0,105, 0, 0, 0, 0, 0, 0, 0,106, + 0,107, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, + 8, 0, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, 0, 0, + 14, 15, 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, 0, 0, + 0, 0, 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, 28, 29, + 0, 0, 0, 30, 31, 32, 0, 0, 31, 0, 0, 33, 31, 0, 0, 0, + 31, 34, 0, 0, 0, 0, 0, 35, 36, 0, 0, 0, 0, 0, 0, 37, + 38, 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 41, 0, 42, + 0, 0, 0, 43, 44, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 46, + 47, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 49, 0, 50, 0, 0, + 0, 0, 51, 0, 0, 0, 0, 52, 0, 53, 0, 0, 0, 0, 54, 55, + 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 0, 58, 49, 0, 59, 60, + 0, 0, 61, 0, 0, 0, 62, 63, 0, 0, 0, 64, 0, 65, 66, 67, + 68, 69, 1, 70, 0, 71, 72, 73, 0, 0, 74, 75, 0, 0, 0, 76, + 0, 0, 1, 1, 0, 0, 77, 0, 0, 78, 0, 0, 0, 0, 74, 79, + 0, 80, 0, 0, 0, 0, 0, 75, 81, 0, 82, 0, 49, 0, 1, 75, + 0, 0, 83, 0, 0, 84, 0, 0, 0, 0, 0, 85, 54, 0, 0, 0, + 0, 0, 0, 86, 87, 0, 0, 81, 0, 0, 31, 0, 0, 88, 0, 0, + 0, 0, 89, 0, 0, 0, 0, 47, 0, 0, 57, 0, 0, 0, 0, 90, + 91, 0, 0, 92, 0, 0, 93, 0, 0, 0, 94, 0, 0, 0, 95, 0, + 96, 57, 0, 0, 81, 0, 0, 76, 0, 0, 0, 97, 98, 0, 0, 99, + 100, 0, 0, 0, 0, 0, 0,101, 0, 0,102, 0, 0, 0, 0,103, + 31, 0,104,105,106, 33, 0, 0,107, 0, 0, 0,108, 0, 0, 0, + 0, 0, 0,109, 0, 0,110, 0, 0, 0, 0,111, 85, 0, 0, 0, + 0, 0, 54, 0, 0, 0, 0, 49,112, 0, 0, 0, 0,113, 0, 0, + 114, 0, 0, 0, 0,112, 0, 0, 0, 0, 0,115, 0, 0, 0,116, + 0, 0, 0,117, 0,118, 0, 0, 0, 0,119,120,121, 0,122, 0, + 123, 0, 0, 0,124,125,126, 0, 0, 0,127, 0, 0,128, 0, 0, + 129, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, + 5, 6, 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, + 18, 1, 1, 1, 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, + 25, 26, 27, 28, 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, + 34, 35, 1, 36, 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, + 42, 0, 0, 0, 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, + 21, 0, 0, 47, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, + 52, 1, 1, 1, 53, 21, 43, 54, 55, 21, 35, 1, 0, 0, 0, 56, + 0, 0, 0, 57, 58, 59, 0, 0, 0, 0, 0, 60, 0, 61, 0, 0, + 0, 0, 62, 63, 0, 0, 64, 0, 0, 0, 65, 0, 0, 0, 66, 0, + 0, 0, 67, 0, 0, 0, 68, 0, 0, 0, 69, 0, 0, 70, 71, 0, + 72, 73, 74, 75, 76, 77, 0, 0, 0, 78, 0, 0, 0, 79, 80, 0, + 0, 0, 0, 47, 0, 0, 0, 49, 0, 63, 0, 0, 64, 0, 0, 81, + 0, 0, 82, 0, 0, 0, 83, 0, 0, 19, 84, 0, 63, 0, 0, 0, + 0, 49, 1, 85, 1, 54, 15, 86, 84, 0, 0, 0, 0, 56, 0, 0, + 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, 87, 0, 0, 88, 0, 0, + 87, 0, 0, 0, 0, 79, 0, 0, 89, 9, 12, 4, 90, 8, 91, 47, + 0, 59, 50, 0, 21, 1, 21, 92, 93, 1, 1, 1, 1, 94, 95, 96, + 97, 1, 98, 59, 81, 99,100, 4, 59, 0, 0, 0, 0, 0, 0, 19, + 50, 0, 0, 0, 0, 0, 0, 62, 0, 0,101,102, 0, 0,103, 0, + 0, 1, 1, 50, 0, 0, 0, 38, 0, 64, 0, 0, 0, 0, 0, 63, + 0, 0, 52, 69, 62, 0, 0, 0, 79, 0, 0, 0,104,105, 59, 38, + 81, 0, 0, 0, 0, 0, 0,106, 1, 14, 4, 12, 0, 38, 89, 0, + 0, 0, 0,107, 0, 0,108, 62, 0,109, 0, 0, 0, 1, 0, 0, + 0, 0, 19, 59, 0,110, 14, 54, 0, 0,111, 0, 89, 0, 0, 0, + 62, 63, 0, 0, 63, 0, 88, 0, 0,111, 0, 0, 0, 0,112, 0, + 0, 0, 79, 56, 0, 38, 1, 59, 1, 59, 0, 0, 64, 88, 0, 0, + 113, 0, 0, 0, 56, 0, 0, 0, 0,113, 0, 0, 0, 0, 62, 0, + 0, 0, 0, 80, 0, 62, 0, 0, 0, 0, 57, 0, 88,114, 0, 0, + 8, 91, 0, 0, 1, 89, 0, 0,115, 0, 0, 0, 0, 0, 0,116, + 0,117,118,119,120, 0, 52, 4,121, 49, 23, 0, 0, 0, 38, 50, + 38, 59, 0, 0, 1, 89, 1, 1, 1, 1, 39, 1, 48,104, 89, 0, + 0, 0, 0, 1, 4,121, 0, 0, 0, 1,122, 0, 0, 0, 0, 0, + 230,230,230,230,230,232,220,220,220,220,232,216,220,220,220,220, + 220,202,202,220,220,220,220,202,202,220,220,220, 1, 1, 1, 1, + 1,220,220,220,220,230,230,230,230,240,230,220,220,220,230,230, + 230,220,220, 0,230,230,230,220,220,220,220,230,232,220,220,230, + 233,234,234,233,234,234,233,230, 0, 0, 0,230, 0,220,230,230, + 230,230,220,230,230,230,222,220,230,230,220,220,230,222,228,230, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, + 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, + 28, 29, 30, 31, 32, 33, 34,230,230,220,220,230,220,230,230,220, + 35, 0, 0, 0, 0, 0,230,230,230, 0, 0,230,230, 0,220,230, + 230,220, 0, 0, 0, 36, 0, 0,230,220,230,230,220,220,230,220, + 220,230,220,230,220,230,230, 0, 0,220, 0, 0,230,230, 0,230, + 0,230,230,230,230,230, 0, 0, 0,220,220,220, 0, 0, 0,220, + 230,230, 0,220,230,220,220,220, 27, 28, 29,230, 7, 0, 0, 0, + 0, 9, 0, 0, 0,230,220,230,230, 0, 0, 0, 0, 0,230, 0, + 0, 84, 91, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 9, 0, + 103,103, 9, 0,107,107,107,107,118,118, 9, 0,122,122,122,122, + 220,220, 0, 0, 0,220, 0,220, 0,216, 0, 0, 0,129,130, 0, + 132, 0, 0, 0, 0, 0,130,130,130,130, 0, 0,130, 0,230,230, + 9, 0,230,230, 0, 0,220, 0, 0, 0, 0, 7, 0, 9, 9, 0, + 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220,220, 0, 0, 0, + 230, 0, 0,220,230,220, 0,220, 0, 0, 9, 9, 0, 0, 7, 0, + 230,230,230, 0,230, 0, 1, 1, 1, 0, 0, 0,230,234,214,220, + 202,230,230,230,230,230,232,228,228,220, 0,230,233,220,230,220, + 230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230,220,230, 1, 1, + 0, 0,218,228,232,222,224,224, 0, 8, 8, 0,230, 0,230,230, + 220, 0, 0,230, 0, 0, 26, 0, 0,220, 0,230,230, 1,220, 0, + 0,230,220, 0, 0, 0,220,220, 0, 9, 7, 0, 0, 7, 9, 0, + 0, 0, 9, 7, 9, 9, 0, 0, 6, 6, 0, 0, 0, 0, 1, 0, + 0,216,216, 1, 1, 1, 0, 0, 0,226,216,216,216,216,216, 0, + 220,220,220, 0,230,230, 7, 0, 16, 17, 17, 33, 17, 49, 17, 17, + 84, 97,135,145, 26, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17,177, 0, 1, 2, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, + 3, 3, 5, 3, 3, 3, 3, 3, 6, 7, 8, 3, 3, 3, 3, 3, + 9, 10, 11, 12, 13, 3, 3, 3, 3, 3, 3, 3, 3, 14, 3, 15, + 3, 3, 3, 3, 3, 3, 16, 17, 18, 19, 20, 21, 3, 3, 3, 22, + 23, 3, 3, 3, 3, 3, 3, 3, 24, 3, 3, 3, 3, 3, 3, 3, + 3, 25, 3, 3, 26, 27, 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, + 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 4, 0, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, + 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, + 9, 0, 0, 0, 0, 0, 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, + 0, 10, 11, 12, 13, 0, 0, 14, 15, 16, 6, 0, 17, 18, 19, 19, + 19, 20, 21, 22, 23, 24, 19, 25, 0, 26, 27, 19, 19, 28, 29, 30, + 0, 31, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 19, 28, 0, + 32, 33, 9, 34, 35, 19, 0, 0, 36, 37, 38, 39, 40, 19, 0, 41, + 42, 43, 44, 31, 0, 1, 45, 42, 0, 0, 0, 0, 0, 32, 14, 14, + 0, 0, 0, 0, 14, 0, 0, 46, 47, 47, 47, 47, 48, 49, 47, 47, + 47, 47, 50, 51, 52, 53, 43, 21, 0, 0, 0, 0, 0, 0, 0, 54, + 6, 55, 0, 14, 19, 1, 0, 0, 0, 19, 56, 31, 0, 0, 0, 0, + 0, 0, 0, 57, 14, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, + 0, 0, 0, 58, 59, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 2, 3, 0, 4, 5, 0, 0, 6, 0, 0, 0, 7, 0, 0, + 0, 1, 1, 0, 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, 8, 9, + 10, 11, 12, 0, 0, 0, 13, 0, 0, 0, 0, 14, 15, 16, 17, 0, + 0, 0, 1, 0, 0, 18, 19, 0, 0, 0, 20, 0, 0, 0, 1, 1, + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 8, 21, 9, 0, 0, + 22, 0, 0, 0, 0, 1, 0, 23, 24, 25, 0, 0, 26, 0, 0, 0, + 8, 21, 27, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 28, 29, 30, + 0, 31, 32, 20, 1, 1, 0, 0, 0, 8, 21, 9, 1, 4, 5, 0, + 0, 0, 33, 9, 0, 1, 1, 1, 0, 8, 21, 21, 21, 21, 34, 1, + 35, 21, 21, 21, 9, 36, 0, 0, 37, 38, 1, 0, 39, 0, 0, 0, + 1, 0, 1, 0, 0, 0, 0, 8, 21, 9, 1, 0, 0, 0, 40, 0, + 8, 21, 21, 21, 21, 21, 21, 21, 21, 9, 0, 1, 1, 1, 1, 8, + 21, 21, 21, 9, 0, 0, 0, 41, 0, 42, 43, 0, 0, 0, 1, 44, + 0, 0, 0, 45, 8, 9, 1, 0, 1, 0, 1, 1, 8, 21, 21, 9, + 0, 4, 5, 8, 9, 1, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, + 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 9, 10, 11, 11, + 11, 11, 11, 12, 12, 12, 12, 13, 14, 15, 16, 17, 18, 12, 19, 12, + 20, 12, 12, 12, 12, 21, 22, 22, 22, 23, 12, 12, 12, 12, 24, 25, + 12, 12, 26, 27, 28, 29, 30, 31, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 32, 12, 33, 7, 7, 34, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 35, 0, 0, 1, 2, 2, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 33, + 33, 34, 35, 35, 35, 35, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 2, 2, 51, 51, 52, 53, 54, 55, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 56, 56, 56, 56, + 56, 56, 58, 59, 60, 61, 56, 62, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 56, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 71, 62, 62, 62, 62, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 73, 74, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 62, 62, 62, 62, + 88, 89, 89, 89, 90, 89, 91, 92, 93, 94, 95, 95, 96, 97, 87, 98, + 99,100,101,102,103, 87,104,104,104, 87,105,106,107,108,109,110, + 111,112,113,114,115, 87, 89,116,117,118,119,120,121,122,123,124, + 125, 87,126,127, 87,128,129,130,131, 87,132,133,134,135,136,137, + 87, 87,138,139,140,141, 87,142, 87,143,144,144,144,144,144,144, + 144,144,144,144,144, 87, 87, 87, 87, 87,145,145,145,145,145,145, + 145,145,145, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87,146,146,146,146,146, 87, 87, 87,147,147,147,147,148,149, + 150,150, 87, 87, 87, 87,151,151,152,153,154,154,154,154,154,154, + 154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154, + 155,155,155,155,154, 87, 87, 87, 87, 87,156,157,158,159,159,159, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87,160,161, 87, 87, 87, 87, 87, 87, 56, 56,162,163, 51, 56, + 56, 87, 56, 56, 56, 56, 56, 56, 56, 56,164,164,164,164,164,164, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87,165, 87,166, 87, 87,167, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87,168,168,169, 87, 87, 87, + 87, 87, 56, 56, 56, 87, 89, 89, 87, 87, 56, 56, 56, 56,170, 87, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 87, 87, 87, 87, 87, 87, 87, 87, 62, 62, 62, 62, 62, 62, + 62, 62, 87, 87, 87, 87, 87, 87, 87, 87, 62, 62, 62, 62, 62, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 62, 62, 62, 62, 62, 62, + 62, 87, 87, 87, 87, 87, 87, 87, 87, 87, 56, 87,171,171, 0, 1, + 2, 2, 0, 1, 2, 2, 2, 3, 4, 5, 0, 0, 0, 0, 1, 2, + 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 6, 0, 0, 7, 0, 8, 8, 8, 8, 8, 8, + 8, 9, 10, 11, 11, 11, 11, 11, 12, 11, 13, 13, 13, 13, 13, 13, + 13, 13, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 15, 16, 16, + 16, 16, 16, 17, 18, 18, 18, 18, 18, 18, 19, 20, 21, 21, 22, 23, + 21, 24, 21, 21, 21, 21, 21, 25, 21, 21, 26, 26, 26, 26, 26, 21, + 21, 21, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, + 26, 26, 21, 21, 21, 21, 21, 21, 31, 21, 32, 32, 32, 32, 32, 33, + 34, 32, 35, 35, 35, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36, + 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 38, + 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, + 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, + 42, 42, 43, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 45, 44, 44, + 44, 44, 46, 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 48, 47, 47, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 50, 50, 50, 50, 50, 51, 52, 52, 52, 52, 52, 52, + 52, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, + 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, 57, 57, 57, 57, + 58, 57, 59, 59, 60, 61, 62, 62, 63, 63, 64, 64, 64, 64, 64, 64, + 64, 64, 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 55, 55, 55, + 55, 55, 67, 67, 67, 67, 67, 68, 68, 68, 69, 69, 69, 69, 69, 69, + 64, 64, 70, 70, 71, 71, 71, 71, 71, 71, 71, 71, 71, 8, 8, 8, + 8, 8, 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 74, 74, + 74, 74, 75, 75, 75, 75, 75, 76, 76, 76, 13, 50, 50, 50, 73, 77, + 78, 79, 4, 4, 80, 4, 4, 81, 82, 83, 4, 4, 4, 84, 8, 8, + 8, 8, 11, 11, 11, 11, 11, 11, 11, 11, 85, 0, 0, 0, 0, 0, + 0, 86, 0, 4, 0, 0, 0, 8, 8, 8, 0, 0, 87, 88, 89, 0, + 4, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 90, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, + 4, 4, 92, 92, 92, 92, 92, 92, 92, 92, 50, 50, 50, 93, 93, 93, + 93, 93, 53, 53, 53, 53, 53, 53, 13, 13, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 0, 95, 0, 96, 97, 98, 99, + 99, 99, 99,100,101,102,102,102,102,103,104,104,104,105, 52, 52, + 52, 52, 52, 0,104,104, 0, 0, 0,102, 52, 52, 0, 0, 0, 0, + 52,106, 0, 0, 0, 0, 0,102,102,107,102,102,102,102,102,108, + 0, 0, 94, 94, 94, 94, 0, 0, 0, 0,109,109,109,109,109,109, + 109,109,109,109,109,109,109,110,110,110,111,111,111,111,111,111, + 111,111,111,111,111,111, 13, 13, 13, 13, 13, 13,112,112,112,112, + 112,112, 0, 0,113, 4, 4, 4, 4, 4,114, 4, 4, 4, 4, 4, + 4, 4,115,115,115, 0,116,116,116,116,117,117,117,117,117,117, + 32, 32,118,118,119,120,120,120, 52, 52,121,121,121,121,122,121, + 49, 49,123,123,123,123,123,123, 49, 49,124,124,124,124,124,124, + 125,125, 53, 53, 53, 4, 4,126,127, 54, 54, 54, 54, 54,125,125, + 125,125,128,128,128,128,128,128,128,128, 4,129, 18, 18, 18, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,130, 0, 21, + 21, 21, 8, 0,131, 0, 0, 0, 0, 21, 21, 21, 21, 21, 21, 21, + 21,132, 0, 0, 1, 2, 1, 2,133,101,102,134, 52, 52, 52, 52, + 0, 0,135,135,135,135,135,135,135,135, 0, 0, 0, 0, 11, 11, + 11, 11, 11, 0, 11, 11, 11, 0, 0,136,137,137,138,138,138,138, + 139, 0,140,140,140,141,141,142,142,142,143,143,144,144,144,144, + 144,144,145,145,145,145,145,146,146,146,147,147,147,148,148,148, + 148,148,149,149,149,150,150,150,150,150,151,151,151,151,151,151, + 151,151,152,152,152,152,153,153,154,154,155,155,155,155,155,155, + 156,156,157,157,158,158,158,158,158,158,159,159,160,160,160,160, + 160,160,161,161,161,161,161,161,162,162,163,163,163,163,164,164, + 164,164,165,165,165,165,166,166,167,167,168,168,168,168,168,168, + 168,168,169,169,169,169,169,169,169,169,170,170,170,170,170,170, + 170,170,171,171,171,171,171,171,171,171,172,172,172,172,172,172, + 172,172,173,173,173,174,174,174,174,174,175,175,175,175,175,175, + 176,176,177,177,177,177,177,177,177,177,178,178,178,178,178,179, + 179,179,180,180,180,180,180,181,181,181,182,182,182,182,182,182, + 183, 43,184,184,184,184,184,184,184,184,185,185,185,186,186,186, + 186,186,187,187,187,188,187,187,187,187,189,189,189,189,189,189, + 189,189,190,190,190,190,190,190,190,190,191,191,191,191,191,191, + 191,191,192,192,192,192,192,192, 66, 66,193,193,193,193,193,193, + 193,193,194,194,194,194,194,194,194,194,195,195,195,195,195,195, + 195,195,196,196,196,196,196,196,196,196,197,197,197,197,197,197, + 197,197,198,198,198,198,198,198,198,198,199,199,199,199,199,200, + 200,200,200,200,200,200,201,201,201,201,202,202,202,202,202,202, + 202,203,203,203,203,203,203,203,203,203,204,204,204,204,204,204, + 205,205,205,205,205,205,205,205,205,205,206,206,206,206,206,206, + 206,206,110,110,110,110, 39, 39, 39, 39,207,207,207,207,207,207, + 207,207,208,208,208,208,208,208,208,208,209,209,209,209,209,209, + 209,209,112,112,112,112,112,112,112,112,112,112,112,112,210,210, + 210,210,211,211,211,211,211,211,211,211,212,212,212,212,212,212, + 212,212,213,213,213,213,213,213,213,213,214,214,214,214,214,214, + 214,214,214,214,214,214,214,214,215, 94,216,216,216,216,216,216, + 216,216,217,217,217,217,217,217,217,217,218, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 219,220,220,220,220,220,220,220,220,220,221,221,221,221,221,221, + 221,221,221,221, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 222,223,224, 0,225, 0, 0, 0, 0, 0,226,226,226,226,226,226, + 226,226, 91, 91, 91, 91, 91, 91, 91, 91,227,227,227,227,227,227, + 227,227,228,228,228,228,228,228,228,228,229,229,229,229,229,229, + 229,229,230,230,230,230,230,230,230,230,231, 0, 0, 0, 0, 0, + 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 1, 2, + 2, 2, 2, 2, 3, 0, 0, 0, 4, 0, 2, 2, 2, 2, 2, 3, + 2, 2, 2, 2, 5, 0, 2, 5, 6, 0, 7, 7, 7, 7, 8, 9, + 8, 10, 8, 11, 8, 8, 8, 8, 8, 8, 12, 13, 13, 13, 14, 14, + 14, 14, 14, 15, 14, 14, 16, 17, 17, 17, 17, 17, 17, 17, 18, 19, + 19, 19, 19, 19, 19, 19, 20, 21, 20, 22, 20, 20, 23, 23, 20, 20, + 20, 20, 22, 20, 24, 7, 7, 25, 20, 20, 26, 20, 20, 20, 20, 20, + 20, 21, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, + 30, 30, 31, 31, 31, 31, 32, 20, 20, 20, 33, 33, 33, 33, 34, 35, + 33, 33, 33, 36, 33, 33, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, + 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, + 43, 43, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, + 46, 47, 48, 48, 48, 48, 49, 49, 49, 49, 49, 50, 51, 49, 52, 52, + 52, 52, 53, 53, 53, 53, 53, 53, 54, 53, 55, 55, 55, 55, 56, 56, + 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, + 60, 60, 60, 60, 61, 62, 63, 63, 63, 63, 64, 64, 64, 64, 64, 65, + 0, 0, 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 70, + 71, 71, 71, 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, 73, 74, 74, + 74, 74, 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, + 78, 78, 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 7, + 7, 7, 83, 7, 84, 85, 0, 84, 86, 0, 2, 87, 88, 2, 2, 2, + 2, 89, 90, 87, 91, 2, 2, 2, 92, 2, 2, 2, 2, 93, 0, 0, + 0, 86, 1, 0, 0, 94, 0, 95, 96, 0, 4, 0, 0, 0, 0, 0, + 0, 4, 97, 97, 97, 97, 98, 98, 98, 98, 13, 13, 13, 13, 99, 99, + 99, 99,100,100,100,100, 0,101, 0, 0,102,100,103,104, 0, 0, + 100, 0,105,106,106,106,106,106,106,106,106,106,107,105,108,109, + 109,109,109,109,109,109,109,109,110,108,111,111,111,111,112, 55, + 55, 55, 55, 55, 55,113,109,109,109,110,109,109, 0, 0,114,114, + 114,114,115,115,115,115,116,116,116,116,117,117,117,117, 96, 2, + 2, 2, 2, 2, 94, 2,118,118,118,118,119,119,119,119,120,120, + 120,120,121,121,121,121,121,121,121,122,123,123,123,123,124,124, + 124,124,124,124,124,125,126,126,126,126,127,127,127,127,128,128, + 128,128, 2, 2, 3, 2, 2,129,130, 0,131,131,131,131,132, 17, + 17, 18, 20, 20, 20,133, 7, 7, 7,134, 20, 20, 20, 23, 0,135, + 109,109,109,109,109,136,137,137,137,137, 0, 0, 0,138,139,139, + 139,139,140,140,140,140, 84, 0, 0, 0,141,141,141,141,142,142, + 142,142,143,143,143,143,144,144,144,144,145,145,145,145,146,146, + 146,146,147,147,147,147,148,148,148,148,149,149,149,149,150,150, + 150,150,151,151,151,151,152,152,152,152,153,153,153,153,154,154, + 154,154,155,155,155,155,156,156,156,156,157,157,157,157,158,158, + 158,158,159,159,159,159,160,160,160,160,161,161,161,161,162,162, + 162,162,163,163,163,163,164,164,164,164,165,165,165,165,166,166, + 166,166,167,167,167,167,168,168,168,168,169,169,169,169,170,170, + 170,170,171,171,171,171,172,172,172,172,173,173,173,173,174,174, + 174,174,175,175,175,175,176,176,176,176,177,177,177,177,178,178, + 178,178,179,179,179,179,180,180,180,180,181,181,181,181,182,182, + 182,182,183,183,183,183,184, 45, 45, 45,185,185,185,185,186,186, + 186,186,187,187,187,187,188,188,188,188,188,188,189,188,190,190, + 190,190,191,191,191,191,192,192,192,192,193,193,193,193,194,194, + 194,194,195,195,195,195,196,196,196,196,197,197,197,197,198,198, + 198,198,199,199,199,199,200,200,200,200,201,201,201,201,202,202, + 202,202,203,203,203,203,204,204,204,204,205,205,205,205,206,206, + 206,206,207,207,207,207,208,208,208,208,209,209,209,209,210,210, + 210,210,211,211,211,211,212,212,212,212,213,213,213,213,214,214, + 214,214,215,215,215,215,216,217,217,217,218,218,218,218,217,217, + 217,217,219,106,106,106,106,109,109,109,220,220,220,220,221,221, + 221,221, 0,222, 86, 0, 0, 0,222, 7, 82,138, 7, 0, 0, 0, + 223, 86,224,224,224,224,225,225,225,225,226,226,226,226,227,227, + 227,227,228,228,228,228,229, 0, 0, 0, 0, 0, 0, 0, 0, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 0, 19, 0, + 0, 0, 0, 0, 26, 26, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, + 9, 9, 9, 9, 0, 9, 9, 0, 9, 0, 9, 9, 55, 55, 55, 55, + 55, 55, 6, 6, 6, 6, 6, 1, 1, 6, 6, 4, 4, 4, 4, 4, + 4, 4, 4, 14, 14, 14, 14, 14, 14, 14, 3, 3, 3, 3, 3, 0, + 3, 3, 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 1, 1, 1, + 3, 3, 1, 3, 3, 3, 37, 37, 37, 37, 38, 38, 38, 38, 64, 64, + 64, 64, 90, 90, 90, 90, 95, 95, 95, 95, 3, 3, 0, 3, 7, 7, + 7, 7, 7, 1, 1, 1, 1, 7, 7, 7, 0, 0, 7, 7, 5, 5, + 5, 5, 11, 11, 11, 11, 10, 10, 10, 10, 21, 21, 21, 21, 22, 22, + 22, 22, 23, 23, 23, 23, 16, 16, 16, 16, 20, 20, 20, 20, 36, 36, + 36, 36, 24, 24, 24, 24, 24, 24, 24, 0, 18, 18, 18, 18, 25, 25, + 25, 25, 25, 0, 0, 0, 0, 25, 25, 25, 33, 33, 33, 33, 8, 8, + 8, 8, 8, 8, 8, 0, 12, 12, 12, 12, 30, 30, 30, 30, 29, 29, + 29, 29, 28, 28, 28, 28, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, + 35, 0, 0, 0, 35, 35, 45, 45, 45, 45, 44, 44, 44, 44, 44, 0, + 0, 0, 43, 43, 43, 43, 46, 46, 46, 46, 31, 31, 31, 31, 32, 32, + 0, 0, 32, 0, 32, 32, 32, 32, 32, 32, 48, 48, 48, 48, 52, 52, + 52, 52, 58, 58, 58, 58, 54, 54, 54, 54, 91, 91, 91, 91, 62, 62, + 62, 62, 76, 76, 76, 76, 93, 93, 93, 93, 70, 70, 70, 70, 73, 73, + 73, 73, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, + 0, 0, 1, 1, 0, 0, 19, 19, 9, 9, 9, 9, 9, 6, 19, 9, + 9, 9, 9, 9, 19, 19, 9, 9, 9, 19, 6, 19, 19, 19, 19, 19, + 19, 9, 0, 0, 0, 19, 0, 0, 9, 0, 0, 0, 19, 19, 27, 27, + 27, 27, 56, 56, 56, 56, 61, 61, 61, 61, 13, 13, 13, 13, 0, 13, + 0, 13, 0, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 0, 15, + 15, 15, 15, 15, 15, 15, 15, 1, 1, 0, 0, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 0, 26, 26, 26, 26, 26, 12, 12, 12, 12, 12, + 12, 0, 39, 39, 39, 39, 86, 86, 86, 86, 77, 77, 77, 77, 79, 79, + 79, 79, 60, 60, 60, 60, 65, 65, 65, 65, 75, 75, 75, 75, 69, 69, + 69, 69, 69, 69, 0, 69, 74, 74, 74, 74, 84, 84, 84, 84, 84, 84, + 84, 0, 68, 68, 68, 68, 92, 92, 92, 92, 87, 87, 87, 87, 19, 9, + 19, 19, 19, 19, 0, 0, 2, 2, 2, 2, 19, 19, 19, 4, 3, 3, + 0, 0, 1, 1, 6, 6, 0, 0, 17, 17, 17, 17, 0, 0, 49, 49, + 49, 49, 0, 1, 1, 1, 71, 71, 71, 71, 67, 67, 67, 67, 42, 42, + 42, 42, 41, 41, 41, 41,118,118,118,118, 53, 53, 53, 53, 59, 59, + 59, 59, 40, 40, 40, 40, 51, 51, 51, 51, 50, 50, 50, 50,135,135, + 135,135,106,106,106,106,104,104,104,104,110,110,110,110, 47, 47, + 47, 47, 81, 81, 81, 81,120,120,120,120,116,116,116,116,128,128, + 128,128, 66, 66, 66, 66, 72, 72, 72, 72, 98, 98, 98, 98, 97, 97, + 97, 97, 57, 57, 57, 57, 88, 88, 88, 88,117,117,117,117,112,112, + 112,112, 78, 78, 78, 78, 83, 83, 83, 83, 82, 82, 82, 82,122,122, + 122,122, 89, 89, 89, 89,130,130,130,130,144,144,144,144,156,156, + 156,156,147,147,147,147,148,148,148,148,153,153,153,153,149,149, + 149,149, 94, 94, 94, 94, 85, 85, 85, 85,101,101,101,101, 96, 96, + 96, 96,111,111,111,111,100,100,100,100,100, 36, 36, 36,108,108, + 108,108,129,129,129,129,109,109,109,109,107,107,107,107,107,107, + 107, 1,137,137,137,137,124,124,124,124,123,123,123,123,114,114, + 114,114,102,102,102,102,126,126,126,126,142,142,142,142,125,125, + 125,125,154,154,154,154,150,150,150,150,141,141,141,141,140,140, + 140,140,121,121,121,121,133,133,133,133,134,134,134,134,138,138, + 138,138,143,143,143,143,145,145,145,145, 63, 63, 63, 63, 80, 80, + 80, 80,127,127,127,127,115,115,115,115,103,103,103,103,119,119, + 119,119,146,146,146,146, 99, 99, 99, 99,136,139, 0, 0,155,155, + 155,155,136,136,136,136, 17, 15, 15, 15,139,139,139,139,105,105, + 105,105, 0, 0, 0, 1, 0, 0, 1, 1,131,131,131,131,151,151, + 151,151,152,152,152,152,113,113,113,113,132,132,132,132, 15, 0, + 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, + 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 16, 17, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 18, 19, 20, 9, 21, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 22, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 23, 24, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, 0, 0, + 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, 36, 37, 38, 39, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, 0, 0, + 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, + 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, + 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, 0, 0, + 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102, + 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0,107, 0, 0, 0, + 108, 0,109, 0,110, 0,111,112,113, 0,114, 0, 0, 0,115, 0, + 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,117, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,118,119,120,121, 0,122,123,124,125,126, 0,127, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151,152,153,154,155,156,157, 0, 0, + 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,162,163, 0, 0, 0, 0, 0, + 0, 0,164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,166, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,167, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,169,170, 0, 0, 0, 0,171,172, 0, 0, 0, + 173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188, + 189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204, + 205,206, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, +}; +static const uint16_t +_hb_ucd_u16[4848] = +{ + 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, + 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, + 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, + 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, + 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, + 13, 13, 13, 35, 9, 42, 11, 11, 43, 44, 32, 45, 46, 47, 47, 48, + 49, 50, 47, 47, 51, 32, 52, 53, 47, 47, 47, 47, 47, 54, 55, 56, + 57, 58, 47, 32, 59, 47, 47, 47, 47, 47, 60, 53, 61, 47, 62, 63, + 47, 64, 65, 66, 47, 67, 47, 47, 47, 47, 47, 47, 47, 68, 69, 32, + 70, 47, 47, 71, 72, 73, 74, 75, 76, 47, 47, 77, 78, 79, 80, 81, + 82, 47, 47, 83, 84, 85, 86, 87, 82, 47, 47, 77, 88, 47, 80, 89, + 90, 47, 47, 91, 92, 93, 80, 94, 95, 47, 47, 96, 97, 98, 99, 100, + 101, 47, 47, 102, 103, 104, 80, 105, 106, 47, 47, 91, 107, 108, 80, 109, + 110, 47, 47, 111, 112, 113, 80, 114, 90, 47, 47, 47, 115, 116, 99, 117, + 47, 47, 47, 118, 119, 120, 66, 66, 47, 47, 47, 121, 122, 123, 47, 47, + 124, 125, 126, 127, 47, 47, 47, 128, 129, 32, 32, 130, 131, 132, 66, 66, + 47, 47, 133, 134, 120, 135, 136, 137, 138, 139, 9, 9, 9, 11, 11, 140, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 141, 142, 143, + 47, 144, 9, 9, 9, 9, 9, 145, 146, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 147, 47, 148, 149, 47, 47, 47, 47, 150, 151, + 47, 152, 47, 153, 47, 152, 47, 152, 47, 47, 47, 154, 155, 156, 157, 143, + 158, 157, 47, 47, 159, 47, 47, 47, 160, 47, 161, 47, 47, 47, 47, 47, + 47, 47, 162, 163, 164, 47, 47, 47, 47, 47, 47, 47, 47, 165, 144, 144, + 47, 166, 47, 47, 47, 167, 168, 169, 157, 157, 170, 171, 32, 32, 32, 32, + 172, 47, 47, 173, 174, 120, 175, 176, 177, 47, 178, 61, 47, 47, 179, 180, + 47, 47, 181, 182, 183, 61, 47, 184, 11, 9, 9, 9, 66, 185, 186, 187, + 11, 11, 188, 27, 27, 27, 189, 190, 11, 191, 27, 27, 32, 32, 32, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 192, 13, 13, 13, 13, 13, 13, + 193, 193, 193, 193, 193, 194, 193, 11, 195, 195, 195, 196, 197, 198, 198, 197, + 199, 200, 201, 202, 203, 204, 205, 206, 207, 27, 208, 208, 208, 209, 210, 32, + 211, 212, 213, 214, 215, 143, 216, 216, 217, 218, 219, 144, 220, 221, 144, 222, + 223, 223, 223, 223, 223, 223, 223, 223, 224, 144, 225, 144, 144, 144, 144, 226, + 144, 227, 223, 228, 144, 229, 230, 144, 144, 144, 144, 144, 144, 144, 143, 143, + 143, 231, 144, 144, 144, 144, 232, 143, 144, 144, 144, 144, 144, 144, 144, 144, + 144, 144, 144, 233, 234, 144, 144, 235, 144, 144, 144, 144, 144, 144, 236, 144, + 144, 144, 144, 144, 144, 144, 237, 238, 143, 239, 144, 144, 240, 223, 241, 223, + 242, 243, 223, 223, 223, 244, 223, 245, 144, 144, 144, 223, 246, 144, 144, 144, + 9, 9, 9, 11, 11, 11, 247, 248, 13, 13, 13, 13, 13, 13, 249, 250, + 11, 11, 11, 47, 47, 47, 251, 252, 47, 47, 47, 47, 47, 47, 32, 32, + 253, 254, 255, 256, 257, 258, 66, 66, 259, 260, 261, 262, 263, 47, 47, 47, + 47, 264, 146, 47, 47, 47, 47, 265, 47, 266, 47, 47, 144, 144, 144, 47, + 144, 144, 267, 144, 268, 269, 144, 144, 267, 144, 144, 269, 144, 144, 144, 144, + 47, 47, 47, 47, 144, 144, 144, 144, 47, 270, 47, 47, 47, 47, 47, 47, + 47, 144, 144, 144, 144, 47, 47, 184, 271, 47, 61, 47, 13, 13, 272, 273, + 13, 274, 47, 47, 47, 47, 275, 276, 31, 277, 278, 279, 13, 13, 13, 280, + 281, 282, 283, 284, 285, 11, 11, 286, 287, 47, 288, 289, 47, 47, 47, 290, + 291, 47, 47, 292, 293, 157, 32, 294, 61, 47, 295, 47, 296, 297, 47, 47, + 70, 47, 47, 298, 299, 300, 301, 61, 47, 47, 302, 303, 304, 305, 47, 306, + 47, 47, 47, 307, 58, 308, 309, 310, 47, 47, 47, 11, 11, 311, 312, 11, + 11, 11, 11, 11, 47, 47, 313, 157, 314, 314, 314, 314, 314, 314, 314, 314, + 315, 315, 315, 315, 315, 315, 315, 315, 11, 316, 317, 47, 47, 47, 47, 47, + 47, 47, 47, 318, 31, 319, 47, 47, 47, 47, 47, 320, 321, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 322, 32, 323, 32, 324, 325, 326, 327, 47, + 47, 47, 47, 47, 47, 47, 47, 328, 329, 2, 3, 4, 5, 330, 331, 332, + 47, 333, 47, 47, 47, 47, 334, 335, 336, 143, 143, 337, 216, 216, 216, 338, + 339, 144, 144, 144, 144, 144, 144, 340, 341, 341, 341, 341, 341, 341, 341, 341, + 47, 47, 47, 47, 47, 47, 342, 143, 47, 47, 343, 47, 344, 47, 47, 60, + 47, 345, 47, 47, 47, 346, 216, 216, 9, 9, 145, 11, 11, 47, 47, 47, + 47, 47, 157, 9, 9, 145, 11, 11, 47, 47, 47, 47, 47, 47, 345, 66, + 47, 47, 47, 47, 47, 347, 47, 348, 47, 47, 349, 143, 143, 143, 47, 350, + 47, 351, 47, 345, 66, 66, 66, 66, 47, 47, 47, 352, 143, 143, 143, 143, + 353, 47, 47, 354, 143, 66, 47, 355, 47, 356, 143, 143, 357, 47, 358, 66, + 47, 47, 47, 359, 47, 360, 47, 360, 47, 359, 142, 143, 143, 143, 143, 143, + 9, 9, 9, 9, 11, 11, 11, 361, 47, 47, 362, 157, 157, 157, 157, 157, + 143, 143, 143, 143, 143, 143, 143, 143, 47, 47, 363, 47, 47, 47, 47, 47, + 47, 356, 364, 47, 60, 365, 66, 66, 47, 47, 47, 47, 366, 143, 47, 47, + 367, 47, 47, 354, 368, 369, 370, 371, 177, 47, 47, 372, 373, 47, 47, 157, + 95, 47, 374, 375, 376, 47, 47, 377, 177, 47, 47, 378, 379, 380, 381, 143, + 47, 47, 382, 383, 32, 32, 32, 32, 47, 47, 359, 47, 47, 384, 169, 157, + 90, 47, 47, 111, 385, 386, 387, 32, 47, 47, 47, 388, 389, 390, 47, 47, + 47, 47, 47, 391, 392, 157, 157, 157, 47, 47, 393, 394, 395, 396, 32, 32, + 47, 47, 47, 397, 398, 157, 66, 66, 47, 47, 399, 400, 157, 157, 157, 157, + 47, 141, 401, 402, 144, 144, 144, 144, 47, 47, 382, 403, 66, 66, 66, 66, + 9, 9, 9, 9, 11, 11, 126, 404, 47, 47, 47, 405, 406, 157, 157, 157, + 47, 47, 47, 47, 47, 407, 408, 409, 410, 47, 47, 411, 412, 413, 47, 47, + 414, 415, 66, 66, 47, 47, 47, 47, 47, 47, 393, 416, 417, 126, 143, 418, + 47, 152, 419, 420, 32, 32, 32, 32, 47, 47, 47, 353, 421, 157, 47, 47, + 422, 423, 157, 157, 157, 157, 157, 157, 47, 47, 47, 47, 47, 47, 47, 424, + 47, 47, 47, 47, 143, 425, 426, 427, 216, 216, 216, 216, 216, 216, 216, 66, + 47, 47, 47, 205, 205, 205, 205, 205, 47, 47, 47, 47, 47, 47, 300, 66, + 47, 47, 47, 47, 47, 47, 47, 428, 47, 47, 47, 429, 430, 431, 432, 47, + 9, 9, 9, 9, 9, 9, 11, 11, 143, 433, 66, 66, 66, 66, 66, 66, + 47, 47, 47, 47, 384, 434, 409, 409, 435, 436, 27, 27, 27, 27, 437, 409, + 47, 438, 205, 205, 205, 205, 205, 205, 144, 144, 144, 144, 144, 144, 439, 440, + 441, 144, 442, 144, 144, 144, 144, 144, 144, 144, 144, 144, 443, 144, 144, 144, + 9, 444, 11, 445, 446, 11, 193, 9, 447, 448, 9, 449, 11, 9, 444, 11, + 445, 446, 11, 193, 9, 447, 448, 9, 449, 11, 9, 444, 11, 445, 446, 11, + 193, 9, 447, 448, 9, 449, 11, 9, 444, 11, 193, 9, 450, 451, 452, 453, + 11, 454, 9, 455, 456, 457, 458, 11, 459, 9, 460, 11, 461, 157, 157, 157, + 32, 32, 32, 462, 32, 32, 463, 464, 465, 466, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 47, 467, 468, 144, 144, 144, + 47, 47, 47, 47, 47, 47, 469, 470, 47, 47, 47, 47, 349, 32, 32, 32, + 9, 9, 447, 11, 471, 300, 66, 66, 143, 143, 472, 473, 143, 143, 143, 143, + 143, 143, 474, 143, 143, 143, 143, 143, 47, 47, 47, 47, 47, 47, 47, 223, + 475, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 476, + 144, 144, 144, 144, 144, 144, 144, 157, 205, 205, 205, 205, 205, 205, 205, 205, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, + 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0, + 1124,1125,1126,1127,1131,1133, 0,1147,1154,1155,1156,1161,1187,1188,1189,1193, + 0,1219,1226,1227,1228,1229,1233, 0, 0,1267,1268,1269,1273,1298, 0,1303, + 943,1128, 944,1129, 954,1139, 958,1143, 959,1144, 960,1145, 961,1146, 964,1149, + 0, 0, 973,1158, 974,1159, 975,1160, 983,1168, 978,1163, 988,1173, 990,1175, + 991,1176, 993,1178, 994,1179, 0, 0,1004,1190,1005,1191,1006,1192,1014,1199, + 1007, 0, 0, 0,1016,1201,1020,1206, 0,1022,1208,1025,1211,1023,1209, 0, + 0, 0, 0,1032,1218,1037,1223,1035,1221, 0, 0, 0,1044,1230,1045,1231, + 1049,1235, 0, 0,1058,1244,1064,1250,1060,1246,1066,1252,1067,1253,1072,1258, + 1069,1255,1077,1264,1074,1261, 0, 0,1083,1270,1084,1271,1085,1272,1088,1275, + 1089,1276,1096,1283,1103,1290,1111,1299,1115,1118,1307,1120,1309,1121,1310, 0, + 1053,1239, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1093, + 1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 949,1134,1010, + 1195,1050,1236,1090,1277,1341,1368,1340,1367,1342,1369,1339,1366, 0,1320,1347, + 1418,1419,1323,1350, 0, 0, 992,1177,1018,1204,1055,1241,1416,1417,1415,1424, + 1202, 0, 0, 0, 987,1172, 0, 0,1031,1217,1321,1348,1322,1349,1338,1365, + 950,1135, 951,1136, 979,1164, 980,1165,1011,1196,1012,1197,1051,1237,1052,1238, + 1061,1247,1062,1248,1091,1278,1092,1279,1071,1257,1076,1263, 0, 0, 997,1182, + 0, 0, 0, 0, 0, 0, 945,1130, 982,1167,1337,1364,1335,1362,1046,1232, + 1422,1423,1113,1301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 0, 10,1425, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0,1314,1427, 5,1434,1438,1443, 0,1450, 0,1455,1461, + 1514, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1446,1458,1468,1476,1480,1486, + 1517, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1489,1503,1494,1500,1508, 0, + 0, 0, 0,1520,1521, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1526,1528, 0,1525, 0, 0, 0,1522, 0, 0, 0, 0,1536,1532,1539, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1534, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1556, 0, 0, 0, 0, 0, 0, + 1548,1550, 0,1547, 0, 0, 0,1567, 0, 0, 0, 0,1558,1554,1561, 0, + 0, 0, 0, 0, 0, 0,1568,1569, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1529,1551, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1523,1545,1524,1546, 0, 0,1527,1549, 0, 0,1570,1571,1530,1552,1531,1553, + 0, 0,1533,1555,1535,1557,1537,1559, 0, 0,1572,1573,1544,1566,1538,1560, + 1540,1562,1541,1563,1542,1564, 0, 0,1543,1565, 0, 0, 0, 0, 0, 0, + 0, 0,1606,1607,1609,1608,1610, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1613, 0,1611, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1620, 0, 0, 0, 0, 0, 0, + 0,1623, 0, 0,1624, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1614,1615,1616,1617,1618,1619,1621,1622, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1628,1629, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1625,1626, 0,1627, + 0, 0, 0,1634, 0, 0,1635, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1630,1631,1632, 0, 0,1633, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1639, 0, 0,1638,1640, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1636,1637, 0, 0, + 0, 0, 0, 0,1641, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1642,1644,1643, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1645, 0, 0, 0, 0, 0, 0, 0, + 1646, 0, 0, 0, 0, 0, 0,1648,1649, 0,1647,1650, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1651,1653,1652, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1654, 0,1655,1657,1656, 0, + 0, 0, 0,1659, 0, 0, 0, 0, 0, 0, 0, 0, 0,1660, 0, 0, + 0, 0,1661, 0, 0, 0, 0,1662, 0, 0, 0, 0,1663, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1658, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1664, 0,1665,1673, 0,1674, 0, 0, 0, 0, 0, 0, 0, + 0,1666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1668, 0, 0, 0, 0, 0, 0, 0, 0, 0,1669, 0, 0, + 0, 0,1670, 0, 0, 0, 0,1671, 0, 0, 0, 0,1672, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1667, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1675, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1676, 0,1677, 0,1678, 0,1679, 0,1680, 0, + 0, 0,1681, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1682, 0,1683, 0, 0, + 1684,1685, 0,1686, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 953,1138, 955,1140, 956,1141, 957,1142,1324,1351, 963,1148, 965,1150, 968,1153, + 966,1151, 967,1152,1378,1380,1379,1381, 984,1169, 985,1170,1420,1421, 986,1171, + 989,1174, 995,1180, 998,1183, 996,1181, 999,1184,1000,1185,1015,1200,1329,1356, + 1017,1203,1019,1205,1021,1207,1024,1210,1687,1688,1027,1213,1026,1212,1028,1214, + 1029,1215,1030,1216,1034,1220,1036,1222,1039,1225,1038,1224,1334,1361,1336,1363, + 1382,1384,1383,1385,1056,1242,1057,1243,1059,1245,1063,1249,1689,1690,1065,1251, + 1068,1254,1070,1256,1386,1387,1388,1389,1691,1692,1073,1259,1075,1262,1079,1266, + 1078,1265,1095,1282,1098,1285,1097,1284,1390,1391,1392,1393,1099,1286,1100,1287, + 1101,1288,1102,1289,1105,1292,1104,1291,1106,1294,1107,1295,1108,1296,1114,1302, + 1119,1308,1122,1311,1123,1312,1186,1260,1293,1305, 0,1394, 0, 0, 0, 0, + 952,1137, 947,1132,1317,1344,1316,1343,1319,1346,1318,1345,1693,1695,1371,1375, + 1370,1374,1373,1377,1372,1376,1694,1696, 981,1166, 977,1162, 972,1157,1326,1353, + 1325,1352,1328,1355,1327,1354,1697,1698,1009,1194,1013,1198,1054,1240,1048,1234, + 1331,1358,1330,1357,1333,1360,1332,1359,1699,1700,1396,1401,1395,1400,1398,1403, + 1397,1402,1399,1404,1094,1281,1087,1274,1406,1411,1405,1410,1408,1413,1407,1412, + 1409,1414,1109,1297,1117,1306,1116,1304,1112,1300, 0, 0, 0, 0, 0, 0, + 1471,1472,1701,1705,1702,1706,1703,1707,1430,1431,1715,1719,1716,1720,1717,1721, + 1477,1478,1729,1731,1730,1732, 0, 0,1435,1436,1733,1735,1734,1736, 0, 0, + 1481,1482,1737,1741,1738,1742,1739,1743,1439,1440,1751,1755,1752,1756,1753,1757, + 1490,1491,1765,1768,1766,1769,1767,1770,1447,1448,1771,1774,1772,1775,1773,1776, + 1495,1496,1777,1779,1778,1780, 0, 0,1451,1452,1781,1783,1782,1784, 0, 0, + 1504,1505,1785,1788,1786,1789,1787,1790, 0,1459, 0,1791, 0,1792, 0,1793, + 1509,1510,1794,1798,1795,1799,1796,1800,1462,1463,1808,1812,1809,1813,1810,1814, + 1467, 21,1475, 22,1479, 23,1485, 24,1493, 27,1499, 28,1507, 29, 0, 0, + 1704,1708,1709,1710,1711,1712,1713,1714,1718,1722,1723,1724,1725,1726,1727,1728, + 1740,1744,1745,1746,1747,1748,1749,1750,1754,1758,1759,1760,1761,1762,1763,1764, + 1797,1801,1802,1803,1804,1805,1806,1807,1811,1815,1816,1817,1818,1819,1820,1821, + 1470,1469,1822,1474,1465, 0,1473,1825,1429,1428,1426, 12,1432, 0, 26, 0, + 0,1315,1823,1484,1466, 0,1483,1829,1433, 13,1437, 14,1441,1826,1827,1828, + 1488,1487,1513, 19, 0, 0,1492,1515,1445,1444,1442, 15, 0,1831,1832,1833, + 1502,1501,1516, 25,1497,1498,1506,1518,1457,1456,1454, 17,1453,1313, 11, 3, + 0, 0,1824,1512,1519, 0,1511,1830,1449, 16,1460, 18,1464, 4, 0, 0, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1834,1835, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1836, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1837,1839,1838, + 0, 0, 0, 0,1840, 0, 0, 0, 0,1841, 0, 0,1842, 0, 0, 0, + 0, 0, 0, 0,1843, 0,1844, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1845, 0, 0,1846, 0, 0,1847, 0,1848, 0, 0, 0, 0, 0, 0, + 937, 0,1850, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1849, 936, 938, + 1851,1852, 0, 0,1853,1854, 0, 0,1855,1856, 0, 0, 0, 0, 0, 0, + 1857,1858, 0, 0,1861,1862, 0, 0,1863,1864, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1867,1868,1869,1870, + 1859,1860,1865,1866, 0, 0, 0, 0, 0, 0,1871,1872,1873,1874, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1875, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1877, 0,1878, 0, + 1879, 0,1880, 0,1881, 0,1882, 0,1883, 0,1884, 0,1885, 0,1886, 0, + 1887, 0,1888, 0, 0,1889, 0,1890, 0,1891, 0, 0, 0, 0, 0, 0, + 1892,1893, 0,1894,1895, 0,1896,1897, 0,1898,1899, 0,1900,1901, 0, 0, + 0, 0, 0, 0,1876, 0, 0, 0, 0, 0, 0, 0, 0, 0,1902, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1904, 0,1905, 0, + 1906, 0,1907, 0,1908, 0,1909, 0,1910, 0,1911, 0,1912, 0,1913, 0, + 1914, 0,1915, 0, 0,1916, 0,1917, 0,1918, 0, 0, 0, 0, 0, 0, + 1919,1920, 0,1921,1922, 0,1923,1924, 0,1925,1926, 0,1927,1928, 0, 0, + 0, 0, 0, 0,1903, 0, 0,1929,1930,1931,1932, 0, 0, 0,1933, 0, + 710, 385, 724, 715, 455, 103, 186, 825, 825, 242, 751, 205, 241, 336, 524, 601, + 663, 676, 688, 738, 411, 434, 474, 500, 649, 746, 799, 108, 180, 416, 482, 662, + 810, 275, 462, 658, 692, 344, 618, 679, 293, 388, 440, 492, 740, 116, 146, 168, + 368, 414, 481, 527, 606, 660, 665, 722, 781, 803, 809, 538, 553, 588, 642, 758, + 811, 701, 233, 299, 573, 612, 487, 540, 714, 779, 232, 267, 412, 445, 457, 585, + 594, 766, 167, 613, 149, 148, 560, 589, 648, 768, 708, 345, 411, 704, 105, 259, + 313, 496, 518, 174, 542, 120, 307, 101, 430, 372, 584, 183, 228, 529, 650, 697, + 424, 732, 428, 349, 632, 355, 517, 110, 135, 147, 403, 580, 624, 700, 750, 170, + 193, 245, 297, 374, 463, 543, 763, 801, 812, 815, 162, 384, 420, 730, 287, 330, + 337, 366, 459, 476, 509, 558, 591, 610, 726, 652, 734, 759, 154, 163, 198, 473, + 683, 697, 292, 311, 353, 423, 572, 494, 113, 217, 259, 280, 314, 499, 506, 603, + 608, 752, 778, 782, 788, 117, 557, 748, 774, 320, 109, 126, 260, 265, 373, 411, + 479, 523, 655, 737, 823, 380, 765, 161, 395, 398, 438, 451, 502, 516, 537, 583, + 791, 136, 340, 769, 122, 273, 446, 727, 305, 322, 400, 496, 771, 155, 190, 269, + 377, 391, 406, 432, 501, 519, 599, 684, 687, 749, 776, 175, 452, 191, 480, 510, + 659, 772, 805, 813, 397, 444, 619, 566, 568, 575, 491, 471, 707, 111, 636, 156, + 153, 288, 346, 578, 256, 435, 383, 729, 680, 767, 694, 295, 128, 210, 0, 0, + 227, 0, 379, 0, 0, 150, 493, 525, 544, 551, 552, 556, 783, 576, 604, 0, + 661, 0, 703, 0, 0, 735, 743, 0, 0, 0, 793, 794, 795, 808, 741, 773, + 118, 127, 130, 166, 169, 177, 207, 213, 215, 226, 229, 268, 270, 317, 327, 329, + 335, 369, 375, 381, 404, 441, 448, 458, 477, 484, 503, 539, 545, 547, 546, 548, + 549, 550, 554, 555, 561, 564, 569, 591, 593, 595, 598, 607, 620, 625, 625, 651, + 690, 695, 705, 706, 716, 717, 733, 735, 777, 786, 790, 315, 869, 623, 0, 0, + 102, 145, 134, 115, 129, 138, 165, 171, 207, 202, 206, 212, 227, 231, 240, 243, + 250, 254, 294, 296, 303, 308, 319, 325, 321, 329, 326, 335, 341, 357, 360, 362, + 370, 379, 388, 389, 393, 421, 424, 438, 456, 454, 458, 465, 477, 535, 485, 490, + 493, 507, 512, 514, 521, 522, 525, 526, 528, 533, 532, 541, 565, 569, 574, 586, + 591, 597, 607, 637, 647, 674, 691, 693, 695, 698, 703, 699, 705, 704, 702, 706, + 709, 717, 728, 736, 747, 754, 770, 777, 783, 784, 786, 787, 790, 802, 825, 848, + 847, 857, 55, 65, 66, 883, 892, 916, 822, 824, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1586, 0,1605, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1602,1603,1934,1935,1574,1575, + 1576,1577,1579,1580,1581,1583,1584, 0,1585,1587,1588,1589,1591, 0,1592, 0, + 1593,1594, 0,1595,1596, 0,1598,1599,1600,1601,1604,1582,1578,1590,1597, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1936, 0,1937, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1938, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1939,1940, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1941,1942, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1944,1943, 0,1945, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1946,1947, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1948, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1949,1950, + 1951,1952,1953,1954,1955, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1956,1957,1958,1960,1959, + 1961, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 106, 104, 107, 826, 114, 118, 119, 121, 123, 124, 127, 125, 34, 830, 130, 131, + 132, 137, 827, 35, 133, 139, 829, 142, 143, 112, 144, 145, 924, 151, 152, 37, + 157, 158, 159, 160, 38, 165, 166, 169, 171, 172, 173, 174, 176, 177, 178, 179, + 181, 182, 182, 182, 833, 468, 184, 185, 834, 187, 188, 189, 196, 192, 194, 195, + 197, 199, 200, 201, 203, 204, 204, 206, 208, 209, 211, 218, 213, 219, 214, 216, + 153, 234, 221, 222, 223, 220, 225, 224, 230, 835, 235, 236, 237, 238, 239, 244, + 836, 837, 247, 248, 249, 246, 251, 39, 40, 253, 255, 255, 838, 257, 258, 259, + 261, 839, 262, 263, 301, 264, 41, 266, 270, 272, 271, 841, 274, 842, 277, 276, + 278, 281, 282, 42, 283, 284, 285, 286, 43, 843, 44, 289, 290, 291, 293, 934, + 298, 845, 845, 621, 300, 300, 45, 852, 894, 302, 304, 46, 306, 309, 310, 312, + 316, 48, 47, 317, 846, 318, 323, 324, 325, 324, 328, 329, 333, 331, 332, 334, + 335, 336, 338, 339, 342, 343, 347, 351, 849, 350, 348, 352, 354, 359, 850, 361, + 358, 356, 49, 363, 365, 367, 364, 50, 369, 371, 851, 376, 386, 378, 53, 381, + 52, 51, 140, 141, 387, 382, 614, 78, 388, 389, 390, 394, 392, 856, 54, 399, + 396, 402, 404, 858, 405, 401, 407, 55, 408, 409, 410, 413, 859, 415, 56, 417, + 860, 418, 57, 419, 422, 424, 425, 861, 840, 862, 426, 863, 429, 431, 427, 433, + 437, 441, 438, 439, 442, 443, 864, 436, 449, 450, 58, 454, 453, 865, 447, 460, + 866, 867, 461, 466, 465, 464, 59, 467, 470, 469, 472, 828, 475, 868, 478, 870, + 483, 485, 486, 871, 488, 489, 872, 873, 495, 497, 60, 498, 61, 61, 504, 505, + 507, 508, 511, 62, 513, 874, 515, 875, 518, 844, 520, 876, 877, 878, 63, 64, + 528, 880, 879, 881, 882, 530, 531, 531, 533, 66, 534, 67, 68, 884, 536, 538, + 541, 69, 885, 549, 886, 887, 556, 559, 70, 561, 562, 563, 888, 889, 889, 567, + 71, 890, 570, 571, 72, 891, 577, 73, 581, 579, 582, 893, 587, 74, 590, 592, + 596, 75, 895, 896, 76, 897, 600, 898, 602, 605, 607, 899, 900, 609, 901, 611, + 853, 77, 615, 616, 79, 617, 252, 902, 903, 854, 855, 621, 622, 731, 80, 627, + 626, 628, 164, 629, 630, 631, 633, 904, 632, 634, 639, 640, 635, 641, 646, 651, + 638, 643, 644, 645, 905, 907, 906, 81, 653, 654, 656, 911, 657, 908, 82, 83, + 909, 910, 84, 664, 665, 666, 667, 669, 668, 671, 670, 674, 672, 673, 675, 85, + 677, 678, 86, 681, 682, 912, 685, 686, 87, 689, 36, 913, 914, 88, 89, 696, + 702, 709, 711, 915, 712, 713, 718, 719, 917, 831, 721, 720, 723, 832, 725, 728, + 918, 919, 739, 742, 744, 920, 745, 753, 756, 757, 755, 760, 761, 921, 762, 90, + 764, 922, 91, 775, 279, 780, 923, 925, 92, 93, 785, 926, 94, 927, 787, 787, + 789, 928, 792, 95, 796, 797, 798, 800, 96, 929, 802, 804, 806, 97, 98, 807, + 930, 99, 931, 932, 933, 814, 100, 816, 817, 818, 819, 820, 821, 935, 0, 0, +}; +static const int16_t +_hb_ucd_i16[92] = +{ + 0, 0, 1, -1, 2, 0, -2, 0, 0, 2, 0, -2, 0, 16, 0, -16, + 0, 1, -1, 0, 3, 3, 3, -3, -3, -3, 0, 2016, 0, 2527, 1923, 1914, + 1918, 0, 2250, 0, 0, 138, 0, 7, -7, 0, -1, 1, 1824, 0, 2104, 0, + 2108, 2106, 0, 2106, 1316, 0, -1, -138, 8, 8, 8, 0, 7, 7, -8, -8, + -8, -7,-1316, 1, -1, 3, -3, 1, 0,-1914,-1918, 0, 0,-1923,-1824, 0, + 0,-2016,-2104, 0, 0,-2106,-2108,-2106,-2250, 0,-2527, 0, +}; + +static inline uint_fast8_t +_hb_ucd_gc (unsigned u) +{ + return u<1114112u?_hb_ucd_u8[4920+(((_hb_ucd_u8[1104+(((_hb_ucd_u16[((_hb_ucd_u8[272+(((_hb_ucd_u8[u>>1>>3>>3>>5])<<5)+((u>>1>>3>>3)&31u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; +} +static inline uint_fast8_t +_hb_ucd_ccc (unsigned u) +{ + return u<125259u?_hb_ucd_u8[6796+(((_hb_ucd_u8[6276+(((_hb_ucd_u8[5844+(((_hb_ucd_u8[5508+(((_hb_ucd_u8[5262+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; +} +static inline unsigned +_hb_ucd_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline int_fast16_t +_hb_ucd_bmg (unsigned u) +{ + return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[7672+(((_hb_ucd_u8[7448+(((_hb_ucd_u8[7352+(((_hb_ucd_b4(7288+_hb_ucd_u8,u>>1>>2>>3>>3))<<3)+((u>>1>>2>>3)&7u))])<<3)+((u>>1>>2)&7u))])<<2)+((u>>1)&3u))])<<1)+((u)&1u)]:0; +} +static inline uint_fast8_t +_hb_ucd_sc (unsigned u) +{ + return u<918016u?_hb_ucd_u8[11242+(((_hb_ucd_u8[10314+(((_hb_ucd_u8[8938+(((_hb_ucd_u8[8362+(((_hb_ucd_u8[7912+(u>>2>>2>>3>>4)])<<4)+((u>>2>>2>>3)&15u))])<<3)+((u>>2>>2)&7u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:2; +} +static inline uint_fast16_t +_hb_ucd_dm (unsigned u) +{ + return u<195102u?_hb_ucd_u16[1536+(((_hb_ucd_u8[12544+(((_hb_ucd_u8[12162+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; +} + +#endif + + +#endif /* HB_UCD_TABLE_HH */ + +/* == End of generated table == */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ucd.cc b/src/java.desktop/share/native/libharfbuzz/hb-ucd.cc new file mode 100644 index 00000000000..f56f05f1707 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ucd.cc @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2012 Grigori Goronzy + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "hb.hh" +#include "hb-unicode.hh" +#include "hb-machinery.hh" + +#include "hb-ucd-table.hh" + +static hb_unicode_combining_class_t +hb_ucd_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return (hb_unicode_combining_class_t) _hb_ucd_ccc (unicode); +} + +static hb_unicode_general_category_t +hb_ucd_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return (hb_unicode_general_category_t) _hb_ucd_gc (unicode); +} + +static hb_codepoint_t +hb_ucd_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return unicode + _hb_ucd_bmg (unicode); +} + +static hb_script_t +hb_ucd_script (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return _hb_ucd_sc_map[_hb_ucd_sc (unicode)]; +} + + +#define SBASE 0xAC00u +#define LBASE 0x1100u +#define VBASE 0x1161u +#define TBASE 0x11A7u +#define SCOUNT 11172u +#define LCOUNT 19u +#define VCOUNT 21u +#define TCOUNT 28u +#define NCOUNT (VCOUNT * TCOUNT) + +static inline bool +_hb_ucd_decompose_hangul (hb_codepoint_t ab, hb_codepoint_t *a, hb_codepoint_t *b) +{ + unsigned si = ab - SBASE; + + if (si >= SCOUNT) + return false; + + if (si % TCOUNT) + { + /* LV,T */ + *a = SBASE + (si / TCOUNT) * TCOUNT; + *b = TBASE + (si % TCOUNT); + return true; + } else { + /* L,V */ + *a = LBASE + (si / NCOUNT); + *b = VBASE + (si % NCOUNT) / TCOUNT; + return true; + } +} + +static inline bool +_hb_ucd_compose_hangul (hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab) +{ + if (a >= SBASE && a < (SBASE + SCOUNT) && b > TBASE && b < (TBASE + TCOUNT) && + !((a - SBASE) % TCOUNT)) + { + /* LV,T */ + *ab = a + (b - TBASE); + return true; + } + else if (a >= LBASE && a < (LBASE + LCOUNT) && b >= VBASE && b < (VBASE + VCOUNT)) + { + /* L,V */ + int li = a - LBASE; + int vi = b - VBASE; + *ab = SBASE + li * NCOUNT + vi * TCOUNT; + return true; + } + else + return false; +} + +static int +_cmp_pair (const void *_key, const void *_item) +{ + uint64_t& a = * (uint64_t*) _key; + uint64_t b = (* (uint64_t*) _item) & HB_CODEPOINT_ENCODE3(0x1FFFFFu, 0x1FFFFFu, 0); + + return a < b ? -1 : a > b ? +1 : 0; +} +static int +_cmp_pair_11_7_14 (const void *_key, const void *_item) +{ + uint32_t& a = * (uint32_t*) _key; + uint32_t b = (* (uint32_t*) _item) & HB_CODEPOINT_ENCODE3_11_7_14(0x1FFFFFu, 0x1FFFFFu, 0); + + return a < b ? -1 : a > b ? +1 : 0; +} + +static hb_bool_t +hb_ucd_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab, + void *user_data HB_UNUSED) +{ + if (_hb_ucd_compose_hangul (a, b, ab)) return true; + + hb_codepoint_t u = 0; + + if ((a & 0xFFFFF800u) == 0x0000u && (b & 0xFFFFFF80) == 0x0300u) + { + uint32_t k = HB_CODEPOINT_ENCODE3_11_7_14 (a, b, 0); + const uint32_t *v = hb_bsearch (k, + _hb_ucd_dm2_u32_map, + ARRAY_LENGTH (_hb_ucd_dm2_u32_map), + sizeof (*_hb_ucd_dm2_u32_map), + _cmp_pair_11_7_14); + if (likely (!v)) return false; + u = HB_CODEPOINT_DECODE3_11_7_14_3 (*v); + } + else + { + uint64_t k = HB_CODEPOINT_ENCODE3 (a, b, 0); + const uint64_t *v = hb_bsearch (k, + _hb_ucd_dm2_u64_map, + ARRAY_LENGTH (_hb_ucd_dm2_u64_map), + sizeof (*_hb_ucd_dm2_u64_map), + _cmp_pair); + if (likely (!v)) return false; + u = HB_CODEPOINT_DECODE3_3 (*v); + } + + if (unlikely (!u)) return false; + *ab = u; + return true; +} + +static hb_bool_t +hb_ucd_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t ab, hb_codepoint_t *a, hb_codepoint_t *b, + void *user_data HB_UNUSED) +{ + if (_hb_ucd_decompose_hangul (ab, a, b)) return true; + + unsigned i = _hb_ucd_dm (ab); + + if (likely (!i)) return false; + i--; + + if (i < ARRAY_LENGTH (_hb_ucd_dm1_p0_map) + ARRAY_LENGTH (_hb_ucd_dm1_p2_map)) + { + if (i < ARRAY_LENGTH (_hb_ucd_dm1_p0_map)) + *a = _hb_ucd_dm1_p0_map[i]; + else + { + i -= ARRAY_LENGTH (_hb_ucd_dm1_p0_map); + *a = 0x20000 | _hb_ucd_dm1_p2_map[i]; + } + *b = 0; + return true; + } + i -= ARRAY_LENGTH (_hb_ucd_dm1_p0_map) + ARRAY_LENGTH (_hb_ucd_dm1_p2_map); + + if (i < ARRAY_LENGTH (_hb_ucd_dm2_u32_map)) + { + uint32_t v = _hb_ucd_dm2_u32_map[i]; + *a = HB_CODEPOINT_DECODE3_11_7_14_1 (v); + *b = HB_CODEPOINT_DECODE3_11_7_14_2 (v); + return true; + } + i -= ARRAY_LENGTH (_hb_ucd_dm2_u32_map); + + uint64_t v = _hb_ucd_dm2_u64_map[i]; + *a = HB_CODEPOINT_DECODE3_1 (v); + *b = HB_CODEPOINT_DECODE3_2 (v); + return true; +} + + +#if HB_USE_ATEXIT +static void free_static_ucd_funcs (); +#endif + +static struct hb_ucd_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t +{ + static hb_unicode_funcs_t *create () + { + hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr); + + hb_unicode_funcs_set_combining_class_func (funcs, hb_ucd_combining_class, nullptr, nullptr); + hb_unicode_funcs_set_general_category_func (funcs, hb_ucd_general_category, nullptr, nullptr); + hb_unicode_funcs_set_mirroring_func (funcs, hb_ucd_mirroring, nullptr, nullptr); + hb_unicode_funcs_set_script_func (funcs, hb_ucd_script, nullptr, nullptr); + hb_unicode_funcs_set_compose_func (funcs, hb_ucd_compose, nullptr, nullptr); + hb_unicode_funcs_set_decompose_func (funcs, hb_ucd_decompose, nullptr, nullptr); + + hb_unicode_funcs_make_immutable (funcs); + +#if HB_USE_ATEXIT + atexit (free_static_ucd_funcs); +#endif + + return funcs; + } +} static_ucd_funcs; + +#if HB_USE_ATEXIT +static +void free_static_ucd_funcs () +{ + static_ucd_funcs.free_instance (); +} +#endif + +hb_unicode_funcs_t * +hb_ucd_get_unicode_funcs () +{ +#ifdef HB_NO_UCD + return hb_unicode_funcs_get_empty (); +#endif + return static_ucd_funcs.get_unconst (); +} diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ucdn.cc b/src/java.desktop/share/native/libharfbuzz/hb-ucdn.cc deleted file mode 100644 index d80f6e97b8d..00000000000 --- a/src/java.desktop/share/native/libharfbuzz/hb-ucdn.cc +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2012 Grigori Goronzy - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "hb.hh" - -#include "hb-machinery.hh" - -#include "ucdn.h" - -static const hb_script_t ucdn_script_translate[] = -{ - HB_SCRIPT_COMMON, - HB_SCRIPT_LATIN, - HB_SCRIPT_GREEK, - HB_SCRIPT_CYRILLIC, - HB_SCRIPT_ARMENIAN, - HB_SCRIPT_HEBREW, - HB_SCRIPT_ARABIC, - HB_SCRIPT_SYRIAC, - HB_SCRIPT_THAANA, - HB_SCRIPT_DEVANAGARI, - HB_SCRIPT_BENGALI, - HB_SCRIPT_GURMUKHI, - HB_SCRIPT_GUJARATI, - HB_SCRIPT_ORIYA, - HB_SCRIPT_TAMIL, - HB_SCRIPT_TELUGU, - HB_SCRIPT_KANNADA, - HB_SCRIPT_MALAYALAM, - HB_SCRIPT_SINHALA, - HB_SCRIPT_THAI, - HB_SCRIPT_LAO, - HB_SCRIPT_TIBETAN, - HB_SCRIPT_MYANMAR, - HB_SCRIPT_GEORGIAN, - HB_SCRIPT_HANGUL, - HB_SCRIPT_ETHIOPIC, - HB_SCRIPT_CHEROKEE, - HB_SCRIPT_CANADIAN_SYLLABICS, - HB_SCRIPT_OGHAM, - HB_SCRIPT_RUNIC, - HB_SCRIPT_KHMER, - HB_SCRIPT_MONGOLIAN, - HB_SCRIPT_HIRAGANA, - HB_SCRIPT_KATAKANA, - HB_SCRIPT_BOPOMOFO, - HB_SCRIPT_HAN, - HB_SCRIPT_YI, - HB_SCRIPT_OLD_ITALIC, - HB_SCRIPT_GOTHIC, - HB_SCRIPT_DESERET, - HB_SCRIPT_INHERITED, - HB_SCRIPT_TAGALOG, - HB_SCRIPT_HANUNOO, - HB_SCRIPT_BUHID, - HB_SCRIPT_TAGBANWA, - HB_SCRIPT_LIMBU, - HB_SCRIPT_TAI_LE, - HB_SCRIPT_LINEAR_B, - HB_SCRIPT_UGARITIC, - HB_SCRIPT_SHAVIAN, - HB_SCRIPT_OSMANYA, - HB_SCRIPT_CYPRIOT, - HB_SCRIPT_BRAILLE, - HB_SCRIPT_BUGINESE, - HB_SCRIPT_COPTIC, - HB_SCRIPT_NEW_TAI_LUE, - HB_SCRIPT_GLAGOLITIC, - HB_SCRIPT_TIFINAGH, - HB_SCRIPT_SYLOTI_NAGRI, - HB_SCRIPT_OLD_PERSIAN, - HB_SCRIPT_KHAROSHTHI, - HB_SCRIPT_BALINESE, - HB_SCRIPT_CUNEIFORM, - HB_SCRIPT_PHOENICIAN, - HB_SCRIPT_PHAGS_PA, - HB_SCRIPT_NKO, - HB_SCRIPT_SUNDANESE, - HB_SCRIPT_LEPCHA, - HB_SCRIPT_OL_CHIKI, - HB_SCRIPT_VAI, - HB_SCRIPT_SAURASHTRA, - HB_SCRIPT_KAYAH_LI, - HB_SCRIPT_REJANG, - HB_SCRIPT_LYCIAN, - HB_SCRIPT_CARIAN, - HB_SCRIPT_LYDIAN, - HB_SCRIPT_CHAM, - HB_SCRIPT_TAI_THAM, - HB_SCRIPT_TAI_VIET, - HB_SCRIPT_AVESTAN, - HB_SCRIPT_EGYPTIAN_HIEROGLYPHS, - HB_SCRIPT_SAMARITAN, - HB_SCRIPT_LISU, - HB_SCRIPT_BAMUM, - HB_SCRIPT_JAVANESE, - HB_SCRIPT_MEETEI_MAYEK, - HB_SCRIPT_IMPERIAL_ARAMAIC, - HB_SCRIPT_OLD_SOUTH_ARABIAN, - HB_SCRIPT_INSCRIPTIONAL_PARTHIAN, - HB_SCRIPT_INSCRIPTIONAL_PAHLAVI, - HB_SCRIPT_OLD_TURKIC, - HB_SCRIPT_KAITHI, - HB_SCRIPT_BATAK, - HB_SCRIPT_BRAHMI, - HB_SCRIPT_MANDAIC, - HB_SCRIPT_CHAKMA, - HB_SCRIPT_MEROITIC_CURSIVE, - HB_SCRIPT_MEROITIC_HIEROGLYPHS, - HB_SCRIPT_MIAO, - HB_SCRIPT_SHARADA, - HB_SCRIPT_SORA_SOMPENG, - HB_SCRIPT_TAKRI, - HB_SCRIPT_UNKNOWN, - HB_SCRIPT_BASSA_VAH, - HB_SCRIPT_CAUCASIAN_ALBANIAN, - HB_SCRIPT_DUPLOYAN, - HB_SCRIPT_ELBASAN, - HB_SCRIPT_GRANTHA, - HB_SCRIPT_KHOJKI, - HB_SCRIPT_KHUDAWADI, - HB_SCRIPT_LINEAR_A, - HB_SCRIPT_MAHAJANI, - HB_SCRIPT_MANICHAEAN, - HB_SCRIPT_MENDE_KIKAKUI, - HB_SCRIPT_MODI, - HB_SCRIPT_MRO, - HB_SCRIPT_NABATAEAN, - HB_SCRIPT_OLD_NORTH_ARABIAN, - HB_SCRIPT_OLD_PERMIC, - HB_SCRIPT_PAHAWH_HMONG, - HB_SCRIPT_PALMYRENE, - HB_SCRIPT_PAU_CIN_HAU, - HB_SCRIPT_PSALTER_PAHLAVI, - HB_SCRIPT_SIDDHAM, - HB_SCRIPT_TIRHUTA, - HB_SCRIPT_WARANG_CITI, - HB_SCRIPT_AHOM, - HB_SCRIPT_ANATOLIAN_HIEROGLYPHS, - HB_SCRIPT_HATRAN, - HB_SCRIPT_MULTANI, - HB_SCRIPT_OLD_HUNGARIAN, - HB_SCRIPT_SIGNWRITING, - HB_SCRIPT_ADLAM, - HB_SCRIPT_BHAIKSUKI, - HB_SCRIPT_MARCHEN, - HB_SCRIPT_NEWA, - HB_SCRIPT_OSAGE, - HB_SCRIPT_TANGUT, - HB_SCRIPT_MASARAM_GONDI, - HB_SCRIPT_NUSHU, - HB_SCRIPT_SOYOMBO, - HB_SCRIPT_ZANABAZAR_SQUARE, - HB_SCRIPT_DOGRA, - HB_SCRIPT_GUNJALA_GONDI, - HB_SCRIPT_HANIFI_ROHINGYA, - HB_SCRIPT_MAKASAR, - HB_SCRIPT_MEDEFAIDRIN, - HB_SCRIPT_OLD_SOGDIAN, - HB_SCRIPT_SOGDIAN, -}; - -static hb_unicode_combining_class_t -hb_ucdn_combining_class(hb_unicode_funcs_t *ufuncs HB_UNUSED, - hb_codepoint_t unicode, - void *user_data HB_UNUSED) -{ - return (hb_unicode_combining_class_t) ucdn_get_combining_class(unicode); -} - -static hb_unicode_general_category_t -hb_ucdn_general_category(hb_unicode_funcs_t *ufuncs HB_UNUSED, - hb_codepoint_t unicode, - void *user_data HB_UNUSED) -{ - return (hb_unicode_general_category_t)ucdn_get_general_category(unicode); -} - -static hb_codepoint_t -hb_ucdn_mirroring(hb_unicode_funcs_t *ufuncs HB_UNUSED, - hb_codepoint_t unicode, - void *user_data HB_UNUSED) -{ - return ucdn_mirror(unicode); -} - -static hb_script_t -hb_ucdn_script(hb_unicode_funcs_t *ufuncs HB_UNUSED, - hb_codepoint_t unicode, - void *user_data HB_UNUSED) -{ - return ucdn_script_translate[ucdn_get_script(unicode)]; -} - -static hb_bool_t -hb_ucdn_compose(hb_unicode_funcs_t *ufuncs HB_UNUSED, - hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab, - void *user_data HB_UNUSED) -{ - return ucdn_compose(ab, a, b); -} - -static hb_bool_t -hb_ucdn_decompose(hb_unicode_funcs_t *ufuncs HB_UNUSED, - hb_codepoint_t ab, hb_codepoint_t *a, hb_codepoint_t *b, - void *user_data HB_UNUSED) -{ - return ucdn_decompose(ab, a, b); -} - - -#if HB_USE_ATEXIT -static void free_static_ucdn_funcs (); -#endif - -static struct hb_ucdn_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t -{ - static hb_unicode_funcs_t *create () - { - hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr); - - hb_unicode_funcs_set_combining_class_func (funcs, hb_ucdn_combining_class, nullptr, nullptr); - hb_unicode_funcs_set_general_category_func (funcs, hb_ucdn_general_category, nullptr, nullptr); - hb_unicode_funcs_set_mirroring_func (funcs, hb_ucdn_mirroring, nullptr, nullptr); - hb_unicode_funcs_set_script_func (funcs, hb_ucdn_script, nullptr, nullptr); - hb_unicode_funcs_set_compose_func (funcs, hb_ucdn_compose, nullptr, nullptr); - hb_unicode_funcs_set_decompose_func (funcs, hb_ucdn_decompose, nullptr, nullptr); - - hb_unicode_funcs_make_immutable (funcs); - -#if HB_USE_ATEXIT - atexit (free_static_ucdn_funcs); -#endif - - return funcs; - } -} static_ucdn_funcs; - -#if HB_USE_ATEXIT -static -void free_static_ucdn_funcs () -{ - static_ucdn_funcs.free_instance (); -} -#endif - -extern "C" HB_INTERNAL -hb_unicode_funcs_t * -hb_ucdn_get_unicode_funcs (); - -hb_unicode_funcs_t * -hb_ucdn_get_unicode_funcs () -{ - return static_ucdn_funcs.get_unconst (); -} diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn.c b/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn.c deleted file mode 100644 index 30747fea251..00000000000 --- a/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn.c +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (C) 2012 Grigori Goronzy - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include -#include "ucdn.h" - -typedef struct { - unsigned char category; - unsigned char combining; - unsigned char bidi_class; - unsigned char east_asian_width; - unsigned char script; - unsigned char linebreak_class; -} UCDRecord; - -typedef struct { - unsigned short from, to; -} MirrorPair; - -typedef struct { - unsigned short from, to; - unsigned char type; -} BracketPair; - -typedef struct { - unsigned int start; - short count, index; -} Reindex; - -#include "ucdn_db.h" - -/* constants required for Hangul (de)composition */ -#define SBASE 0xAC00 -#define LBASE 0x1100 -#define VBASE 0x1161 -#define TBASE 0x11A7 -#define SCOUNT 11172 -#define LCOUNT 19 -#define VCOUNT 21 -#define TCOUNT 28 -#define NCOUNT (VCOUNT * TCOUNT) - -static const UCDRecord *get_ucd_record(uint32_t code) -{ - int index, offset; - - if (code >= 0x110000) - index = 0; - else { - index = index0[code >> (SHIFT1+SHIFT2)] << SHIFT1; - offset = (code >> SHIFT2) & ((1<= 0x110000) - index = 0; - else { - index = decomp_index0[code >> (DECOMP_SHIFT1+DECOMP_SHIFT2)] - << DECOMP_SHIFT1; - offset = (code >> DECOMP_SHIFT2) & ((1<start < rb->start) - return -1; - else if (ra->start > (rb->start + rb->count)) - return 1; - else - return 0; -} - -static int get_comp_index(uint32_t code, const Reindex *idx, size_t len) -{ - Reindex *res; - Reindex r = {0, 0, 0}; - r.start = code; - res = (Reindex *) bsearch(&r, idx, len, sizeof(Reindex), compare_reindex); - - if (res != NULL) - return res->index + (code - res->start); - else - return -1; -} - -static int compare_mp(const void *a, const void *b) -{ - MirrorPair *mpa = (MirrorPair *)a; - MirrorPair *mpb = (MirrorPair *)b; - return mpa->from - mpb->from; -} - -static int compare_bp(const void *a, const void *b) -{ - BracketPair *bpa = (BracketPair *)a; - BracketPair *bpb = (BracketPair *)b; - return bpa->from - bpb->from; -} - -static BracketPair *search_bp(uint32_t code) -{ - BracketPair bp = {0,0,2}; - BracketPair *res; - - bp.from = code; - res = (BracketPair *) bsearch(&bp, bracket_pairs, BIDI_BRACKET_LEN, - sizeof(BracketPair), compare_bp); - return res; -} - -static int hangul_pair_decompose(uint32_t code, uint32_t *a, uint32_t *b) -{ - int si = code - SBASE; - - if (si < 0 || si >= SCOUNT) - return 0; - - if (si % TCOUNT) { - /* LV,T */ - *a = SBASE + (si / TCOUNT) * TCOUNT; - *b = TBASE + (si % TCOUNT); - return 3; - } else { - /* L,V */ - *a = LBASE + (si / NCOUNT); - *b = VBASE + (si % NCOUNT) / TCOUNT; - return 2; - } -} - -static int hangul_pair_compose(uint32_t *code, uint32_t a, uint32_t b) -{ - if (a >= SBASE && a < (SBASE + SCOUNT) && b >= TBASE && b < (TBASE + TCOUNT)) { - /* LV,T */ - *code = a + (b - TBASE); - return 3; - } else if (a >= LBASE && a < (LBASE + LCOUNT) && b >= VBASE && b < (VBASE + VCOUNT)) { - /* L,V */ - int li = a - LBASE; - int vi = b - VBASE; - *code = SBASE + li * NCOUNT + vi * TCOUNT; - return 2; - } else { - return 0; - } -} - -static uint32_t decode_utf16(const unsigned short **code_ptr) -{ - const unsigned short *code = *code_ptr; - - if (code[0] < 0xd800 || code[0] > 0xdc00) { - *code_ptr += 1; - return (uint32_t)code[0]; - } else { - *code_ptr += 2; - return 0x10000 + ((uint32_t)code[1] - 0xdc00) + - (((uint32_t)code[0] - 0xd800) << 10); - } -} - -const char *ucdn_get_unicode_version(void) -{ - return UNIDATA_VERSION; -} - -int ucdn_get_combining_class(uint32_t code) -{ - return get_ucd_record(code)->combining; -} - -int ucdn_get_east_asian_width(uint32_t code) -{ - return get_ucd_record(code)->east_asian_width; -} - -int ucdn_get_general_category(uint32_t code) -{ - return get_ucd_record(code)->category; -} - -int ucdn_get_bidi_class(uint32_t code) -{ - return get_ucd_record(code)->bidi_class; -} - -int ucdn_get_mirrored(uint32_t code) -{ - return ucdn_mirror(code) != code; -} - -int ucdn_get_script(uint32_t code) -{ - return get_ucd_record(code)->script; -} - -int ucdn_get_linebreak_class(uint32_t code) -{ - return get_ucd_record(code)->linebreak_class; -} - -int ucdn_get_resolved_linebreak_class(uint32_t code) -{ - const UCDRecord *record = get_ucd_record(code); - - switch (record->linebreak_class) - { - case UCDN_LINEBREAK_CLASS_AI: - case UCDN_LINEBREAK_CLASS_SG: - case UCDN_LINEBREAK_CLASS_XX: - return UCDN_LINEBREAK_CLASS_AL; - - case UCDN_LINEBREAK_CLASS_SA: - if (record->category == UCDN_GENERAL_CATEGORY_MC || - record->category == UCDN_GENERAL_CATEGORY_MN) - return UCDN_LINEBREAK_CLASS_CM; - return UCDN_LINEBREAK_CLASS_AL; - - case UCDN_LINEBREAK_CLASS_CJ: - return UCDN_LINEBREAK_CLASS_NS; - - case UCDN_LINEBREAK_CLASS_CB: - return UCDN_LINEBREAK_CLASS_B2; - - case UCDN_LINEBREAK_CLASS_NL: - return UCDN_LINEBREAK_CLASS_BK; - - default: - return record->linebreak_class; - } -} - -uint32_t ucdn_mirror(uint32_t code) -{ - MirrorPair mp = {0}; - MirrorPair *res; - - mp.from = code; - res = (MirrorPair *) bsearch(&mp, mirror_pairs, BIDI_MIRROR_LEN, - sizeof(MirrorPair), compare_mp); - - if (res == NULL) - return code; - else - return res->to; -} - -uint32_t ucdn_paired_bracket(uint32_t code) -{ - BracketPair *res = search_bp(code); - if (res == NULL) - return code; - else - return res->to; -} - -int ucdn_paired_bracket_type(uint32_t code) -{ - BracketPair *res = search_bp(code); - if (res == NULL) - return UCDN_BIDI_PAIRED_BRACKET_TYPE_NONE; - else - return res->type; -} - -int ucdn_decompose(uint32_t code, uint32_t *a, uint32_t *b) -{ - const unsigned short *rec; - int len; - - if (hangul_pair_decompose(code, a, b)) - return 1; - - rec = get_decomp_record(code); - len = rec[0] >> 8; - - if ((rec[0] & 0xff) != 0 || len == 0) - return 0; - - rec++; - *a = decode_utf16(&rec); - if (len > 1) - *b = decode_utf16(&rec); - else - *b = 0; - - return 1; -} - -int ucdn_compose(uint32_t *code, uint32_t a, uint32_t b) -{ - int l, r, index, indexi, offset; - - if (hangul_pair_compose(code, a, b)) - return 1; - - l = get_comp_index(a, nfc_first, sizeof(nfc_first) / sizeof(Reindex)); - r = get_comp_index(b, nfc_last, sizeof(nfc_last) / sizeof(Reindex)); - - if (l < 0 || r < 0) - return 0; - - indexi = l * TOTAL_LAST + r; - index = comp_index0[indexi >> (COMP_SHIFT1+COMP_SHIFT2)] << COMP_SHIFT1; - offset = (indexi >> COMP_SHIFT2) & ((1<> 8; - - if (len == 0) - return 0; - - rec++; - for (i = 0; i < len; i++) - decomposed[i] = decode_utf16(&rec); - - return len; -} diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn.h b/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn.h deleted file mode 100644 index 05d46d2b1be..00000000000 --- a/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn.h +++ /dev/null @@ -1,461 +0,0 @@ -/* - * Copyright (C) 2012 Grigori Goronzy - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef UCDN_H -#define UCDN_H - - - -#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__) -# define HB_BEGIN_VISIBILITY _Pragma ("GCC visibility push(hidden)") -# define HB_END_VISIBILITY _Pragma ("GCC visibility pop") -#else -# define HB_BEGIN_VISIBILITY -# define HB_END_VISIBILITY -#endif -#ifdef __cplusplus -# define HB_BEGIN_HEADER extern "C" { HB_BEGIN_VISIBILITY -# define HB_END_HEADER HB_END_VISIBILITY } -#else -# define HB_BEGIN_HEADER HB_BEGIN_VISIBILITY -# define HB_END_HEADER HB_END_VISIBILITY -#endif - -HB_BEGIN_HEADER - -#if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \ - defined (_sgi) || defined (__sun) || defined (sun) || \ - defined (__digital__) || defined (__HP_cc) -# include -#elif defined (_AIX) -# include -#elif defined (_MSC_VER) && _MSC_VER < 1600 -/* VS 2010 (_MSC_VER 1600) has stdint.h */ -typedef __int8 int8_t; -typedef unsigned __int8 uint8_t; -typedef __int16 int16_t; -typedef unsigned __int16 uint16_t; -typedef __int32 int32_t; -typedef unsigned __int32 uint32_t; -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -#else -# include -#endif - - -#define UCDN_EAST_ASIAN_F 0 -#define UCDN_EAST_ASIAN_H 1 -#define UCDN_EAST_ASIAN_W 2 -#define UCDN_EAST_ASIAN_NA 3 -#define UCDN_EAST_ASIAN_A 4 -#define UCDN_EAST_ASIAN_N 5 - -#define UCDN_SCRIPT_COMMON 0 -#define UCDN_SCRIPT_LATIN 1 -#define UCDN_SCRIPT_GREEK 2 -#define UCDN_SCRIPT_CYRILLIC 3 -#define UCDN_SCRIPT_ARMENIAN 4 -#define UCDN_SCRIPT_HEBREW 5 -#define UCDN_SCRIPT_ARABIC 6 -#define UCDN_SCRIPT_SYRIAC 7 -#define UCDN_SCRIPT_THAANA 8 -#define UCDN_SCRIPT_DEVANAGARI 9 -#define UCDN_SCRIPT_BENGALI 10 -#define UCDN_SCRIPT_GURMUKHI 11 -#define UCDN_SCRIPT_GUJARATI 12 -#define UCDN_SCRIPT_ORIYA 13 -#define UCDN_SCRIPT_TAMIL 14 -#define UCDN_SCRIPT_TELUGU 15 -#define UCDN_SCRIPT_KANNADA 16 -#define UCDN_SCRIPT_MALAYALAM 17 -#define UCDN_SCRIPT_SINHALA 18 -#define UCDN_SCRIPT_THAI 19 -#define UCDN_SCRIPT_LAO 20 -#define UCDN_SCRIPT_TIBETAN 21 -#define UCDN_SCRIPT_MYANMAR 22 -#define UCDN_SCRIPT_GEORGIAN 23 -#define UCDN_SCRIPT_HANGUL 24 -#define UCDN_SCRIPT_ETHIOPIC 25 -#define UCDN_SCRIPT_CHEROKEE 26 -#define UCDN_SCRIPT_CANADIAN_ABORIGINAL 27 -#define UCDN_SCRIPT_OGHAM 28 -#define UCDN_SCRIPT_RUNIC 29 -#define UCDN_SCRIPT_KHMER 30 -#define UCDN_SCRIPT_MONGOLIAN 31 -#define UCDN_SCRIPT_HIRAGANA 32 -#define UCDN_SCRIPT_KATAKANA 33 -#define UCDN_SCRIPT_BOPOMOFO 34 -#define UCDN_SCRIPT_HAN 35 -#define UCDN_SCRIPT_YI 36 -#define UCDN_SCRIPT_OLD_ITALIC 37 -#define UCDN_SCRIPT_GOTHIC 38 -#define UCDN_SCRIPT_DESERET 39 -#define UCDN_SCRIPT_INHERITED 40 -#define UCDN_SCRIPT_TAGALOG 41 -#define UCDN_SCRIPT_HANUNOO 42 -#define UCDN_SCRIPT_BUHID 43 -#define UCDN_SCRIPT_TAGBANWA 44 -#define UCDN_SCRIPT_LIMBU 45 -#define UCDN_SCRIPT_TAI_LE 46 -#define UCDN_SCRIPT_LINEAR_B 47 -#define UCDN_SCRIPT_UGARITIC 48 -#define UCDN_SCRIPT_SHAVIAN 49 -#define UCDN_SCRIPT_OSMANYA 50 -#define UCDN_SCRIPT_CYPRIOT 51 -#define UCDN_SCRIPT_BRAILLE 52 -#define UCDN_SCRIPT_BUGINESE 53 -#define UCDN_SCRIPT_COPTIC 54 -#define UCDN_SCRIPT_NEW_TAI_LUE 55 -#define UCDN_SCRIPT_GLAGOLITIC 56 -#define UCDN_SCRIPT_TIFINAGH 57 -#define UCDN_SCRIPT_SYLOTI_NAGRI 58 -#define UCDN_SCRIPT_OLD_PERSIAN 59 -#define UCDN_SCRIPT_KHAROSHTHI 60 -#define UCDN_SCRIPT_BALINESE 61 -#define UCDN_SCRIPT_CUNEIFORM 62 -#define UCDN_SCRIPT_PHOENICIAN 63 -#define UCDN_SCRIPT_PHAGS_PA 64 -#define UCDN_SCRIPT_NKO 65 -#define UCDN_SCRIPT_SUNDANESE 66 -#define UCDN_SCRIPT_LEPCHA 67 -#define UCDN_SCRIPT_OL_CHIKI 68 -#define UCDN_SCRIPT_VAI 69 -#define UCDN_SCRIPT_SAURASHTRA 70 -#define UCDN_SCRIPT_KAYAH_LI 71 -#define UCDN_SCRIPT_REJANG 72 -#define UCDN_SCRIPT_LYCIAN 73 -#define UCDN_SCRIPT_CARIAN 74 -#define UCDN_SCRIPT_LYDIAN 75 -#define UCDN_SCRIPT_CHAM 76 -#define UCDN_SCRIPT_TAI_THAM 77 -#define UCDN_SCRIPT_TAI_VIET 78 -#define UCDN_SCRIPT_AVESTAN 79 -#define UCDN_SCRIPT_EGYPTIAN_HIEROGLYPHS 80 -#define UCDN_SCRIPT_SAMARITAN 81 -#define UCDN_SCRIPT_LISU 82 -#define UCDN_SCRIPT_BAMUM 83 -#define UCDN_SCRIPT_JAVANESE 84 -#define UCDN_SCRIPT_MEETEI_MAYEK 85 -#define UCDN_SCRIPT_IMPERIAL_ARAMAIC 86 -#define UCDN_SCRIPT_OLD_SOUTH_ARABIAN 87 -#define UCDN_SCRIPT_INSCRIPTIONAL_PARTHIAN 88 -#define UCDN_SCRIPT_INSCRIPTIONAL_PAHLAVI 89 -#define UCDN_SCRIPT_OLD_TURKIC 90 -#define UCDN_SCRIPT_KAITHI 91 -#define UCDN_SCRIPT_BATAK 92 -#define UCDN_SCRIPT_BRAHMI 93 -#define UCDN_SCRIPT_MANDAIC 94 -#define UCDN_SCRIPT_CHAKMA 95 -#define UCDN_SCRIPT_MEROITIC_CURSIVE 96 -#define UCDN_SCRIPT_MEROITIC_HIEROGLYPHS 97 -#define UCDN_SCRIPT_MIAO 98 -#define UCDN_SCRIPT_SHARADA 99 -#define UCDN_SCRIPT_SORA_SOMPENG 100 -#define UCDN_SCRIPT_TAKRI 101 -#define UCDN_SCRIPT_UNKNOWN 102 -#define UCDN_SCRIPT_BASSA_VAH 103 -#define UCDN_SCRIPT_CAUCASIAN_ALBANIAN 104 -#define UCDN_SCRIPT_DUPLOYAN 105 -#define UCDN_SCRIPT_ELBASAN 106 -#define UCDN_SCRIPT_GRANTHA 107 -#define UCDN_SCRIPT_KHOJKI 108 -#define UCDN_SCRIPT_KHUDAWADI 109 -#define UCDN_SCRIPT_LINEAR_A 110 -#define UCDN_SCRIPT_MAHAJANI 111 -#define UCDN_SCRIPT_MANICHAEAN 112 -#define UCDN_SCRIPT_MENDE_KIKAKUI 113 -#define UCDN_SCRIPT_MODI 114 -#define UCDN_SCRIPT_MRO 115 -#define UCDN_SCRIPT_NABATAEAN 116 -#define UCDN_SCRIPT_OLD_NORTH_ARABIAN 117 -#define UCDN_SCRIPT_OLD_PERMIC 118 -#define UCDN_SCRIPT_PAHAWH_HMONG 119 -#define UCDN_SCRIPT_PALMYRENE 120 -#define UCDN_SCRIPT_PAU_CIN_HAU 121 -#define UCDN_SCRIPT_PSALTER_PAHLAVI 122 -#define UCDN_SCRIPT_SIDDHAM 123 -#define UCDN_SCRIPT_TIRHUTA 124 -#define UCDN_SCRIPT_WARANG_CITI 125 -#define UCDN_SCRIPT_AHOM 126 -#define UCDN_SCRIPT_ANATOLIAN_HIEROGLYPHS 127 -#define UCDN_SCRIPT_HATRAN 128 -#define UCDN_SCRIPT_MULTANI 129 -#define UCDN_SCRIPT_OLD_HUNGARIAN 130 -#define UCDN_SCRIPT_SIGNWRITING 131 -#define UCDN_SCRIPT_ADLAM 132 -#define UCDN_SCRIPT_BHAIKSUKI 133 -#define UCDN_SCRIPT_MARCHEN 134 -#define UCDN_SCRIPT_NEWA 135 -#define UCDN_SCRIPT_OSAGE 136 -#define UCDN_SCRIPT_TANGUT 137 -#define UCDN_SCRIPT_MASARAM_GONDI 138 -#define UCDN_SCRIPT_NUSHU 139 -#define UCDN_SCRIPT_SOYOMBO 140 -#define UCDN_SCRIPT_ZANABAZAR_SQUARE 141 - -#define UCDN_LINEBREAK_CLASS_OP 0 -#define UCDN_LINEBREAK_CLASS_CL 1 -#define UCDN_LINEBREAK_CLASS_CP 2 -#define UCDN_LINEBREAK_CLASS_QU 3 -#define UCDN_LINEBREAK_CLASS_GL 4 -#define UCDN_LINEBREAK_CLASS_NS 5 -#define UCDN_LINEBREAK_CLASS_EX 6 -#define UCDN_LINEBREAK_CLASS_SY 7 -#define UCDN_LINEBREAK_CLASS_IS 8 -#define UCDN_LINEBREAK_CLASS_PR 9 -#define UCDN_LINEBREAK_CLASS_PO 10 -#define UCDN_LINEBREAK_CLASS_NU 11 -#define UCDN_LINEBREAK_CLASS_AL 12 -#define UCDN_LINEBREAK_CLASS_HL 13 -#define UCDN_LINEBREAK_CLASS_ID 14 -#define UCDN_LINEBREAK_CLASS_IN 15 -#define UCDN_LINEBREAK_CLASS_HY 16 -#define UCDN_LINEBREAK_CLASS_BA 17 -#define UCDN_LINEBREAK_CLASS_BB 18 -#define UCDN_LINEBREAK_CLASS_B2 19 -#define UCDN_LINEBREAK_CLASS_ZW 20 -#define UCDN_LINEBREAK_CLASS_CM 21 -#define UCDN_LINEBREAK_CLASS_WJ 22 -#define UCDN_LINEBREAK_CLASS_H2 23 -#define UCDN_LINEBREAK_CLASS_H3 24 -#define UCDN_LINEBREAK_CLASS_JL 25 -#define UCDN_LINEBREAK_CLASS_JV 26 -#define UCDN_LINEBREAK_CLASS_JT 27 -#define UCDN_LINEBREAK_CLASS_RI 28 -#define UCDN_LINEBREAK_CLASS_AI 29 -#define UCDN_LINEBREAK_CLASS_BK 30 -#define UCDN_LINEBREAK_CLASS_CB 31 -#define UCDN_LINEBREAK_CLASS_CJ 32 -#define UCDN_LINEBREAK_CLASS_CR 33 -#define UCDN_LINEBREAK_CLASS_LF 34 -#define UCDN_LINEBREAK_CLASS_NL 35 -#define UCDN_LINEBREAK_CLASS_SA 36 -#define UCDN_LINEBREAK_CLASS_SG 37 -#define UCDN_LINEBREAK_CLASS_SP 38 -#define UCDN_LINEBREAK_CLASS_XX 39 -#define UCDN_LINEBREAK_CLASS_ZWJ 40 -#define UCDN_LINEBREAK_CLASS_EB 41 -#define UCDN_LINEBREAK_CLASS_EM 42 - -#define UCDN_GENERAL_CATEGORY_CC 0 -#define UCDN_GENERAL_CATEGORY_CF 1 -#define UCDN_GENERAL_CATEGORY_CN 2 -#define UCDN_GENERAL_CATEGORY_CO 3 -#define UCDN_GENERAL_CATEGORY_CS 4 -#define UCDN_GENERAL_CATEGORY_LL 5 -#define UCDN_GENERAL_CATEGORY_LM 6 -#define UCDN_GENERAL_CATEGORY_LO 7 -#define UCDN_GENERAL_CATEGORY_LT 8 -#define UCDN_GENERAL_CATEGORY_LU 9 -#define UCDN_GENERAL_CATEGORY_MC 10 -#define UCDN_GENERAL_CATEGORY_ME 11 -#define UCDN_GENERAL_CATEGORY_MN 12 -#define UCDN_GENERAL_CATEGORY_ND 13 -#define UCDN_GENERAL_CATEGORY_NL 14 -#define UCDN_GENERAL_CATEGORY_NO 15 -#define UCDN_GENERAL_CATEGORY_PC 16 -#define UCDN_GENERAL_CATEGORY_PD 17 -#define UCDN_GENERAL_CATEGORY_PE 18 -#define UCDN_GENERAL_CATEGORY_PF 19 -#define UCDN_GENERAL_CATEGORY_PI 20 -#define UCDN_GENERAL_CATEGORY_PO 21 -#define UCDN_GENERAL_CATEGORY_PS 22 -#define UCDN_GENERAL_CATEGORY_SC 23 -#define UCDN_GENERAL_CATEGORY_SK 24 -#define UCDN_GENERAL_CATEGORY_SM 25 -#define UCDN_GENERAL_CATEGORY_SO 26 -#define UCDN_GENERAL_CATEGORY_ZL 27 -#define UCDN_GENERAL_CATEGORY_ZP 28 -#define UCDN_GENERAL_CATEGORY_ZS 29 - -#define UCDN_BIDI_CLASS_L 0 -#define UCDN_BIDI_CLASS_LRE 1 -#define UCDN_BIDI_CLASS_LRO 2 -#define UCDN_BIDI_CLASS_R 3 -#define UCDN_BIDI_CLASS_AL 4 -#define UCDN_BIDI_CLASS_RLE 5 -#define UCDN_BIDI_CLASS_RLO 6 -#define UCDN_BIDI_CLASS_PDF 7 -#define UCDN_BIDI_CLASS_EN 8 -#define UCDN_BIDI_CLASS_ES 9 -#define UCDN_BIDI_CLASS_ET 10 -#define UCDN_BIDI_CLASS_AN 11 -#define UCDN_BIDI_CLASS_CS 12 -#define UCDN_BIDI_CLASS_NSM 13 -#define UCDN_BIDI_CLASS_BN 14 -#define UCDN_BIDI_CLASS_B 15 -#define UCDN_BIDI_CLASS_S 16 -#define UCDN_BIDI_CLASS_WS 17 -#define UCDN_BIDI_CLASS_ON 18 -#define UCDN_BIDI_CLASS_LRI 19 -#define UCDN_BIDI_CLASS_RLI 20 -#define UCDN_BIDI_CLASS_FSI 21 -#define UCDN_BIDI_CLASS_PDI 22 - -#define UCDN_BIDI_PAIRED_BRACKET_TYPE_OPEN 0 -#define UCDN_BIDI_PAIRED_BRACKET_TYPE_CLOSE 1 -#define UCDN_BIDI_PAIRED_BRACKET_TYPE_NONE 2 - -/** - * Return version of the Unicode database. - * - * @return Unicode database version - */ -const char *ucdn_get_unicode_version(void); - -/** - * Get combining class of a codepoint. - * - * @param code Unicode codepoint - * @return combining class value, as defined in UAX#44 - */ -int ucdn_get_combining_class(uint32_t code); - -/** - * Get east-asian width of a codepoint. - * - * @param code Unicode codepoint - * @return value according to UCDN_EAST_ASIAN_* and as defined in UAX#11. - */ -int ucdn_get_east_asian_width(uint32_t code); - -/** - * Get general category of a codepoint. - * - * @param code Unicode codepoint - * @return value according to UCDN_GENERAL_CATEGORY_* and as defined in - * UAX#44. - */ -int ucdn_get_general_category(uint32_t code); - -/** - * Get bidirectional class of a codepoint. - * - * @param code Unicode codepoint - * @return value according to UCDN_BIDI_CLASS_* and as defined in UAX#44. - */ -int ucdn_get_bidi_class(uint32_t code); - -/** - * Get script of a codepoint. - * - * @param code Unicode codepoint - * @return value according to UCDN_SCRIPT_* and as defined in UAX#24. - */ -int ucdn_get_script(uint32_t code); - -/** - * Get unresolved linebreak class of a codepoint. This does not take - * rule LB1 of UAX#14 into account. See ucdn_get_resolved_linebreak_class() - * for resolved linebreak classes. - * - * @param code Unicode codepoint - * @return value according to UCDN_LINEBREAK_* and as defined in UAX#14. - */ -int ucdn_get_linebreak_class(uint32_t code); - -/** - * Get resolved linebreak class of a codepoint. This resolves characters - * in the AI, SG, XX, SA and CJ classes according to rule LB1 of UAX#14. - * In addition the CB class is resolved as the equivalent B2 class and - * the NL class is resolved as the equivalent BK class. - * - * @param code Unicode codepoint - * @return value according to UCDN_LINEBREAK_* and as defined in UAX#14. - */ -int ucdn_get_resolved_linebreak_class(uint32_t code); - -/** - * Check if codepoint can be mirrored. - * - * @param code Unicode codepoint - * @return 1 if mirrored character exists, otherwise 0 - */ -int ucdn_get_mirrored(uint32_t code); - -/** - * Mirror a codepoint. - * - * @param code Unicode codepoint - * @return mirrored codepoint or the original codepoint if no - * mirrored character exists - */ -uint32_t ucdn_mirror(uint32_t code); - -/** - * Get paired bracket for a codepoint. - * - * @param code Unicode codepoint - * @return paired bracket codepoint or the original codepoint if no - * paired bracket character exists - */ -uint32_t ucdn_paired_bracket(uint32_t code); - -/** - * Get paired bracket type for a codepoint. - * - * @param code Unicode codepoint - * @return value according to UCDN_BIDI_PAIRED_BRACKET_TYPE_* and as defined - * in UAX#9. - * - */ -int ucdn_paired_bracket_type(uint32_t code); - -/** - * Pairwise canonical decomposition of a codepoint. This includes - * Hangul Jamo decomposition (see chapter 3.12 of the Unicode core - * specification). - * - * Hangul is decomposed into L and V jamos for LV forms, and an - * LV precomposed syllable and a T jamo for LVT forms. - * - * @param code Unicode codepoint - * @param a filled with first codepoint of decomposition - * @param b filled with second codepoint of decomposition, or 0 - * @return success - */ -int ucdn_decompose(uint32_t code, uint32_t *a, uint32_t *b); - -/** - * Compatibility decomposition of a codepoint. - * - * @param code Unicode codepoint - * @param decomposed filled with decomposition, must be able to hold 18 - * characters - * @return length of decomposition or 0 in case none exists - */ -int ucdn_compat_decompose(uint32_t code, uint32_t *decomposed); - -/** - * Pairwise canonical composition of two codepoints. This includes - * Hangul Jamo composition (see chapter 3.12 of the Unicode core - * specification). - * - * Hangul composition expects either L and V jamos, or an LV - * precomposed syllable and a T jamo. This is exactly the inverse - * of pairwise Hangul decomposition. - * - * @param code filled with composition - * @param a first codepoint - * @param b second codepoint - * @return success - */ -int ucdn_compose(uint32_t *code, uint32_t a, uint32_t b); - -HB_END_HEADER - -#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn_db.h b/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn_db.h deleted file mode 100644 index d72c7ba0dff..00000000000 --- a/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn_db.h +++ /dev/null @@ -1,5730 +0,0 @@ -/* this file was generated by makeunicodedata.py 3.2 */ - -#define UNIDATA_VERSION "11.0.0" -/* a list of unique database records */ -static const UCDRecord ucd_records[] = { - {2, 0, 18, 5, 102, 39}, - {0, 0, 14, 5, 0, 21}, - {0, 0, 16, 5, 0, 17}, - {0, 0, 15, 5, 0, 34}, - {0, 0, 16, 5, 0, 30}, - {0, 0, 17, 5, 0, 30}, - {0, 0, 15, 5, 0, 33}, - {0, 0, 15, 5, 0, 21}, - {0, 0, 16, 5, 0, 21}, - {29, 0, 17, 3, 0, 38}, - {21, 0, 18, 3, 0, 6}, - {21, 0, 18, 3, 0, 3}, - {21, 0, 10, 3, 0, 12}, - {23, 0, 10, 3, 0, 9}, - {21, 0, 10, 3, 0, 10}, - {21, 0, 18, 3, 0, 12}, - {22, 0, 18, 3, 0, 0}, - {18, 0, 18, 3, 0, 2}, - {25, 0, 9, 3, 0, 9}, - {21, 0, 12, 3, 0, 8}, - {17, 0, 9, 3, 0, 16}, - {21, 0, 12, 3, 0, 7}, - {13, 0, 8, 3, 0, 11}, - {21, 0, 18, 3, 0, 8}, - {25, 0, 18, 3, 0, 12}, - {9, 0, 0, 3, 1, 12}, - {21, 0, 18, 3, 0, 9}, - {24, 0, 18, 3, 0, 12}, - {16, 0, 18, 3, 0, 12}, - {5, 0, 0, 3, 1, 12}, - {25, 0, 18, 3, 0, 17}, - {18, 0, 18, 3, 0, 1}, - {0, 0, 15, 5, 0, 35}, - {29, 0, 12, 5, 0, 4}, - {21, 0, 18, 4, 0, 0}, - {23, 0, 10, 3, 0, 10}, - {23, 0, 10, 4, 0, 9}, - {26, 0, 18, 3, 0, 12}, - {21, 0, 18, 4, 0, 29}, - {24, 0, 18, 4, 0, 29}, - {26, 0, 18, 5, 0, 12}, - {7, 0, 0, 4, 1, 29}, - {20, 0, 18, 5, 0, 3}, - {1, 0, 14, 4, 0, 17}, - {26, 0, 18, 4, 0, 12}, - {26, 0, 10, 4, 0, 10}, - {25, 0, 10, 4, 0, 9}, - {15, 0, 8, 4, 0, 29}, - {24, 0, 18, 4, 0, 18}, - {5, 0, 0, 5, 0, 12}, - {19, 0, 18, 5, 0, 3}, - {15, 0, 18, 4, 0, 29}, - {9, 0, 0, 5, 1, 12}, - {9, 0, 0, 4, 1, 12}, - {25, 0, 18, 4, 0, 29}, - {5, 0, 0, 4, 1, 12}, - {5, 0, 0, 5, 1, 12}, - {7, 0, 0, 5, 1, 12}, - {8, 0, 0, 5, 1, 12}, - {6, 0, 0, 5, 1, 12}, - {6, 0, 18, 5, 0, 12}, - {6, 0, 0, 5, 0, 12}, - {24, 0, 18, 5, 0, 12}, - {24, 0, 18, 4, 0, 12}, - {6, 0, 18, 4, 0, 29}, - {6, 0, 18, 5, 0, 18}, - {6, 0, 0, 4, 0, 29}, - {24, 0, 18, 5, 34, 12}, - {12, 230, 13, 4, 40, 21}, - {12, 232, 13, 4, 40, 21}, - {12, 220, 13, 4, 40, 21}, - {12, 216, 13, 4, 40, 21}, - {12, 202, 13, 4, 40, 21}, - {12, 1, 13, 4, 40, 21}, - {12, 240, 13, 4, 40, 21}, - {12, 0, 13, 4, 40, 4}, - {12, 233, 13, 4, 40, 4}, - {12, 234, 13, 4, 40, 4}, - {9, 0, 0, 5, 2, 12}, - {5, 0, 0, 5, 2, 12}, - {24, 0, 18, 5, 2, 12}, - {2, 0, 18, 5, 102, 39}, - {6, 0, 0, 5, 2, 12}, - {21, 0, 18, 5, 0, 8}, - {21, 0, 18, 5, 0, 12}, - {9, 0, 0, 4, 2, 12}, - {5, 0, 0, 4, 2, 12}, - {9, 0, 0, 5, 54, 12}, - {5, 0, 0, 5, 54, 12}, - {25, 0, 18, 5, 2, 12}, - {9, 0, 0, 5, 3, 12}, - {9, 0, 0, 4, 3, 12}, - {5, 0, 0, 4, 3, 12}, - {5, 0, 0, 5, 3, 12}, - {26, 0, 0, 5, 3, 12}, - {12, 230, 13, 5, 3, 21}, - {12, 230, 13, 5, 40, 21}, - {11, 0, 13, 5, 3, 21}, - {9, 0, 0, 5, 4, 12}, - {6, 0, 0, 5, 4, 12}, - {21, 0, 0, 5, 4, 12}, - {5, 0, 0, 5, 4, 12}, - {21, 0, 0, 5, 0, 8}, - {17, 0, 18, 5, 4, 17}, - {26, 0, 18, 5, 4, 12}, - {23, 0, 10, 5, 4, 9}, - {12, 220, 13, 5, 5, 21}, - {12, 230, 13, 5, 5, 21}, - {12, 222, 13, 5, 5, 21}, - {12, 228, 13, 5, 5, 21}, - {12, 10, 13, 5, 5, 21}, - {12, 11, 13, 5, 5, 21}, - {12, 12, 13, 5, 5, 21}, - {12, 13, 13, 5, 5, 21}, - {12, 14, 13, 5, 5, 21}, - {12, 15, 13, 5, 5, 21}, - {12, 16, 13, 5, 5, 21}, - {12, 17, 13, 5, 5, 21}, - {12, 18, 13, 5, 5, 21}, - {12, 19, 13, 5, 5, 21}, - {12, 20, 13, 5, 5, 21}, - {12, 21, 13, 5, 5, 21}, - {12, 22, 13, 5, 5, 21}, - {17, 0, 3, 5, 5, 17}, - {12, 23, 13, 5, 5, 21}, - {21, 0, 3, 5, 5, 12}, - {12, 24, 13, 5, 5, 21}, - {12, 25, 13, 5, 5, 21}, - {21, 0, 3, 5, 5, 6}, - {7, 0, 3, 5, 5, 13}, - {1, 0, 11, 5, 6, 12}, - {1, 0, 11, 5, 0, 12}, - {25, 0, 18, 5, 6, 12}, - {25, 0, 4, 5, 6, 12}, - {21, 0, 10, 5, 6, 10}, - {23, 0, 4, 5, 6, 10}, - {21, 0, 12, 5, 0, 8}, - {21, 0, 4, 5, 6, 8}, - {26, 0, 18, 5, 6, 12}, - {12, 230, 13, 5, 6, 21}, - {12, 30, 13, 5, 6, 21}, - {12, 31, 13, 5, 6, 21}, - {12, 32, 13, 5, 6, 21}, - {21, 0, 4, 5, 0, 6}, - {1, 0, 4, 5, 6, 21}, - {21, 0, 4, 5, 6, 6}, - {7, 0, 4, 5, 6, 12}, - {6, 0, 4, 5, 0, 12}, - {12, 27, 13, 5, 40, 21}, - {12, 28, 13, 5, 40, 21}, - {12, 29, 13, 5, 40, 21}, - {12, 30, 13, 5, 40, 21}, - {12, 31, 13, 5, 40, 21}, - {12, 32, 13, 5, 40, 21}, - {12, 33, 13, 5, 40, 21}, - {12, 34, 13, 5, 40, 21}, - {12, 220, 13, 5, 40, 21}, - {12, 220, 13, 5, 6, 21}, - {13, 0, 11, 5, 6, 11}, - {21, 0, 11, 5, 6, 11}, - {21, 0, 4, 5, 6, 12}, - {12, 35, 13, 5, 40, 21}, - {6, 0, 4, 5, 6, 12}, - {13, 0, 8, 5, 6, 11}, - {26, 0, 4, 5, 6, 12}, - {21, 0, 4, 5, 7, 12}, - {1, 0, 4, 5, 7, 12}, - {7, 0, 4, 5, 7, 12}, - {12, 36, 13, 5, 7, 21}, - {12, 230, 13, 5, 7, 21}, - {12, 220, 13, 5, 7, 21}, - {7, 0, 4, 5, 8, 12}, - {12, 0, 13, 5, 8, 21}, - {13, 0, 3, 5, 65, 11}, - {7, 0, 3, 5, 65, 12}, - {12, 230, 13, 5, 65, 21}, - {12, 220, 13, 5, 65, 21}, - {6, 0, 3, 5, 65, 12}, - {26, 0, 18, 5, 65, 12}, - {21, 0, 18, 5, 65, 12}, - {21, 0, 18, 5, 65, 8}, - {21, 0, 18, 5, 65, 6}, - {23, 0, 3, 5, 65, 9}, - {7, 0, 3, 5, 81, 12}, - {12, 230, 13, 5, 81, 21}, - {6, 0, 3, 5, 81, 12}, - {21, 0, 3, 5, 81, 12}, - {7, 0, 3, 5, 94, 12}, - {12, 220, 13, 5, 94, 21}, - {21, 0, 3, 5, 94, 12}, - {12, 27, 13, 5, 6, 21}, - {12, 28, 13, 5, 6, 21}, - {12, 29, 13, 5, 6, 21}, - {12, 0, 13, 5, 9, 21}, - {10, 0, 0, 5, 9, 21}, - {7, 0, 0, 5, 9, 12}, - {12, 7, 13, 5, 9, 21}, - {12, 9, 13, 5, 9, 21}, - {12, 230, 13, 5, 9, 21}, - {21, 0, 0, 5, 0, 17}, - {13, 0, 0, 5, 9, 11}, - {21, 0, 0, 5, 9, 12}, - {6, 0, 0, 5, 9, 12}, - {7, 0, 0, 5, 10, 12}, - {12, 0, 13, 5, 10, 21}, - {10, 0, 0, 5, 10, 21}, - {12, 7, 13, 5, 10, 21}, - {12, 9, 13, 5, 10, 21}, - {13, 0, 0, 5, 10, 11}, - {23, 0, 10, 5, 10, 10}, - {15, 0, 0, 5, 10, 12}, - {15, 0, 0, 5, 10, 10}, - {26, 0, 0, 5, 10, 12}, - {23, 0, 10, 5, 10, 9}, - {21, 0, 0, 5, 10, 12}, - {12, 230, 13, 5, 10, 21}, - {12, 0, 13, 5, 11, 21}, - {10, 0, 0, 5, 11, 21}, - {7, 0, 0, 5, 11, 12}, - {12, 7, 13, 5, 11, 21}, - {12, 9, 13, 5, 11, 21}, - {13, 0, 0, 5, 11, 11}, - {21, 0, 0, 5, 11, 12}, - {12, 0, 13, 5, 12, 21}, - {10, 0, 0, 5, 12, 21}, - {7, 0, 0, 5, 12, 12}, - {12, 7, 13, 5, 12, 21}, - {12, 9, 13, 5, 12, 21}, - {13, 0, 0, 5, 12, 11}, - {21, 0, 0, 5, 12, 12}, - {23, 0, 10, 5, 12, 9}, - {12, 0, 13, 5, 13, 21}, - {10, 0, 0, 5, 13, 21}, - {7, 0, 0, 5, 13, 12}, - {12, 7, 13, 5, 13, 21}, - {12, 9, 13, 5, 13, 21}, - {13, 0, 0, 5, 13, 11}, - {26, 0, 0, 5, 13, 12}, - {15, 0, 0, 5, 13, 12}, - {12, 0, 13, 5, 14, 21}, - {7, 0, 0, 5, 14, 12}, - {10, 0, 0, 5, 14, 21}, - {12, 9, 13, 5, 14, 21}, - {13, 0, 0, 5, 14, 11}, - {15, 0, 0, 5, 14, 12}, - {26, 0, 18, 5, 14, 12}, - {23, 0, 10, 5, 14, 9}, - {12, 0, 13, 5, 15, 21}, - {10, 0, 0, 5, 15, 21}, - {7, 0, 0, 5, 15, 12}, - {12, 9, 13, 5, 15, 21}, - {12, 84, 13, 5, 15, 21}, - {12, 91, 13, 5, 15, 21}, - {13, 0, 0, 5, 15, 11}, - {15, 0, 18, 5, 15, 12}, - {26, 0, 0, 5, 15, 12}, - {7, 0, 0, 5, 16, 12}, - {12, 0, 13, 5, 16, 21}, - {10, 0, 0, 5, 16, 21}, - {21, 0, 0, 5, 16, 18}, - {12, 7, 13, 5, 16, 21}, - {12, 0, 0, 5, 16, 21}, - {12, 9, 13, 5, 16, 21}, - {13, 0, 0, 5, 16, 11}, - {12, 0, 13, 5, 17, 21}, - {10, 0, 0, 5, 17, 21}, - {7, 0, 0, 5, 17, 12}, - {12, 9, 13, 5, 17, 21}, - {26, 0, 0, 5, 17, 12}, - {15, 0, 0, 5, 17, 12}, - {13, 0, 0, 5, 17, 11}, - {26, 0, 0, 5, 17, 10}, - {10, 0, 0, 5, 18, 21}, - {7, 0, 0, 5, 18, 12}, - {12, 9, 13, 5, 18, 21}, - {12, 0, 13, 5, 18, 21}, - {13, 0, 0, 5, 18, 11}, - {21, 0, 0, 5, 18, 12}, - {7, 0, 0, 5, 19, 36}, - {12, 0, 13, 5, 19, 36}, - {12, 103, 13, 5, 19, 36}, - {12, 9, 13, 5, 19, 36}, - {23, 0, 10, 5, 0, 9}, - {6, 0, 0, 5, 19, 36}, - {12, 107, 13, 5, 19, 36}, - {21, 0, 0, 5, 19, 12}, - {13, 0, 0, 5, 19, 11}, - {21, 0, 0, 5, 19, 17}, - {7, 0, 0, 5, 20, 36}, - {12, 0, 13, 5, 20, 36}, - {12, 118, 13, 5, 20, 36}, - {6, 0, 0, 5, 20, 36}, - {12, 122, 13, 5, 20, 36}, - {13, 0, 0, 5, 20, 11}, - {7, 0, 0, 5, 21, 12}, - {26, 0, 0, 5, 21, 18}, - {21, 0, 0, 5, 21, 18}, - {21, 0, 0, 5, 21, 12}, - {21, 0, 0, 5, 21, 4}, - {21, 0, 0, 5, 21, 17}, - {21, 0, 0, 5, 21, 6}, - {26, 0, 0, 5, 21, 12}, - {12, 220, 13, 5, 21, 21}, - {13, 0, 0, 5, 21, 11}, - {15, 0, 0, 5, 21, 12}, - {26, 0, 0, 5, 21, 17}, - {12, 216, 13, 5, 21, 21}, - {22, 0, 18, 5, 21, 0}, - {18, 0, 18, 5, 21, 1}, - {10, 0, 0, 5, 21, 21}, - {12, 129, 13, 5, 21, 21}, - {12, 130, 13, 5, 21, 21}, - {12, 0, 13, 5, 21, 21}, - {12, 132, 13, 5, 21, 21}, - {10, 0, 0, 5, 21, 17}, - {12, 230, 13, 5, 21, 21}, - {12, 9, 13, 5, 21, 21}, - {26, 0, 0, 5, 0, 12}, - {7, 0, 0, 5, 22, 36}, - {10, 0, 0, 5, 22, 36}, - {12, 0, 13, 5, 22, 36}, - {12, 7, 13, 5, 22, 36}, - {12, 9, 13, 5, 22, 36}, - {13, 0, 0, 5, 22, 11}, - {21, 0, 0, 5, 22, 17}, - {21, 0, 0, 5, 22, 12}, - {12, 220, 13, 5, 22, 36}, - {26, 0, 0, 5, 22, 36}, - {9, 0, 0, 5, 23, 12}, - {5, 0, 0, 5, 23, 12}, - {21, 0, 0, 5, 0, 12}, - {6, 0, 0, 5, 23, 12}, - {7, 0, 0, 2, 24, 25}, - {7, 0, 0, 5, 24, 26}, - {7, 0, 0, 5, 24, 27}, - {7, 0, 0, 5, 25, 12}, - {12, 230, 13, 5, 25, 21}, - {21, 0, 0, 5, 25, 12}, - {21, 0, 0, 5, 25, 17}, - {15, 0, 0, 5, 25, 12}, - {26, 0, 18, 5, 25, 12}, - {9, 0, 0, 5, 26, 12}, - {5, 0, 0, 5, 26, 12}, - {17, 0, 18, 5, 27, 17}, - {7, 0, 0, 5, 27, 12}, - {21, 0, 0, 5, 27, 12}, - {29, 0, 17, 5, 28, 17}, - {7, 0, 0, 5, 28, 12}, - {22, 0, 18, 5, 28, 0}, - {18, 0, 18, 5, 28, 1}, - {7, 0, 0, 5, 29, 12}, - {14, 0, 0, 5, 29, 12}, - {7, 0, 0, 5, 41, 12}, - {12, 0, 13, 5, 41, 21}, - {12, 9, 13, 5, 41, 21}, - {7, 0, 0, 5, 42, 12}, - {12, 0, 13, 5, 42, 21}, - {12, 9, 13, 5, 42, 21}, - {7, 0, 0, 5, 43, 12}, - {12, 0, 13, 5, 43, 21}, - {7, 0, 0, 5, 44, 12}, - {12, 0, 13, 5, 44, 21}, - {7, 0, 0, 5, 30, 36}, - {12, 0, 13, 5, 30, 36}, - {10, 0, 0, 5, 30, 36}, - {12, 9, 13, 5, 30, 36}, - {21, 0, 0, 5, 30, 17}, - {21, 0, 0, 5, 30, 5}, - {6, 0, 0, 5, 30, 36}, - {21, 0, 0, 5, 30, 12}, - {23, 0, 10, 5, 30, 9}, - {12, 230, 13, 5, 30, 36}, - {13, 0, 0, 5, 30, 11}, - {15, 0, 18, 5, 30, 12}, - {21, 0, 18, 5, 31, 12}, - {21, 0, 18, 5, 0, 6}, - {21, 0, 18, 5, 31, 17}, - {21, 0, 18, 5, 0, 17}, - {17, 0, 18, 5, 31, 18}, - {21, 0, 18, 5, 31, 6}, - {12, 0, 13, 5, 31, 21}, - {1, 0, 14, 5, 31, 4}, - {13, 0, 0, 5, 31, 11}, - {7, 0, 0, 5, 31, 12}, - {6, 0, 0, 5, 31, 12}, - {12, 228, 13, 5, 31, 21}, - {7, 0, 0, 5, 45, 12}, - {12, 0, 13, 5, 45, 21}, - {10, 0, 0, 5, 45, 21}, - {12, 222, 13, 5, 45, 21}, - {12, 230, 13, 5, 45, 21}, - {12, 220, 13, 5, 45, 21}, - {26, 0, 18, 5, 45, 12}, - {21, 0, 18, 5, 45, 6}, - {13, 0, 0, 5, 45, 11}, - {7, 0, 0, 5, 46, 36}, - {7, 0, 0, 5, 55, 36}, - {13, 0, 0, 5, 55, 11}, - {15, 0, 0, 5, 55, 36}, - {26, 0, 18, 5, 55, 36}, - {26, 0, 18, 5, 30, 12}, - {7, 0, 0, 5, 53, 12}, - {12, 230, 13, 5, 53, 21}, - {12, 220, 13, 5, 53, 21}, - {10, 0, 0, 5, 53, 21}, - {12, 0, 13, 5, 53, 21}, - {21, 0, 0, 5, 53, 12}, - {7, 0, 0, 5, 77, 36}, - {10, 0, 0, 5, 77, 36}, - {12, 0, 13, 5, 77, 36}, - {12, 9, 13, 5, 77, 36}, - {12, 230, 13, 5, 77, 36}, - {12, 220, 13, 5, 77, 21}, - {13, 0, 0, 5, 77, 11}, - {21, 0, 0, 5, 77, 36}, - {6, 0, 0, 5, 77, 36}, - {11, 0, 13, 5, 40, 21}, - {12, 0, 13, 5, 61, 21}, - {10, 0, 0, 5, 61, 21}, - {7, 0, 0, 5, 61, 12}, - {12, 7, 13, 5, 61, 21}, - {10, 9, 0, 5, 61, 21}, - {13, 0, 0, 5, 61, 11}, - {21, 0, 0, 5, 61, 17}, - {21, 0, 0, 5, 61, 12}, - {26, 0, 0, 5, 61, 12}, - {12, 230, 13, 5, 61, 21}, - {12, 220, 13, 5, 61, 21}, - {12, 0, 13, 5, 66, 21}, - {10, 0, 0, 5, 66, 21}, - {7, 0, 0, 5, 66, 12}, - {10, 9, 0, 5, 66, 21}, - {12, 9, 13, 5, 66, 21}, - {13, 0, 0, 5, 66, 11}, - {7, 0, 0, 5, 92, 12}, - {12, 7, 13, 5, 92, 21}, - {10, 0, 0, 5, 92, 21}, - {12, 0, 13, 5, 92, 21}, - {10, 9, 0, 5, 92, 21}, - {21, 0, 0, 5, 92, 12}, - {7, 0, 0, 5, 67, 12}, - {10, 0, 0, 5, 67, 21}, - {12, 0, 13, 5, 67, 21}, - {12, 7, 13, 5, 67, 21}, - {21, 0, 0, 5, 67, 17}, - {13, 0, 0, 5, 67, 11}, - {13, 0, 0, 5, 68, 11}, - {7, 0, 0, 5, 68, 12}, - {6, 0, 0, 5, 68, 12}, - {21, 0, 0, 5, 68, 17}, - {21, 0, 0, 5, 66, 12}, - {12, 1, 13, 5, 40, 21}, - {10, 0, 0, 5, 0, 21}, - {7, 0, 0, 5, 0, 12}, - {6, 0, 0, 5, 3, 12}, - {12, 234, 13, 5, 40, 21}, - {12, 214, 13, 5, 40, 21}, - {12, 202, 13, 5, 40, 21}, - {12, 232, 13, 5, 40, 21}, - {12, 228, 13, 5, 40, 21}, - {12, 233, 13, 5, 40, 21}, - {8, 0, 0, 5, 2, 12}, - {24, 0, 18, 5, 2, 18}, - {29, 0, 17, 5, 0, 17}, - {29, 0, 17, 5, 0, 4}, - {1, 0, 14, 5, 0, 20}, - {1, 0, 14, 5, 40, 21}, - {1, 0, 14, 5, 40, 40}, - {1, 0, 0, 5, 0, 21}, - {1, 0, 3, 5, 0, 21}, - {17, 0, 18, 4, 0, 17}, - {17, 0, 18, 5, 0, 4}, - {17, 0, 18, 5, 0, 17}, - {17, 0, 18, 4, 0, 19}, - {17, 0, 18, 4, 0, 29}, - {20, 0, 18, 4, 0, 3}, - {19, 0, 18, 4, 0, 3}, - {22, 0, 18, 5, 0, 0}, - {21, 0, 18, 4, 0, 12}, - {21, 0, 18, 4, 0, 15}, - {21, 0, 18, 4, 0, 17}, - {27, 0, 17, 5, 0, 30}, - {28, 0, 15, 5, 0, 30}, - {1, 0, 1, 5, 0, 21}, - {1, 0, 5, 5, 0, 21}, - {1, 0, 7, 5, 0, 21}, - {1, 0, 2, 5, 0, 21}, - {1, 0, 6, 5, 0, 21}, - {21, 0, 10, 4, 0, 10}, - {21, 0, 10, 5, 0, 10}, - {21, 0, 18, 4, 0, 10}, - {21, 0, 18, 5, 0, 10}, - {21, 0, 18, 5, 0, 5}, - {16, 0, 18, 5, 0, 12}, - {25, 0, 12, 5, 0, 8}, - {18, 0, 18, 5, 0, 1}, - {25, 0, 18, 5, 0, 12}, - {1, 0, 14, 5, 0, 22}, - {1, 0, 14, 5, 0, 12}, - {1, 0, 19, 5, 0, 21}, - {1, 0, 20, 5, 0, 21}, - {1, 0, 21, 5, 0, 21}, - {1, 0, 22, 5, 0, 21}, - {1, 0, 14, 5, 0, 21}, - {15, 0, 8, 5, 0, 12}, - {25, 0, 9, 5, 0, 12}, - {6, 0, 0, 4, 1, 29}, - {23, 0, 10, 5, 0, 10}, - {23, 0, 10, 1, 0, 9}, - {2, 0, 18, 5, 102, 9}, - {9, 0, 0, 5, 0, 12}, - {26, 0, 18, 4, 0, 10}, - {26, 0, 18, 4, 0, 29}, - {5, 0, 0, 4, 0, 29}, - {26, 0, 18, 4, 0, 9}, - {9, 0, 0, 4, 1, 29}, - {26, 0, 10, 5, 0, 12}, - {15, 0, 18, 5, 0, 12}, - {15, 0, 18, 4, 0, 12}, - {15, 0, 18, 5, 0, 29}, - {14, 0, 0, 4, 1, 29}, - {14, 0, 0, 5, 1, 12}, - {25, 0, 9, 5, 0, 9}, - {25, 0, 10, 5, 0, 9}, - {25, 0, 18, 5, 0, 15}, - {26, 0, 18, 2, 0, 14}, - {22, 0, 18, 2, 0, 0}, - {18, 0, 18, 2, 0, 1}, - {26, 0, 18, 2, 0, 12}, - {26, 0, 18, 5, 0, 14}, - {26, 0, 0, 4, 0, 29}, - {26, 0, 18, 5, 0, 29}, - {25, 0, 18, 2, 0, 12}, - {26, 0, 18, 4, 0, 14}, - {26, 0, 18, 5, 0, 41}, - {26, 0, 18, 4, 0, 41}, - {26, 0, 18, 2, 0, 41}, - {26, 0, 18, 2, 0, 29}, - {26, 0, 18, 5, 0, 3}, - {26, 0, 18, 5, 0, 6}, - {26, 0, 0, 5, 52, 12}, - {9, 0, 0, 5, 56, 12}, - {5, 0, 0, 5, 56, 12}, - {26, 0, 18, 5, 54, 12}, - {12, 230, 13, 5, 54, 21}, - {21, 0, 18, 5, 54, 6}, - {21, 0, 18, 5, 54, 17}, - {15, 0, 18, 5, 54, 12}, - {7, 0, 0, 5, 57, 12}, - {6, 0, 0, 5, 57, 12}, - {21, 0, 0, 5, 57, 17}, - {12, 9, 13, 5, 57, 21}, - {21, 0, 18, 5, 0, 3}, - {21, 0, 18, 5, 0, 0}, - {17, 0, 18, 5, 0, 12}, - {17, 0, 18, 5, 0, 19}, - {26, 0, 18, 2, 35, 14}, - {29, 0, 17, 0, 0, 17}, - {21, 0, 18, 2, 0, 1}, - {21, 0, 18, 2, 0, 14}, - {6, 0, 0, 2, 35, 5}, - {7, 0, 0, 2, 0, 14}, - {14, 0, 0, 2, 35, 14}, - {17, 0, 18, 2, 0, 5}, - {12, 218, 13, 2, 40, 21}, - {12, 228, 13, 2, 40, 21}, - {12, 232, 13, 2, 40, 21}, - {12, 222, 13, 2, 40, 21}, - {10, 224, 0, 2, 24, 21}, - {17, 0, 18, 2, 0, 14}, - {6, 0, 0, 2, 0, 14}, - {6, 0, 0, 2, 0, 21}, - {7, 0, 0, 2, 0, 5}, - {7, 0, 0, 2, 32, 32}, - {7, 0, 0, 2, 32, 14}, - {12, 8, 13, 2, 40, 21}, - {24, 0, 18, 2, 0, 5}, - {6, 0, 0, 2, 32, 5}, - {7, 0, 0, 2, 33, 32}, - {7, 0, 0, 2, 33, 14}, - {21, 0, 18, 2, 0, 5}, - {6, 0, 0, 2, 0, 32}, - {6, 0, 0, 2, 33, 5}, - {7, 0, 0, 2, 34, 14}, - {7, 0, 0, 2, 24, 14}, - {26, 0, 0, 2, 0, 14}, - {15, 0, 0, 2, 0, 14}, - {26, 0, 0, 2, 24, 14}, - {26, 0, 18, 2, 24, 14}, - {15, 0, 0, 4, 0, 29}, - {15, 0, 18, 2, 0, 14}, - {26, 0, 0, 2, 33, 14}, - {7, 0, 0, 2, 35, 14}, - {2, 0, 18, 2, 102, 14}, - {7, 0, 0, 2, 36, 14}, - {6, 0, 0, 2, 36, 5}, - {26, 0, 18, 2, 36, 14}, - {7, 0, 0, 5, 82, 12}, - {6, 0, 0, 5, 82, 12}, - {21, 0, 0, 5, 82, 17}, - {7, 0, 0, 5, 69, 12}, - {6, 0, 0, 5, 69, 12}, - {21, 0, 18, 5, 69, 17}, - {21, 0, 18, 5, 69, 6}, - {13, 0, 0, 5, 69, 11}, - {7, 0, 0, 5, 3, 12}, - {21, 0, 18, 5, 3, 12}, - {6, 0, 18, 5, 3, 12}, - {7, 0, 0, 5, 83, 12}, - {14, 0, 0, 5, 83, 12}, - {12, 230, 13, 5, 83, 21}, - {21, 0, 0, 5, 83, 12}, - {21, 0, 0, 5, 83, 17}, - {24, 0, 0, 5, 0, 12}, - {7, 0, 0, 5, 58, 12}, - {12, 0, 13, 5, 58, 21}, - {12, 9, 13, 5, 58, 21}, - {10, 0, 0, 5, 58, 21}, - {26, 0, 18, 5, 58, 12}, - {15, 0, 0, 5, 0, 12}, - {7, 0, 0, 5, 64, 12}, - {21, 0, 18, 5, 64, 18}, - {21, 0, 18, 5, 64, 6}, - {10, 0, 0, 5, 70, 21}, - {7, 0, 0, 5, 70, 12}, - {12, 9, 13, 5, 70, 21}, - {12, 0, 13, 5, 70, 21}, - {21, 0, 0, 5, 70, 17}, - {13, 0, 0, 5, 70, 11}, - {21, 0, 0, 5, 9, 18}, - {13, 0, 0, 5, 71, 11}, - {7, 0, 0, 5, 71, 12}, - {12, 0, 13, 5, 71, 21}, - {12, 220, 13, 5, 71, 21}, - {21, 0, 0, 5, 71, 17}, - {7, 0, 0, 5, 72, 12}, - {12, 0, 13, 5, 72, 21}, - {10, 0, 0, 5, 72, 21}, - {10, 9, 0, 5, 72, 21}, - {21, 0, 0, 5, 72, 12}, - {12, 0, 13, 5, 84, 21}, - {10, 0, 0, 5, 84, 21}, - {7, 0, 0, 5, 84, 12}, - {12, 7, 13, 5, 84, 21}, - {10, 9, 0, 5, 84, 21}, - {21, 0, 0, 5, 84, 12}, - {21, 0, 0, 5, 84, 17}, - {13, 0, 0, 5, 84, 11}, - {6, 0, 0, 5, 22, 36}, - {7, 0, 0, 5, 76, 12}, - {12, 0, 13, 5, 76, 21}, - {10, 0, 0, 5, 76, 21}, - {13, 0, 0, 5, 76, 11}, - {21, 0, 0, 5, 76, 12}, - {21, 0, 0, 5, 76, 17}, - {7, 0, 0, 5, 78, 36}, - {12, 230, 13, 5, 78, 36}, - {12, 220, 13, 5, 78, 36}, - {6, 0, 0, 5, 78, 36}, - {21, 0, 0, 5, 78, 36}, - {7, 0, 0, 5, 85, 12}, - {10, 0, 0, 5, 85, 21}, - {12, 0, 13, 5, 85, 21}, - {21, 0, 0, 5, 85, 17}, - {6, 0, 0, 5, 85, 12}, - {12, 9, 13, 5, 85, 21}, - {13, 0, 0, 5, 85, 11}, - {7, 0, 0, 2, 24, 23}, - {7, 0, 0, 2, 24, 24}, - {4, 0, 0, 5, 102, 37}, - {3, 0, 0, 4, 102, 39}, - {12, 26, 13, 5, 5, 21}, - {25, 0, 9, 5, 5, 12}, - {24, 0, 4, 5, 6, 12}, - {12, 0, 13, 4, 40, 21}, - {21, 0, 18, 2, 0, 8}, - {21, 0, 18, 2, 0, 6}, - {21, 0, 18, 2, 0, 15}, - {16, 0, 18, 2, 0, 14}, - {21, 0, 12, 2, 0, 1}, - {21, 0, 12, 2, 0, 5}, - {21, 0, 10, 2, 0, 14}, - {25, 0, 9, 2, 0, 14}, - {17, 0, 9, 2, 0, 14}, - {25, 0, 18, 2, 0, 14}, - {23, 0, 10, 2, 0, 9}, - {21, 0, 10, 2, 0, 10}, - {21, 0, 18, 0, 0, 6}, - {21, 0, 18, 0, 0, 14}, - {21, 0, 10, 0, 0, 14}, - {23, 0, 10, 0, 0, 9}, - {21, 0, 10, 0, 0, 10}, - {22, 0, 18, 0, 0, 0}, - {18, 0, 18, 0, 0, 1}, - {25, 0, 9, 0, 0, 14}, - {21, 0, 12, 0, 0, 1}, - {17, 0, 9, 0, 0, 14}, - {21, 0, 12, 0, 0, 14}, - {13, 0, 8, 0, 0, 14}, - {21, 0, 12, 0, 0, 5}, - {21, 0, 18, 0, 0, 5}, - {25, 0, 18, 0, 0, 14}, - {9, 0, 0, 0, 1, 14}, - {24, 0, 18, 0, 0, 14}, - {16, 0, 18, 0, 0, 14}, - {5, 0, 0, 0, 1, 14}, - {21, 0, 18, 1, 0, 1}, - {22, 0, 18, 1, 0, 0}, - {18, 0, 18, 1, 0, 1}, - {21, 0, 18, 1, 0, 5}, - {7, 0, 0, 1, 33, 14}, - {7, 0, 0, 1, 33, 32}, - {6, 0, 0, 1, 0, 32}, - {6, 0, 0, 1, 0, 5}, - {7, 0, 0, 1, 24, 14}, - {23, 0, 10, 0, 0, 10}, - {26, 0, 18, 0, 0, 14}, - {26, 0, 18, 1, 0, 12}, - {25, 0, 18, 1, 0, 12}, - {1, 0, 18, 5, 0, 21}, - {26, 0, 18, 5, 0, 31}, - {7, 0, 0, 5, 47, 12}, - {14, 0, 18, 5, 2, 12}, - {15, 0, 18, 5, 2, 12}, - {26, 0, 18, 5, 2, 12}, - {26, 0, 0, 5, 2, 12}, - {7, 0, 0, 5, 73, 12}, - {7, 0, 0, 5, 74, 12}, - {7, 0, 0, 5, 37, 12}, - {15, 0, 0, 5, 37, 12}, - {7, 0, 0, 5, 38, 12}, - {14, 0, 0, 5, 38, 12}, - {7, 0, 0, 5, 118, 12}, - {12, 230, 13, 5, 118, 21}, - {7, 0, 0, 5, 48, 12}, - {21, 0, 0, 5, 48, 17}, - {7, 0, 0, 5, 59, 12}, - {21, 0, 0, 5, 59, 17}, - {14, 0, 0, 5, 59, 12}, - {9, 0, 0, 5, 39, 12}, - {5, 0, 0, 5, 39, 12}, - {7, 0, 0, 5, 49, 12}, - {7, 0, 0, 5, 50, 12}, - {13, 0, 0, 5, 50, 11}, - {9, 0, 0, 5, 136, 12}, - {5, 0, 0, 5, 136, 12}, - {7, 0, 0, 5, 106, 12}, - {7, 0, 0, 5, 104, 12}, - {21, 0, 0, 5, 104, 12}, - {7, 0, 0, 5, 110, 12}, - {7, 0, 3, 5, 51, 12}, - {7, 0, 3, 5, 86, 12}, - {21, 0, 3, 5, 86, 17}, - {15, 0, 3, 5, 86, 12}, - {7, 0, 3, 5, 120, 12}, - {26, 0, 3, 5, 120, 12}, - {15, 0, 3, 5, 120, 12}, - {7, 0, 3, 5, 116, 12}, - {15, 0, 3, 5, 116, 12}, - {7, 0, 3, 5, 128, 12}, - {15, 0, 3, 5, 128, 12}, - {7, 0, 3, 5, 63, 12}, - {15, 0, 3, 5, 63, 12}, - {21, 0, 18, 5, 63, 17}, - {7, 0, 3, 5, 75, 12}, - {21, 0, 3, 5, 75, 12}, - {7, 0, 3, 5, 97, 12}, - {7, 0, 3, 5, 96, 12}, - {15, 0, 3, 5, 96, 12}, - {7, 0, 3, 5, 60, 12}, - {12, 0, 13, 5, 60, 21}, - {12, 220, 13, 5, 60, 21}, - {12, 230, 13, 5, 60, 21}, - {12, 1, 13, 5, 60, 21}, - {12, 9, 13, 5, 60, 21}, - {15, 0, 3, 5, 60, 12}, - {21, 0, 3, 5, 60, 17}, - {21, 0, 3, 5, 60, 12}, - {7, 0, 3, 5, 87, 12}, - {15, 0, 3, 5, 87, 12}, - {21, 0, 3, 5, 87, 12}, - {7, 0, 3, 5, 117, 12}, - {15, 0, 3, 5, 117, 12}, - {7, 0, 3, 5, 112, 12}, - {26, 0, 3, 5, 112, 12}, - {12, 230, 13, 5, 112, 21}, - {12, 220, 13, 5, 112, 21}, - {15, 0, 3, 5, 112, 12}, - {21, 0, 3, 5, 112, 17}, - {21, 0, 3, 5, 112, 15}, - {7, 0, 3, 5, 79, 12}, - {21, 0, 18, 5, 79, 17}, - {7, 0, 3, 5, 88, 12}, - {15, 0, 3, 5, 88, 12}, - {7, 0, 3, 5, 89, 12}, - {15, 0, 3, 5, 89, 12}, - {7, 0, 3, 5, 122, 12}, - {21, 0, 3, 5, 122, 12}, - {15, 0, 3, 5, 122, 12}, - {7, 0, 3, 5, 90, 12}, - {9, 0, 3, 5, 130, 12}, - {5, 0, 3, 5, 130, 12}, - {15, 0, 3, 5, 130, 12}, - {7, 0, 4, 5, 144, 12}, - {12, 230, 13, 5, 144, 21}, - {13, 0, 11, 5, 144, 11}, - {15, 0, 11, 5, 6, 12}, - {7, 0, 3, 5, 147, 12}, - {15, 0, 3, 5, 147, 12}, - {7, 0, 4, 5, 148, 12}, - {12, 220, 13, 5, 148, 21}, - {12, 230, 13, 5, 148, 21}, - {15, 0, 4, 5, 148, 12}, - {21, 0, 4, 5, 148, 12}, - {10, 0, 0, 5, 93, 21}, - {12, 0, 13, 5, 93, 21}, - {7, 0, 0, 5, 93, 12}, - {12, 9, 13, 5, 93, 21}, - {21, 0, 0, 5, 93, 17}, - {21, 0, 0, 5, 93, 12}, - {15, 0, 18, 5, 93, 12}, - {13, 0, 0, 5, 93, 11}, - {12, 0, 13, 5, 91, 21}, - {10, 0, 0, 5, 91, 21}, - {7, 0, 0, 5, 91, 12}, - {12, 9, 13, 5, 91, 21}, - {12, 7, 13, 5, 91, 21}, - {21, 0, 0, 5, 91, 12}, - {1, 0, 0, 5, 91, 12}, - {21, 0, 0, 5, 91, 17}, - {7, 0, 0, 5, 100, 12}, - {13, 0, 0, 5, 100, 11}, - {12, 230, 13, 5, 95, 21}, - {7, 0, 0, 5, 95, 12}, - {12, 0, 13, 5, 95, 21}, - {10, 0, 0, 5, 95, 21}, - {12, 9, 13, 5, 95, 21}, - {13, 0, 0, 5, 95, 11}, - {21, 0, 0, 5, 95, 17}, - {7, 0, 0, 5, 111, 12}, - {12, 7, 13, 5, 111, 21}, - {21, 0, 0, 5, 111, 12}, - {21, 0, 0, 5, 111, 18}, - {12, 0, 13, 5, 99, 21}, - {10, 0, 0, 5, 99, 21}, - {7, 0, 0, 5, 99, 12}, - {10, 9, 0, 5, 99, 21}, - {21, 0, 0, 5, 99, 17}, - {21, 0, 0, 5, 99, 12}, - {12, 7, 13, 5, 99, 21}, - {13, 0, 0, 5, 99, 11}, - {21, 0, 0, 5, 99, 18}, - {15, 0, 0, 5, 18, 12}, - {7, 0, 0, 5, 108, 12}, - {10, 0, 0, 5, 108, 21}, - {12, 0, 13, 5, 108, 21}, - {10, 9, 0, 5, 108, 21}, - {12, 7, 13, 5, 108, 21}, - {21, 0, 0, 5, 108, 17}, - {21, 0, 0, 5, 108, 12}, - {7, 0, 0, 5, 129, 12}, - {21, 0, 0, 5, 129, 17}, - {7, 0, 0, 5, 109, 12}, - {12, 0, 13, 5, 109, 21}, - {10, 0, 0, 5, 109, 21}, - {12, 7, 13, 5, 109, 21}, - {12, 9, 13, 5, 109, 21}, - {13, 0, 0, 5, 109, 11}, - {12, 0, 13, 5, 107, 21}, - {10, 0, 0, 5, 107, 21}, - {7, 0, 0, 5, 107, 12}, - {12, 7, 13, 5, 40, 21}, - {12, 7, 13, 5, 107, 21}, - {10, 9, 0, 5, 107, 21}, - {12, 230, 13, 5, 107, 21}, - {7, 0, 0, 5, 135, 12}, - {10, 0, 0, 5, 135, 21}, - {12, 0, 13, 5, 135, 21}, - {12, 9, 13, 5, 135, 21}, - {12, 7, 13, 5, 135, 21}, - {21, 0, 0, 5, 135, 17}, - {21, 0, 0, 5, 135, 12}, - {13, 0, 0, 5, 135, 11}, - {12, 230, 13, 5, 135, 21}, - {7, 0, 0, 5, 124, 12}, - {10, 0, 0, 5, 124, 21}, - {12, 0, 13, 5, 124, 21}, - {12, 9, 13, 5, 124, 21}, - {12, 7, 13, 5, 124, 21}, - {21, 0, 0, 5, 124, 12}, - {13, 0, 0, 5, 124, 11}, - {7, 0, 0, 5, 123, 12}, - {10, 0, 0, 5, 123, 21}, - {12, 0, 13, 5, 123, 21}, - {12, 9, 13, 5, 123, 21}, - {12, 7, 13, 5, 123, 21}, - {21, 0, 0, 5, 123, 18}, - {21, 0, 0, 5, 123, 17}, - {21, 0, 0, 5, 123, 6}, - {21, 0, 0, 5, 123, 12}, - {7, 0, 0, 5, 114, 12}, - {10, 0, 0, 5, 114, 21}, - {12, 0, 13, 5, 114, 21}, - {12, 9, 13, 5, 114, 21}, - {21, 0, 0, 5, 114, 17}, - {21, 0, 0, 5, 114, 12}, - {13, 0, 0, 5, 114, 11}, - {21, 0, 18, 5, 31, 18}, - {7, 0, 0, 5, 101, 12}, - {12, 0, 13, 5, 101, 21}, - {10, 0, 0, 5, 101, 21}, - {10, 9, 0, 5, 101, 21}, - {12, 7, 13, 5, 101, 21}, - {13, 0, 0, 5, 101, 11}, - {7, 0, 0, 5, 126, 36}, - {12, 0, 13, 5, 126, 36}, - {10, 0, 0, 5, 126, 36}, - {12, 9, 13, 5, 126, 36}, - {13, 0, 0, 5, 126, 11}, - {15, 0, 0, 5, 126, 36}, - {21, 0, 0, 5, 126, 17}, - {26, 0, 0, 5, 126, 36}, - {7, 0, 0, 5, 142, 12}, - {10, 0, 0, 5, 142, 21}, - {12, 0, 13, 5, 142, 21}, - {12, 9, 13, 5, 142, 21}, - {12, 7, 13, 5, 142, 21}, - {21, 0, 0, 5, 142, 12}, - {9, 0, 0, 5, 125, 12}, - {5, 0, 0, 5, 125, 12}, - {13, 0, 0, 5, 125, 11}, - {15, 0, 0, 5, 125, 12}, - {7, 0, 0, 5, 125, 12}, - {7, 0, 0, 5, 141, 12}, - {12, 0, 13, 5, 141, 21}, - {12, 0, 0, 5, 141, 21}, - {12, 9, 13, 5, 141, 21}, - {10, 0, 0, 5, 141, 21}, - {21, 0, 0, 5, 141, 18}, - {21, 0, 0, 5, 141, 12}, - {21, 0, 0, 5, 141, 17}, - {7, 0, 0, 5, 140, 12}, - {12, 0, 13, 5, 140, 21}, - {10, 0, 0, 5, 140, 21}, - {12, 9, 13, 5, 140, 21}, - {21, 0, 0, 5, 140, 17}, - {21, 0, 0, 5, 140, 18}, - {7, 0, 0, 5, 121, 12}, - {7, 0, 0, 5, 133, 12}, - {10, 0, 0, 5, 133, 21}, - {12, 0, 13, 5, 133, 21}, - {12, 9, 0, 5, 133, 21}, - {21, 0, 0, 5, 133, 17}, - {13, 0, 0, 5, 133, 11}, - {15, 0, 0, 5, 133, 12}, - {21, 0, 0, 5, 134, 18}, - {21, 0, 0, 5, 134, 6}, - {7, 0, 0, 5, 134, 12}, - {12, 0, 13, 5, 134, 21}, - {10, 0, 0, 5, 134, 21}, - {7, 0, 0, 5, 138, 12}, - {12, 0, 13, 5, 138, 21}, - {12, 7, 13, 5, 138, 21}, - {12, 9, 13, 5, 138, 21}, - {13, 0, 0, 5, 138, 11}, - {7, 0, 0, 5, 143, 12}, - {10, 0, 0, 5, 143, 21}, - {12, 0, 13, 5, 143, 21}, - {12, 9, 13, 5, 143, 21}, - {13, 0, 0, 5, 143, 11}, - {7, 0, 0, 5, 145, 12}, - {12, 0, 13, 5, 145, 21}, - {10, 0, 0, 5, 145, 21}, - {21, 0, 0, 5, 145, 12}, - {7, 0, 0, 5, 62, 12}, - {14, 0, 0, 5, 62, 12}, - {21, 0, 0, 5, 62, 17}, - {7, 0, 0, 5, 80, 12}, - {7, 0, 0, 5, 80, 0}, - {7, 0, 0, 5, 80, 1}, - {7, 0, 0, 5, 127, 12}, - {7, 0, 0, 5, 127, 0}, - {7, 0, 0, 5, 127, 1}, - {7, 0, 0, 5, 115, 12}, - {13, 0, 0, 5, 115, 11}, - {21, 0, 0, 5, 115, 17}, - {7, 0, 0, 5, 103, 12}, - {12, 1, 13, 5, 103, 21}, - {21, 0, 0, 5, 103, 17}, - {7, 0, 0, 5, 119, 12}, - {12, 230, 13, 5, 119, 21}, - {21, 0, 0, 5, 119, 17}, - {21, 0, 0, 5, 119, 12}, - {26, 0, 0, 5, 119, 12}, - {6, 0, 0, 5, 119, 12}, - {13, 0, 0, 5, 119, 11}, - {15, 0, 0, 5, 119, 12}, - {9, 0, 0, 5, 146, 12}, - {5, 0, 0, 5, 146, 12}, - {15, 0, 0, 5, 146, 12}, - {21, 0, 0, 5, 146, 17}, - {21, 0, 0, 5, 146, 12}, - {7, 0, 0, 5, 98, 12}, - {10, 0, 0, 5, 98, 21}, - {12, 0, 13, 5, 98, 21}, - {6, 0, 0, 5, 98, 12}, - {6, 0, 0, 2, 137, 5}, - {6, 0, 0, 2, 139, 5}, - {7, 0, 0, 2, 137, 14}, - {7, 0, 0, 2, 139, 14}, - {7, 0, 0, 5, 105, 12}, - {26, 0, 0, 5, 105, 12}, - {12, 0, 13, 5, 105, 21}, - {12, 1, 13, 5, 105, 21}, - {21, 0, 0, 5, 105, 17}, - {10, 216, 0, 5, 0, 21}, - {10, 226, 0, 5, 0, 21}, - {12, 230, 13, 5, 2, 21}, - {25, 0, 0, 5, 0, 12}, - {13, 0, 8, 5, 0, 11}, - {26, 0, 0, 5, 131, 12}, - {12, 0, 13, 5, 131, 21}, - {21, 0, 0, 5, 131, 17}, - {21, 0, 0, 5, 131, 12}, - {12, 230, 13, 5, 56, 21}, - {7, 0, 3, 5, 113, 12}, - {15, 0, 3, 5, 113, 12}, - {12, 220, 13, 5, 113, 21}, - {9, 0, 3, 5, 132, 12}, - {5, 0, 3, 5, 132, 12}, - {12, 230, 13, 5, 132, 21}, - {12, 7, 13, 5, 132, 21}, - {13, 0, 3, 5, 132, 11}, - {21, 0, 3, 5, 132, 0}, - {15, 0, 4, 5, 0, 12}, - {26, 0, 4, 5, 0, 10}, - {23, 0, 4, 5, 0, 10}, - {2, 0, 18, 5, 102, 14}, - {26, 0, 0, 2, 0, 29}, - {26, 0, 0, 5, 0, 28}, - {26, 0, 0, 2, 32, 14}, - {24, 0, 18, 2, 0, 42}, - {26, 0, 18, 5, 0, 5}, -}; - -#define BIDI_MIRROR_LEN 420 -static const MirrorPair mirror_pairs[] = { - {40, 41}, - {41, 40}, - {60, 62}, - {62, 60}, - {91, 93}, - {93, 91}, - {123, 125}, - {125, 123}, - {171, 187}, - {187, 171}, - {3898, 3899}, - {3899, 3898}, - {3900, 3901}, - {3901, 3900}, - {5787, 5788}, - {5788, 5787}, - {8249, 8250}, - {8250, 8249}, - {8261, 8262}, - {8262, 8261}, - {8317, 8318}, - {8318, 8317}, - {8333, 8334}, - {8334, 8333}, - {8712, 8715}, - {8713, 8716}, - {8714, 8717}, - {8715, 8712}, - {8716, 8713}, - {8717, 8714}, - {8725, 10741}, - {8735, 11262}, - {8736, 10659}, - {8737, 10651}, - {8738, 10656}, - {8740, 10990}, - {8764, 8765}, - {8765, 8764}, - {8771, 8909}, - {8773, 8780}, - {8780, 8773}, - {8786, 8787}, - {8787, 8786}, - {8788, 8789}, - {8789, 8788}, - {8804, 8805}, - {8805, 8804}, - {8806, 8807}, - {8807, 8806}, - {8808, 8809}, - {8809, 8808}, - {8810, 8811}, - {8811, 8810}, - {8814, 8815}, - {8815, 8814}, - {8816, 8817}, - {8817, 8816}, - {8818, 8819}, - {8819, 8818}, - {8820, 8821}, - {8821, 8820}, - {8822, 8823}, - {8823, 8822}, - {8824, 8825}, - {8825, 8824}, - {8826, 8827}, - {8827, 8826}, - {8828, 8829}, - {8829, 8828}, - {8830, 8831}, - {8831, 8830}, - {8832, 8833}, - {8833, 8832}, - {8834, 8835}, - {8835, 8834}, - {8836, 8837}, - {8837, 8836}, - {8838, 8839}, - {8839, 8838}, - {8840, 8841}, - {8841, 8840}, - {8842, 8843}, - {8843, 8842}, - {8847, 8848}, - {8848, 8847}, - {8849, 8850}, - {8850, 8849}, - {8856, 10680}, - {8866, 8867}, - {8867, 8866}, - {8870, 10974}, - {8872, 10980}, - {8873, 10979}, - {8875, 10981}, - {8880, 8881}, - {8881, 8880}, - {8882, 8883}, - {8883, 8882}, - {8884, 8885}, - {8885, 8884}, - {8886, 8887}, - {8887, 8886}, - {8888, 10204}, - {8905, 8906}, - {8906, 8905}, - {8907, 8908}, - {8908, 8907}, - {8909, 8771}, - {8912, 8913}, - {8913, 8912}, - {8918, 8919}, - {8919, 8918}, - {8920, 8921}, - {8921, 8920}, - {8922, 8923}, - {8923, 8922}, - {8924, 8925}, - {8925, 8924}, - {8926, 8927}, - {8927, 8926}, - {8928, 8929}, - {8929, 8928}, - {8930, 8931}, - {8931, 8930}, - {8932, 8933}, - {8933, 8932}, - {8934, 8935}, - {8935, 8934}, - {8936, 8937}, - {8937, 8936}, - {8938, 8939}, - {8939, 8938}, - {8940, 8941}, - {8941, 8940}, - {8944, 8945}, - {8945, 8944}, - {8946, 8954}, - {8947, 8955}, - {8948, 8956}, - {8950, 8957}, - {8951, 8958}, - {8954, 8946}, - {8955, 8947}, - {8956, 8948}, - {8957, 8950}, - {8958, 8951}, - {8968, 8969}, - {8969, 8968}, - {8970, 8971}, - {8971, 8970}, - {9001, 9002}, - {9002, 9001}, - {10088, 10089}, - {10089, 10088}, - {10090, 10091}, - {10091, 10090}, - {10092, 10093}, - {10093, 10092}, - {10094, 10095}, - {10095, 10094}, - {10096, 10097}, - {10097, 10096}, - {10098, 10099}, - {10099, 10098}, - {10100, 10101}, - {10101, 10100}, - {10179, 10180}, - {10180, 10179}, - {10181, 10182}, - {10182, 10181}, - {10184, 10185}, - {10185, 10184}, - {10187, 10189}, - {10189, 10187}, - {10197, 10198}, - {10198, 10197}, - {10204, 8888}, - {10205, 10206}, - {10206, 10205}, - {10210, 10211}, - {10211, 10210}, - {10212, 10213}, - {10213, 10212}, - {10214, 10215}, - {10215, 10214}, - {10216, 10217}, - {10217, 10216}, - {10218, 10219}, - {10219, 10218}, - {10220, 10221}, - {10221, 10220}, - {10222, 10223}, - {10223, 10222}, - {10627, 10628}, - {10628, 10627}, - {10629, 10630}, - {10630, 10629}, - {10631, 10632}, - {10632, 10631}, - {10633, 10634}, - {10634, 10633}, - {10635, 10636}, - {10636, 10635}, - {10637, 10640}, - {10638, 10639}, - {10639, 10638}, - {10640, 10637}, - {10641, 10642}, - {10642, 10641}, - {10643, 10644}, - {10644, 10643}, - {10645, 10646}, - {10646, 10645}, - {10647, 10648}, - {10648, 10647}, - {10651, 8737}, - {10656, 8738}, - {10659, 8736}, - {10660, 10661}, - {10661, 10660}, - {10664, 10665}, - {10665, 10664}, - {10666, 10667}, - {10667, 10666}, - {10668, 10669}, - {10669, 10668}, - {10670, 10671}, - {10671, 10670}, - {10680, 8856}, - {10688, 10689}, - {10689, 10688}, - {10692, 10693}, - {10693, 10692}, - {10703, 10704}, - {10704, 10703}, - {10705, 10706}, - {10706, 10705}, - {10708, 10709}, - {10709, 10708}, - {10712, 10713}, - {10713, 10712}, - {10714, 10715}, - {10715, 10714}, - {10728, 10729}, - {10729, 10728}, - {10741, 8725}, - {10744, 10745}, - {10745, 10744}, - {10748, 10749}, - {10749, 10748}, - {10795, 10796}, - {10796, 10795}, - {10797, 10798}, - {10798, 10797}, - {10804, 10805}, - {10805, 10804}, - {10812, 10813}, - {10813, 10812}, - {10852, 10853}, - {10853, 10852}, - {10873, 10874}, - {10874, 10873}, - {10875, 10876}, - {10876, 10875}, - {10877, 10878}, - {10878, 10877}, - {10879, 10880}, - {10880, 10879}, - {10881, 10882}, - {10882, 10881}, - {10883, 10884}, - {10884, 10883}, - {10885, 10886}, - {10886, 10885}, - {10887, 10888}, - {10888, 10887}, - {10889, 10890}, - {10890, 10889}, - {10891, 10892}, - {10892, 10891}, - {10893, 10894}, - {10894, 10893}, - {10895, 10896}, - {10896, 10895}, - {10897, 10898}, - {10898, 10897}, - {10899, 10900}, - {10900, 10899}, - {10901, 10902}, - {10902, 10901}, - {10903, 10904}, - {10904, 10903}, - {10905, 10906}, - {10906, 10905}, - {10907, 10908}, - {10908, 10907}, - {10909, 10910}, - {10910, 10909}, - {10911, 10912}, - {10912, 10911}, - {10913, 10914}, - {10914, 10913}, - {10918, 10919}, - {10919, 10918}, - {10920, 10921}, - {10921, 10920}, - {10922, 10923}, - {10923, 10922}, - {10924, 10925}, - {10925, 10924}, - {10927, 10928}, - {10928, 10927}, - {10929, 10930}, - {10930, 10929}, - {10931, 10932}, - {10932, 10931}, - {10933, 10934}, - {10934, 10933}, - {10935, 10936}, - {10936, 10935}, - {10937, 10938}, - {10938, 10937}, - {10939, 10940}, - {10940, 10939}, - {10941, 10942}, - {10942, 10941}, - {10943, 10944}, - {10944, 10943}, - {10945, 10946}, - {10946, 10945}, - {10947, 10948}, - {10948, 10947}, - {10949, 10950}, - {10950, 10949}, - {10951, 10952}, - {10952, 10951}, - {10953, 10954}, - {10954, 10953}, - {10955, 10956}, - {10956, 10955}, - {10957, 10958}, - {10958, 10957}, - {10959, 10960}, - {10960, 10959}, - {10961, 10962}, - {10962, 10961}, - {10963, 10964}, - {10964, 10963}, - {10965, 10966}, - {10966, 10965}, - {10974, 8870}, - {10979, 8873}, - {10980, 8872}, - {10981, 8875}, - {10988, 10989}, - {10989, 10988}, - {10990, 8740}, - {10999, 11000}, - {11000, 10999}, - {11001, 11002}, - {11002, 11001}, - {11262, 8735}, - {11778, 11779}, - {11779, 11778}, - {11780, 11781}, - {11781, 11780}, - {11785, 11786}, - {11786, 11785}, - {11788, 11789}, - {11789, 11788}, - {11804, 11805}, - {11805, 11804}, - {11808, 11809}, - {11809, 11808}, - {11810, 11811}, - {11811, 11810}, - {11812, 11813}, - {11813, 11812}, - {11814, 11815}, - {11815, 11814}, - {11816, 11817}, - {11817, 11816}, - {12296, 12297}, - {12297, 12296}, - {12298, 12299}, - {12299, 12298}, - {12300, 12301}, - {12301, 12300}, - {12302, 12303}, - {12303, 12302}, - {12304, 12305}, - {12305, 12304}, - {12308, 12309}, - {12309, 12308}, - {12310, 12311}, - {12311, 12310}, - {12312, 12313}, - {12313, 12312}, - {12314, 12315}, - {12315, 12314}, - {65113, 65114}, - {65114, 65113}, - {65115, 65116}, - {65116, 65115}, - {65117, 65118}, - {65118, 65117}, - {65124, 65125}, - {65125, 65124}, - {65288, 65289}, - {65289, 65288}, - {65308, 65310}, - {65310, 65308}, - {65339, 65341}, - {65341, 65339}, - {65371, 65373}, - {65373, 65371}, - {65375, 65376}, - {65376, 65375}, - {65378, 65379}, - {65379, 65378}, -}; - -#define BIDI_BRACKET_LEN 120 -static const BracketPair bracket_pairs[] = { - {40, 41, 0}, - {41, 40, 1}, - {91, 93, 0}, - {93, 91, 1}, - {123, 125, 0}, - {125, 123, 1}, - {3898, 3899, 0}, - {3899, 3898, 1}, - {3900, 3901, 0}, - {3901, 3900, 1}, - {5787, 5788, 0}, - {5788, 5787, 1}, - {8261, 8262, 0}, - {8262, 8261, 1}, - {8317, 8318, 0}, - {8318, 8317, 1}, - {8333, 8334, 0}, - {8334, 8333, 1}, - {8968, 8969, 0}, - {8969, 8968, 1}, - {8970, 8971, 0}, - {8971, 8970, 1}, - {9001, 9002, 0}, - {9002, 9001, 1}, - {10088, 10089, 0}, - {10089, 10088, 1}, - {10090, 10091, 0}, - {10091, 10090, 1}, - {10092, 10093, 0}, - {10093, 10092, 1}, - {10094, 10095, 0}, - {10095, 10094, 1}, - {10096, 10097, 0}, - {10097, 10096, 1}, - {10098, 10099, 0}, - {10099, 10098, 1}, - {10100, 10101, 0}, - {10101, 10100, 1}, - {10181, 10182, 0}, - {10182, 10181, 1}, - {10214, 10215, 0}, - {10215, 10214, 1}, - {10216, 10217, 0}, - {10217, 10216, 1}, - {10218, 10219, 0}, - {10219, 10218, 1}, - {10220, 10221, 0}, - {10221, 10220, 1}, - {10222, 10223, 0}, - {10223, 10222, 1}, - {10627, 10628, 0}, - {10628, 10627, 1}, - {10629, 10630, 0}, - {10630, 10629, 1}, - {10631, 10632, 0}, - {10632, 10631, 1}, - {10633, 10634, 0}, - {10634, 10633, 1}, - {10635, 10636, 0}, - {10636, 10635, 1}, - {10637, 10640, 0}, - {10638, 10639, 1}, - {10639, 10638, 0}, - {10640, 10637, 1}, - {10641, 10642, 0}, - {10642, 10641, 1}, - {10643, 10644, 0}, - {10644, 10643, 1}, - {10645, 10646, 0}, - {10646, 10645, 1}, - {10647, 10648, 0}, - {10648, 10647, 1}, - {10712, 10713, 0}, - {10713, 10712, 1}, - {10714, 10715, 0}, - {10715, 10714, 1}, - {10748, 10749, 0}, - {10749, 10748, 1}, - {11810, 11811, 0}, - {11811, 11810, 1}, - {11812, 11813, 0}, - {11813, 11812, 1}, - {11814, 11815, 0}, - {11815, 11814, 1}, - {11816, 11817, 0}, - {11817, 11816, 1}, - {12296, 12297, 0}, - {12297, 12296, 1}, - {12298, 12299, 0}, - {12299, 12298, 1}, - {12300, 12301, 0}, - {12301, 12300, 1}, - {12302, 12303, 0}, - {12303, 12302, 1}, - {12304, 12305, 0}, - {12305, 12304, 1}, - {12308, 12309, 0}, - {12309, 12308, 1}, - {12310, 12311, 0}, - {12311, 12310, 1}, - {12312, 12313, 0}, - {12313, 12312, 1}, - {12314, 12315, 0}, - {12315, 12314, 1}, - {65113, 65114, 0}, - {65114, 65113, 1}, - {65115, 65116, 0}, - {65116, 65115, 1}, - {65117, 65118, 0}, - {65118, 65117, 1}, - {65288, 65289, 0}, - {65289, 65288, 1}, - {65339, 65341, 0}, - {65341, 65339, 1}, - {65371, 65373, 0}, - {65373, 65371, 1}, - {65375, 65376, 0}, - {65376, 65375, 1}, - {65378, 65379, 0}, - {65379, 65378, 1}, -}; - -/* Reindexing of NFC first characters. */ -#define TOTAL_FIRST 376 -#define TOTAL_LAST 62 -static const Reindex nfc_first[] = { - { 60, 2, 0}, - { 65, 15, 3}, - { 82, 8, 19}, - { 97, 15, 28}, - { 114, 8, 44}, - { 168, 0, 53}, - { 194, 0, 54}, - { 196, 3, 55}, - { 202, 0, 59}, - { 207, 0, 60}, - { 212, 2, 61}, - { 216, 0, 64}, - { 220, 0, 65}, - { 226, 0, 66}, - { 228, 3, 67}, - { 234, 0, 71}, - { 239, 0, 72}, - { 244, 2, 73}, - { 248, 0, 76}, - { 252, 0, 77}, - { 258, 1, 78}, - { 274, 1, 80}, - { 332, 1, 82}, - { 346, 1, 84}, - { 352, 1, 86}, - { 360, 3, 88}, - { 383, 0, 92}, - { 416, 1, 93}, - { 431, 1, 95}, - { 439, 0, 97}, - { 490, 1, 98}, - { 550, 3, 100}, - { 558, 1, 104}, - { 658, 0, 106}, - { 913, 0, 107}, - { 917, 0, 108}, - { 919, 0, 109}, - { 921, 0, 110}, - { 927, 0, 111}, - { 929, 0, 112}, - { 933, 0, 113}, - { 937, 0, 114}, - { 940, 0, 115}, - { 942, 0, 116}, - { 945, 0, 117}, - { 949, 0, 118}, - { 951, 0, 119}, - { 953, 0, 120}, - { 959, 0, 121}, - { 961, 0, 122}, - { 965, 0, 123}, - { 969, 2, 124}, - { 974, 0, 127}, - { 978, 0, 128}, - { 1030, 0, 129}, - { 1040, 0, 130}, - { 1043, 0, 131}, - { 1045, 3, 132}, - { 1050, 0, 136}, - { 1054, 0, 137}, - { 1059, 0, 138}, - { 1063, 0, 139}, - { 1067, 0, 140}, - { 1069, 0, 141}, - { 1072, 0, 142}, - { 1075, 0, 143}, - { 1077, 3, 144}, - { 1082, 0, 148}, - { 1086, 0, 149}, - { 1091, 0, 150}, - { 1095, 0, 151}, - { 1099, 0, 152}, - { 1101, 0, 153}, - { 1110, 0, 154}, - { 1140, 1, 155}, - { 1240, 1, 157}, - { 1256, 1, 159}, - { 1575, 0, 161}, - { 1608, 0, 162}, - { 1610, 0, 163}, - { 1729, 0, 164}, - { 1746, 0, 165}, - { 1749, 0, 166}, - { 2344, 0, 167}, - { 2352, 0, 168}, - { 2355, 0, 169}, - { 2503, 0, 170}, - { 2887, 0, 171}, - { 2962, 0, 172}, - { 3014, 1, 173}, - { 3142, 0, 175}, - { 3263, 0, 176}, - { 3270, 0, 177}, - { 3274, 0, 178}, - { 3398, 1, 179}, - { 3545, 0, 181}, - { 3548, 0, 182}, - { 4133, 0, 183}, - { 6917, 0, 184}, - { 6919, 0, 185}, - { 6921, 0, 186}, - { 6923, 0, 187}, - { 6925, 0, 188}, - { 6929, 0, 189}, - { 6970, 0, 190}, - { 6972, 0, 191}, - { 6974, 1, 192}, - { 6978, 0, 194}, - { 7734, 1, 195}, - { 7770, 1, 197}, - { 7778, 1, 199}, - { 7840, 1, 201}, - { 7864, 1, 203}, - { 7884, 1, 205}, - { 7936, 17, 207}, - { 7960, 1, 225}, - { 7968, 17, 227}, - { 7992, 1, 245}, - { 8000, 1, 247}, - { 8008, 1, 249}, - { 8016, 1, 251}, - { 8025, 0, 253}, - { 8032, 16, 254}, - { 8052, 0, 271}, - { 8060, 0, 272}, - { 8118, 0, 273}, - { 8127, 0, 274}, - { 8134, 0, 275}, - { 8182, 0, 276}, - { 8190, 0, 277}, - { 8592, 0, 278}, - { 8594, 0, 279}, - { 8596, 0, 280}, - { 8656, 0, 281}, - { 8658, 0, 282}, - { 8660, 0, 283}, - { 8707, 0, 284}, - { 8712, 0, 285}, - { 8715, 0, 286}, - { 8739, 0, 287}, - { 8741, 0, 288}, - { 8764, 0, 289}, - { 8771, 0, 290}, - { 8773, 0, 291}, - { 8776, 0, 292}, - { 8781, 0, 293}, - { 8801, 0, 294}, - { 8804, 1, 295}, - { 8818, 1, 297}, - { 8822, 1, 299}, - { 8826, 3, 301}, - { 8834, 1, 305}, - { 8838, 1, 307}, - { 8849, 1, 309}, - { 8866, 0, 311}, - { 8872, 1, 312}, - { 8875, 0, 314}, - { 8882, 3, 315}, - { 12358, 0, 319}, - { 12363, 0, 320}, - { 12365, 0, 321}, - { 12367, 0, 322}, - { 12369, 0, 323}, - { 12371, 0, 324}, - { 12373, 0, 325}, - { 12375, 0, 326}, - { 12377, 0, 327}, - { 12379, 0, 328}, - { 12381, 0, 329}, - { 12383, 0, 330}, - { 12385, 0, 331}, - { 12388, 0, 332}, - { 12390, 0, 333}, - { 12392, 0, 334}, - { 12399, 0, 335}, - { 12402, 0, 336}, - { 12405, 0, 337}, - { 12408, 0, 338}, - { 12411, 0, 339}, - { 12445, 0, 340}, - { 12454, 0, 341}, - { 12459, 0, 342}, - { 12461, 0, 343}, - { 12463, 0, 344}, - { 12465, 0, 345}, - { 12467, 0, 346}, - { 12469, 0, 347}, - { 12471, 0, 348}, - { 12473, 0, 349}, - { 12475, 0, 350}, - { 12477, 0, 351}, - { 12479, 0, 352}, - { 12481, 0, 353}, - { 12484, 0, 354}, - { 12486, 0, 355}, - { 12488, 0, 356}, - { 12495, 0, 357}, - { 12498, 0, 358}, - { 12501, 0, 359}, - { 12504, 0, 360}, - { 12507, 0, 361}, - { 12527, 3, 362}, - { 12541, 0, 366}, - { 69785, 0, 367}, - { 69787, 0, 368}, - { 69797, 0, 369}, - { 69937, 1, 370}, - { 70471, 0, 372}, - { 70841, 0, 373}, - { 71096, 1, 374}, - {0,0,0} -}; - -static const Reindex nfc_last[] = { - { 768, 4, 0}, - { 774, 6, 5}, - { 783, 0, 12}, - { 785, 0, 13}, - { 787, 1, 14}, - { 795, 0, 16}, - { 803, 5, 17}, - { 813, 1, 23}, - { 816, 1, 25}, - { 824, 0, 27}, - { 834, 0, 28}, - { 837, 0, 29}, - { 1619, 2, 30}, - { 2364, 0, 33}, - { 2494, 0, 34}, - { 2519, 0, 35}, - { 2878, 0, 36}, - { 2902, 1, 37}, - { 3006, 0, 39}, - { 3031, 0, 40}, - { 3158, 0, 41}, - { 3266, 0, 42}, - { 3285, 1, 43}, - { 3390, 0, 45}, - { 3415, 0, 46}, - { 3530, 0, 47}, - { 3535, 0, 48}, - { 3551, 0, 49}, - { 4142, 0, 50}, - { 6965, 0, 51}, - { 12441, 1, 52}, - { 69818, 0, 54}, - { 69927, 0, 55}, - { 70462, 0, 56}, - { 70487, 0, 57}, - { 70832, 0, 58}, - { 70842, 0, 59}, - { 70845, 0, 60}, - { 71087, 0, 61}, - {0,0,0} -}; - -#define UCDN_EAST_ASIAN_F 0 -#define UCDN_EAST_ASIAN_H 1 -#define UCDN_EAST_ASIAN_W 2 -#define UCDN_EAST_ASIAN_NA 3 -#define UCDN_EAST_ASIAN_A 4 -#define UCDN_EAST_ASIAN_N 5 - -#define UCDN_SCRIPT_COMMON 0 -#define UCDN_SCRIPT_LATIN 1 -#define UCDN_SCRIPT_GREEK 2 -#define UCDN_SCRIPT_CYRILLIC 3 -#define UCDN_SCRIPT_ARMENIAN 4 -#define UCDN_SCRIPT_HEBREW 5 -#define UCDN_SCRIPT_ARABIC 6 -#define UCDN_SCRIPT_SYRIAC 7 -#define UCDN_SCRIPT_THAANA 8 -#define UCDN_SCRIPT_DEVANAGARI 9 -#define UCDN_SCRIPT_BENGALI 10 -#define UCDN_SCRIPT_GURMUKHI 11 -#define UCDN_SCRIPT_GUJARATI 12 -#define UCDN_SCRIPT_ORIYA 13 -#define UCDN_SCRIPT_TAMIL 14 -#define UCDN_SCRIPT_TELUGU 15 -#define UCDN_SCRIPT_KANNADA 16 -#define UCDN_SCRIPT_MALAYALAM 17 -#define UCDN_SCRIPT_SINHALA 18 -#define UCDN_SCRIPT_THAI 19 -#define UCDN_SCRIPT_LAO 20 -#define UCDN_SCRIPT_TIBETAN 21 -#define UCDN_SCRIPT_MYANMAR 22 -#define UCDN_SCRIPT_GEORGIAN 23 -#define UCDN_SCRIPT_HANGUL 24 -#define UCDN_SCRIPT_ETHIOPIC 25 -#define UCDN_SCRIPT_CHEROKEE 26 -#define UCDN_SCRIPT_CANADIAN_ABORIGINAL 27 -#define UCDN_SCRIPT_OGHAM 28 -#define UCDN_SCRIPT_RUNIC 29 -#define UCDN_SCRIPT_KHMER 30 -#define UCDN_SCRIPT_MONGOLIAN 31 -#define UCDN_SCRIPT_HIRAGANA 32 -#define UCDN_SCRIPT_KATAKANA 33 -#define UCDN_SCRIPT_BOPOMOFO 34 -#define UCDN_SCRIPT_HAN 35 -#define UCDN_SCRIPT_YI 36 -#define UCDN_SCRIPT_OLD_ITALIC 37 -#define UCDN_SCRIPT_GOTHIC 38 -#define UCDN_SCRIPT_DESERET 39 -#define UCDN_SCRIPT_INHERITED 40 -#define UCDN_SCRIPT_TAGALOG 41 -#define UCDN_SCRIPT_HANUNOO 42 -#define UCDN_SCRIPT_BUHID 43 -#define UCDN_SCRIPT_TAGBANWA 44 -#define UCDN_SCRIPT_LIMBU 45 -#define UCDN_SCRIPT_TAI_LE 46 -#define UCDN_SCRIPT_LINEAR_B 47 -#define UCDN_SCRIPT_UGARITIC 48 -#define UCDN_SCRIPT_SHAVIAN 49 -#define UCDN_SCRIPT_OSMANYA 50 -#define UCDN_SCRIPT_CYPRIOT 51 -#define UCDN_SCRIPT_BRAILLE 52 -#define UCDN_SCRIPT_BUGINESE 53 -#define UCDN_SCRIPT_COPTIC 54 -#define UCDN_SCRIPT_NEW_TAI_LUE 55 -#define UCDN_SCRIPT_GLAGOLITIC 56 -#define UCDN_SCRIPT_TIFINAGH 57 -#define UCDN_SCRIPT_SYLOTI_NAGRI 58 -#define UCDN_SCRIPT_OLD_PERSIAN 59 -#define UCDN_SCRIPT_KHAROSHTHI 60 -#define UCDN_SCRIPT_BALINESE 61 -#define UCDN_SCRIPT_CUNEIFORM 62 -#define UCDN_SCRIPT_PHOENICIAN 63 -#define UCDN_SCRIPT_PHAGS_PA 64 -#define UCDN_SCRIPT_NKO 65 -#define UCDN_SCRIPT_SUNDANESE 66 -#define UCDN_SCRIPT_LEPCHA 67 -#define UCDN_SCRIPT_OL_CHIKI 68 -#define UCDN_SCRIPT_VAI 69 -#define UCDN_SCRIPT_SAURASHTRA 70 -#define UCDN_SCRIPT_KAYAH_LI 71 -#define UCDN_SCRIPT_REJANG 72 -#define UCDN_SCRIPT_LYCIAN 73 -#define UCDN_SCRIPT_CARIAN 74 -#define UCDN_SCRIPT_LYDIAN 75 -#define UCDN_SCRIPT_CHAM 76 -#define UCDN_SCRIPT_TAI_THAM 77 -#define UCDN_SCRIPT_TAI_VIET 78 -#define UCDN_SCRIPT_AVESTAN 79 -#define UCDN_SCRIPT_EGYPTIAN_HIEROGLYPHS 80 -#define UCDN_SCRIPT_SAMARITAN 81 -#define UCDN_SCRIPT_LISU 82 -#define UCDN_SCRIPT_BAMUM 83 -#define UCDN_SCRIPT_JAVANESE 84 -#define UCDN_SCRIPT_MEETEI_MAYEK 85 -#define UCDN_SCRIPT_IMPERIAL_ARAMAIC 86 -#define UCDN_SCRIPT_OLD_SOUTH_ARABIAN 87 -#define UCDN_SCRIPT_INSCRIPTIONAL_PARTHIAN 88 -#define UCDN_SCRIPT_INSCRIPTIONAL_PAHLAVI 89 -#define UCDN_SCRIPT_OLD_TURKIC 90 -#define UCDN_SCRIPT_KAITHI 91 -#define UCDN_SCRIPT_BATAK 92 -#define UCDN_SCRIPT_BRAHMI 93 -#define UCDN_SCRIPT_MANDAIC 94 -#define UCDN_SCRIPT_CHAKMA 95 -#define UCDN_SCRIPT_MEROITIC_CURSIVE 96 -#define UCDN_SCRIPT_MEROITIC_HIEROGLYPHS 97 -#define UCDN_SCRIPT_MIAO 98 -#define UCDN_SCRIPT_SHARADA 99 -#define UCDN_SCRIPT_SORA_SOMPENG 100 -#define UCDN_SCRIPT_TAKRI 101 -#define UCDN_SCRIPT_UNKNOWN 102 -#define UCDN_SCRIPT_BASSA_VAH 103 -#define UCDN_SCRIPT_CAUCASIAN_ALBANIAN 104 -#define UCDN_SCRIPT_DUPLOYAN 105 -#define UCDN_SCRIPT_ELBASAN 106 -#define UCDN_SCRIPT_GRANTHA 107 -#define UCDN_SCRIPT_KHOJKI 108 -#define UCDN_SCRIPT_KHUDAWADI 109 -#define UCDN_SCRIPT_LINEAR_A 110 -#define UCDN_SCRIPT_MAHAJANI 111 -#define UCDN_SCRIPT_MANICHAEAN 112 -#define UCDN_SCRIPT_MENDE_KIKAKUI 113 -#define UCDN_SCRIPT_MODI 114 -#define UCDN_SCRIPT_MRO 115 -#define UCDN_SCRIPT_NABATAEAN 116 -#define UCDN_SCRIPT_OLD_NORTH_ARABIAN 117 -#define UCDN_SCRIPT_OLD_PERMIC 118 -#define UCDN_SCRIPT_PAHAWH_HMONG 119 -#define UCDN_SCRIPT_PALMYRENE 120 -#define UCDN_SCRIPT_PAU_CIN_HAU 121 -#define UCDN_SCRIPT_PSALTER_PAHLAVI 122 -#define UCDN_SCRIPT_SIDDHAM 123 -#define UCDN_SCRIPT_TIRHUTA 124 -#define UCDN_SCRIPT_WARANG_CITI 125 -#define UCDN_SCRIPT_AHOM 126 -#define UCDN_SCRIPT_ANATOLIAN_HIEROGLYPHS 127 -#define UCDN_SCRIPT_HATRAN 128 -#define UCDN_SCRIPT_MULTANI 129 -#define UCDN_SCRIPT_OLD_HUNGARIAN 130 -#define UCDN_SCRIPT_SIGNWRITING 131 -#define UCDN_SCRIPT_ADLAM 132 -#define UCDN_SCRIPT_BHAIKSUKI 133 -#define UCDN_SCRIPT_MARCHEN 134 -#define UCDN_SCRIPT_NEWA 135 -#define UCDN_SCRIPT_OSAGE 136 -#define UCDN_SCRIPT_TANGUT 137 -#define UCDN_SCRIPT_MASARAM_GONDI 138 -#define UCDN_SCRIPT_NUSHU 139 -#define UCDN_SCRIPT_SOYOMBO 140 -#define UCDN_SCRIPT_ZANABAZAR_SQUARE 141 -#define UCDN_SCRIPT_DOGRA 142 -#define UCDN_SCRIPT_GUNJALA_GONDI 143 -#define UCDN_SCRIPT_HANIFI_ROHINGYA 144 -#define UCDN_SCRIPT_MAKASAR 145 -#define UCDN_SCRIPT_MEDEFAIDRIN 146 -#define UCDN_SCRIPT_OLD_SOGDIAN 147 -#define UCDN_SCRIPT_SOGDIAN 148 - -#define UCDN_GENERAL_CATEGORY_CC 0 -#define UCDN_GENERAL_CATEGORY_CF 1 -#define UCDN_GENERAL_CATEGORY_CN 2 -#define UCDN_GENERAL_CATEGORY_CO 3 -#define UCDN_GENERAL_CATEGORY_CS 4 -#define UCDN_GENERAL_CATEGORY_LL 5 -#define UCDN_GENERAL_CATEGORY_LM 6 -#define UCDN_GENERAL_CATEGORY_LO 7 -#define UCDN_GENERAL_CATEGORY_LT 8 -#define UCDN_GENERAL_CATEGORY_LU 9 -#define UCDN_GENERAL_CATEGORY_MC 10 -#define UCDN_GENERAL_CATEGORY_ME 11 -#define UCDN_GENERAL_CATEGORY_MN 12 -#define UCDN_GENERAL_CATEGORY_ND 13 -#define UCDN_GENERAL_CATEGORY_NL 14 -#define UCDN_GENERAL_CATEGORY_NO 15 -#define UCDN_GENERAL_CATEGORY_PC 16 -#define UCDN_GENERAL_CATEGORY_PD 17 -#define UCDN_GENERAL_CATEGORY_PE 18 -#define UCDN_GENERAL_CATEGORY_PF 19 -#define UCDN_GENERAL_CATEGORY_PI 20 -#define UCDN_GENERAL_CATEGORY_PO 21 -#define UCDN_GENERAL_CATEGORY_PS 22 -#define UCDN_GENERAL_CATEGORY_SC 23 -#define UCDN_GENERAL_CATEGORY_SK 24 -#define UCDN_GENERAL_CATEGORY_SM 25 -#define UCDN_GENERAL_CATEGORY_SO 26 -#define UCDN_GENERAL_CATEGORY_ZL 27 -#define UCDN_GENERAL_CATEGORY_ZP 28 -#define UCDN_GENERAL_CATEGORY_ZS 29 - -#define UCDN_BIDI_CLASS_L 0 -#define UCDN_BIDI_CLASS_LRE 1 -#define UCDN_BIDI_CLASS_LRO 2 -#define UCDN_BIDI_CLASS_R 3 -#define UCDN_BIDI_CLASS_AL 4 -#define UCDN_BIDI_CLASS_RLE 5 -#define UCDN_BIDI_CLASS_RLO 6 -#define UCDN_BIDI_CLASS_PDF 7 -#define UCDN_BIDI_CLASS_EN 8 -#define UCDN_BIDI_CLASS_ES 9 -#define UCDN_BIDI_CLASS_ET 10 -#define UCDN_BIDI_CLASS_AN 11 -#define UCDN_BIDI_CLASS_CS 12 -#define UCDN_BIDI_CLASS_NSM 13 -#define UCDN_BIDI_CLASS_BN 14 -#define UCDN_BIDI_CLASS_B 15 -#define UCDN_BIDI_CLASS_S 16 -#define UCDN_BIDI_CLASS_WS 17 -#define UCDN_BIDI_CLASS_ON 18 -#define UCDN_BIDI_CLASS_LRI 19 -#define UCDN_BIDI_CLASS_RLI 20 -#define UCDN_BIDI_CLASS_FSI 21 -#define UCDN_BIDI_CLASS_PDI 22 - -/* index tables for the database records */ -#define SHIFT1 5 -#define SHIFT2 3 -static const unsigned char index0[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 53, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 54, 55, 56, 56, 56, 57, - 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 65, 66, 67, 68, - 69, 70, 71, 65, 66, 67, 68, 69, 70, 71, 65, 66, 67, 68, 69, 70, 71, 65, - 66, 67, 68, 69, 70, 71, 65, 66, 67, 68, 69, 70, 71, 65, 72, 73, 73, 73, - 73, 73, 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 52, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, - 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 106, 108, 109, 110, 106, - 111, 111, 111, 112, 113, 114, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 115, 115, 116, 117, 118, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 119, 120, 121, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 122, 122, 123, 124, 106, 106, 125, 126, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 128, 127, 127, 129, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 130, 131, 132, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 133, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 134, 135, 136, 137, 138, 139, - 140, 141, 142, 142, 143, 106, 106, 106, 106, 106, 144, 106, 106, 106, - 106, 106, 106, 106, 145, 146, 106, 106, 147, 106, 148, 106, 149, 150, - 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 160, 160, 160, 161, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 162, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 163, 164, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 165, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 166, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 52, 52, - 168, 167, 167, 167, 167, 169, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 169, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 170, 171, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 172, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 172, -}; - -static const unsigned short index1[] = { - 0, 1, 0, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 11, 12, 13, 0, 0, 0, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 29, 31, 32, - 33, 34, 35, 27, 30, 29, 27, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, - 47, 48, 27, 27, 49, 27, 27, 27, 27, 27, 27, 27, 50, 51, 52, 27, 53, 54, - 53, 54, 54, 54, 54, 54, 55, 54, 54, 54, 56, 57, 58, 59, 60, 61, 62, 63, - 64, 64, 65, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 65, 77, 78, - 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, - 97, 97, 97, 97, 98, 98, 98, 98, 99, 100, 101, 101, 101, 101, 102, 103, - 101, 101, 101, 101, 101, 101, 104, 105, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 106, 107, 107, 107, 108, 109, 110, 110, - 110, 110, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 120, - 120, 121, 122, 119, 123, 124, 125, 126, 127, 127, 127, 127, 128, 129, - 130, 131, 132, 133, 134, 127, 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 144, - 145, 146, 147, 148, 127, 127, 127, 127, 127, 127, 149, 149, 149, 149, - 150, 151, 152, 119, 153, 154, 155, 155, 155, 156, 157, 158, 159, 159, - 160, 161, 162, 163, 164, 165, 166, 166, 166, 167, 144, 168, 119, 119, - 119, 119, 119, 119, 127, 127, 169, 170, 119, 119, 171, 125, 172, 173, - 174, 175, 176, 177, 177, 177, 177, 177, 177, 178, 179, 180, 181, 177, - 182, 183, 184, 177, 185, 186, 187, 188, 188, 189, 190, 191, 192, 193, - 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 203, 204, 205, 206, - 207, 208, 209, 210, 211, 212, 213, 119, 214, 215, 216, 217, 217, 218, - 219, 220, 221, 222, 223, 119, 224, 225, 226, 227, 228, 229, 230, 231, - 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 119, 242, 243, - 244, 245, 246, 243, 247, 248, 249, 250, 251, 119, 252, 253, 254, 255, - 256, 257, 258, 259, 259, 258, 259, 260, 261, 262, 263, 264, 265, 266, - 119, 267, 268, 269, 270, 271, 271, 270, 272, 273, 274, 275, 276, 277, - 278, 279, 280, 119, 281, 282, 283, 284, 284, 284, 284, 285, 286, 287, - 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 295, 295, 298, 299, - 296, 300, 301, 302, 303, 304, 305, 119, 306, 307, 307, 307, 307, 307, - 308, 309, 310, 311, 312, 313, 119, 119, 119, 119, 314, 315, 316, 317, - 318, 319, 320, 321, 322, 323, 324, 325, 119, 119, 119, 119, 326, 327, - 328, 329, 330, 331, 332, 333, 334, 335, 334, 334, 334, 336, 337, 338, - 339, 340, 341, 342, 341, 341, 341, 343, 344, 345, 346, 347, 119, 119, - 119, 119, 348, 348, 348, 348, 348, 349, 350, 351, 352, 353, 354, 355, - 356, 357, 358, 348, 359, 360, 352, 361, 362, 362, 362, 362, 363, 364, - 365, 365, 365, 365, 365, 366, 367, 367, 367, 367, 367, 367, 367, 367, - 367, 367, 367, 367, 368, 368, 368, 368, 368, 368, 368, 368, 368, 369, - 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 370, 370, 370, 370, - 370, 370, 370, 370, 370, 371, 372, 371, 370, 370, 370, 370, 370, 371, - 370, 370, 370, 370, 371, 372, 371, 370, 372, 370, 370, 370, 370, 370, - 370, 370, 371, 370, 370, 370, 370, 370, 370, 370, 370, 373, 374, 375, - 376, 377, 370, 370, 378, 379, 380, 380, 380, 380, 380, 380, 380, 380, - 380, 380, 381, 382, 383, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 385, 384, 384, - 386, 387, 387, 388, 389, 389, 389, 389, 389, 389, 389, 389, 389, 390, - 391, 392, 393, 394, 395, 119, 396, 396, 397, 119, 398, 398, 399, 119, - 400, 401, 402, 119, 403, 403, 403, 403, 403, 403, 404, 405, 406, 407, - 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 418, 418, 418, - 419, 418, 418, 418, 418, 418, 418, 420, 421, 418, 418, 418, 418, 422, - 384, 384, 384, 384, 384, 384, 384, 384, 423, 119, 424, 424, 424, 425, - 426, 427, 428, 429, 430, 431, 432, 432, 432, 433, 434, 119, 435, 435, - 435, 435, 435, 436, 435, 435, 435, 437, 438, 439, 440, 440, 440, 440, - 441, 441, 442, 443, 444, 444, 444, 444, 444, 444, 445, 446, 447, 448, - 449, 450, 451, 452, 451, 452, 453, 454, 455, 456, 119, 119, 119, 119, - 119, 119, 119, 119, 457, 458, 458, 458, 458, 458, 459, 460, 461, 462, - 463, 464, 465, 466, 467, 468, 469, 470, 470, 470, 471, 472, 473, 474, - 475, 475, 475, 475, 476, 477, 478, 479, 480, 480, 480, 480, 481, 482, - 483, 484, 485, 486, 487, 488, 489, 489, 489, 490, 100, 491, 362, 362, - 362, 362, 362, 492, 493, 119, 494, 495, 496, 497, 498, 499, 54, 54, 54, - 54, 500, 501, 56, 56, 56, 56, 56, 502, 503, 504, 54, 505, 54, 54, 54, - 506, 56, 56, 56, 507, 508, 509, 510, 511, 511, 511, 512, 513, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 514, 515, 27, - 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 516, 517, 518, 519, 516, 517, - 516, 517, 518, 519, 516, 520, 516, 517, 516, 518, 516, 521, 516, 521, - 516, 521, 522, 523, 524, 525, 526, 527, 516, 528, 529, 530, 531, 532, - 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, - 547, 548, 56, 549, 550, 551, 552, 553, 554, 554, 555, 556, 557, 558, 559, - 119, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, - 573, 572, 574, 575, 576, 577, 578, 579, 580, 581, 582, 581, 583, 584, - 581, 585, 581, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 587, - 596, 597, 587, 598, 599, 587, 587, 599, 587, 600, 601, 600, 587, 587, - 602, 587, 587, 587, 587, 587, 603, 587, 587, 581, 604, 605, 606, 607, - 608, 609, 610, 610, 610, 610, 610, 610, 610, 610, 611, 581, 581, 612, - 613, 587, 587, 614, 581, 581, 581, 581, 586, 607, 615, 616, 581, 581, - 581, 581, 581, 617, 119, 119, 119, 581, 618, 119, 119, 619, 619, 619, - 619, 619, 620, 620, 621, 622, 622, 622, 622, 622, 622, 622, 622, 622, - 623, 619, 624, 625, 625, 625, 625, 625, 625, 625, 625, 625, 626, 625, - 625, 625, 625, 627, 581, 625, 625, 628, 581, 629, 630, 631, 632, 633, - 634, 630, 581, 628, 635, 581, 636, 637, 638, 639, 640, 581, 581, 581, - 641, 642, 643, 644, 581, 645, 646, 581, 647, 581, 581, 648, 649, 650, - 651, 581, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 581, - 581, 581, 663, 581, 664, 581, 665, 666, 667, 668, 669, 670, 619, 671, - 671, 672, 581, 581, 581, 663, 673, 674, 587, 587, 587, 675, 676, 587, - 587, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, - 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, - 677, 677, 677, 677, 677, 587, 587, 587, 587, 587, 587, 587, 587, 587, - 587, 587, 587, 587, 587, 587, 587, 678, 679, 679, 680, 587, 587, 587, - 587, 587, 587, 587, 681, 587, 587, 587, 682, 587, 587, 587, 587, 587, - 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, - 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 581, - 581, 581, 683, 581, 581, 587, 587, 684, 685, 686, 630, 581, 581, 687, - 581, 581, 581, 688, 581, 581, 581, 581, 581, 581, 689, 581, 581, 581, - 581, 581, 617, 690, 690, 690, 690, 690, 691, 692, 692, 692, 692, 692, - 693, 694, 695, 696, 697, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, - 698, 699, 700, 701, 365, 365, 365, 365, 702, 703, 704, 704, 704, 704, - 704, 704, 704, 705, 706, 707, 370, 370, 372, 119, 372, 372, 372, 372, - 372, 372, 372, 372, 708, 708, 708, 708, 709, 710, 711, 712, 713, 714, - 715, 716, 717, 718, 119, 119, 119, 119, 119, 119, 719, 719, 719, 720, - 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 721, 119, 719, 719, - 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, - 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 722, 119, 119, 119, - 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 735, - 736, 735, 735, 735, 737, 738, 739, 740, 741, 742, 743, 743, 744, 743, - 743, 743, 745, 746, 747, 748, 749, 750, 750, 750, 750, 750, 751, 752, - 752, 752, 752, 752, 752, 752, 752, 752, 752, 753, 754, 755, 750, 750, - 750, 756, 723, 723, 723, 723, 724, 119, 757, 757, 758, 758, 758, 759, - 760, 761, 755, 755, 755, 762, 763, 764, 758, 758, 758, 765, 760, 761, - 755, 755, 755, 755, 766, 764, 755, 767, 768, 768, 768, 768, 768, 769, - 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 755, 755, 755, - 770, 771, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 772, - 755, 755, 755, 770, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 774, 775, 581, 581, 581, 581, 581, 581, 581, 581, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 775, 775, 776, 776, 777, 776, 776, 776, 776, 776, 776, 776, 776, 776, - 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, - 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, - 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, - 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, - 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 778, - 779, 779, 779, 779, 779, 779, 780, 119, 781, 781, 781, 781, 781, 782, - 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, - 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, - 783, 783, 783, 783, 783, 784, 783, 783, 785, 786, 119, 119, 101, 101, - 101, 101, 101, 787, 788, 789, 101, 101, 101, 790, 791, 791, 791, 791, - 791, 791, 791, 791, 792, 793, 794, 119, 64, 64, 795, 796, 797, 27, 798, - 27, 27, 27, 27, 27, 27, 27, 799, 800, 27, 801, 802, 27, 27, 803, 804, - 805, 119, 119, 119, 119, 119, 119, 806, 807, 808, 809, 810, 810, 811, - 812, 813, 814, 815, 815, 815, 815, 815, 815, 816, 119, 817, 818, 818, - 818, 818, 818, 819, 820, 821, 822, 823, 824, 825, 825, 826, 827, 828, - 829, 830, 830, 831, 832, 833, 833, 834, 835, 836, 837, 367, 367, 367, - 838, 839, 840, 840, 840, 840, 840, 841, 842, 843, 844, 845, 846, 847, - 348, 352, 848, 849, 849, 849, 849, 849, 850, 851, 119, 852, 853, 854, - 855, 348, 348, 856, 857, 858, 858, 858, 858, 858, 858, 859, 860, 861, - 119, 119, 862, 863, 864, 865, 119, 866, 866, 866, 119, 372, 372, 54, 54, - 54, 54, 54, 867, 868, 119, 869, 869, 869, 869, 869, 869, 869, 869, 869, - 869, 863, 863, 863, 863, 870, 871, 872, 873, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 875, 875, 875, 874, 875, - 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, - 875, 877, 119, 368, 368, 878, 879, 369, 369, 369, 369, 369, 880, 881, - 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, - 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, - 881, 881, 881, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, - 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, - 882, 882, 882, 882, 882, 882, 882, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 774, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 883, 775, 775, 775, 775, 884, 119, 885, - 886, 120, 887, 888, 889, 890, 120, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 891, 892, 893, 119, 894, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 895, 119, - 119, 127, 127, 127, 127, 127, 127, 127, 127, 896, 127, 127, 127, 127, - 127, 127, 119, 119, 119, 119, 119, 127, 897, 898, 898, 899, 900, 901, - 902, 903, 904, 905, 906, 907, 908, 909, 910, 169, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 911, 912, - 913, 914, 915, 916, 917, 917, 918, 919, 920, 920, 921, 922, 923, 924, - 925, 925, 925, 925, 926, 927, 927, 927, 928, 929, 929, 929, 930, 931, - 932, 119, 933, 934, 935, 934, 934, 936, 934, 934, 937, 934, 938, 934, - 938, 119, 119, 119, 119, 934, 934, 934, 934, 934, 934, 934, 934, 934, - 934, 934, 934, 934, 934, 934, 939, 940, 941, 941, 941, 941, 941, 942, - 610, 943, 943, 943, 943, 943, 943, 944, 945, 946, 947, 581, 948, 949, - 119, 119, 119, 119, 119, 610, 610, 610, 610, 610, 950, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 951, - 951, 951, 952, 953, 953, 953, 953, 953, 953, 954, 119, 955, 956, 956, - 957, 958, 958, 958, 958, 959, 960, 961, 961, 962, 963, 964, 964, 964, - 964, 965, 966, 967, 967, 967, 968, 969, 969, 969, 969, 970, 969, 971, - 119, 119, 119, 119, 119, 972, 972, 972, 972, 972, 973, 973, 973, 973, - 973, 974, 974, 974, 974, 974, 974, 975, 975, 975, 976, 977, 978, 979, - 979, 979, 979, 980, 981, 981, 981, 981, 982, 983, 983, 983, 983, 983, - 119, 984, 984, 984, 984, 984, 984, 985, 986, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 987, - 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, - 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, - 987, 987, 987, 987, 987, 987, 987, 987, 987, 988, 119, 987, 987, 989, - 119, 987, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 990, 991, 992, 992, 992, 992, 993, - 994, 995, 995, 996, 997, 998, 998, 999, 1000, 1001, 1001, 1001, 1002, - 1003, 1004, 119, 119, 119, 119, 119, 119, 1005, 1005, 1006, 1007, 1008, - 1008, 1009, 1010, 1011, 1011, 1011, 1012, 119, 119, 119, 119, 119, 119, - 119, 119, 1013, 1013, 1013, 1013, 1014, 1014, 1014, 1015, 1016, 1016, - 1017, 1016, 1016, 1016, 1016, 1016, 1018, 1019, 1020, 1021, 1022, 1022, - 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1029, 1029, 1030, 1031, 1031, - 1031, 1032, 119, 119, 119, 119, 1033, 1034, 1033, 1033, 1035, 1036, 1037, - 119, 1038, 1038, 1038, 1038, 1038, 1038, 1039, 1040, 1041, 1041, 1042, - 1043, 1044, 1044, 1045, 1046, 1047, 1047, 1048, 1049, 119, 1050, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 1051, 1051, 1051, 1051, - 1051, 1051, 1051, 1051, 1051, 1052, 119, 119, 119, 119, 119, 119, 1053, - 1053, 1053, 1053, 1053, 1053, 1054, 119, 1055, 1055, 1055, 1055, 1055, - 1055, 1056, 1057, 1058, 1058, 1058, 1058, 1059, 119, 1060, 1061, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 1062, 1062, 1062, 1063, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1064, - 1064, 1064, 1065, 1066, 119, 1067, 1067, 1068, 1069, 1070, 1071, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 1072, 1073, 1073, 1073, 1073, 1073, 1073, 1074, - 1075, 1076, 1077, 1078, 1079, 1080, 119, 1081, 1082, 1083, 1083, 1083, - 1083, 1083, 1084, 1085, 1086, 1087, 1088, 1088, 1088, 1089, 1090, 1091, - 1092, 1093, 1093, 1093, 1094, 1095, 1096, 1097, 1098, 119, 1099, 1099, - 1099, 1099, 1100, 119, 1101, 1102, 1102, 1102, 1102, 1102, 1103, 1104, - 1105, 1106, 1107, 1108, 1109, 1110, 1111, 119, 1112, 1112, 1113, 1112, - 1112, 1114, 1115, 1116, 119, 119, 119, 119, 119, 119, 119, 119, 1117, - 1118, 1119, 1120, 1119, 1121, 1122, 1122, 1122, 1122, 1122, 1123, 1124, - 1125, 1126, 1127, 1128, 1129, 1130, 1131, 1131, 1132, 1133, 1134, 1135, - 1136, 1137, 1138, 1139, 1140, 1140, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1141, 1141, 1141, 1141, - 1141, 1141, 1142, 1143, 1144, 1145, 1146, 1147, 119, 119, 119, 119, 1148, - 1148, 1148, 1148, 1148, 1148, 1149, 1150, 1151, 119, 1152, 1153, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 1154, 1154, 1154, 1154, 1154, 1155, 1156, 1157, - 1158, 1159, 1160, 1161, 119, 119, 119, 119, 1162, 1162, 1162, 1162, 1162, - 1162, 1163, 1164, 1165, 119, 1166, 1167, 1168, 1169, 119, 119, 1170, - 1170, 1170, 1170, 1170, 1171, 1172, 119, 1173, 1174, 119, 119, 119, 119, - 119, 119, 1175, 1175, 1175, 1176, 1177, 1178, 1179, 1180, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 1181, 1181, 1181, 1181, 1181, 1182, - 1183, 1184, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 1185, 1185, 1185, 1185, 1186, 1186, 1186, 1186, 1187, 1188, 1189, 1190, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 1191, 1192, 1193, 1193, 1193, 1193, 1194, 1195, 1196, - 119, 1197, 1198, 1199, 1199, 1199, 1199, 1200, 1201, 1202, 1203, 1204, - 119, 119, 119, 1205, 1205, 1205, 1205, 1205, 1205, 1205, 1206, 1207, - 1208, 1207, 1207, 1207, 1209, 1210, 1211, 1212, 119, 1213, 1214, 1215, - 1216, 1217, 1218, 1218, 1218, 1219, 1220, 1220, 1221, 1222, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 1223, 1224, 1225, 1225, 1225, 1225, - 1226, 1227, 1228, 119, 1229, 1230, 1231, 1232, 1233, 1233, 1233, 1234, - 1235, 1236, 1237, 1238, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 1239, 1239, 1240, 1241, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1243, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 1244, 1244, 1244, 1244, 1244, 1244, - 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1245, 1246, 119, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1247, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1249, 1248, 1248, 1248, 1248, 1250, 1251, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1252, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1253, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1254, - 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1255, 1254, 1254, 1254, - 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1256, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 791, 791, 791, 791, 791, - 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, - 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, - 791, 791, 791, 791, 791, 791, 1257, 1258, 1258, 1258, 1259, 1260, 1261, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1262, 1262, - 1262, 1263, 1264, 119, 1265, 1265, 1265, 1265, 1265, 1265, 1266, 1267, - 1268, 119, 1269, 1270, 1271, 1265, 1265, 1272, 1265, 1265, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 1273, 1273, 1273, 1273, 1274, 1274, 1274, 1274, - 1275, 1275, 1276, 1277, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 1278, 1278, 1278, 1278, 1278, 1278, 1278, 1278, 1279, 119, - 1280, 1281, 1281, 1281, 1281, 1282, 119, 1283, 1284, 1285, 119, 119, 119, - 119, 119, 119, 119, 119, 1286, 119, 119, 119, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1288, 119, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1289, 119, 1290, 735, 735, 735, 735, - 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, - 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, - 735, 735, 1291, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1292, - 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, - 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, - 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, - 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, - 1293, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, - 1294, 1294, 1295, 1294, 1296, 1294, 1297, 1294, 1298, 1299, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 610, 610, 610, 610, 610, - 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, - 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 1300, 119, 610, - 610, 610, 610, 1301, 1302, 610, 610, 610, 610, 610, 610, 1303, 1304, - 1305, 1306, 1307, 1308, 610, 610, 610, 1309, 610, 610, 610, 610, 610, - 610, 610, 1310, 119, 119, 946, 946, 946, 946, 946, 946, 946, 946, 1311, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 941, 941, 1312, 119, 581, 581, 581, 581, 581, - 581, 581, 581, 581, 581, 617, 119, 941, 941, 941, 1313, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1314, - 1314, 1314, 1315, 1316, 1316, 1317, 1314, 1314, 1318, 1319, 1316, 1316, - 1314, 1314, 1314, 1315, 1316, 1316, 1320, 1321, 1322, 1318, 1323, 1324, - 1316, 1314, 1314, 1314, 1315, 1316, 1316, 1325, 1326, 1327, 1328, 1316, - 1316, 1316, 1329, 1330, 1331, 1332, 1316, 1316, 1317, 1314, 1314, 1318, - 1316, 1316, 1316, 1314, 1314, 1314, 1315, 1316, 1316, 1317, 1314, 1314, - 1318, 1316, 1316, 1316, 1314, 1314, 1314, 1315, 1316, 1316, 1317, 1314, - 1314, 1318, 1316, 1316, 1316, 1314, 1314, 1314, 1315, 1316, 1316, 1333, - 1314, 1314, 1314, 1334, 1316, 1316, 1335, 1336, 1314, 1314, 1337, 1316, - 1316, 1338, 1317, 1314, 1314, 1339, 1316, 1316, 1340, 1341, 1314, 1314, - 1342, 1316, 1316, 1316, 1343, 1314, 1314, 1314, 1334, 1316, 1316, 1335, - 1344, 1345, 1345, 1345, 1345, 1345, 1345, 1346, 1346, 1346, 1346, 1346, - 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, - 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, - 1346, 1346, 1346, 1347, 1347, 1347, 1347, 1347, 1347, 1348, 1349, 1347, - 1347, 1347, 1347, 1347, 1350, 1351, 1346, 1352, 1353, 119, 1354, 1355, - 1347, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1356, 1357, 1357, - 1358, 1359, 1360, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, - 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, - 1361, 1362, 1363, 1364, 119, 119, 119, 119, 119, 1365, 1365, 1365, 1365, - 1366, 1367, 1367, 1367, 1368, 1369, 1370, 1371, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 1372, 1373, 1373, 1373, 1373, 1373, 1373, 1374, 1375, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 1376, 127, 127, 127, 1377, 1378, 1379, - 1380, 1381, 1382, 1377, 1383, 1377, 1379, 1379, 1384, 127, 1385, 127, - 1386, 1387, 1385, 127, 1386, 119, 119, 119, 119, 119, 119, 1388, 119, - 1389, 1390, 1390, 1390, 1390, 1391, 1390, 1390, 1390, 1390, 1390, 1390, - 1390, 1390, 1390, 1390, 1390, 1390, 1391, 1392, 1390, 1393, 1394, 1390, - 1394, 1395, 1394, 1390, 1390, 1390, 1396, 1392, 620, 1397, 622, 622, 622, - 1398, 622, 622, 622, 622, 622, 622, 622, 1399, 622, 622, 622, 1400, 1401, - 1402, 622, 1403, 1392, 1392, 1392, 1392, 1392, 1392, 1404, 1405, 1405, - 1405, 1406, 1392, 755, 755, 755, 755, 755, 1407, 755, 1408, 1409, 1392, - 1410, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 723, 723, 723, 723, 1411, - 1412, 1413, 723, 723, 723, 723, 723, 723, 723, 723, 1414, 1415, 723, - 1416, 1417, 723, 723, 1418, 1419, 1420, 1421, 1416, 1390, 723, 723, 1422, - 1423, 723, 723, 723, 723, 723, 723, 723, 1424, 1425, 1426, 1427, 723, - 1428, 1429, 1426, 1430, 1431, 723, 723, 723, 1432, 1433, 1434, 723, 723, - 723, 723, 723, 723, 723, 723, 1435, 1436, 723, 1437, 643, 1438, 723, - 1439, 1440, 581, 1441, 723, 723, 723, 1390, 1442, 1443, 1390, 1390, 1444, - 1390, 1389, 1390, 1390, 1390, 1390, 1390, 1445, 1446, 1390, 1390, 1445, - 1447, 723, 723, 723, 723, 723, 723, 723, 723, 1448, 1449, 581, 581, 581, - 581, 1450, 1451, 723, 723, 723, 723, 1452, 723, 1453, 723, 1454, 1455, - 1456, 1392, 1390, 1457, 1458, 1459, 581, 581, 581, 581, 581, 581, 581, - 581, 581, 581, 581, 581, 581, 581, 1460, 1392, 581, 581, 581, 581, 581, - 581, 581, 581, 581, 581, 1461, 1462, 1392, 1392, 1392, 1392, 581, 1460, - 581, 581, 581, 581, 581, 581, 581, 1392, 581, 1463, 581, 581, 581, 581, - 581, 1392, 581, 581, 581, 1464, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 581, 1460, 723, 1465, 1466, 723, 1426, 1467, 723, 723, - 723, 723, 723, 723, 1468, 1469, 723, 723, 723, 723, 1470, 1392, 1471, - 1472, 1470, 1392, 1473, 1474, 723, 723, 723, 723, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1390, 1396, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1475, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 1476, 775, 775, 775, 775, 775, 773, - 773, 773, 773, 773, 773, 1477, 775, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 774, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 883, - 775, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 1478, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 773, 773, 773, 774, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 1479, 1480, - 119, 119, 119, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, - 1481, 1481, 1481, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 898, 898, 898, 898, 898, 898, 898, 898, 898, - 898, 898, 898, 898, 898, 898, 898, 898, 898, 898, 898, 898, 898, 898, - 898, 898, 898, 898, 898, 898, 898, 119, 119, 882, 882, 882, 882, 882, - 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, - 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 1482, -}; - -static const unsigned short index2[] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 1, 1, 1, 1, 1, 1, 7, 7, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 11, 16, 17, 15, 18, 19, 20, 19, 21, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 19, 23, 24, 24, 24, 10, 15, 25, 25, 25, - 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 16, 26, 17, - 27, 28, 27, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, - 29, 29, 29, 16, 30, 31, 24, 1, 1, 1, 1, 1, 1, 32, 1, 1, 33, 34, 35, 13, - 36, 13, 37, 38, 39, 40, 41, 42, 24, 43, 44, 27, 45, 46, 47, 47, 48, 49, - 38, 38, 39, 47, 41, 50, 51, 51, 51, 34, 52, 52, 52, 52, 52, 52, 53, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 53, 52, 52, 52, 52, 52, 52, 54, 53, 52, - 52, 52, 52, 52, 53, 55, 55, 55, 56, 56, 56, 56, 55, 56, 55, 55, 55, 56, - 55, 55, 56, 56, 55, 56, 55, 55, 56, 56, 56, 54, 55, 55, 55, 56, 55, 56, - 55, 56, 52, 55, 52, 56, 52, 56, 52, 56, 52, 56, 52, 56, 52, 56, 52, 56, - 52, 55, 52, 55, 52, 56, 52, 56, 52, 56, 52, 55, 52, 56, 52, 56, 52, 56, - 52, 56, 52, 56, 53, 55, 52, 55, 53, 55, 52, 56, 52, 56, 55, 52, 56, 52, - 56, 52, 56, 53, 55, 53, 55, 52, 55, 52, 56, 52, 55, 55, 53, 55, 52, 55, - 52, 56, 52, 56, 53, 55, 52, 56, 52, 56, 52, 52, 56, 52, 56, 52, 56, 56, - 56, 52, 52, 56, 52, 56, 52, 52, 56, 52, 52, 52, 56, 56, 52, 52, 52, 52, - 56, 52, 52, 56, 52, 52, 52, 56, 56, 56, 52, 52, 56, 52, 52, 56, 52, 56, - 52, 56, 52, 52, 56, 52, 56, 56, 52, 56, 52, 52, 56, 52, 52, 52, 56, 52, - 56, 52, 52, 56, 56, 57, 52, 56, 56, 56, 57, 57, 57, 57, 52, 58, 56, 52, - 58, 56, 52, 58, 56, 52, 55, 52, 55, 52, 55, 52, 55, 52, 55, 52, 55, 52, - 55, 52, 55, 56, 52, 56, 56, 52, 58, 56, 52, 56, 52, 52, 52, 56, 52, 56, - 56, 56, 56, 56, 56, 56, 52, 52, 56, 52, 52, 56, 56, 52, 56, 52, 52, 52, - 52, 56, 56, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, - 56, 56, 56, 56, 57, 56, 56, 56, 59, 59, 59, 59, 59, 59, 59, 59, 59, 60, - 60, 61, 61, 61, 61, 61, 61, 61, 62, 62, 63, 62, 60, 64, 65, 64, 64, 64, - 65, 64, 60, 60, 66, 61, 62, 62, 62, 62, 62, 62, 39, 39, 39, 39, 62, 39, - 62, 48, 59, 59, 59, 59, 59, 62, 62, 62, 62, 62, 67, 67, 60, 62, 61, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 69, 70, 70, 70, 70, 69, 71, 70, 70, 70, 70, 70, 72, 72, 70, - 70, 70, 70, 72, 72, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 73, 73, - 73, 73, 73, 70, 70, 70, 70, 68, 68, 68, 68, 68, 68, 68, 68, 74, 68, 70, - 70, 70, 68, 68, 68, 70, 70, 75, 68, 68, 68, 70, 70, 70, 70, 68, 69, 70, - 70, 68, 76, 77, 77, 76, 77, 77, 76, 68, 68, 68, 68, 68, 78, 79, 78, 79, - 60, 80, 78, 79, 81, 81, 82, 79, 79, 79, 83, 78, 81, 81, 81, 81, 80, 62, - 78, 84, 78, 78, 78, 81, 78, 81, 78, 78, 79, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 81, 85, 85, 85, 85, 85, 85, 85, - 78, 78, 79, 79, 79, 79, 79, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 79, 86, 86, 86, 86, 86, 86, 86, 79, 79, 79, 79, - 79, 78, 79, 79, 78, 78, 78, 79, 79, 79, 78, 79, 78, 79, 78, 79, 78, 79, - 78, 79, 87, 88, 87, 88, 87, 88, 87, 88, 87, 88, 87, 88, 87, 88, 79, 79, - 79, 79, 78, 79, 89, 78, 79, 78, 78, 79, 79, 78, 78, 78, 90, 91, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, - 91, 91, 92, 92, 92, 92, 92, 92, 92, 92, 93, 92, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 90, 93, 90, 93, 90, 93, 90, 93, 90, 93, - 94, 95, 95, 96, 96, 95, 97, 97, 90, 93, 90, 93, 90, 93, 90, 90, 93, 90, - 93, 90, 93, 90, 93, 90, 93, 90, 93, 90, 93, 93, 81, 98, 98, 98, 98, 98, - 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 81, - 81, 99, 100, 100, 100, 100, 100, 100, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 102, 103, 81, 81, 104, 104, 105, 81, 106, 107, 107, 107, 107, - 106, 107, 107, 107, 108, 106, 107, 107, 107, 107, 107, 107, 106, 106, - 106, 106, 106, 106, 107, 107, 106, 107, 107, 108, 109, 107, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, 119, 120, 121, 122, 123, 124, - 125, 126, 127, 125, 107, 106, 128, 118, 81, 81, 81, 81, 81, 81, 81, 81, - 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 81, 81, 81, 81, - 129, 129, 129, 129, 125, 125, 81, 81, 81, 130, 130, 130, 130, 130, 131, - 132, 132, 133, 134, 134, 135, 136, 137, 138, 138, 139, 139, 139, 139, - 139, 139, 139, 139, 140, 141, 142, 143, 144, 81, 145, 143, 146, 146, 146, - 146, 146, 146, 146, 146, 147, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 148, 149, 150, 151, 152, 153, 154, 155, 96, 96, 156, 157, 139, - 139, 139, 139, 139, 157, 139, 139, 157, 158, 158, 158, 158, 158, 158, - 158, 158, 158, 158, 134, 159, 159, 160, 146, 146, 161, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 145, 146, 139, 139, 139, 139, - 139, 139, 139, 131, 138, 139, 139, 139, 139, 157, 139, 162, 162, 139, - 139, 138, 157, 139, 139, 157, 146, 146, 163, 163, 163, 163, 163, 163, - 163, 163, 163, 163, 146, 146, 146, 164, 164, 146, 165, 165, 165, 165, - 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 81, 166, 167, 168, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 169, - 170, 169, 169, 170, 169, 169, 170, 170, 170, 169, 170, 170, 169, 170, - 169, 169, 169, 170, 169, 170, 169, 170, 169, 170, 169, 169, 81, 81, 167, - 167, 167, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, - 171, 171, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 171, 81, - 81, 81, 81, 81, 81, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, - 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, - 174, 174, 174, 175, 175, 175, 175, 175, 175, 175, 176, 175, 177, 177, - 178, 179, 180, 181, 177, 81, 81, 176, 182, 182, 183, 183, 183, 183, 183, - 183, 183, 183, 183, 183, 183, 183, 183, 183, 184, 184, 184, 184, 185, - 184, 184, 184, 184, 184, 184, 184, 184, 184, 185, 184, 184, 184, 185, - 184, 184, 184, 184, 184, 81, 81, 186, 186, 186, 186, 186, 186, 186, 186, - 186, 186, 186, 186, 186, 186, 186, 81, 187, 187, 187, 187, 187, 187, 187, - 187, 187, 188, 188, 188, 81, 81, 189, 81, 167, 167, 167, 81, 81, 81, 81, - 81, 146, 146, 146, 146, 146, 81, 146, 146, 146, 146, 146, 146, 146, 146, - 81, 81, 81, 81, 81, 157, 139, 139, 139, 139, 139, 139, 131, 157, 139, - 139, 157, 139, 139, 157, 139, 139, 139, 157, 157, 157, 190, 191, 192, - 139, 139, 139, 157, 139, 139, 157, 157, 139, 139, 139, 139, 139, 193, - 193, 193, 194, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, - 195, 195, 195, 193, 194, 196, 195, 194, 194, 194, 193, 193, 193, 193, - 193, 193, 193, 193, 194, 194, 194, 194, 197, 194, 194, 195, 96, 156, 198, - 198, 193, 193, 193, 195, 195, 193, 193, 199, 199, 200, 200, 200, 200, - 200, 200, 200, 200, 200, 200, 201, 202, 195, 195, 195, 195, 195, 195, - 203, 204, 205, 205, 81, 203, 203, 203, 203, 203, 203, 203, 203, 81, 81, - 203, 203, 81, 81, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, - 203, 203, 203, 81, 203, 203, 203, 203, 203, 203, 203, 81, 203, 81, 81, - 81, 203, 203, 203, 203, 81, 81, 206, 203, 205, 205, 205, 204, 204, 204, - 204, 81, 81, 205, 205, 81, 81, 205, 205, 207, 203, 81, 81, 81, 81, 81, - 81, 81, 81, 205, 81, 81, 81, 81, 203, 203, 81, 203, 203, 203, 204, 204, - 81, 81, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 203, 203, 209, - 209, 210, 210, 210, 210, 210, 211, 212, 213, 203, 214, 215, 81, 81, 216, - 216, 217, 81, 218, 218, 218, 218, 218, 218, 81, 81, 81, 81, 218, 218, 81, - 81, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, - 81, 218, 218, 218, 218, 218, 218, 218, 81, 218, 218, 81, 218, 218, 81, - 218, 218, 81, 81, 219, 81, 217, 217, 217, 216, 216, 81, 81, 81, 81, 216, - 216, 81, 81, 216, 216, 220, 81, 81, 81, 216, 81, 81, 81, 81, 81, 81, 81, - 218, 218, 218, 218, 81, 218, 81, 81, 81, 81, 81, 81, 81, 221, 221, 221, - 221, 221, 221, 221, 221, 221, 221, 216, 216, 218, 218, 218, 216, 222, 81, - 81, 223, 223, 224, 81, 225, 225, 225, 225, 225, 225, 225, 225, 225, 81, - 225, 225, 225, 81, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, - 225, 225, 225, 81, 225, 225, 225, 225, 225, 225, 225, 81, 225, 225, 81, - 225, 225, 225, 225, 225, 81, 81, 226, 225, 224, 224, 224, 223, 223, 223, - 223, 223, 81, 223, 223, 224, 81, 224, 224, 227, 81, 81, 225, 81, 81, 81, - 81, 81, 81, 81, 225, 225, 223, 223, 81, 81, 228, 228, 228, 228, 228, 228, - 228, 228, 228, 228, 229, 230, 81, 81, 81, 81, 81, 81, 81, 225, 223, 223, - 223, 223, 223, 223, 81, 231, 232, 232, 81, 233, 233, 233, 233, 233, 233, - 233, 233, 81, 81, 233, 233, 81, 81, 233, 233, 233, 233, 233, 233, 233, - 233, 233, 233, 233, 233, 233, 233, 81, 233, 233, 233, 233, 233, 233, 233, - 81, 233, 233, 81, 233, 233, 233, 233, 233, 81, 81, 234, 233, 232, 231, - 232, 231, 231, 231, 231, 81, 81, 232, 232, 81, 81, 232, 232, 235, 81, 81, - 81, 81, 81, 81, 81, 81, 231, 232, 81, 81, 81, 81, 233, 233, 81, 233, 233, - 233, 231, 231, 81, 81, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, - 237, 233, 238, 238, 238, 238, 238, 238, 81, 81, 239, 240, 81, 240, 240, - 240, 240, 240, 240, 81, 81, 81, 240, 240, 240, 81, 240, 240, 240, 240, - 81, 81, 81, 240, 240, 81, 240, 81, 240, 240, 81, 81, 81, 240, 240, 81, - 81, 81, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 81, 81, 81, 81, - 241, 241, 239, 241, 241, 81, 81, 81, 241, 241, 241, 81, 241, 241, 241, - 242, 81, 81, 240, 81, 81, 81, 81, 81, 81, 241, 81, 81, 81, 81, 81, 81, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 244, 244, 244, 245, - 245, 245, 245, 245, 245, 246, 245, 81, 81, 81, 81, 81, 247, 248, 248, - 248, 247, 249, 249, 249, 249, 249, 249, 249, 249, 81, 249, 249, 249, 81, - 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, - 249, 249, 81, 81, 81, 249, 247, 247, 247, 248, 248, 248, 248, 81, 247, - 247, 247, 81, 247, 247, 247, 250, 81, 81, 81, 81, 81, 81, 81, 251, 252, - 81, 249, 249, 249, 81, 81, 81, 81, 81, 249, 249, 247, 247, 81, 81, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 254, 254, 254, 254, 254, - 254, 254, 255, 256, 257, 258, 258, 259, 256, 256, 256, 256, 256, 256, - 256, 256, 81, 256, 256, 256, 81, 256, 256, 256, 256, 256, 256, 256, 256, - 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 81, 256, 256, 256, 256, - 256, 81, 81, 260, 256, 258, 261, 258, 258, 258, 258, 258, 81, 261, 258, - 258, 81, 258, 258, 257, 262, 81, 81, 81, 81, 81, 81, 81, 258, 258, 81, - 81, 81, 81, 81, 81, 81, 256, 81, 256, 256, 257, 257, 81, 81, 263, 263, - 263, 263, 263, 263, 263, 263, 263, 263, 81, 256, 256, 81, 81, 81, 81, 81, - 264, 264, 265, 265, 81, 266, 266, 266, 266, 266, 266, 266, 266, 81, 266, - 266, 266, 81, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, - 266, 266, 266, 266, 266, 267, 267, 266, 265, 265, 265, 264, 264, 264, - 264, 81, 265, 265, 265, 81, 265, 265, 265, 267, 266, 268, 81, 81, 81, 81, - 266, 266, 266, 265, 269, 269, 269, 269, 269, 269, 269, 266, 266, 266, - 264, 264, 81, 81, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 269, - 269, 269, 269, 269, 269, 269, 269, 269, 271, 266, 266, 266, 266, 266, - 266, 81, 81, 272, 272, 81, 273, 273, 273, 273, 273, 273, 273, 273, 273, - 273, 273, 273, 273, 273, 273, 273, 273, 273, 81, 81, 81, 273, 273, 273, - 273, 273, 273, 273, 273, 81, 273, 273, 273, 273, 273, 273, 273, 273, 273, - 81, 273, 81, 81, 81, 81, 274, 81, 81, 81, 81, 272, 272, 272, 275, 275, - 275, 81, 275, 81, 272, 272, 272, 272, 272, 272, 272, 272, 81, 81, 81, 81, - 81, 81, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 81, 81, 272, - 272, 277, 81, 81, 81, 81, 278, 278, 278, 278, 278, 278, 278, 278, 278, - 278, 278, 278, 278, 278, 278, 278, 279, 278, 278, 279, 279, 279, 279, - 280, 280, 281, 81, 81, 81, 81, 282, 278, 278, 278, 278, 278, 278, 283, - 279, 284, 284, 284, 284, 279, 279, 279, 285, 286, 286, 286, 286, 286, - 286, 286, 286, 286, 286, 287, 287, 81, 81, 81, 81, 81, 288, 288, 81, 288, - 81, 81, 288, 288, 81, 288, 81, 81, 288, 81, 81, 81, 81, 81, 81, 288, 288, - 288, 288, 81, 288, 288, 288, 288, 288, 288, 288, 81, 288, 288, 288, 81, - 288, 81, 288, 81, 81, 288, 288, 81, 288, 288, 288, 288, 289, 288, 288, - 289, 289, 289, 289, 290, 290, 81, 289, 289, 288, 81, 81, 288, 288, 288, - 288, 288, 81, 291, 81, 292, 292, 292, 292, 289, 289, 81, 81, 293, 293, - 293, 293, 293, 293, 293, 293, 293, 293, 81, 81, 288, 288, 288, 288, 294, - 295, 295, 295, 296, 297, 296, 296, 298, 296, 296, 299, 298, 300, 300, - 300, 300, 300, 298, 301, 300, 301, 301, 301, 302, 302, 301, 301, 301, - 301, 301, 301, 303, 303, 303, 303, 303, 303, 303, 303, 303, 303, 304, - 304, 304, 304, 304, 304, 304, 304, 304, 304, 305, 302, 301, 302, 301, - 306, 307, 308, 307, 308, 309, 309, 294, 294, 294, 294, 294, 294, 294, - 294, 81, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 81, - 81, 81, 81, 310, 311, 312, 313, 312, 312, 312, 312, 312, 311, 311, 311, - 311, 312, 314, 311, 312, 315, 315, 316, 299, 315, 315, 294, 294, 294, - 294, 294, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 81, 312, - 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 81, 305, 305, 301, - 301, 301, 301, 301, 301, 302, 301, 301, 301, 301, 301, 301, 81, 301, 301, - 296, 296, 299, 296, 297, 317, 317, 317, 317, 298, 298, 81, 81, 81, 81, - 81, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 319, 319, 320, - 320, 320, 320, 319, 320, 320, 320, 320, 320, 321, 319, 322, 322, 319, - 319, 320, 320, 318, 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, - 324, 324, 325, 325, 325, 325, 318, 318, 318, 318, 318, 318, 319, 319, - 320, 320, 318, 318, 318, 318, 320, 320, 320, 318, 319, 319, 319, 318, - 318, 319, 319, 319, 319, 319, 319, 319, 318, 318, 318, 320, 320, 320, - 320, 318, 318, 318, 318, 318, 320, 319, 319, 320, 320, 319, 319, 319, - 319, 319, 319, 326, 318, 319, 323, 323, 319, 319, 319, 320, 327, 327, - 328, 328, 328, 328, 328, 328, 328, 328, 328, 328, 328, 328, 328, 328, 81, - 328, 81, 81, 81, 81, 81, 328, 81, 81, 329, 329, 329, 329, 329, 329, 329, - 329, 329, 329, 329, 330, 331, 329, 329, 329, 332, 332, 332, 332, 332, - 332, 332, 332, 333, 333, 333, 333, 333, 333, 333, 333, 334, 334, 334, - 334, 334, 334, 334, 334, 335, 335, 335, 335, 335, 335, 335, 335, 335, 81, - 335, 335, 335, 335, 81, 81, 335, 335, 335, 335, 335, 335, 335, 81, 335, - 335, 335, 81, 81, 336, 336, 336, 337, 338, 337, 337, 337, 337, 337, 337, - 337, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, - 339, 339, 339, 339, 339, 339, 339, 81, 81, 81, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 81, 81, 81, 81, 81, 81, 341, 341, 341, 341, 341, - 341, 341, 341, 341, 341, 341, 341, 341, 341, 81, 81, 342, 342, 342, 342, - 342, 342, 81, 81, 343, 344, 344, 344, 344, 344, 344, 344, 344, 344, 344, - 344, 344, 344, 344, 344, 344, 344, 344, 344, 344, 345, 345, 344, 346, - 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, - 347, 347, 347, 347, 348, 349, 81, 81, 81, 350, 350, 350, 350, 350, 350, - 350, 350, 350, 350, 350, 199, 199, 199, 351, 351, 351, 350, 350, 350, - 350, 350, 350, 350, 350, 81, 81, 81, 81, 81, 81, 81, 352, 352, 352, 352, - 352, 352, 352, 352, 352, 352, 352, 352, 352, 81, 352, 352, 352, 352, 353, - 353, 354, 81, 81, 81, 355, 355, 355, 355, 355, 355, 355, 355, 355, 355, - 356, 356, 357, 199, 199, 81, 358, 358, 358, 358, 358, 358, 358, 358, 358, - 358, 359, 359, 81, 81, 81, 81, 360, 360, 360, 360, 360, 360, 360, 360, - 360, 360, 360, 360, 360, 81, 360, 360, 360, 81, 361, 361, 81, 81, 81, 81, - 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, 363, 363, - 364, 363, 363, 363, 363, 363, 363, 363, 364, 364, 364, 364, 364, 364, - 364, 364, 363, 364, 364, 363, 363, 363, 363, 363, 363, 363, 363, 363, - 365, 363, 366, 366, 367, 368, 366, 369, 366, 370, 362, 371, 81, 81, 372, - 372, 372, 372, 372, 372, 372, 372, 372, 372, 81, 81, 81, 81, 81, 81, 373, - 373, 373, 373, 373, 373, 373, 373, 373, 373, 81, 81, 81, 81, 81, 81, 374, - 374, 375, 375, 376, 377, 378, 374, 379, 379, 374, 380, 380, 380, 381, 81, - 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 81, 81, 81, 81, 81, 81, - 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 384, 383, 383, - 383, 383, 383, 81, 81, 81, 81, 81, 81, 81, 383, 383, 383, 383, 383, 380, - 380, 383, 383, 385, 383, 81, 81, 81, 81, 81, 344, 344, 344, 344, 344, - 344, 81, 81, 386, 386, 386, 386, 386, 386, 386, 386, 386, 386, 386, 386, - 386, 386, 386, 81, 387, 387, 387, 388, 388, 388, 388, 387, 387, 388, 388, - 388, 81, 81, 81, 81, 388, 388, 387, 388, 388, 388, 388, 388, 388, 389, - 390, 391, 81, 81, 81, 81, 392, 81, 81, 81, 393, 393, 394, 394, 394, 394, - 394, 394, 394, 394, 394, 394, 395, 395, 395, 395, 395, 395, 395, 395, - 395, 395, 395, 395, 395, 395, 81, 81, 395, 395, 395, 395, 395, 81, 81, - 81, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 81, 81, - 81, 81, 396, 396, 81, 81, 81, 81, 81, 81, 397, 397, 397, 397, 397, 397, - 397, 397, 397, 397, 398, 81, 81, 81, 399, 399, 400, 400, 400, 400, 400, - 400, 400, 400, 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, - 401, 401, 401, 401, 402, 403, 404, 404, 405, 81, 81, 406, 406, 407, 407, - 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 408, 409, 408, - 409, 409, 409, 409, 409, 409, 409, 81, 410, 408, 409, 408, 408, 409, 409, - 409, 409, 409, 409, 409, 409, 408, 408, 408, 408, 408, 408, 409, 409, - 411, 411, 411, 411, 411, 411, 411, 411, 81, 81, 412, 413, 413, 413, 413, - 413, 413, 413, 413, 413, 413, 81, 81, 81, 81, 81, 81, 414, 414, 414, 414, - 414, 414, 414, 415, 414, 414, 414, 414, 414, 414, 81, 81, 96, 96, 96, 96, - 96, 156, 156, 156, 156, 156, 156, 96, 96, 156, 416, 81, 417, 417, 417, - 417, 418, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, - 419, 419, 419, 420, 418, 417, 417, 417, 417, 417, 418, 417, 418, 418, - 418, 418, 418, 417, 418, 421, 419, 419, 419, 419, 419, 419, 419, 81, 81, - 81, 81, 422, 422, 422, 422, 422, 422, 422, 422, 422, 422, 423, 423, 424, - 423, 423, 423, 423, 425, 425, 425, 425, 425, 425, 425, 425, 425, 425, - 426, 427, 426, 426, 426, 426, 426, 426, 426, 425, 425, 425, 425, 425, - 425, 425, 425, 425, 81, 81, 81, 428, 428, 429, 430, 430, 430, 430, 430, - 430, 430, 430, 430, 430, 430, 430, 430, 430, 429, 428, 428, 428, 428, - 429, 429, 428, 428, 431, 432, 428, 428, 430, 430, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 430, 430, 430, 430, 430, 430, 434, 434, - 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 435, 436, - 437, 437, 436, 436, 436, 437, 436, 437, 437, 437, 438, 438, 81, 81, 81, - 81, 81, 81, 81, 81, 439, 439, 439, 439, 440, 440, 440, 440, 440, 440, - 440, 440, 440, 440, 440, 440, 441, 441, 441, 441, 441, 441, 441, 441, - 442, 442, 442, 442, 442, 442, 442, 442, 441, 441, 442, 443, 81, 81, 81, - 444, 444, 444, 444, 444, 445, 445, 445, 445, 445, 445, 445, 445, 445, - 445, 81, 81, 81, 440, 440, 440, 446, 446, 446, 446, 446, 446, 446, 446, - 446, 446, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, - 447, 447, 448, 448, 448, 448, 448, 448, 449, 449, 93, 81, 81, 81, 81, 81, - 81, 81, 328, 328, 328, 81, 81, 328, 328, 328, 450, 450, 450, 450, 450, - 450, 450, 450, 96, 96, 96, 330, 451, 156, 156, 156, 156, 156, 96, 96, - 156, 156, 156, 156, 96, 452, 451, 451, 451, 451, 451, 451, 451, 453, 453, - 453, 453, 156, 453, 453, 453, 453, 452, 452, 96, 453, 453, 452, 96, 96, - 81, 81, 81, 81, 81, 81, 56, 56, 56, 56, 56, 56, 79, 79, 79, 79, 79, 93, - 59, 59, 59, 59, 59, 59, 59, 59, 59, 82, 82, 82, 82, 82, 59, 59, 59, 59, - 82, 82, 82, 82, 82, 56, 56, 56, 56, 56, 454, 56, 56, 56, 56, 56, 56, 56, - 56, 56, 56, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 82, 96, 96, - 156, 96, 96, 96, 96, 96, 96, 96, 156, 96, 96, 455, 456, 156, 457, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 458, 459, 459, 156, 81, 96, 460, 156, 96, 156, 52, 56, 52, 56, 52, - 56, 56, 56, 56, 56, 56, 56, 56, 56, 52, 56, 79, 79, 79, 79, 79, 79, 79, - 79, 78, 78, 78, 78, 78, 78, 78, 78, 79, 79, 79, 79, 79, 79, 81, 81, 78, - 78, 78, 78, 78, 78, 81, 81, 81, 78, 81, 78, 81, 78, 81, 78, 461, 461, - 461, 461, 461, 461, 461, 461, 79, 79, 79, 79, 79, 81, 79, 79, 78, 78, 78, - 78, 461, 80, 79, 80, 80, 80, 79, 79, 79, 81, 79, 79, 78, 78, 78, 78, 461, - 80, 80, 80, 79, 79, 79, 79, 81, 81, 79, 79, 78, 78, 78, 78, 81, 80, 80, - 80, 78, 78, 78, 78, 78, 80, 80, 80, 81, 81, 79, 79, 79, 81, 79, 79, 78, - 78, 78, 78, 461, 462, 80, 81, 463, 463, 463, 463, 463, 463, 463, 464, - 463, 463, 463, 465, 466, 467, 468, 469, 470, 471, 472, 470, 473, 474, 38, - 84, 475, 476, 477, 42, 475, 476, 477, 42, 38, 38, 478, 84, 479, 479, 479, - 480, 481, 482, 483, 484, 485, 486, 487, 33, 488, 489, 488, 488, 489, 490, - 491, 491, 84, 42, 50, 38, 492, 492, 478, 493, 493, 84, 84, 84, 494, 477, - 495, 492, 492, 492, 84, 84, 84, 84, 84, 84, 84, 84, 496, 84, 493, 84, - 377, 84, 377, 377, 377, 377, 84, 377, 377, 463, 497, 498, 498, 498, 498, - 81, 499, 500, 501, 502, 503, 503, 503, 503, 503, 503, 504, 59, 81, 81, - 47, 504, 504, 504, 504, 504, 505, 505, 496, 477, 495, 506, 504, 47, 47, - 47, 47, 504, 504, 504, 504, 504, 505, 505, 496, 477, 495, 81, 59, 59, 59, - 59, 59, 81, 81, 81, 282, 282, 282, 282, 282, 282, 282, 507, 282, 508, - 282, 282, 36, 282, 282, 282, 282, 282, 282, 282, 282, 282, 507, 282, 282, - 282, 282, 507, 282, 282, 507, 282, 509, 509, 509, 509, 509, 509, 509, - 509, 96, 96, 451, 451, 96, 96, 96, 96, 451, 451, 451, 96, 96, 416, 416, - 416, 416, 96, 416, 416, 416, 451, 451, 96, 156, 96, 451, 451, 156, 156, - 156, 156, 96, 81, 81, 81, 81, 81, 81, 81, 40, 40, 510, 511, 40, 512, 40, - 510, 40, 511, 49, 510, 510, 510, 49, 49, 510, 510, 510, 513, 40, 510, - 514, 40, 496, 510, 510, 510, 510, 510, 40, 40, 40, 512, 512, 40, 510, 40, - 85, 40, 510, 40, 52, 515, 510, 510, 516, 49, 510, 510, 52, 510, 49, 453, - 453, 453, 453, 49, 40, 40, 49, 49, 510, 510, 496, 496, 496, 496, 496, - 510, 49, 49, 49, 49, 40, 496, 40, 40, 56, 317, 517, 517, 517, 518, 51, - 519, 517, 517, 517, 517, 517, 51, 518, 518, 51, 517, 520, 520, 520, 520, - 520, 520, 520, 520, 520, 520, 520, 520, 521, 521, 521, 521, 520, 520, - 521, 521, 521, 521, 521, 521, 521, 521, 521, 52, 56, 521, 521, 521, 521, - 51, 40, 40, 81, 81, 81, 81, 54, 54, 54, 54, 54, 512, 512, 512, 512, 512, - 496, 496, 40, 40, 40, 40, 496, 40, 40, 496, 40, 40, 496, 40, 40, 40, 40, - 40, 40, 40, 496, 40, 40, 40, 40, 40, 40, 40, 40, 40, 44, 44, 40, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 40, 40, 496, 496, 40, 40, 54, 40, 54, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 40, 44, 40, 40, 40, 40, 496, 496, 496, 496, - 496, 496, 496, 496, 496, 496, 496, 496, 54, 496, 54, 54, 496, 496, 496, - 54, 54, 496, 496, 54, 496, 496, 496, 54, 496, 54, 522, 523, 496, 54, 496, - 496, 496, 496, 54, 496, 496, 54, 54, 54, 54, 496, 496, 54, 496, 54, 496, - 54, 54, 54, 54, 54, 54, 496, 54, 496, 496, 496, 496, 496, 54, 54, 54, 54, - 496, 496, 496, 496, 54, 54, 496, 496, 54, 496, 496, 496, 54, 496, 496, - 496, 496, 496, 54, 496, 496, 496, 496, 496, 54, 54, 496, 496, 54, 54, 54, - 54, 496, 496, 54, 54, 496, 496, 54, 54, 496, 496, 496, 496, 496, 54, 496, - 496, 496, 54, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, - 496, 54, 496, 496, 496, 496, 496, 496, 496, 524, 477, 495, 477, 495, 40, - 40, 40, 40, 40, 40, 512, 40, 40, 40, 40, 40, 40, 40, 525, 525, 40, 40, - 40, 40, 496, 496, 40, 40, 40, 40, 40, 40, 40, 526, 527, 40, 40, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 317, 317, 317, 317, 317, 317, 317, 317, 317, - 317, 317, 317, 317, 40, 496, 40, 40, 40, 40, 40, 40, 40, 40, 317, 40, 40, - 40, 40, 40, 496, 496, 496, 496, 496, 496, 496, 496, 496, 40, 40, 40, 40, - 40, 528, 528, 528, 528, 40, 40, 40, 525, 529, 529, 525, 40, 40, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 81, 40, 40, 40, 81, 81, 81, 81, 81, 51, 51, - 51, 51, 51, 51, 51, 51, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, - 519, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 518, 512, 512, 512, - 512, 512, 512, 512, 512, 512, 512, 512, 512, 40, 40, 40, 40, 512, 512, - 512, 512, 531, 40, 40, 40, 40, 40, 512, 512, 512, 512, 40, 40, 512, 512, - 40, 512, 512, 512, 512, 512, 512, 512, 40, 40, 40, 40, 40, 40, 40, 40, - 512, 512, 40, 40, 512, 54, 40, 40, 40, 40, 512, 512, 40, 40, 512, 54, 40, - 40, 40, 40, 512, 512, 512, 40, 40, 512, 40, 40, 512, 512, 40, 40, 40, 40, - 40, 40, 40, 512, 496, 496, 496, 496, 496, 532, 532, 496, 529, 529, 529, - 529, 40, 512, 512, 40, 40, 512, 40, 40, 40, 40, 512, 512, 40, 40, 40, 40, - 525, 525, 531, 531, 529, 40, 529, 529, 533, 534, 533, 529, 40, 529, 529, - 529, 40, 40, 40, 40, 512, 40, 512, 40, 40, 40, 40, 40, 528, 528, 528, - 528, 528, 528, 528, 528, 528, 528, 528, 528, 40, 40, 40, 40, 512, 512, - 40, 512, 512, 512, 40, 512, 533, 512, 512, 40, 512, 512, 40, 54, 40, 40, - 40, 40, 40, 40, 40, 525, 40, 40, 40, 528, 40, 40, 40, 40, 40, 40, 40, 40, - 40, 40, 512, 512, 40, 528, 40, 40, 40, 40, 40, 40, 40, 40, 528, 528, 317, - 40, 40, 40, 40, 40, 40, 40, 40, 525, 525, 533, 529, 529, 529, 529, 525, - 525, 533, 533, 533, 512, 512, 512, 512, 533, 528, 533, 533, 533, 512, - 533, 525, 512, 512, 512, 533, 533, 512, 512, 533, 512, 512, 533, 533, - 533, 40, 512, 40, 40, 40, 40, 512, 512, 525, 512, 512, 512, 512, 512, - 512, 533, 525, 525, 533, 525, 512, 533, 533, 535, 525, 512, 512, 525, - 533, 533, 529, 529, 529, 529, 529, 528, 40, 40, 529, 529, 536, 536, 534, - 534, 40, 40, 528, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 44, 40, - 40, 40, 40, 40, 40, 528, 40, 528, 40, 40, 40, 40, 528, 528, 528, 40, 537, - 40, 40, 40, 538, 538, 538, 538, 538, 538, 40, 539, 539, 529, 40, 40, 40, - 477, 495, 477, 495, 477, 495, 477, 495, 477, 495, 477, 495, 477, 495, 51, - 51, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 40, 528, - 528, 528, 40, 40, 40, 40, 40, 40, 40, 528, 496, 496, 496, 496, 496, 477, - 495, 496, 496, 496, 496, 496, 496, 496, 16, 31, 16, 31, 16, 31, 16, 31, - 477, 495, 540, 540, 540, 540, 540, 540, 540, 540, 496, 496, 496, 477, - 495, 16, 31, 477, 495, 477, 495, 477, 495, 477, 495, 477, 495, 496, 496, - 496, 496, 496, 496, 496, 477, 495, 477, 495, 496, 496, 496, 496, 496, - 496, 496, 496, 477, 495, 496, 496, 40, 40, 40, 528, 528, 40, 40, 40, 496, - 496, 496, 496, 496, 40, 40, 496, 496, 496, 496, 496, 496, 40, 40, 40, - 528, 40, 40, 40, 40, 537, 512, 512, 40, 40, 40, 40, 81, 81, 40, 40, 40, - 40, 40, 40, 40, 40, 81, 81, 40, 81, 40, 40, 40, 40, 40, 40, 541, 541, - 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 81, 542, - 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 81, - 52, 56, 52, 52, 52, 56, 56, 52, 56, 52, 56, 52, 56, 52, 52, 52, 52, 56, - 52, 56, 56, 52, 56, 56, 56, 56, 56, 56, 59, 59, 52, 52, 87, 88, 87, 88, - 88, 543, 543, 543, 543, 543, 543, 87, 88, 87, 88, 544, 544, 544, 87, 88, - 81, 81, 81, 81, 81, 545, 546, 546, 546, 547, 545, 546, 329, 329, 329, - 329, 329, 329, 81, 329, 81, 81, 81, 81, 81, 329, 81, 81, 548, 548, 548, - 548, 548, 548, 548, 548, 81, 81, 81, 81, 81, 81, 81, 549, 550, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 551, 95, 95, 95, 95, 95, - 95, 95, 95, 552, 552, 42, 50, 42, 50, 552, 552, 552, 42, 50, 552, 42, 50, - 377, 377, 377, 377, 377, 377, 377, 377, 84, 472, 553, 377, 554, 84, 42, - 50, 84, 84, 42, 50, 477, 495, 477, 495, 477, 495, 477, 495, 377, 377, - 377, 377, 375, 60, 377, 377, 84, 377, 377, 84, 84, 84, 84, 84, 555, 555, - 377, 377, 377, 84, 472, 377, 477, 377, 377, 377, 377, 377, 377, 377, 377, - 84, 377, 84, 377, 81, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, - 81, 556, 556, 556, 556, 556, 556, 556, 556, 556, 81, 81, 81, 81, 556, - 556, 556, 556, 556, 556, 81, 81, 525, 525, 525, 525, 525, 525, 525, 525, - 525, 525, 525, 525, 81, 81, 81, 81, 557, 558, 558, 559, 525, 560, 561, - 562, 526, 527, 526, 527, 526, 527, 526, 527, 526, 527, 525, 525, 526, - 527, 526, 527, 526, 527, 526, 527, 563, 526, 527, 527, 525, 562, 562, - 562, 562, 562, 562, 562, 562, 562, 564, 565, 566, 567, 568, 568, 569, - 570, 570, 570, 570, 571, 525, 525, 562, 562, 562, 560, 572, 559, 525, - 529, 81, 573, 574, 573, 574, 573, 574, 573, 574, 573, 574, 574, 574, 574, - 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 573, - 574, 574, 574, 574, 574, 574, 574, 573, 574, 573, 574, 573, 574, 574, - 574, 574, 574, 574, 573, 574, 574, 574, 574, 574, 574, 573, 573, 81, 81, - 575, 575, 576, 576, 577, 577, 574, 563, 578, 579, 578, 579, 578, 579, - 578, 579, 578, 579, 579, 579, 579, 579, 579, 579, 579, 579, 579, 579, - 579, 579, 579, 579, 579, 579, 578, 579, 579, 579, 579, 579, 579, 579, - 578, 579, 578, 579, 578, 579, 579, 579, 579, 579, 579, 578, 579, 579, - 579, 579, 579, 579, 578, 578, 579, 579, 579, 579, 580, 581, 582, 582, - 579, 81, 81, 81, 81, 81, 583, 583, 583, 583, 583, 583, 583, 583, 583, - 583, 583, 81, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, - 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 81, 585, 585, 586, 586, - 586, 586, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 583, 583, - 583, 81, 81, 81, 81, 81, 578, 578, 578, 578, 578, 578, 578, 578, 587, - 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 588, 588, 81, - 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 585, 585, 585, 585, - 585, 585, 589, 589, 589, 589, 589, 589, 589, 589, 525, 590, 590, 590, - 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 587, 587, - 587, 587, 588, 588, 588, 585, 585, 590, 590, 590, 590, 590, 590, 590, - 585, 585, 585, 585, 525, 525, 525, 525, 591, 591, 591, 591, 591, 591, - 591, 591, 591, 591, 591, 591, 591, 591, 591, 81, 585, 585, 585, 585, 585, - 585, 585, 525, 525, 525, 525, 585, 585, 585, 585, 585, 585, 585, 585, - 585, 585, 585, 525, 525, 592, 592, 592, 592, 592, 592, 592, 592, 592, - 592, 592, 592, 592, 592, 593, 593, 593, 593, 593, 593, 593, 593, 593, - 593, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, - 595, 594, 594, 594, 594, 594, 594, 594, 81, 81, 81, 596, 596, 596, 596, - 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 81, 597, 597, 597, - 597, 597, 597, 597, 597, 598, 598, 598, 598, 598, 598, 599, 599, 600, - 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 601, 602, 603, - 602, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 600, 600, 81, 81, - 81, 81, 90, 93, 90, 93, 90, 93, 605, 95, 97, 97, 97, 606, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 606, 607, 90, 93, 90, 93, 454, 454, 95, 95, 608, - 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 609, - 609, 609, 609, 609, 609, 609, 609, 609, 609, 610, 610, 611, 612, 612, - 612, 612, 612, 62, 62, 62, 62, 62, 62, 62, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 62, 62, 52, 56, 52, 56, 52, 56, 56, 56, 52, 56, 52, 56, 52, 56, - 59, 56, 56, 56, 56, 56, 56, 56, 56, 52, 56, 52, 56, 52, 52, 56, 60, 613, - 613, 52, 56, 52, 56, 57, 52, 56, 52, 56, 56, 56, 52, 56, 52, 56, 52, 52, - 52, 52, 52, 56, 52, 52, 52, 52, 52, 56, 52, 56, 52, 56, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 57, 59, 59, 56, 57, 57, 57, 57, 57, - 614, 614, 615, 614, 614, 614, 616, 614, 614, 614, 614, 615, 614, 614, - 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 617, - 617, 615, 615, 617, 618, 618, 618, 618, 81, 81, 81, 81, 619, 619, 619, - 619, 619, 619, 317, 317, 507, 516, 81, 81, 81, 81, 81, 81, 620, 620, 620, - 620, 620, 620, 620, 620, 620, 620, 620, 620, 621, 621, 622, 622, 623, - 623, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, - 624, 624, 624, 624, 624, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 625, 626, 81, 81, 81, 81, 81, 81, 81, - 81, 627, 627, 628, 628, 628, 628, 628, 628, 628, 628, 628, 628, 81, 81, - 81, 81, 81, 81, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 195, - 195, 195, 195, 195, 195, 201, 201, 201, 195, 629, 195, 195, 193, 630, - 630, 630, 630, 630, 630, 630, 630, 630, 630, 631, 631, 631, 631, 631, - 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, - 631, 632, 632, 632, 632, 632, 633, 633, 633, 199, 634, 635, 635, 635, - 635, 635, 635, 635, 635, 635, 635, 635, 635, 635, 635, 635, 636, 636, - 636, 636, 636, 636, 636, 636, 636, 636, 636, 637, 638, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 639, 332, 332, 332, 332, 332, 81, 81, 81, - 640, 640, 640, 641, 642, 642, 642, 642, 642, 642, 642, 642, 642, 642, - 642, 642, 642, 642, 642, 643, 641, 641, 640, 640, 640, 640, 641, 641, - 640, 641, 641, 641, 644, 645, 645, 645, 645, 645, 645, 646, 646, 646, - 645, 645, 645, 645, 81, 61, 647, 647, 647, 647, 647, 647, 647, 647, 647, - 647, 81, 81, 81, 81, 645, 645, 318, 318, 318, 318, 318, 320, 648, 318, - 323, 323, 318, 318, 318, 318, 318, 81, 649, 649, 649, 649, 649, 649, 649, - 649, 649, 650, 650, 650, 650, 650, 650, 651, 651, 650, 650, 651, 651, - 650, 650, 81, 649, 649, 649, 650, 649, 649, 649, 649, 649, 649, 649, 649, - 650, 651, 81, 81, 652, 652, 652, 652, 652, 652, 652, 652, 652, 652, 81, - 81, 653, 654, 654, 654, 648, 318, 318, 318, 318, 318, 318, 327, 327, 327, - 318, 319, 320, 319, 318, 318, 655, 655, 655, 655, 655, 655, 655, 655, - 656, 655, 656, 656, 657, 655, 655, 656, 656, 655, 655, 655, 655, 655, - 656, 656, 655, 656, 655, 81, 81, 81, 81, 81, 81, 81, 81, 655, 655, 658, - 659, 659, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 661, - 662, 662, 661, 661, 663, 663, 660, 664, 664, 661, 665, 81, 81, 335, 335, - 335, 335, 335, 335, 81, 56, 56, 56, 613, 59, 59, 59, 59, 56, 56, 56, 56, - 56, 79, 81, 81, 342, 342, 342, 342, 342, 342, 342, 342, 660, 660, 660, - 661, 661, 662, 661, 661, 662, 661, 661, 663, 661, 665, 81, 81, 666, 666, - 666, 666, 666, 666, 666, 666, 666, 666, 81, 81, 81, 81, 81, 81, 667, 668, - 668, 668, 668, 668, 668, 668, 668, 668, 668, 668, 668, 668, 668, 668, - 668, 668, 668, 668, 667, 668, 668, 668, 668, 668, 668, 668, 81, 81, 81, - 81, 333, 333, 333, 333, 333, 333, 333, 81, 81, 81, 81, 334, 334, 334, - 334, 334, 334, 334, 334, 334, 81, 81, 81, 81, 669, 669, 669, 669, 669, - 669, 669, 669, 670, 670, 670, 670, 670, 670, 670, 670, 592, 592, 593, - 593, 593, 593, 593, 593, 56, 56, 56, 56, 56, 56, 56, 81, 81, 81, 81, 101, - 101, 101, 101, 101, 81, 81, 81, 81, 81, 129, 671, 129, 129, 672, 129, - 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 81, 129, 129, - 129, 129, 129, 81, 129, 81, 129, 129, 81, 129, 129, 81, 129, 129, 146, - 146, 673, 673, 673, 673, 673, 673, 673, 673, 673, 673, 673, 673, 673, - 673, 673, 673, 81, 81, 81, 81, 81, 81, 81, 81, 81, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 495, 477, 81, 81, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 135, 138, 81, 81, 674, 674, 674, 674, 674, - 674, 674, 674, 675, 558, 558, 675, 675, 676, 676, 526, 527, 677, 81, 81, - 81, 81, 81, 81, 96, 96, 96, 96, 96, 96, 96, 156, 156, 156, 156, 156, 156, - 156, 95, 95, 559, 569, 569, 678, 678, 526, 527, 526, 527, 526, 527, 526, - 527, 526, 527, 526, 527, 526, 527, 526, 527, 559, 559, 526, 527, 559, - 559, 559, 559, 678, 678, 678, 679, 559, 679, 81, 580, 680, 676, 676, 569, - 526, 527, 526, 527, 526, 527, 681, 559, 559, 682, 683, 684, 684, 684, 81, - 559, 685, 686, 559, 81, 81, 81, 81, 146, 146, 146, 146, 146, 81, 81, 497, - 81, 687, 688, 689, 690, 691, 688, 688, 692, 693, 688, 694, 695, 696, 695, - 697, 698, 698, 698, 698, 698, 698, 698, 698, 698, 698, 699, 700, 701, - 701, 701, 687, 688, 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, - 702, 702, 702, 702, 702, 702, 702, 702, 692, 688, 693, 703, 704, 703, - 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, - 705, 705, 705, 705, 692, 701, 693, 701, 692, 693, 706, 707, 708, 706, - 709, 710, 711, 711, 711, 711, 711, 711, 711, 711, 711, 712, 710, 710, - 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, - 710, 710, 710, 710, 710, 713, 713, 714, 714, 714, 714, 714, 714, 714, - 714, 714, 714, 714, 714, 714, 714, 714, 81, 81, 81, 714, 714, 714, 714, - 714, 714, 81, 81, 714, 714, 714, 81, 81, 81, 715, 690, 701, 703, 716, - 690, 690, 81, 717, 718, 718, 718, 718, 717, 717, 81, 81, 719, 719, 719, - 720, 512, 81, 81, 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, - 721, 81, 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 81, 721, 721, - 721, 81, 721, 721, 81, 721, 721, 721, 721, 721, 721, 721, 81, 81, 721, - 721, 721, 81, 81, 81, 81, 81, 199, 377, 199, 81, 81, 81, 81, 619, 619, - 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 81, 81, 81, 317, - 722, 722, 722, 722, 722, 722, 722, 722, 722, 722, 722, 722, 722, 723, - 723, 723, 723, 724, 724, 724, 724, 724, 724, 724, 724, 724, 724, 724, - 724, 724, 724, 724, 724, 724, 723, 723, 724, 725, 725, 81, 40, 40, 40, - 40, 81, 81, 81, 81, 724, 81, 81, 81, 81, 81, 81, 81, 317, 317, 317, 317, - 317, 156, 81, 81, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, - 726, 726, 81, 81, 81, 727, 727, 727, 727, 727, 727, 727, 727, 727, 81, - 81, 81, 81, 81, 81, 81, 156, 504, 504, 504, 504, 504, 504, 504, 504, 504, - 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 81, 81, 81, 81, 728, - 728, 728, 728, 728, 728, 728, 728, 729, 729, 729, 729, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 728, 728, 728, 730, 730, 730, 730, 730, 730, 730, - 730, 730, 731, 730, 730, 730, 730, 730, 730, 730, 730, 731, 81, 81, 81, - 81, 81, 732, 732, 732, 732, 732, 732, 732, 732, 732, 732, 732, 732, 732, - 732, 733, 733, 733, 733, 733, 81, 81, 81, 81, 81, 734, 734, 734, 734, - 734, 734, 734, 734, 734, 734, 734, 734, 734, 734, 81, 735, 736, 736, 736, - 736, 736, 736, 736, 736, 736, 736, 736, 736, 81, 81, 81, 81, 737, 738, - 738, 738, 738, 738, 81, 81, 739, 739, 739, 739, 739, 739, 739, 739, 740, - 740, 740, 740, 740, 740, 740, 740, 741, 741, 741, 741, 741, 741, 741, - 741, 742, 742, 742, 742, 742, 742, 742, 742, 742, 742, 742, 742, 742, - 742, 81, 81, 743, 743, 743, 743, 743, 743, 743, 743, 743, 743, 81, 81, - 81, 81, 81, 81, 744, 744, 744, 744, 744, 744, 744, 744, 744, 744, 744, - 744, 81, 81, 81, 81, 745, 745, 745, 745, 745, 745, 745, 745, 745, 745, - 745, 745, 81, 81, 81, 81, 746, 746, 746, 746, 746, 746, 746, 746, 747, - 747, 747, 747, 747, 747, 747, 747, 747, 747, 747, 747, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 748, 749, 749, 749, 749, 749, 749, 749, 749, - 749, 749, 749, 749, 749, 749, 749, 81, 749, 749, 749, 749, 749, 749, 81, - 81, 750, 750, 750, 750, 750, 750, 81, 81, 750, 81, 750, 750, 750, 750, - 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, - 750, 750, 81, 750, 750, 81, 81, 81, 750, 81, 81, 750, 751, 751, 751, 751, - 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 81, 752, 753, 753, 753, - 753, 753, 753, 753, 753, 754, 754, 754, 754, 754, 754, 754, 754, 754, - 754, 754, 754, 754, 754, 754, 755, 755, 756, 756, 756, 756, 756, 756, - 756, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, - 757, 757, 81, 81, 81, 81, 81, 81, 81, 81, 758, 758, 758, 758, 758, 758, - 758, 758, 758, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 81, - 759, 759, 81, 81, 81, 81, 81, 760, 760, 760, 760, 760, 761, 761, 761, - 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 762, 762, 762, - 762, 762, 762, 81, 81, 81, 763, 764, 764, 764, 764, 764, 764, 764, 764, - 764, 764, 81, 81, 81, 81, 81, 765, 766, 766, 766, 766, 766, 766, 766, - 766, 767, 767, 767, 767, 767, 767, 767, 767, 81, 81, 81, 81, 768, 768, - 767, 767, 768, 768, 768, 768, 768, 768, 768, 768, 81, 81, 768, 768, 768, - 768, 768, 768, 769, 770, 770, 770, 81, 770, 770, 81, 81, 81, 81, 81, 770, - 771, 770, 772, 769, 769, 769, 769, 81, 769, 769, 769, 81, 769, 769, 769, - 769, 769, 769, 769, 769, 769, 769, 769, 769, 769, 769, 769, 769, 769, - 769, 769, 769, 769, 81, 81, 772, 773, 771, 81, 81, 81, 81, 774, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 81, 81, 81, 81, 81, 81, 81, 776, 776, - 776, 776, 776, 776, 776, 776, 777, 81, 81, 81, 81, 81, 81, 81, 778, 778, - 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 779, 779, 780, - 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 782, - 782, 782, 783, 783, 783, 783, 783, 783, 783, 783, 784, 783, 783, 783, - 783, 783, 783, 783, 783, 783, 783, 783, 783, 785, 786, 81, 81, 81, 81, - 787, 787, 787, 787, 787, 788, 788, 788, 788, 788, 788, 789, 81, 790, 790, - 790, 790, 790, 790, 790, 790, 790, 790, 790, 790, 790, 790, 81, 81, 81, - 791, 791, 791, 791, 791, 791, 791, 792, 792, 792, 792, 792, 792, 792, - 792, 792, 792, 792, 792, 792, 792, 81, 81, 793, 793, 793, 793, 793, 793, - 793, 793, 794, 794, 794, 794, 794, 794, 794, 794, 794, 794, 794, 81, 81, - 81, 81, 81, 795, 795, 795, 795, 795, 795, 795, 795, 796, 796, 796, 796, - 796, 796, 796, 796, 796, 796, 81, 81, 81, 81, 81, 81, 81, 797, 797, 797, - 797, 81, 81, 81, 81, 798, 798, 798, 798, 798, 798, 798, 799, 799, 799, - 799, 799, 799, 799, 799, 799, 81, 81, 81, 81, 81, 81, 81, 800, 800, 800, - 800, 800, 800, 800, 800, 800, 800, 800, 81, 81, 81, 81, 81, 801, 801, - 801, 801, 801, 801, 801, 801, 801, 801, 801, 81, 81, 81, 81, 81, 81, 81, - 802, 802, 802, 802, 802, 802, 803, 803, 803, 803, 803, 803, 803, 803, - 803, 803, 803, 803, 804, 804, 804, 804, 805, 805, 805, 805, 805, 805, - 805, 805, 805, 805, 81, 81, 81, 81, 81, 81, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 81, 807, 807, 807, 807, 807, - 807, 807, 807, 807, 807, 807, 807, 807, 808, 808, 808, 808, 808, 808, - 808, 808, 808, 808, 807, 809, 809, 809, 809, 809, 809, 809, 809, 809, - 809, 809, 809, 809, 809, 810, 810, 811, 811, 811, 810, 811, 810, 810, - 810, 810, 812, 812, 812, 812, 813, 813, 813, 813, 813, 81, 81, 81, 81, - 81, 81, 814, 815, 814, 816, 816, 816, 816, 816, 816, 816, 816, 816, 816, - 816, 816, 816, 815, 815, 815, 815, 815, 815, 815, 815, 815, 815, 815, - 815, 815, 815, 817, 818, 818, 819, 819, 819, 819, 819, 81, 81, 81, 81, - 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, - 820, 820, 820, 820, 820, 820, 821, 821, 821, 821, 821, 821, 821, 821, - 821, 821, 81, 81, 81, 81, 81, 81, 81, 817, 822, 822, 823, 824, 824, 824, - 824, 824, 824, 824, 824, 824, 824, 824, 824, 824, 823, 823, 823, 822, - 822, 822, 822, 823, 823, 825, 826, 827, 827, 828, 829, 829, 829, 829, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 828, 81, 81, 830, 830, 830, 830, - 830, 830, 830, 830, 830, 81, 81, 81, 81, 81, 81, 81, 831, 831, 831, 831, - 831, 831, 831, 831, 831, 831, 81, 81, 81, 81, 81, 81, 832, 832, 832, 833, - 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, - 833, 833, 833, 833, 833, 834, 834, 834, 834, 834, 835, 834, 834, 834, - 834, 834, 834, 836, 836, 81, 837, 837, 837, 837, 837, 837, 837, 837, 837, - 837, 838, 838, 838, 838, 833, 835, 835, 81, 839, 839, 839, 839, 839, 839, - 839, 839, 839, 839, 839, 840, 841, 842, 839, 81, 843, 843, 844, 845, 845, - 845, 845, 845, 845, 845, 845, 845, 845, 845, 845, 845, 845, 845, 845, - 844, 844, 844, 843, 843, 843, 843, 843, 843, 843, 843, 843, 844, 846, - 845, 845, 845, 845, 847, 847, 848, 847, 843, 849, 843, 843, 848, 81, 81, - 850, 850, 850, 850, 850, 850, 850, 850, 850, 850, 845, 851, 845, 847, - 847, 847, 81, 852, 852, 852, 852, 852, 852, 852, 852, 852, 852, 852, 852, - 852, 852, 852, 852, 852, 852, 852, 852, 81, 81, 81, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 81, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 854, 854, 854, 855, 855, 855, 854, 854, 855, 856, 857, 855, 858, - 858, 859, 858, 858, 859, 855, 81, 860, 860, 860, 860, 860, 860, 860, 81, - 860, 81, 860, 860, 860, 860, 81, 860, 860, 860, 860, 860, 860, 860, 860, - 860, 860, 860, 860, 860, 860, 860, 81, 860, 860, 861, 81, 81, 81, 81, 81, - 81, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, - 862, 863, 864, 864, 864, 863, 863, 863, 863, 863, 863, 865, 866, 81, 81, - 81, 81, 81, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 81, 81, 81, - 81, 81, 81, 868, 868, 869, 869, 81, 870, 870, 870, 870, 870, 870, 870, - 870, 81, 81, 870, 870, 81, 81, 870, 870, 870, 870, 870, 870, 870, 870, - 870, 870, 870, 870, 870, 870, 81, 870, 870, 870, 870, 870, 870, 870, 81, - 870, 870, 81, 870, 870, 870, 870, 870, 81, 871, 872, 870, 869, 869, 868, - 869, 869, 869, 869, 81, 81, 869, 869, 81, 81, 869, 869, 873, 81, 81, 870, - 81, 81, 81, 81, 81, 81, 869, 81, 81, 81, 81, 81, 870, 870, 870, 870, 870, - 869, 869, 81, 81, 874, 874, 874, 874, 874, 874, 874, 81, 81, 81, 875, - 875, 875, 875, 875, 875, 875, 875, 875, 875, 875, 875, 875, 876, 876, - 876, 877, 877, 877, 877, 877, 877, 877, 877, 876, 876, 878, 877, 877, - 876, 879, 875, 875, 875, 875, 880, 880, 880, 880, 881, 882, 882, 882, - 882, 882, 882, 882, 882, 882, 882, 81, 880, 81, 881, 883, 81, 884, 884, - 884, 884, 884, 884, 884, 884, 885, 885, 885, 886, 886, 886, 886, 886, - 886, 885, 886, 885, 885, 885, 885, 886, 886, 885, 887, 888, 884, 884, - 889, 884, 890, 890, 890, 890, 890, 890, 890, 890, 890, 890, 81, 81, 81, - 81, 81, 81, 891, 891, 891, 891, 891, 891, 891, 891, 891, 891, 891, 891, - 891, 891, 891, 892, 892, 892, 893, 893, 893, 893, 81, 81, 892, 892, 892, - 892, 893, 893, 892, 894, 895, 896, 897, 897, 898, 898, 899, 899, 899, - 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, - 897, 891, 891, 891, 891, 893, 893, 81, 81, 900, 900, 900, 900, 900, 900, - 900, 900, 901, 901, 901, 902, 902, 902, 902, 902, 902, 902, 902, 901, - 901, 902, 901, 903, 902, 904, 904, 905, 900, 81, 81, 81, 906, 906, 906, - 906, 906, 906, 906, 906, 906, 906, 81, 81, 81, 81, 81, 81, 907, 907, 907, - 907, 907, 907, 907, 907, 907, 907, 907, 907, 907, 81, 81, 81, 908, 908, - 908, 908, 908, 908, 908, 908, 908, 908, 908, 909, 910, 909, 910, 910, - 909, 909, 909, 909, 909, 909, 911, 912, 913, 913, 913, 913, 913, 913, - 913, 913, 913, 913, 81, 81, 81, 81, 81, 81, 914, 914, 914, 914, 914, 914, - 914, 914, 914, 914, 914, 81, 81, 915, 915, 915, 916, 916, 915, 915, 915, - 915, 916, 915, 915, 915, 915, 917, 81, 81, 81, 81, 918, 918, 918, 918, - 918, 918, 918, 918, 918, 918, 919, 919, 920, 920, 920, 921, 922, 922, - 922, 922, 922, 922, 922, 922, 922, 922, 922, 922, 923, 923, 923, 924, - 924, 924, 924, 924, 924, 924, 924, 924, 923, 925, 926, 927, 81, 81, 81, - 81, 928, 928, 928, 928, 928, 928, 928, 928, 929, 929, 929, 929, 929, 929, - 929, 929, 930, 930, 930, 930, 930, 930, 930, 930, 930, 930, 931, 931, - 931, 931, 931, 931, 931, 931, 931, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 932, 933, 934, 934, 934, 934, 934, 934, 935, 935, 934, 934, - 933, 933, 933, 933, 933, 933, 933, 933, 933, 933, 933, 933, 933, 933, - 933, 933, 934, 936, 934, 934, 934, 934, 937, 933, 934, 934, 934, 934, - 938, 939, 940, 940, 940, 940, 938, 939, 936, 941, 942, 942, 942, 942, - 942, 942, 943, 943, 942, 942, 942, 941, 941, 941, 941, 941, 941, 941, - 941, 941, 941, 941, 941, 941, 941, 941, 941, 81, 81, 941, 941, 941, 941, - 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 943, - 942, 944, 945, 945, 945, 941, 946, 946, 946, 945, 945, 81, 81, 81, 81, - 81, 947, 947, 947, 947, 947, 947, 947, 947, 947, 81, 81, 81, 81, 81, 81, - 81, 948, 948, 948, 948, 948, 948, 948, 948, 948, 81, 948, 948, 948, 948, - 948, 948, 948, 948, 948, 948, 948, 948, 948, 949, 950, 950, 950, 950, - 950, 950, 950, 81, 950, 950, 950, 950, 950, 950, 949, 951, 948, 952, 952, - 952, 952, 952, 81, 81, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, - 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, - 954, 954, 954, 954, 954, 81, 81, 81, 955, 956, 957, 957, 957, 957, 957, - 957, 957, 957, 957, 957, 957, 957, 957, 957, 81, 81, 958, 958, 958, 958, - 958, 958, 958, 958, 958, 958, 958, 958, 958, 958, 81, 959, 958, 958, 958, - 958, 958, 958, 958, 959, 958, 958, 959, 958, 958, 81, 960, 960, 960, 960, - 960, 960, 960, 81, 960, 960, 81, 960, 960, 960, 960, 960, 960, 960, 960, - 960, 960, 960, 960, 960, 960, 961, 961, 961, 961, 961, 961, 81, 81, 81, - 961, 81, 961, 961, 81, 961, 961, 961, 962, 961, 963, 963, 960, 961, 964, - 964, 964, 964, 964, 964, 964, 964, 964, 964, 81, 81, 81, 81, 81, 81, 965, - 965, 965, 965, 965, 965, 81, 965, 965, 81, 965, 965, 965, 965, 965, 965, - 965, 965, 965, 965, 965, 965, 965, 965, 965, 965, 966, 966, 966, 966, - 966, 81, 967, 967, 81, 966, 966, 967, 966, 968, 965, 81, 81, 81, 81, 81, - 81, 81, 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, 81, 81, 81, 81, - 81, 81, 970, 970, 970, 970, 970, 970, 970, 970, 970, 970, 970, 971, 971, - 972, 972, 973, 973, 81, 81, 81, 81, 81, 81, 81, 974, 974, 974, 974, 974, - 974, 974, 974, 974, 974, 81, 81, 81, 81, 81, 81, 975, 975, 975, 975, 975, - 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 81, 976, 976, 976, 976, - 976, 81, 81, 81, 974, 974, 974, 974, 81, 81, 81, 81, 977, 977, 977, 977, - 977, 977, 977, 977, 978, 978, 978, 979, 979, 979, 977, 977, 977, 977, - 979, 977, 977, 977, 978, 979, 978, 979, 977, 977, 977, 977, 977, 977, - 977, 978, 979, 979, 977, 977, 977, 977, 977, 977, 977, 977, 977, 977, - 977, 81, 980, 980, 980, 980, 980, 980, 980, 980, 980, 980, 980, 980, 980, - 980, 981, 982, 980, 980, 980, 980, 980, 980, 980, 81, 608, 81, 81, 81, - 81, 81, 81, 81, 983, 983, 983, 983, 983, 983, 983, 983, 983, 983, 983, - 983, 983, 983, 983, 81, 984, 984, 984, 984, 984, 984, 984, 984, 984, 984, - 81, 81, 81, 81, 985, 985, 986, 986, 986, 986, 986, 986, 986, 986, 986, - 986, 986, 986, 986, 986, 81, 81, 987, 987, 987, 987, 987, 988, 81, 81, - 989, 989, 989, 989, 989, 989, 989, 989, 990, 990, 990, 990, 990, 990, - 990, 991, 991, 991, 992, 992, 993, 993, 993, 993, 994, 994, 994, 994, - 991, 993, 81, 81, 995, 995, 995, 995, 995, 995, 995, 995, 995, 995, 81, - 996, 996, 996, 996, 996, 996, 996, 81, 989, 989, 989, 989, 989, 81, 81, - 81, 81, 81, 989, 989, 989, 997, 997, 997, 997, 997, 997, 997, 997, 998, - 998, 998, 998, 998, 998, 998, 998, 999, 999, 999, 999, 999, 999, 999, - 999, 999, 999, 999, 999, 999, 999, 999, 1000, 1000, 1001, 1001, 81, 81, - 81, 81, 81, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, - 1002, 1002, 1002, 81, 81, 81, 1002, 1003, 1003, 1003, 1003, 1003, 1003, - 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, - 1003, 1003, 1003, 1003, 81, 81, 81, 81, 81, 81, 81, 81, 1004, 1004, 1004, - 1004, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, - 1005, 1005, 1006, 1007, 81, 81, 81, 81, 81, 81, 1008, 1008, 1008, 1008, - 1008, 1008, 1008, 1008, 1008, 1008, 81, 81, 81, 81, 81, 81, 1008, 1008, - 1008, 81, 81, 81, 81, 81, 579, 574, 574, 574, 574, 574, 574, 574, 574, - 574, 574, 574, 574, 574, 574, 81, 1009, 1009, 1009, 1009, 1009, 1009, - 1009, 1009, 1009, 1009, 1009, 1009, 81, 81, 81, 81, 1010, 1010, 1010, - 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 81, 81, 81, 81, 81, 1010, - 1010, 1010, 1010, 1010, 81, 81, 81, 1010, 81, 81, 81, 81, 81, 81, 81, - 1010, 1010, 81, 81, 1011, 1012, 1013, 1014, 503, 503, 503, 503, 81, 81, - 81, 81, 317, 317, 317, 317, 317, 317, 81, 81, 317, 317, 317, 317, 317, - 317, 317, 81, 81, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, - 317, 1015, 1015, 451, 451, 451, 317, 317, 317, 1016, 1015, 1015, 1015, - 1015, 1015, 503, 503, 503, 503, 503, 503, 503, 503, 156, 156, 156, 156, - 156, 156, 156, 156, 317, 317, 96, 96, 96, 96, 96, 156, 156, 317, 317, - 317, 317, 317, 317, 96, 96, 96, 96, 317, 317, 317, 81, 81, 81, 81, 81, - 81, 81, 724, 724, 1017, 1017, 1017, 724, 81, 81, 619, 619, 619, 619, 81, - 81, 81, 81, 619, 81, 81, 81, 81, 81, 81, 81, 510, 510, 510, 510, 510, - 510, 510, 510, 510, 510, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, - 49, 49, 49, 49, 49, 49, 49, 81, 49, 49, 49, 49, 49, 49, 510, 81, 510, - 510, 81, 81, 510, 81, 81, 510, 510, 81, 81, 510, 510, 510, 510, 81, 510, - 510, 49, 49, 81, 49, 81, 49, 49, 49, 49, 49, 49, 49, 81, 49, 49, 49, 49, - 49, 49, 49, 510, 510, 81, 510, 510, 510, 510, 81, 81, 510, 510, 510, 510, - 510, 510, 510, 510, 81, 510, 510, 510, 510, 510, 510, 510, 81, 49, 49, - 510, 510, 81, 510, 510, 510, 510, 81, 510, 510, 510, 510, 510, 81, 510, - 81, 81, 81, 510, 510, 510, 510, 510, 510, 510, 81, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 81, 81, 510, 1018, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 496, 49, 49, 49, 49, 49, 49, 510, 510, 510, 510, 510, 510, - 510, 510, 510, 1018, 49, 49, 49, 49, 49, 49, 49, 49, 49, 496, 49, 49, - 510, 510, 510, 510, 510, 1018, 49, 49, 49, 49, 49, 49, 49, 49, 49, 496, - 49, 49, 49, 49, 49, 49, 510, 510, 510, 510, 510, 510, 510, 510, 510, - 1018, 49, 496, 49, 49, 49, 49, 49, 49, 49, 49, 510, 49, 81, 81, 1019, - 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1020, 1020, 1020, - 1020, 1020, 1020, 1020, 1020, 1021, 1021, 1021, 1021, 1021, 1021, 1021, - 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1020, 1020, 1020, 1020, - 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1020, 1020, - 1020, 1020, 1020, 1020, 1020, 1020, 1021, 1020, 1020, 1020, 1020, 1020, - 1020, 1021, 1020, 1020, 1022, 1022, 1022, 1022, 1023, 81, 81, 81, 81, 81, - 81, 81, 1021, 1021, 1021, 1021, 1021, 81, 1021, 1021, 1021, 1021, 1021, - 1021, 1021, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 81, 1024, 1024, - 1024, 1024, 1024, 1024, 1024, 1024, 1024, 81, 81, 1024, 1024, 1024, 1024, - 1024, 1024, 1024, 81, 1024, 1024, 81, 1024, 1024, 1024, 1024, 1024, 81, - 81, 81, 81, 81, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, - 1025, 1025, 1025, 1025, 81, 81, 1026, 1026, 1026, 1026, 1026, 1026, 1026, - 1026, 1026, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 81, 1028, 1028, - 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1029, 1029, 1029, 1029, - 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, - 1029, 1029, 1030, 1030, 1030, 1030, 1030, 1030, 1031, 81, 81, 81, 81, 81, - 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 81, 81, 81, - 81, 1033, 1033, 81, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, - 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1035, 1034, - 1034, 1034, 1036, 1034, 1034, 1034, 1034, 81, 81, 81, 146, 146, 146, 146, - 81, 146, 146, 146, 81, 146, 146, 81, 146, 81, 81, 146, 81, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 81, 146, 146, 146, 146, 81, 146, 81, - 146, 81, 81, 81, 81, 81, 81, 146, 81, 81, 81, 81, 146, 81, 146, 81, 146, - 81, 146, 146, 146, 81, 146, 81, 146, 81, 146, 81, 146, 81, 146, 146, 146, - 146, 81, 146, 81, 146, 146, 81, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 81, 81, 81, 81, 81, 146, 146, 146, 81, 146, 146, 146, 132, 132, 81, - 81, 81, 81, 81, 81, 529, 529, 529, 529, 525, 529, 529, 529, 529, 529, - 529, 529, 529, 529, 529, 529, 529, 529, 529, 529, 1037, 1037, 1037, 1037, - 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 529, 529, 529, 529, 529, - 529, 529, 1037, 1037, 529, 529, 529, 529, 529, 529, 529, 529, 529, 529, - 529, 529, 529, 529, 525, 529, 529, 529, 529, 529, 529, 1037, 1037, 47, - 47, 47, 519, 519, 1037, 1037, 1037, 530, 530, 530, 530, 530, 530, 317, - 40, 530, 530, 40, 40, 1037, 1037, 1037, 1037, 530, 530, 530, 530, 530, - 530, 1038, 530, 530, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, - 1038, 1038, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 1037, 1037, - 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1039, 1039, 1039, 1039, 1039, - 1039, 1039, 1039, 1039, 1039, 1040, 585, 585, 1037, 1037, 1037, 1037, - 1037, 585, 585, 585, 585, 1037, 1037, 1037, 1037, 585, 1037, 1037, 1037, - 1037, 1037, 1037, 1037, 585, 585, 1037, 1037, 1037, 1037, 1037, 1037, - 525, 525, 525, 525, 525, 525, 1037, 1037, 525, 529, 529, 529, 529, 529, - 529, 529, 529, 529, 529, 529, 529, 525, 525, 525, 525, 525, 525, 525, - 525, 525, 529, 525, 525, 525, 525, 525, 525, 529, 525, 525, 525, 525, - 525, 525, 525, 536, 525, 525, 525, 525, 525, 525, 529, 529, 529, 529, - 529, 529, 529, 529, 40, 40, 529, 529, 525, 525, 525, 525, 525, 528, 528, - 525, 525, 525, 525, 525, 528, 525, 525, 525, 525, 525, 536, 536, 536, - 525, 525, 536, 525, 525, 536, 534, 534, 529, 529, 525, 525, 529, 529, - 529, 525, 529, 529, 529, 525, 525, 525, 1041, 1041, 1041, 1041, 1041, - 525, 525, 525, 525, 525, 525, 525, 529, 525, 529, 536, 536, 525, 525, - 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 525, 525, 525, - 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 536, 536, 536, 536, - 525, 525, 525, 525, 536, 525, 536, 525, 525, 525, 536, 525, 525, 525, - 525, 536, 536, 536, 525, 536, 536, 536, 528, 525, 528, 525, 528, 525, - 525, 525, 525, 525, 536, 525, 525, 525, 525, 528, 525, 528, 528, 525, - 525, 525, 525, 525, 525, 525, 525, 525, 525, 529, 529, 525, 528, 528, - 528, 528, 528, 528, 528, 525, 525, 525, 525, 525, 525, 525, 525, 528, - 528, 528, 528, 528, 528, 525, 525, 525, 525, 525, 528, 528, 528, 528, - 528, 528, 528, 528, 528, 528, 528, 528, 40, 40, 40, 40, 529, 525, 525, - 525, 525, 529, 529, 529, 529, 529, 534, 534, 529, 529, 529, 529, 536, - 529, 529, 529, 529, 529, 534, 529, 529, 529, 529, 536, 536, 529, 529, - 529, 529, 529, 40, 40, 40, 40, 40, 40, 40, 40, 529, 529, 529, 529, 40, - 40, 529, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 536, 536, 536, - 525, 525, 525, 536, 536, 536, 536, 536, 40, 40, 40, 40, 40, 40, 538, 538, - 538, 1042, 1042, 1042, 40, 40, 40, 40, 525, 525, 525, 536, 525, 525, 525, - 525, 525, 525, 525, 525, 536, 536, 536, 525, 536, 525, 525, 525, 525, - 525, 529, 529, 529, 529, 529, 529, 536, 529, 529, 529, 525, 525, 525, - 529, 529, 1037, 1037, 1037, 529, 529, 529, 525, 525, 1037, 1037, 1037, - 529, 529, 529, 529, 525, 525, 525, 525, 525, 525, 1037, 1037, 1037, 1037, - 1037, 1037, 40, 40, 40, 40, 1037, 1037, 1037, 1037, 40, 40, 40, 40, 40, - 529, 529, 529, 529, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 40, 40, - 1037, 1037, 1037, 1037, 1037, 1037, 40, 40, 40, 40, 40, 40, 1037, 1037, - 536, 536, 536, 536, 536, 525, 536, 536, 525, 525, 525, 525, 525, 525, - 536, 525, 536, 536, 525, 525, 525, 536, 536, 1037, 525, 1037, 1037, 525, - 525, 525, 525, 1037, 1037, 1037, 525, 1037, 525, 525, 525, 525, 525, 525, - 525, 1037, 1037, 1037, 1037, 1037, 525, 525, 525, 525, 525, 536, 536, - 525, 536, 536, 1037, 1037, 1037, 1037, 1037, 1037, 525, 536, 536, 536, - 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 525, 525, 1037, 1037, - 1037, 1037, 1037, 1037, 81, 81, 592, 592, 592, 592, 592, 592, 592, 593, - 592, 592, 592, 592, 592, 593, 593, 593, 592, 593, 593, 593, 593, 593, - 593, 593, 593, 593, 593, 593, 593, 593, 81, 81, 81, 503, 81, 81, 81, 81, - 81, 81, 503, 503, 503, 503, 503, 503, 503, 503, 670, 670, 670, 670, 670, - 670, 81, 81, -}; - -/* decomposition data */ -static const unsigned short decomp_data[] = { - 0, 257, 32, 514, 32, 776, 259, 97, 514, 32, 772, 259, 50, 259, 51, 514, - 32, 769, 258, 956, 514, 32, 807, 259, 49, 259, 111, 772, 49, 8260, 52, - 772, 49, 8260, 50, 772, 51, 8260, 52, 512, 65, 768, 512, 65, 769, 512, - 65, 770, 512, 65, 771, 512, 65, 776, 512, 65, 778, 512, 67, 807, 512, 69, - 768, 512, 69, 769, 512, 69, 770, 512, 69, 776, 512, 73, 768, 512, 73, - 769, 512, 73, 770, 512, 73, 776, 512, 78, 771, 512, 79, 768, 512, 79, - 769, 512, 79, 770, 512, 79, 771, 512, 79, 776, 512, 85, 768, 512, 85, - 769, 512, 85, 770, 512, 85, 776, 512, 89, 769, 512, 97, 768, 512, 97, - 769, 512, 97, 770, 512, 97, 771, 512, 97, 776, 512, 97, 778, 512, 99, - 807, 512, 101, 768, 512, 101, 769, 512, 101, 770, 512, 101, 776, 512, - 105, 768, 512, 105, 769, 512, 105, 770, 512, 105, 776, 512, 110, 771, - 512, 111, 768, 512, 111, 769, 512, 111, 770, 512, 111, 771, 512, 111, - 776, 512, 117, 768, 512, 117, 769, 512, 117, 770, 512, 117, 776, 512, - 121, 769, 512, 121, 776, 512, 65, 772, 512, 97, 772, 512, 65, 774, 512, - 97, 774, 512, 65, 808, 512, 97, 808, 512, 67, 769, 512, 99, 769, 512, 67, - 770, 512, 99, 770, 512, 67, 775, 512, 99, 775, 512, 67, 780, 512, 99, - 780, 512, 68, 780, 512, 100, 780, 512, 69, 772, 512, 101, 772, 512, 69, - 774, 512, 101, 774, 512, 69, 775, 512, 101, 775, 512, 69, 808, 512, 101, - 808, 512, 69, 780, 512, 101, 780, 512, 71, 770, 512, 103, 770, 512, 71, - 774, 512, 103, 774, 512, 71, 775, 512, 103, 775, 512, 71, 807, 512, 103, - 807, 512, 72, 770, 512, 104, 770, 512, 73, 771, 512, 105, 771, 512, 73, - 772, 512, 105, 772, 512, 73, 774, 512, 105, 774, 512, 73, 808, 512, 105, - 808, 512, 73, 775, 514, 73, 74, 514, 105, 106, 512, 74, 770, 512, 106, - 770, 512, 75, 807, 512, 107, 807, 512, 76, 769, 512, 108, 769, 512, 76, - 807, 512, 108, 807, 512, 76, 780, 512, 108, 780, 514, 76, 183, 514, 108, - 183, 512, 78, 769, 512, 110, 769, 512, 78, 807, 512, 110, 807, 512, 78, - 780, 512, 110, 780, 514, 700, 110, 512, 79, 772, 512, 111, 772, 512, 79, - 774, 512, 111, 774, 512, 79, 779, 512, 111, 779, 512, 82, 769, 512, 114, - 769, 512, 82, 807, 512, 114, 807, 512, 82, 780, 512, 114, 780, 512, 83, - 769, 512, 115, 769, 512, 83, 770, 512, 115, 770, 512, 83, 807, 512, 115, - 807, 512, 83, 780, 512, 115, 780, 512, 84, 807, 512, 116, 807, 512, 84, - 780, 512, 116, 780, 512, 85, 771, 512, 117, 771, 512, 85, 772, 512, 117, - 772, 512, 85, 774, 512, 117, 774, 512, 85, 778, 512, 117, 778, 512, 85, - 779, 512, 117, 779, 512, 85, 808, 512, 117, 808, 512, 87, 770, 512, 119, - 770, 512, 89, 770, 512, 121, 770, 512, 89, 776, 512, 90, 769, 512, 122, - 769, 512, 90, 775, 512, 122, 775, 512, 90, 780, 512, 122, 780, 258, 115, - 512, 79, 795, 512, 111, 795, 512, 85, 795, 512, 117, 795, 514, 68, 381, - 514, 68, 382, 514, 100, 382, 514, 76, 74, 514, 76, 106, 514, 108, 106, - 514, 78, 74, 514, 78, 106, 514, 110, 106, 512, 65, 780, 512, 97, 780, - 512, 73, 780, 512, 105, 780, 512, 79, 780, 512, 111, 780, 512, 85, 780, - 512, 117, 780, 512, 220, 772, 512, 252, 772, 512, 220, 769, 512, 252, - 769, 512, 220, 780, 512, 252, 780, 512, 220, 768, 512, 252, 768, 512, - 196, 772, 512, 228, 772, 512, 550, 772, 512, 551, 772, 512, 198, 772, - 512, 230, 772, 512, 71, 780, 512, 103, 780, 512, 75, 780, 512, 107, 780, - 512, 79, 808, 512, 111, 808, 512, 490, 772, 512, 491, 772, 512, 439, 780, - 512, 658, 780, 512, 106, 780, 514, 68, 90, 514, 68, 122, 514, 100, 122, - 512, 71, 769, 512, 103, 769, 512, 78, 768, 512, 110, 768, 512, 197, 769, - 512, 229, 769, 512, 198, 769, 512, 230, 769, 512, 216, 769, 512, 248, - 769, 512, 65, 783, 512, 97, 783, 512, 65, 785, 512, 97, 785, 512, 69, - 783, 512, 101, 783, 512, 69, 785, 512, 101, 785, 512, 73, 783, 512, 105, - 783, 512, 73, 785, 512, 105, 785, 512, 79, 783, 512, 111, 783, 512, 79, - 785, 512, 111, 785, 512, 82, 783, 512, 114, 783, 512, 82, 785, 512, 114, - 785, 512, 85, 783, 512, 117, 783, 512, 85, 785, 512, 117, 785, 512, 83, - 806, 512, 115, 806, 512, 84, 806, 512, 116, 806, 512, 72, 780, 512, 104, - 780, 512, 65, 775, 512, 97, 775, 512, 69, 807, 512, 101, 807, 512, 214, - 772, 512, 246, 772, 512, 213, 772, 512, 245, 772, 512, 79, 775, 512, 111, - 775, 512, 558, 772, 512, 559, 772, 512, 89, 772, 512, 121, 772, 259, 104, - 259, 614, 259, 106, 259, 114, 259, 633, 259, 635, 259, 641, 259, 119, - 259, 121, 514, 32, 774, 514, 32, 775, 514, 32, 778, 514, 32, 808, 514, - 32, 771, 514, 32, 779, 259, 611, 259, 108, 259, 115, 259, 120, 259, 661, - 256, 768, 256, 769, 256, 787, 512, 776, 769, 256, 697, 514, 32, 837, 256, - 59, 514, 32, 769, 512, 168, 769, 512, 913, 769, 256, 183, 512, 917, 769, - 512, 919, 769, 512, 921, 769, 512, 927, 769, 512, 933, 769, 512, 937, - 769, 512, 970, 769, 512, 921, 776, 512, 933, 776, 512, 945, 769, 512, - 949, 769, 512, 951, 769, 512, 953, 769, 512, 971, 769, 512, 953, 776, - 512, 965, 776, 512, 959, 769, 512, 965, 769, 512, 969, 769, 258, 946, - 258, 952, 258, 933, 512, 978, 769, 512, 978, 776, 258, 966, 258, 960, - 258, 954, 258, 961, 258, 962, 258, 920, 258, 949, 258, 931, 512, 1045, - 768, 512, 1045, 776, 512, 1043, 769, 512, 1030, 776, 512, 1050, 769, 512, - 1048, 768, 512, 1059, 774, 512, 1048, 774, 512, 1080, 774, 512, 1077, - 768, 512, 1077, 776, 512, 1075, 769, 512, 1110, 776, 512, 1082, 769, 512, - 1080, 768, 512, 1091, 774, 512, 1140, 783, 512, 1141, 783, 512, 1046, - 774, 512, 1078, 774, 512, 1040, 774, 512, 1072, 774, 512, 1040, 776, 512, - 1072, 776, 512, 1045, 774, 512, 1077, 774, 512, 1240, 776, 512, 1241, - 776, 512, 1046, 776, 512, 1078, 776, 512, 1047, 776, 512, 1079, 776, 512, - 1048, 772, 512, 1080, 772, 512, 1048, 776, 512, 1080, 776, 512, 1054, - 776, 512, 1086, 776, 512, 1256, 776, 512, 1257, 776, 512, 1069, 776, 512, - 1101, 776, 512, 1059, 772, 512, 1091, 772, 512, 1059, 776, 512, 1091, - 776, 512, 1059, 779, 512, 1091, 779, 512, 1063, 776, 512, 1095, 776, 512, - 1067, 776, 512, 1099, 776, 514, 1381, 1410, 512, 1575, 1619, 512, 1575, - 1620, 512, 1608, 1620, 512, 1575, 1621, 512, 1610, 1620, 514, 1575, 1652, - 514, 1608, 1652, 514, 1735, 1652, 514, 1610, 1652, 512, 1749, 1620, 512, - 1729, 1620, 512, 1746, 1620, 512, 2344, 2364, 512, 2352, 2364, 512, 2355, - 2364, 512, 2325, 2364, 512, 2326, 2364, 512, 2327, 2364, 512, 2332, 2364, - 512, 2337, 2364, 512, 2338, 2364, 512, 2347, 2364, 512, 2351, 2364, 512, - 2503, 2494, 512, 2503, 2519, 512, 2465, 2492, 512, 2466, 2492, 512, 2479, - 2492, 512, 2610, 2620, 512, 2616, 2620, 512, 2582, 2620, 512, 2583, 2620, - 512, 2588, 2620, 512, 2603, 2620, 512, 2887, 2902, 512, 2887, 2878, 512, - 2887, 2903, 512, 2849, 2876, 512, 2850, 2876, 512, 2962, 3031, 512, 3014, - 3006, 512, 3015, 3006, 512, 3014, 3031, 512, 3142, 3158, 512, 3263, 3285, - 512, 3270, 3285, 512, 3270, 3286, 512, 3270, 3266, 512, 3274, 3285, 512, - 3398, 3390, 512, 3399, 3390, 512, 3398, 3415, 512, 3545, 3530, 512, 3545, - 3535, 512, 3548, 3530, 512, 3545, 3551, 514, 3661, 3634, 514, 3789, 3762, - 514, 3755, 3737, 514, 3755, 3745, 257, 3851, 512, 3906, 4023, 512, 3916, - 4023, 512, 3921, 4023, 512, 3926, 4023, 512, 3931, 4023, 512, 3904, 4021, - 512, 3953, 3954, 512, 3953, 3956, 512, 4018, 3968, 514, 4018, 3969, 512, - 4019, 3968, 514, 4019, 3969, 512, 3953, 3968, 512, 3986, 4023, 512, 3996, - 4023, 512, 4001, 4023, 512, 4006, 4023, 512, 4011, 4023, 512, 3984, 4021, - 512, 4133, 4142, 259, 4316, 512, 6917, 6965, 512, 6919, 6965, 512, 6921, - 6965, 512, 6923, 6965, 512, 6925, 6965, 512, 6929, 6965, 512, 6970, 6965, - 512, 6972, 6965, 512, 6974, 6965, 512, 6975, 6965, 512, 6978, 6965, 259, - 65, 259, 198, 259, 66, 259, 68, 259, 69, 259, 398, 259, 71, 259, 72, 259, - 73, 259, 74, 259, 75, 259, 76, 259, 77, 259, 78, 259, 79, 259, 546, 259, - 80, 259, 82, 259, 84, 259, 85, 259, 87, 259, 97, 259, 592, 259, 593, 259, - 7426, 259, 98, 259, 100, 259, 101, 259, 601, 259, 603, 259, 604, 259, - 103, 259, 107, 259, 109, 259, 331, 259, 111, 259, 596, 259, 7446, 259, - 7447, 259, 112, 259, 116, 259, 117, 259, 7453, 259, 623, 259, 118, 259, - 7461, 259, 946, 259, 947, 259, 948, 259, 966, 259, 967, 261, 105, 261, - 114, 261, 117, 261, 118, 261, 946, 261, 947, 261, 961, 261, 966, 261, - 967, 259, 1085, 259, 594, 259, 99, 259, 597, 259, 240, 259, 604, 259, - 102, 259, 607, 259, 609, 259, 613, 259, 616, 259, 617, 259, 618, 259, - 7547, 259, 669, 259, 621, 259, 7557, 259, 671, 259, 625, 259, 624, 259, - 626, 259, 627, 259, 628, 259, 629, 259, 632, 259, 642, 259, 643, 259, - 427, 259, 649, 259, 650, 259, 7452, 259, 651, 259, 652, 259, 122, 259, - 656, 259, 657, 259, 658, 259, 952, 512, 65, 805, 512, 97, 805, 512, 66, - 775, 512, 98, 775, 512, 66, 803, 512, 98, 803, 512, 66, 817, 512, 98, - 817, 512, 199, 769, 512, 231, 769, 512, 68, 775, 512, 100, 775, 512, 68, - 803, 512, 100, 803, 512, 68, 817, 512, 100, 817, 512, 68, 807, 512, 100, - 807, 512, 68, 813, 512, 100, 813, 512, 274, 768, 512, 275, 768, 512, 274, - 769, 512, 275, 769, 512, 69, 813, 512, 101, 813, 512, 69, 816, 512, 101, - 816, 512, 552, 774, 512, 553, 774, 512, 70, 775, 512, 102, 775, 512, 71, - 772, 512, 103, 772, 512, 72, 775, 512, 104, 775, 512, 72, 803, 512, 104, - 803, 512, 72, 776, 512, 104, 776, 512, 72, 807, 512, 104, 807, 512, 72, - 814, 512, 104, 814, 512, 73, 816, 512, 105, 816, 512, 207, 769, 512, 239, - 769, 512, 75, 769, 512, 107, 769, 512, 75, 803, 512, 107, 803, 512, 75, - 817, 512, 107, 817, 512, 76, 803, 512, 108, 803, 512, 7734, 772, 512, - 7735, 772, 512, 76, 817, 512, 108, 817, 512, 76, 813, 512, 108, 813, 512, - 77, 769, 512, 109, 769, 512, 77, 775, 512, 109, 775, 512, 77, 803, 512, - 109, 803, 512, 78, 775, 512, 110, 775, 512, 78, 803, 512, 110, 803, 512, - 78, 817, 512, 110, 817, 512, 78, 813, 512, 110, 813, 512, 213, 769, 512, - 245, 769, 512, 213, 776, 512, 245, 776, 512, 332, 768, 512, 333, 768, - 512, 332, 769, 512, 333, 769, 512, 80, 769, 512, 112, 769, 512, 80, 775, - 512, 112, 775, 512, 82, 775, 512, 114, 775, 512, 82, 803, 512, 114, 803, - 512, 7770, 772, 512, 7771, 772, 512, 82, 817, 512, 114, 817, 512, 83, - 775, 512, 115, 775, 512, 83, 803, 512, 115, 803, 512, 346, 775, 512, 347, - 775, 512, 352, 775, 512, 353, 775, 512, 7778, 775, 512, 7779, 775, 512, - 84, 775, 512, 116, 775, 512, 84, 803, 512, 116, 803, 512, 84, 817, 512, - 116, 817, 512, 84, 813, 512, 116, 813, 512, 85, 804, 512, 117, 804, 512, - 85, 816, 512, 117, 816, 512, 85, 813, 512, 117, 813, 512, 360, 769, 512, - 361, 769, 512, 362, 776, 512, 363, 776, 512, 86, 771, 512, 118, 771, 512, - 86, 803, 512, 118, 803, 512, 87, 768, 512, 119, 768, 512, 87, 769, 512, - 119, 769, 512, 87, 776, 512, 119, 776, 512, 87, 775, 512, 119, 775, 512, - 87, 803, 512, 119, 803, 512, 88, 775, 512, 120, 775, 512, 88, 776, 512, - 120, 776, 512, 89, 775, 512, 121, 775, 512, 90, 770, 512, 122, 770, 512, - 90, 803, 512, 122, 803, 512, 90, 817, 512, 122, 817, 512, 104, 817, 512, - 116, 776, 512, 119, 778, 512, 121, 778, 514, 97, 702, 512, 383, 775, 512, - 65, 803, 512, 97, 803, 512, 65, 777, 512, 97, 777, 512, 194, 769, 512, - 226, 769, 512, 194, 768, 512, 226, 768, 512, 194, 777, 512, 226, 777, - 512, 194, 771, 512, 226, 771, 512, 7840, 770, 512, 7841, 770, 512, 258, - 769, 512, 259, 769, 512, 258, 768, 512, 259, 768, 512, 258, 777, 512, - 259, 777, 512, 258, 771, 512, 259, 771, 512, 7840, 774, 512, 7841, 774, - 512, 69, 803, 512, 101, 803, 512, 69, 777, 512, 101, 777, 512, 69, 771, - 512, 101, 771, 512, 202, 769, 512, 234, 769, 512, 202, 768, 512, 234, - 768, 512, 202, 777, 512, 234, 777, 512, 202, 771, 512, 234, 771, 512, - 7864, 770, 512, 7865, 770, 512, 73, 777, 512, 105, 777, 512, 73, 803, - 512, 105, 803, 512, 79, 803, 512, 111, 803, 512, 79, 777, 512, 111, 777, - 512, 212, 769, 512, 244, 769, 512, 212, 768, 512, 244, 768, 512, 212, - 777, 512, 244, 777, 512, 212, 771, 512, 244, 771, 512, 7884, 770, 512, - 7885, 770, 512, 416, 769, 512, 417, 769, 512, 416, 768, 512, 417, 768, - 512, 416, 777, 512, 417, 777, 512, 416, 771, 512, 417, 771, 512, 416, - 803, 512, 417, 803, 512, 85, 803, 512, 117, 803, 512, 85, 777, 512, 117, - 777, 512, 431, 769, 512, 432, 769, 512, 431, 768, 512, 432, 768, 512, - 431, 777, 512, 432, 777, 512, 431, 771, 512, 432, 771, 512, 431, 803, - 512, 432, 803, 512, 89, 768, 512, 121, 768, 512, 89, 803, 512, 121, 803, - 512, 89, 777, 512, 121, 777, 512, 89, 771, 512, 121, 771, 512, 945, 787, - 512, 945, 788, 512, 7936, 768, 512, 7937, 768, 512, 7936, 769, 512, 7937, - 769, 512, 7936, 834, 512, 7937, 834, 512, 913, 787, 512, 913, 788, 512, - 7944, 768, 512, 7945, 768, 512, 7944, 769, 512, 7945, 769, 512, 7944, - 834, 512, 7945, 834, 512, 949, 787, 512, 949, 788, 512, 7952, 768, 512, - 7953, 768, 512, 7952, 769, 512, 7953, 769, 512, 917, 787, 512, 917, 788, - 512, 7960, 768, 512, 7961, 768, 512, 7960, 769, 512, 7961, 769, 512, 951, - 787, 512, 951, 788, 512, 7968, 768, 512, 7969, 768, 512, 7968, 769, 512, - 7969, 769, 512, 7968, 834, 512, 7969, 834, 512, 919, 787, 512, 919, 788, - 512, 7976, 768, 512, 7977, 768, 512, 7976, 769, 512, 7977, 769, 512, - 7976, 834, 512, 7977, 834, 512, 953, 787, 512, 953, 788, 512, 7984, 768, - 512, 7985, 768, 512, 7984, 769, 512, 7985, 769, 512, 7984, 834, 512, - 7985, 834, 512, 921, 787, 512, 921, 788, 512, 7992, 768, 512, 7993, 768, - 512, 7992, 769, 512, 7993, 769, 512, 7992, 834, 512, 7993, 834, 512, 959, - 787, 512, 959, 788, 512, 8000, 768, 512, 8001, 768, 512, 8000, 769, 512, - 8001, 769, 512, 927, 787, 512, 927, 788, 512, 8008, 768, 512, 8009, 768, - 512, 8008, 769, 512, 8009, 769, 512, 965, 787, 512, 965, 788, 512, 8016, - 768, 512, 8017, 768, 512, 8016, 769, 512, 8017, 769, 512, 8016, 834, 512, - 8017, 834, 512, 933, 788, 512, 8025, 768, 512, 8025, 769, 512, 8025, 834, - 512, 969, 787, 512, 969, 788, 512, 8032, 768, 512, 8033, 768, 512, 8032, - 769, 512, 8033, 769, 512, 8032, 834, 512, 8033, 834, 512, 937, 787, 512, - 937, 788, 512, 8040, 768, 512, 8041, 768, 512, 8040, 769, 512, 8041, 769, - 512, 8040, 834, 512, 8041, 834, 512, 945, 768, 256, 940, 512, 949, 768, - 256, 941, 512, 951, 768, 256, 942, 512, 953, 768, 256, 943, 512, 959, - 768, 256, 972, 512, 965, 768, 256, 973, 512, 969, 768, 256, 974, 512, - 7936, 837, 512, 7937, 837, 512, 7938, 837, 512, 7939, 837, 512, 7940, - 837, 512, 7941, 837, 512, 7942, 837, 512, 7943, 837, 512, 7944, 837, 512, - 7945, 837, 512, 7946, 837, 512, 7947, 837, 512, 7948, 837, 512, 7949, - 837, 512, 7950, 837, 512, 7951, 837, 512, 7968, 837, 512, 7969, 837, 512, - 7970, 837, 512, 7971, 837, 512, 7972, 837, 512, 7973, 837, 512, 7974, - 837, 512, 7975, 837, 512, 7976, 837, 512, 7977, 837, 512, 7978, 837, 512, - 7979, 837, 512, 7980, 837, 512, 7981, 837, 512, 7982, 837, 512, 7983, - 837, 512, 8032, 837, 512, 8033, 837, 512, 8034, 837, 512, 8035, 837, 512, - 8036, 837, 512, 8037, 837, 512, 8038, 837, 512, 8039, 837, 512, 8040, - 837, 512, 8041, 837, 512, 8042, 837, 512, 8043, 837, 512, 8044, 837, 512, - 8045, 837, 512, 8046, 837, 512, 8047, 837, 512, 945, 774, 512, 945, 772, - 512, 8048, 837, 512, 945, 837, 512, 940, 837, 512, 945, 834, 512, 8118, - 837, 512, 913, 774, 512, 913, 772, 512, 913, 768, 256, 902, 512, 913, - 837, 514, 32, 787, 256, 953, 514, 32, 787, 514, 32, 834, 512, 168, 834, - 512, 8052, 837, 512, 951, 837, 512, 942, 837, 512, 951, 834, 512, 8134, - 837, 512, 917, 768, 256, 904, 512, 919, 768, 256, 905, 512, 919, 837, - 512, 8127, 768, 512, 8127, 769, 512, 8127, 834, 512, 953, 774, 512, 953, - 772, 512, 970, 768, 256, 912, 512, 953, 834, 512, 970, 834, 512, 921, - 774, 512, 921, 772, 512, 921, 768, 256, 906, 512, 8190, 768, 512, 8190, - 769, 512, 8190, 834, 512, 965, 774, 512, 965, 772, 512, 971, 768, 256, - 944, 512, 961, 787, 512, 961, 788, 512, 965, 834, 512, 971, 834, 512, - 933, 774, 512, 933, 772, 512, 933, 768, 256, 910, 512, 929, 788, 512, - 168, 768, 256, 901, 256, 96, 512, 8060, 837, 512, 969, 837, 512, 974, - 837, 512, 969, 834, 512, 8182, 837, 512, 927, 768, 256, 908, 512, 937, - 768, 256, 911, 512, 937, 837, 256, 180, 514, 32, 788, 256, 8194, 256, - 8195, 258, 32, 258, 32, 258, 32, 258, 32, 258, 32, 257, 32, 258, 32, 258, - 32, 258, 32, 257, 8208, 514, 32, 819, 258, 46, 514, 46, 46, 770, 46, 46, - 46, 257, 32, 514, 8242, 8242, 770, 8242, 8242, 8242, 514, 8245, 8245, - 770, 8245, 8245, 8245, 514, 33, 33, 514, 32, 773, 514, 63, 63, 514, 63, - 33, 514, 33, 63, 1026, 8242, 8242, 8242, 8242, 258, 32, 259, 48, 259, - 105, 259, 52, 259, 53, 259, 54, 259, 55, 259, 56, 259, 57, 259, 43, 259, - 8722, 259, 61, 259, 40, 259, 41, 259, 110, 261, 48, 261, 49, 261, 50, - 261, 51, 261, 52, 261, 53, 261, 54, 261, 55, 261, 56, 261, 57, 261, 43, - 261, 8722, 261, 61, 261, 40, 261, 41, 261, 97, 261, 101, 261, 111, 261, - 120, 261, 601, 261, 104, 261, 107, 261, 108, 261, 109, 261, 110, 261, - 112, 261, 115, 261, 116, 514, 82, 115, 770, 97, 47, 99, 770, 97, 47, 115, - 262, 67, 514, 176, 67, 770, 99, 47, 111, 770, 99, 47, 117, 258, 400, 514, - 176, 70, 262, 103, 262, 72, 262, 72, 262, 72, 262, 104, 262, 295, 262, - 73, 262, 73, 262, 76, 262, 108, 262, 78, 514, 78, 111, 262, 80, 262, 81, - 262, 82, 262, 82, 262, 82, 515, 83, 77, 770, 84, 69, 76, 515, 84, 77, - 262, 90, 256, 937, 262, 90, 256, 75, 256, 197, 262, 66, 262, 67, 262, - 101, 262, 69, 262, 70, 262, 77, 262, 111, 258, 1488, 258, 1489, 258, - 1490, 258, 1491, 262, 105, 770, 70, 65, 88, 262, 960, 262, 947, 262, 915, - 262, 928, 262, 8721, 262, 68, 262, 100, 262, 101, 262, 105, 262, 106, - 772, 49, 8260, 55, 772, 49, 8260, 57, 1028, 49, 8260, 49, 48, 772, 49, - 8260, 51, 772, 50, 8260, 51, 772, 49, 8260, 53, 772, 50, 8260, 53, 772, - 51, 8260, 53, 772, 52, 8260, 53, 772, 49, 8260, 54, 772, 53, 8260, 54, - 772, 49, 8260, 56, 772, 51, 8260, 56, 772, 53, 8260, 56, 772, 55, 8260, - 56, 516, 49, 8260, 258, 73, 514, 73, 73, 770, 73, 73, 73, 514, 73, 86, - 258, 86, 514, 86, 73, 770, 86, 73, 73, 1026, 86, 73, 73, 73, 514, 73, 88, - 258, 88, 514, 88, 73, 770, 88, 73, 73, 258, 76, 258, 67, 258, 68, 258, - 77, 258, 105, 514, 105, 105, 770, 105, 105, 105, 514, 105, 118, 258, 118, - 514, 118, 105, 770, 118, 105, 105, 1026, 118, 105, 105, 105, 514, 105, - 120, 258, 120, 514, 120, 105, 770, 120, 105, 105, 258, 108, 258, 99, 258, - 100, 258, 109, 772, 48, 8260, 51, 512, 8592, 824, 512, 8594, 824, 512, - 8596, 824, 512, 8656, 824, 512, 8660, 824, 512, 8658, 824, 512, 8707, - 824, 512, 8712, 824, 512, 8715, 824, 512, 8739, 824, 512, 8741, 824, 514, - 8747, 8747, 770, 8747, 8747, 8747, 514, 8750, 8750, 770, 8750, 8750, - 8750, 512, 8764, 824, 512, 8771, 824, 512, 8773, 824, 512, 8776, 824, - 512, 61, 824, 512, 8801, 824, 512, 8781, 824, 512, 60, 824, 512, 62, 824, - 512, 8804, 824, 512, 8805, 824, 512, 8818, 824, 512, 8819, 824, 512, - 8822, 824, 512, 8823, 824, 512, 8826, 824, 512, 8827, 824, 512, 8834, - 824, 512, 8835, 824, 512, 8838, 824, 512, 8839, 824, 512, 8866, 824, 512, - 8872, 824, 512, 8873, 824, 512, 8875, 824, 512, 8828, 824, 512, 8829, - 824, 512, 8849, 824, 512, 8850, 824, 512, 8882, 824, 512, 8883, 824, 512, - 8884, 824, 512, 8885, 824, 256, 12296, 256, 12297, 263, 49, 263, 50, 263, - 51, 263, 52, 263, 53, 263, 54, 263, 55, 263, 56, 263, 57, 519, 49, 48, - 519, 49, 49, 519, 49, 50, 519, 49, 51, 519, 49, 52, 519, 49, 53, 519, 49, - 54, 519, 49, 55, 519, 49, 56, 519, 49, 57, 519, 50, 48, 770, 40, 49, 41, - 770, 40, 50, 41, 770, 40, 51, 41, 770, 40, 52, 41, 770, 40, 53, 41, 770, - 40, 54, 41, 770, 40, 55, 41, 770, 40, 56, 41, 770, 40, 57, 41, 1026, 40, - 49, 48, 41, 1026, 40, 49, 49, 41, 1026, 40, 49, 50, 41, 1026, 40, 49, 51, - 41, 1026, 40, 49, 52, 41, 1026, 40, 49, 53, 41, 1026, 40, 49, 54, 41, - 1026, 40, 49, 55, 41, 1026, 40, 49, 56, 41, 1026, 40, 49, 57, 41, 1026, - 40, 50, 48, 41, 514, 49, 46, 514, 50, 46, 514, 51, 46, 514, 52, 46, 514, - 53, 46, 514, 54, 46, 514, 55, 46, 514, 56, 46, 514, 57, 46, 770, 49, 48, - 46, 770, 49, 49, 46, 770, 49, 50, 46, 770, 49, 51, 46, 770, 49, 52, 46, - 770, 49, 53, 46, 770, 49, 54, 46, 770, 49, 55, 46, 770, 49, 56, 46, 770, - 49, 57, 46, 770, 50, 48, 46, 770, 40, 97, 41, 770, 40, 98, 41, 770, 40, - 99, 41, 770, 40, 100, 41, 770, 40, 101, 41, 770, 40, 102, 41, 770, 40, - 103, 41, 770, 40, 104, 41, 770, 40, 105, 41, 770, 40, 106, 41, 770, 40, - 107, 41, 770, 40, 108, 41, 770, 40, 109, 41, 770, 40, 110, 41, 770, 40, - 111, 41, 770, 40, 112, 41, 770, 40, 113, 41, 770, 40, 114, 41, 770, 40, - 115, 41, 770, 40, 116, 41, 770, 40, 117, 41, 770, 40, 118, 41, 770, 40, - 119, 41, 770, 40, 120, 41, 770, 40, 121, 41, 770, 40, 122, 41, 263, 65, - 263, 66, 263, 67, 263, 68, 263, 69, 263, 70, 263, 71, 263, 72, 263, 73, - 263, 74, 263, 75, 263, 76, 263, 77, 263, 78, 263, 79, 263, 80, 263, 81, - 263, 82, 263, 83, 263, 84, 263, 85, 263, 86, 263, 87, 263, 88, 263, 89, - 263, 90, 263, 97, 263, 98, 263, 99, 263, 100, 263, 101, 263, 102, 263, - 103, 263, 104, 263, 105, 263, 106, 263, 107, 263, 108, 263, 109, 263, - 110, 263, 111, 263, 112, 263, 113, 263, 114, 263, 115, 263, 116, 263, - 117, 263, 118, 263, 119, 263, 120, 263, 121, 263, 122, 263, 48, 1026, - 8747, 8747, 8747, 8747, 770, 58, 58, 61, 514, 61, 61, 770, 61, 61, 61, - 512, 10973, 824, 261, 106, 259, 86, 259, 11617, 258, 27597, 258, 40863, - 258, 19968, 258, 20008, 258, 20022, 258, 20031, 258, 20057, 258, 20101, - 258, 20108, 258, 20128, 258, 20154, 258, 20799, 258, 20837, 258, 20843, - 258, 20866, 258, 20886, 258, 20907, 258, 20960, 258, 20981, 258, 20992, - 258, 21147, 258, 21241, 258, 21269, 258, 21274, 258, 21304, 258, 21313, - 258, 21340, 258, 21353, 258, 21378, 258, 21430, 258, 21448, 258, 21475, - 258, 22231, 258, 22303, 258, 22763, 258, 22786, 258, 22794, 258, 22805, - 258, 22823, 258, 22899, 258, 23376, 258, 23424, 258, 23544, 258, 23567, - 258, 23586, 258, 23608, 258, 23662, 258, 23665, 258, 24027, 258, 24037, - 258, 24049, 258, 24062, 258, 24178, 258, 24186, 258, 24191, 258, 24308, - 258, 24318, 258, 24331, 258, 24339, 258, 24400, 258, 24417, 258, 24435, - 258, 24515, 258, 25096, 258, 25142, 258, 25163, 258, 25903, 258, 25908, - 258, 25991, 258, 26007, 258, 26020, 258, 26041, 258, 26080, 258, 26085, - 258, 26352, 258, 26376, 258, 26408, 258, 27424, 258, 27490, 258, 27513, - 258, 27571, 258, 27595, 258, 27604, 258, 27611, 258, 27663, 258, 27668, - 258, 27700, 258, 28779, 258, 29226, 258, 29238, 258, 29243, 258, 29247, - 258, 29255, 258, 29273, 258, 29275, 258, 29356, 258, 29572, 258, 29577, - 258, 29916, 258, 29926, 258, 29976, 258, 29983, 258, 29992, 258, 30000, - 258, 30091, 258, 30098, 258, 30326, 258, 30333, 258, 30382, 258, 30399, - 258, 30446, 258, 30683, 258, 30690, 258, 30707, 258, 31034, 258, 31160, - 258, 31166, 258, 31348, 258, 31435, 258, 31481, 258, 31859, 258, 31992, - 258, 32566, 258, 32593, 258, 32650, 258, 32701, 258, 32769, 258, 32780, - 258, 32786, 258, 32819, 258, 32895, 258, 32905, 258, 33251, 258, 33258, - 258, 33267, 258, 33276, 258, 33292, 258, 33307, 258, 33311, 258, 33390, - 258, 33394, 258, 33400, 258, 34381, 258, 34411, 258, 34880, 258, 34892, - 258, 34915, 258, 35198, 258, 35211, 258, 35282, 258, 35328, 258, 35895, - 258, 35910, 258, 35925, 258, 35960, 258, 35997, 258, 36196, 258, 36208, - 258, 36275, 258, 36523, 258, 36554, 258, 36763, 258, 36784, 258, 36789, - 258, 37009, 258, 37193, 258, 37318, 258, 37324, 258, 37329, 258, 38263, - 258, 38272, 258, 38428, 258, 38582, 258, 38585, 258, 38632, 258, 38737, - 258, 38750, 258, 38754, 258, 38761, 258, 38859, 258, 38893, 258, 38899, - 258, 38913, 258, 39080, 258, 39131, 258, 39135, 258, 39318, 258, 39321, - 258, 39340, 258, 39592, 258, 39640, 258, 39647, 258, 39717, 258, 39727, - 258, 39730, 258, 39740, 258, 39770, 258, 40165, 258, 40565, 258, 40575, - 258, 40613, 258, 40635, 258, 40643, 258, 40653, 258, 40657, 258, 40697, - 258, 40701, 258, 40718, 258, 40723, 258, 40736, 258, 40763, 258, 40778, - 258, 40786, 258, 40845, 258, 40860, 258, 40864, 264, 32, 258, 12306, 258, - 21313, 258, 21316, 258, 21317, 512, 12363, 12441, 512, 12365, 12441, 512, - 12367, 12441, 512, 12369, 12441, 512, 12371, 12441, 512, 12373, 12441, - 512, 12375, 12441, 512, 12377, 12441, 512, 12379, 12441, 512, 12381, - 12441, 512, 12383, 12441, 512, 12385, 12441, 512, 12388, 12441, 512, - 12390, 12441, 512, 12392, 12441, 512, 12399, 12441, 512, 12399, 12442, - 512, 12402, 12441, 512, 12402, 12442, 512, 12405, 12441, 512, 12405, - 12442, 512, 12408, 12441, 512, 12408, 12442, 512, 12411, 12441, 512, - 12411, 12442, 512, 12358, 12441, 514, 32, 12441, 514, 32, 12442, 512, - 12445, 12441, 521, 12424, 12426, 512, 12459, 12441, 512, 12461, 12441, - 512, 12463, 12441, 512, 12465, 12441, 512, 12467, 12441, 512, 12469, - 12441, 512, 12471, 12441, 512, 12473, 12441, 512, 12475, 12441, 512, - 12477, 12441, 512, 12479, 12441, 512, 12481, 12441, 512, 12484, 12441, - 512, 12486, 12441, 512, 12488, 12441, 512, 12495, 12441, 512, 12495, - 12442, 512, 12498, 12441, 512, 12498, 12442, 512, 12501, 12441, 512, - 12501, 12442, 512, 12504, 12441, 512, 12504, 12442, 512, 12507, 12441, - 512, 12507, 12442, 512, 12454, 12441, 512, 12527, 12441, 512, 12528, - 12441, 512, 12529, 12441, 512, 12530, 12441, 512, 12541, 12441, 521, - 12467, 12488, 258, 4352, 258, 4353, 258, 4522, 258, 4354, 258, 4524, 258, - 4525, 258, 4355, 258, 4356, 258, 4357, 258, 4528, 258, 4529, 258, 4530, - 258, 4531, 258, 4532, 258, 4533, 258, 4378, 258, 4358, 258, 4359, 258, - 4360, 258, 4385, 258, 4361, 258, 4362, 258, 4363, 258, 4364, 258, 4365, - 258, 4366, 258, 4367, 258, 4368, 258, 4369, 258, 4370, 258, 4449, 258, - 4450, 258, 4451, 258, 4452, 258, 4453, 258, 4454, 258, 4455, 258, 4456, - 258, 4457, 258, 4458, 258, 4459, 258, 4460, 258, 4461, 258, 4462, 258, - 4463, 258, 4464, 258, 4465, 258, 4466, 258, 4467, 258, 4468, 258, 4469, - 258, 4448, 258, 4372, 258, 4373, 258, 4551, 258, 4552, 258, 4556, 258, - 4558, 258, 4563, 258, 4567, 258, 4569, 258, 4380, 258, 4573, 258, 4575, - 258, 4381, 258, 4382, 258, 4384, 258, 4386, 258, 4387, 258, 4391, 258, - 4393, 258, 4395, 258, 4396, 258, 4397, 258, 4398, 258, 4399, 258, 4402, - 258, 4406, 258, 4416, 258, 4423, 258, 4428, 258, 4593, 258, 4594, 258, - 4439, 258, 4440, 258, 4441, 258, 4484, 258, 4485, 258, 4488, 258, 4497, - 258, 4498, 258, 4500, 258, 4510, 258, 4513, 259, 19968, 259, 20108, 259, - 19977, 259, 22235, 259, 19978, 259, 20013, 259, 19979, 259, 30002, 259, - 20057, 259, 19993, 259, 19969, 259, 22825, 259, 22320, 259, 20154, 770, - 40, 4352, 41, 770, 40, 4354, 41, 770, 40, 4355, 41, 770, 40, 4357, 41, - 770, 40, 4358, 41, 770, 40, 4359, 41, 770, 40, 4361, 41, 770, 40, 4363, - 41, 770, 40, 4364, 41, 770, 40, 4366, 41, 770, 40, 4367, 41, 770, 40, - 4368, 41, 770, 40, 4369, 41, 770, 40, 4370, 41, 1026, 40, 4352, 4449, 41, - 1026, 40, 4354, 4449, 41, 1026, 40, 4355, 4449, 41, 1026, 40, 4357, 4449, - 41, 1026, 40, 4358, 4449, 41, 1026, 40, 4359, 4449, 41, 1026, 40, 4361, - 4449, 41, 1026, 40, 4363, 4449, 41, 1026, 40, 4364, 4449, 41, 1026, 40, - 4366, 4449, 41, 1026, 40, 4367, 4449, 41, 1026, 40, 4368, 4449, 41, 1026, - 40, 4369, 4449, 41, 1026, 40, 4370, 4449, 41, 1026, 40, 4364, 4462, 41, - 1794, 40, 4363, 4457, 4364, 4453, 4523, 41, 1538, 40, 4363, 4457, 4370, - 4462, 41, 770, 40, 19968, 41, 770, 40, 20108, 41, 770, 40, 19977, 41, - 770, 40, 22235, 41, 770, 40, 20116, 41, 770, 40, 20845, 41, 770, 40, - 19971, 41, 770, 40, 20843, 41, 770, 40, 20061, 41, 770, 40, 21313, 41, - 770, 40, 26376, 41, 770, 40, 28779, 41, 770, 40, 27700, 41, 770, 40, - 26408, 41, 770, 40, 37329, 41, 770, 40, 22303, 41, 770, 40, 26085, 41, - 770, 40, 26666, 41, 770, 40, 26377, 41, 770, 40, 31038, 41, 770, 40, - 21517, 41, 770, 40, 29305, 41, 770, 40, 36001, 41, 770, 40, 31069, 41, - 770, 40, 21172, 41, 770, 40, 20195, 41, 770, 40, 21628, 41, 770, 40, - 23398, 41, 770, 40, 30435, 41, 770, 40, 20225, 41, 770, 40, 36039, 41, - 770, 40, 21332, 41, 770, 40, 31085, 41, 770, 40, 20241, 41, 770, 40, - 33258, 41, 770, 40, 33267, 41, 263, 21839, 263, 24188, 263, 25991, 263, - 31631, 778, 80, 84, 69, 519, 50, 49, 519, 50, 50, 519, 50, 51, 519, 50, - 52, 519, 50, 53, 519, 50, 54, 519, 50, 55, 519, 50, 56, 519, 50, 57, 519, - 51, 48, 519, 51, 49, 519, 51, 50, 519, 51, 51, 519, 51, 52, 519, 51, 53, - 263, 4352, 263, 4354, 263, 4355, 263, 4357, 263, 4358, 263, 4359, 263, - 4361, 263, 4363, 263, 4364, 263, 4366, 263, 4367, 263, 4368, 263, 4369, - 263, 4370, 519, 4352, 4449, 519, 4354, 4449, 519, 4355, 4449, 519, 4357, - 4449, 519, 4358, 4449, 519, 4359, 4449, 519, 4361, 4449, 519, 4363, 4449, - 519, 4364, 4449, 519, 4366, 4449, 519, 4367, 4449, 519, 4368, 4449, 519, - 4369, 4449, 519, 4370, 4449, 1287, 4366, 4449, 4535, 4352, 4457, 1031, - 4364, 4462, 4363, 4468, 519, 4363, 4462, 263, 19968, 263, 20108, 263, - 19977, 263, 22235, 263, 20116, 263, 20845, 263, 19971, 263, 20843, 263, - 20061, 263, 21313, 263, 26376, 263, 28779, 263, 27700, 263, 26408, 263, - 37329, 263, 22303, 263, 26085, 263, 26666, 263, 26377, 263, 31038, 263, - 21517, 263, 29305, 263, 36001, 263, 31069, 263, 21172, 263, 31192, 263, - 30007, 263, 22899, 263, 36969, 263, 20778, 263, 21360, 263, 27880, 263, - 38917, 263, 20241, 263, 20889, 263, 27491, 263, 19978, 263, 20013, 263, - 19979, 263, 24038, 263, 21491, 263, 21307, 263, 23447, 263, 23398, 263, - 30435, 263, 20225, 263, 36039, 263, 21332, 263, 22812, 519, 51, 54, 519, - 51, 55, 519, 51, 56, 519, 51, 57, 519, 52, 48, 519, 52, 49, 519, 52, 50, - 519, 52, 51, 519, 52, 52, 519, 52, 53, 519, 52, 54, 519, 52, 55, 519, 52, - 56, 519, 52, 57, 519, 53, 48, 514, 49, 26376, 514, 50, 26376, 514, 51, - 26376, 514, 52, 26376, 514, 53, 26376, 514, 54, 26376, 514, 55, 26376, - 514, 56, 26376, 514, 57, 26376, 770, 49, 48, 26376, 770, 49, 49, 26376, - 770, 49, 50, 26376, 522, 72, 103, 778, 101, 114, 103, 522, 101, 86, 778, - 76, 84, 68, 263, 12450, 263, 12452, 263, 12454, 263, 12456, 263, 12458, - 263, 12459, 263, 12461, 263, 12463, 263, 12465, 263, 12467, 263, 12469, - 263, 12471, 263, 12473, 263, 12475, 263, 12477, 263, 12479, 263, 12481, - 263, 12484, 263, 12486, 263, 12488, 263, 12490, 263, 12491, 263, 12492, - 263, 12493, 263, 12494, 263, 12495, 263, 12498, 263, 12501, 263, 12504, - 263, 12507, 263, 12510, 263, 12511, 263, 12512, 263, 12513, 263, 12514, - 263, 12516, 263, 12518, 263, 12520, 263, 12521, 263, 12522, 263, 12523, - 263, 12524, 263, 12525, 263, 12527, 263, 12528, 263, 12529, 263, 12530, - 1034, 12450, 12497, 12540, 12488, 1034, 12450, 12523, 12501, 12449, 1034, - 12450, 12531, 12506, 12450, 778, 12450, 12540, 12523, 1034, 12452, 12491, - 12531, 12464, 778, 12452, 12531, 12481, 778, 12454, 12457, 12531, 1290, - 12456, 12473, 12463, 12540, 12489, 1034, 12456, 12540, 12459, 12540, 778, - 12458, 12531, 12473, 778, 12458, 12540, 12512, 778, 12459, 12452, 12522, - 1034, 12459, 12521, 12483, 12488, 1034, 12459, 12525, 12522, 12540, 778, - 12460, 12525, 12531, 778, 12460, 12531, 12510, 522, 12462, 12460, 778, - 12462, 12491, 12540, 1034, 12461, 12517, 12522, 12540, 1034, 12462, - 12523, 12480, 12540, 522, 12461, 12525, 1290, 12461, 12525, 12464, 12521, - 12512, 1546, 12461, 12525, 12513, 12540, 12488, 12523, 1290, 12461, - 12525, 12527, 12483, 12488, 778, 12464, 12521, 12512, 1290, 12464, 12521, - 12512, 12488, 12531, 1290, 12463, 12523, 12476, 12452, 12525, 1034, - 12463, 12525, 12540, 12493, 778, 12465, 12540, 12473, 778, 12467, 12523, - 12490, 778, 12467, 12540, 12509, 1034, 12469, 12452, 12463, 12523, 1290, - 12469, 12531, 12481, 12540, 12512, 1034, 12471, 12522, 12531, 12464, 778, - 12475, 12531, 12481, 778, 12475, 12531, 12488, 778, 12480, 12540, 12473, - 522, 12487, 12471, 522, 12489, 12523, 522, 12488, 12531, 522, 12490, - 12494, 778, 12494, 12483, 12488, 778, 12495, 12452, 12484, 1290, 12497, - 12540, 12475, 12531, 12488, 778, 12497, 12540, 12484, 1034, 12496, 12540, - 12524, 12523, 1290, 12500, 12450, 12473, 12488, 12523, 778, 12500, 12463, - 12523, 522, 12500, 12467, 522, 12499, 12523, 1290, 12501, 12449, 12521, - 12483, 12489, 1034, 12501, 12451, 12540, 12488, 1290, 12502, 12483, - 12471, 12455, 12523, 778, 12501, 12521, 12531, 1290, 12504, 12463, 12479, - 12540, 12523, 522, 12506, 12477, 778, 12506, 12491, 12498, 778, 12504, - 12523, 12484, 778, 12506, 12531, 12473, 778, 12506, 12540, 12472, 778, - 12505, 12540, 12479, 1034, 12509, 12452, 12531, 12488, 778, 12508, 12523, - 12488, 522, 12507, 12531, 778, 12509, 12531, 12489, 778, 12507, 12540, - 12523, 778, 12507, 12540, 12531, 1034, 12510, 12452, 12463, 12525, 778, - 12510, 12452, 12523, 778, 12510, 12483, 12495, 778, 12510, 12523, 12463, - 1290, 12510, 12531, 12471, 12519, 12531, 1034, 12511, 12463, 12525, - 12531, 522, 12511, 12522, 1290, 12511, 12522, 12496, 12540, 12523, 522, - 12513, 12460, 1034, 12513, 12460, 12488, 12531, 1034, 12513, 12540, - 12488, 12523, 778, 12516, 12540, 12489, 778, 12516, 12540, 12523, 778, - 12518, 12450, 12531, 1034, 12522, 12483, 12488, 12523, 522, 12522, 12521, - 778, 12523, 12500, 12540, 1034, 12523, 12540, 12502, 12523, 522, 12524, - 12512, 1290, 12524, 12531, 12488, 12466, 12531, 778, 12527, 12483, 12488, - 514, 48, 28857, 514, 49, 28857, 514, 50, 28857, 514, 51, 28857, 514, 52, - 28857, 514, 53, 28857, 514, 54, 28857, 514, 55, 28857, 514, 56, 28857, - 514, 57, 28857, 770, 49, 48, 28857, 770, 49, 49, 28857, 770, 49, 50, - 28857, 770, 49, 51, 28857, 770, 49, 52, 28857, 770, 49, 53, 28857, 770, - 49, 54, 28857, 770, 49, 55, 28857, 770, 49, 56, 28857, 770, 49, 57, - 28857, 770, 50, 48, 28857, 770, 50, 49, 28857, 770, 50, 50, 28857, 770, - 50, 51, 28857, 770, 50, 52, 28857, 778, 104, 80, 97, 522, 100, 97, 522, - 65, 85, 778, 98, 97, 114, 522, 111, 86, 522, 112, 99, 522, 100, 109, 778, - 100, 109, 178, 778, 100, 109, 179, 522, 73, 85, 522, 24179, 25104, 522, - 26157, 21644, 522, 22823, 27491, 522, 26126, 27835, 1034, 26666, 24335, - 20250, 31038, 522, 112, 65, 522, 110, 65, 522, 956, 65, 522, 109, 65, - 522, 107, 65, 522, 75, 66, 522, 77, 66, 522, 71, 66, 778, 99, 97, 108, - 1034, 107, 99, 97, 108, 522, 112, 70, 522, 110, 70, 522, 956, 70, 522, - 956, 103, 522, 109, 103, 522, 107, 103, 522, 72, 122, 778, 107, 72, 122, - 778, 77, 72, 122, 778, 71, 72, 122, 778, 84, 72, 122, 522, 956, 8467, - 522, 109, 8467, 522, 100, 8467, 522, 107, 8467, 522, 102, 109, 522, 110, - 109, 522, 956, 109, 522, 109, 109, 522, 99, 109, 522, 107, 109, 778, 109, - 109, 178, 778, 99, 109, 178, 522, 109, 178, 778, 107, 109, 178, 778, 109, - 109, 179, 778, 99, 109, 179, 522, 109, 179, 778, 107, 109, 179, 778, 109, - 8725, 115, 1034, 109, 8725, 115, 178, 522, 80, 97, 778, 107, 80, 97, 778, - 77, 80, 97, 778, 71, 80, 97, 778, 114, 97, 100, 1290, 114, 97, 100, 8725, - 115, 1546, 114, 97, 100, 8725, 115, 178, 522, 112, 115, 522, 110, 115, - 522, 956, 115, 522, 109, 115, 522, 112, 86, 522, 110, 86, 522, 956, 86, - 522, 109, 86, 522, 107, 86, 522, 77, 86, 522, 112, 87, 522, 110, 87, 522, - 956, 87, 522, 109, 87, 522, 107, 87, 522, 77, 87, 522, 107, 937, 522, 77, - 937, 1034, 97, 46, 109, 46, 522, 66, 113, 522, 99, 99, 522, 99, 100, - 1034, 67, 8725, 107, 103, 778, 67, 111, 46, 522, 100, 66, 522, 71, 121, - 522, 104, 97, 522, 72, 80, 522, 105, 110, 522, 75, 75, 522, 75, 77, 522, - 107, 116, 522, 108, 109, 522, 108, 110, 778, 108, 111, 103, 522, 108, - 120, 522, 109, 98, 778, 109, 105, 108, 778, 109, 111, 108, 522, 80, 72, - 1034, 112, 46, 109, 46, 778, 80, 80, 77, 522, 80, 82, 522, 115, 114, 522, - 83, 118, 522, 87, 98, 778, 86, 8725, 109, 778, 65, 8725, 109, 514, 49, - 26085, 514, 50, 26085, 514, 51, 26085, 514, 52, 26085, 514, 53, 26085, - 514, 54, 26085, 514, 55, 26085, 514, 56, 26085, 514, 57, 26085, 770, 49, - 48, 26085, 770, 49, 49, 26085, 770, 49, 50, 26085, 770, 49, 51, 26085, - 770, 49, 52, 26085, 770, 49, 53, 26085, 770, 49, 54, 26085, 770, 49, 55, - 26085, 770, 49, 56, 26085, 770, 49, 57, 26085, 770, 50, 48, 26085, 770, - 50, 49, 26085, 770, 50, 50, 26085, 770, 50, 51, 26085, 770, 50, 52, - 26085, 770, 50, 53, 26085, 770, 50, 54, 26085, 770, 50, 55, 26085, 770, - 50, 56, 26085, 770, 50, 57, 26085, 770, 51, 48, 26085, 770, 51, 49, - 26085, 778, 103, 97, 108, 259, 1098, 259, 1100, 259, 42863, 259, 294, - 259, 339, 259, 42791, 259, 43831, 259, 619, 259, 43858, 256, 35912, 256, - 26356, 256, 36554, 256, 36040, 256, 28369, 256, 20018, 256, 21477, 256, - 40860, 256, 40860, 256, 22865, 256, 37329, 256, 21895, 256, 22856, 256, - 25078, 256, 30313, 256, 32645, 256, 34367, 256, 34746, 256, 35064, 256, - 37007, 256, 27138, 256, 27931, 256, 28889, 256, 29662, 256, 33853, 256, - 37226, 256, 39409, 256, 20098, 256, 21365, 256, 27396, 256, 29211, 256, - 34349, 256, 40478, 256, 23888, 256, 28651, 256, 34253, 256, 35172, 256, - 25289, 256, 33240, 256, 34847, 256, 24266, 256, 26391, 256, 28010, 256, - 29436, 256, 37070, 256, 20358, 256, 20919, 256, 21214, 256, 25796, 256, - 27347, 256, 29200, 256, 30439, 256, 32769, 256, 34310, 256, 34396, 256, - 36335, 256, 38706, 256, 39791, 256, 40442, 256, 30860, 256, 31103, 256, - 32160, 256, 33737, 256, 37636, 256, 40575, 256, 35542, 256, 22751, 256, - 24324, 256, 31840, 256, 32894, 256, 29282, 256, 30922, 256, 36034, 256, - 38647, 256, 22744, 256, 23650, 256, 27155, 256, 28122, 256, 28431, 256, - 32047, 256, 32311, 256, 38475, 256, 21202, 256, 32907, 256, 20956, 256, - 20940, 256, 31260, 256, 32190, 256, 33777, 256, 38517, 256, 35712, 256, - 25295, 256, 27138, 256, 35582, 256, 20025, 256, 23527, 256, 24594, 256, - 29575, 256, 30064, 256, 21271, 256, 30971, 256, 20415, 256, 24489, 256, - 19981, 256, 27852, 256, 25976, 256, 32034, 256, 21443, 256, 22622, 256, - 30465, 256, 33865, 256, 35498, 256, 27578, 256, 36784, 256, 27784, 256, - 25342, 256, 33509, 256, 25504, 256, 30053, 256, 20142, 256, 20841, 256, - 20937, 256, 26753, 256, 31975, 256, 33391, 256, 35538, 256, 37327, 256, - 21237, 256, 21570, 256, 22899, 256, 24300, 256, 26053, 256, 28670, 256, - 31018, 256, 38317, 256, 39530, 256, 40599, 256, 40654, 256, 21147, 256, - 26310, 256, 27511, 256, 36706, 256, 24180, 256, 24976, 256, 25088, 256, - 25754, 256, 28451, 256, 29001, 256, 29833, 256, 31178, 256, 32244, 256, - 32879, 256, 36646, 256, 34030, 256, 36899, 256, 37706, 256, 21015, 256, - 21155, 256, 21693, 256, 28872, 256, 35010, 256, 35498, 256, 24265, 256, - 24565, 256, 25467, 256, 27566, 256, 31806, 256, 29557, 256, 20196, 256, - 22265, 256, 23527, 256, 23994, 256, 24604, 256, 29618, 256, 29801, 256, - 32666, 256, 32838, 256, 37428, 256, 38646, 256, 38728, 256, 38936, 256, - 20363, 256, 31150, 256, 37300, 256, 38584, 256, 24801, 256, 20102, 256, - 20698, 256, 23534, 256, 23615, 256, 26009, 256, 27138, 256, 29134, 256, - 30274, 256, 34044, 256, 36988, 256, 40845, 256, 26248, 256, 38446, 256, - 21129, 256, 26491, 256, 26611, 256, 27969, 256, 28316, 256, 29705, 256, - 30041, 256, 30827, 256, 32016, 256, 39006, 256, 20845, 256, 25134, 256, - 38520, 256, 20523, 256, 23833, 256, 28138, 256, 36650, 256, 24459, 256, - 24900, 256, 26647, 256, 29575, 256, 38534, 256, 21033, 256, 21519, 256, - 23653, 256, 26131, 256, 26446, 256, 26792, 256, 27877, 256, 29702, 256, - 30178, 256, 32633, 256, 35023, 256, 35041, 256, 37324, 256, 38626, 256, - 21311, 256, 28346, 256, 21533, 256, 29136, 256, 29848, 256, 34298, 256, - 38563, 256, 40023, 256, 40607, 256, 26519, 256, 28107, 256, 33256, 256, - 31435, 256, 31520, 256, 31890, 256, 29376, 256, 28825, 256, 35672, 256, - 20160, 256, 33590, 256, 21050, 256, 20999, 256, 24230, 256, 25299, 256, - 31958, 256, 23429, 256, 27934, 256, 26292, 256, 36667, 256, 34892, 256, - 38477, 256, 35211, 256, 24275, 256, 20800, 256, 21952, 256, 22618, 256, - 26228, 256, 20958, 256, 29482, 256, 30410, 256, 31036, 256, 31070, 256, - 31077, 256, 31119, 256, 38742, 256, 31934, 256, 32701, 256, 34322, 256, - 35576, 256, 36920, 256, 37117, 256, 39151, 256, 39164, 256, 39208, 256, - 40372, 256, 37086, 256, 38583, 256, 20398, 256, 20711, 256, 20813, 256, - 21193, 256, 21220, 256, 21329, 256, 21917, 256, 22022, 256, 22120, 256, - 22592, 256, 22696, 256, 23652, 256, 23662, 256, 24724, 256, 24936, 256, - 24974, 256, 25074, 256, 25935, 256, 26082, 256, 26257, 256, 26757, 256, - 28023, 256, 28186, 256, 28450, 256, 29038, 256, 29227, 256, 29730, 256, - 30865, 256, 31038, 256, 31049, 256, 31048, 256, 31056, 256, 31062, 256, - 31069, 256, 31117, 256, 31118, 256, 31296, 256, 31361, 256, 31680, 256, - 32244, 256, 32265, 256, 32321, 256, 32626, 256, 32773, 256, 33261, 256, - 33401, 256, 33401, 256, 33879, 256, 35088, 256, 35222, 256, 35585, 256, - 35641, 256, 36051, 256, 36104, 256, 36790, 256, 36920, 256, 38627, 256, - 38911, 256, 38971, 256, 24693, 256, 55376, 57070, 256, 33304, 256, 20006, - 256, 20917, 256, 20840, 256, 20352, 256, 20805, 256, 20864, 256, 21191, - 256, 21242, 256, 21917, 256, 21845, 256, 21913, 256, 21986, 256, 22618, - 256, 22707, 256, 22852, 256, 22868, 256, 23138, 256, 23336, 256, 24274, - 256, 24281, 256, 24425, 256, 24493, 256, 24792, 256, 24910, 256, 24840, - 256, 24974, 256, 24928, 256, 25074, 256, 25140, 256, 25540, 256, 25628, - 256, 25682, 256, 25942, 256, 26228, 256, 26391, 256, 26395, 256, 26454, - 256, 27513, 256, 27578, 256, 27969, 256, 28379, 256, 28363, 256, 28450, - 256, 28702, 256, 29038, 256, 30631, 256, 29237, 256, 29359, 256, 29482, - 256, 29809, 256, 29958, 256, 30011, 256, 30237, 256, 30239, 256, 30410, - 256, 30427, 256, 30452, 256, 30538, 256, 30528, 256, 30924, 256, 31409, - 256, 31680, 256, 31867, 256, 32091, 256, 32244, 256, 32574, 256, 32773, - 256, 33618, 256, 33775, 256, 34681, 256, 35137, 256, 35206, 256, 35222, - 256, 35519, 256, 35576, 256, 35531, 256, 35585, 256, 35582, 256, 35565, - 256, 35641, 256, 35722, 256, 36104, 256, 36664, 256, 36978, 256, 37273, - 256, 37494, 256, 38524, 256, 38627, 256, 38742, 256, 38875, 256, 38911, - 256, 38923, 256, 38971, 256, 39698, 256, 40860, 256, 55370, 56394, 256, - 55370, 56388, 256, 55372, 57301, 256, 15261, 256, 16408, 256, 16441, 256, - 55380, 56905, 256, 55383, 56528, 256, 55391, 57043, 256, 40771, 256, - 40846, 514, 102, 102, 514, 102, 105, 514, 102, 108, 770, 102, 102, 105, - 770, 102, 102, 108, 514, 383, 116, 514, 115, 116, 514, 1396, 1398, 514, - 1396, 1381, 514, 1396, 1387, 514, 1406, 1398, 514, 1396, 1389, 512, 1497, - 1460, 512, 1522, 1463, 262, 1506, 262, 1488, 262, 1491, 262, 1492, 262, - 1499, 262, 1500, 262, 1501, 262, 1512, 262, 1514, 262, 43, 512, 1513, - 1473, 512, 1513, 1474, 512, 64329, 1473, 512, 64329, 1474, 512, 1488, - 1463, 512, 1488, 1464, 512, 1488, 1468, 512, 1489, 1468, 512, 1490, 1468, - 512, 1491, 1468, 512, 1492, 1468, 512, 1493, 1468, 512, 1494, 1468, 512, - 1496, 1468, 512, 1497, 1468, 512, 1498, 1468, 512, 1499, 1468, 512, 1500, - 1468, 512, 1502, 1468, 512, 1504, 1468, 512, 1505, 1468, 512, 1507, 1468, - 512, 1508, 1468, 512, 1510, 1468, 512, 1511, 1468, 512, 1512, 1468, 512, - 1513, 1468, 512, 1514, 1468, 512, 1493, 1465, 512, 1489, 1471, 512, 1499, - 1471, 512, 1508, 1471, 514, 1488, 1500, 267, 1649, 268, 1649, 267, 1659, - 268, 1659, 269, 1659, 270, 1659, 267, 1662, 268, 1662, 269, 1662, 270, - 1662, 267, 1664, 268, 1664, 269, 1664, 270, 1664, 267, 1658, 268, 1658, - 269, 1658, 270, 1658, 267, 1663, 268, 1663, 269, 1663, 270, 1663, 267, - 1657, 268, 1657, 269, 1657, 270, 1657, 267, 1700, 268, 1700, 269, 1700, - 270, 1700, 267, 1702, 268, 1702, 269, 1702, 270, 1702, 267, 1668, 268, - 1668, 269, 1668, 270, 1668, 267, 1667, 268, 1667, 269, 1667, 270, 1667, - 267, 1670, 268, 1670, 269, 1670, 270, 1670, 267, 1671, 268, 1671, 269, - 1671, 270, 1671, 267, 1677, 268, 1677, 267, 1676, 268, 1676, 267, 1678, - 268, 1678, 267, 1672, 268, 1672, 267, 1688, 268, 1688, 267, 1681, 268, - 1681, 267, 1705, 268, 1705, 269, 1705, 270, 1705, 267, 1711, 268, 1711, - 269, 1711, 270, 1711, 267, 1715, 268, 1715, 269, 1715, 270, 1715, 267, - 1713, 268, 1713, 269, 1713, 270, 1713, 267, 1722, 268, 1722, 267, 1723, - 268, 1723, 269, 1723, 270, 1723, 267, 1728, 268, 1728, 267, 1729, 268, - 1729, 269, 1729, 270, 1729, 267, 1726, 268, 1726, 269, 1726, 270, 1726, - 267, 1746, 268, 1746, 267, 1747, 268, 1747, 267, 1709, 268, 1709, 269, - 1709, 270, 1709, 267, 1735, 268, 1735, 267, 1734, 268, 1734, 267, 1736, - 268, 1736, 267, 1655, 267, 1739, 268, 1739, 267, 1733, 268, 1733, 267, - 1737, 268, 1737, 267, 1744, 268, 1744, 269, 1744, 270, 1744, 269, 1609, - 270, 1609, 523, 1574, 1575, 524, 1574, 1575, 523, 1574, 1749, 524, 1574, - 1749, 523, 1574, 1608, 524, 1574, 1608, 523, 1574, 1735, 524, 1574, 1735, - 523, 1574, 1734, 524, 1574, 1734, 523, 1574, 1736, 524, 1574, 1736, 523, - 1574, 1744, 524, 1574, 1744, 525, 1574, 1744, 523, 1574, 1609, 524, 1574, - 1609, 525, 1574, 1609, 267, 1740, 268, 1740, 269, 1740, 270, 1740, 523, - 1574, 1580, 523, 1574, 1581, 523, 1574, 1605, 523, 1574, 1609, 523, 1574, - 1610, 523, 1576, 1580, 523, 1576, 1581, 523, 1576, 1582, 523, 1576, 1605, - 523, 1576, 1609, 523, 1576, 1610, 523, 1578, 1580, 523, 1578, 1581, 523, - 1578, 1582, 523, 1578, 1605, 523, 1578, 1609, 523, 1578, 1610, 523, 1579, - 1580, 523, 1579, 1605, 523, 1579, 1609, 523, 1579, 1610, 523, 1580, 1581, - 523, 1580, 1605, 523, 1581, 1580, 523, 1581, 1605, 523, 1582, 1580, 523, - 1582, 1581, 523, 1582, 1605, 523, 1587, 1580, 523, 1587, 1581, 523, 1587, - 1582, 523, 1587, 1605, 523, 1589, 1581, 523, 1589, 1605, 523, 1590, 1580, - 523, 1590, 1581, 523, 1590, 1582, 523, 1590, 1605, 523, 1591, 1581, 523, - 1591, 1605, 523, 1592, 1605, 523, 1593, 1580, 523, 1593, 1605, 523, 1594, - 1580, 523, 1594, 1605, 523, 1601, 1580, 523, 1601, 1581, 523, 1601, 1582, - 523, 1601, 1605, 523, 1601, 1609, 523, 1601, 1610, 523, 1602, 1581, 523, - 1602, 1605, 523, 1602, 1609, 523, 1602, 1610, 523, 1603, 1575, 523, 1603, - 1580, 523, 1603, 1581, 523, 1603, 1582, 523, 1603, 1604, 523, 1603, 1605, - 523, 1603, 1609, 523, 1603, 1610, 523, 1604, 1580, 523, 1604, 1581, 523, - 1604, 1582, 523, 1604, 1605, 523, 1604, 1609, 523, 1604, 1610, 523, 1605, - 1580, 523, 1605, 1581, 523, 1605, 1582, 523, 1605, 1605, 523, 1605, 1609, - 523, 1605, 1610, 523, 1606, 1580, 523, 1606, 1581, 523, 1606, 1582, 523, - 1606, 1605, 523, 1606, 1609, 523, 1606, 1610, 523, 1607, 1580, 523, 1607, - 1605, 523, 1607, 1609, 523, 1607, 1610, 523, 1610, 1580, 523, 1610, 1581, - 523, 1610, 1582, 523, 1610, 1605, 523, 1610, 1609, 523, 1610, 1610, 523, - 1584, 1648, 523, 1585, 1648, 523, 1609, 1648, 779, 32, 1612, 1617, 779, - 32, 1613, 1617, 779, 32, 1614, 1617, 779, 32, 1615, 1617, 779, 32, 1616, - 1617, 779, 32, 1617, 1648, 524, 1574, 1585, 524, 1574, 1586, 524, 1574, - 1605, 524, 1574, 1606, 524, 1574, 1609, 524, 1574, 1610, 524, 1576, 1585, - 524, 1576, 1586, 524, 1576, 1605, 524, 1576, 1606, 524, 1576, 1609, 524, - 1576, 1610, 524, 1578, 1585, 524, 1578, 1586, 524, 1578, 1605, 524, 1578, - 1606, 524, 1578, 1609, 524, 1578, 1610, 524, 1579, 1585, 524, 1579, 1586, - 524, 1579, 1605, 524, 1579, 1606, 524, 1579, 1609, 524, 1579, 1610, 524, - 1601, 1609, 524, 1601, 1610, 524, 1602, 1609, 524, 1602, 1610, 524, 1603, - 1575, 524, 1603, 1604, 524, 1603, 1605, 524, 1603, 1609, 524, 1603, 1610, - 524, 1604, 1605, 524, 1604, 1609, 524, 1604, 1610, 524, 1605, 1575, 524, - 1605, 1605, 524, 1606, 1585, 524, 1606, 1586, 524, 1606, 1605, 524, 1606, - 1606, 524, 1606, 1609, 524, 1606, 1610, 524, 1609, 1648, 524, 1610, 1585, - 524, 1610, 1586, 524, 1610, 1605, 524, 1610, 1606, 524, 1610, 1609, 524, - 1610, 1610, 525, 1574, 1580, 525, 1574, 1581, 525, 1574, 1582, 525, 1574, - 1605, 525, 1574, 1607, 525, 1576, 1580, 525, 1576, 1581, 525, 1576, 1582, - 525, 1576, 1605, 525, 1576, 1607, 525, 1578, 1580, 525, 1578, 1581, 525, - 1578, 1582, 525, 1578, 1605, 525, 1578, 1607, 525, 1579, 1605, 525, 1580, - 1581, 525, 1580, 1605, 525, 1581, 1580, 525, 1581, 1605, 525, 1582, 1580, - 525, 1582, 1605, 525, 1587, 1580, 525, 1587, 1581, 525, 1587, 1582, 525, - 1587, 1605, 525, 1589, 1581, 525, 1589, 1582, 525, 1589, 1605, 525, 1590, - 1580, 525, 1590, 1581, 525, 1590, 1582, 525, 1590, 1605, 525, 1591, 1581, - 525, 1592, 1605, 525, 1593, 1580, 525, 1593, 1605, 525, 1594, 1580, 525, - 1594, 1605, 525, 1601, 1580, 525, 1601, 1581, 525, 1601, 1582, 525, 1601, - 1605, 525, 1602, 1581, 525, 1602, 1605, 525, 1603, 1580, 525, 1603, 1581, - 525, 1603, 1582, 525, 1603, 1604, 525, 1603, 1605, 525, 1604, 1580, 525, - 1604, 1581, 525, 1604, 1582, 525, 1604, 1605, 525, 1604, 1607, 525, 1605, - 1580, 525, 1605, 1581, 525, 1605, 1582, 525, 1605, 1605, 525, 1606, 1580, - 525, 1606, 1581, 525, 1606, 1582, 525, 1606, 1605, 525, 1606, 1607, 525, - 1607, 1580, 525, 1607, 1605, 525, 1607, 1648, 525, 1610, 1580, 525, 1610, - 1581, 525, 1610, 1582, 525, 1610, 1605, 525, 1610, 1607, 526, 1574, 1605, - 526, 1574, 1607, 526, 1576, 1605, 526, 1576, 1607, 526, 1578, 1605, 526, - 1578, 1607, 526, 1579, 1605, 526, 1579, 1607, 526, 1587, 1605, 526, 1587, - 1607, 526, 1588, 1605, 526, 1588, 1607, 526, 1603, 1604, 526, 1603, 1605, - 526, 1604, 1605, 526, 1606, 1605, 526, 1606, 1607, 526, 1610, 1605, 526, - 1610, 1607, 782, 1600, 1614, 1617, 782, 1600, 1615, 1617, 782, 1600, - 1616, 1617, 523, 1591, 1609, 523, 1591, 1610, 523, 1593, 1609, 523, 1593, - 1610, 523, 1594, 1609, 523, 1594, 1610, 523, 1587, 1609, 523, 1587, 1610, - 523, 1588, 1609, 523, 1588, 1610, 523, 1581, 1609, 523, 1581, 1610, 523, - 1580, 1609, 523, 1580, 1610, 523, 1582, 1609, 523, 1582, 1610, 523, 1589, - 1609, 523, 1589, 1610, 523, 1590, 1609, 523, 1590, 1610, 523, 1588, 1580, - 523, 1588, 1581, 523, 1588, 1582, 523, 1588, 1605, 523, 1588, 1585, 523, - 1587, 1585, 523, 1589, 1585, 523, 1590, 1585, 524, 1591, 1609, 524, 1591, - 1610, 524, 1593, 1609, 524, 1593, 1610, 524, 1594, 1609, 524, 1594, 1610, - 524, 1587, 1609, 524, 1587, 1610, 524, 1588, 1609, 524, 1588, 1610, 524, - 1581, 1609, 524, 1581, 1610, 524, 1580, 1609, 524, 1580, 1610, 524, 1582, - 1609, 524, 1582, 1610, 524, 1589, 1609, 524, 1589, 1610, 524, 1590, 1609, - 524, 1590, 1610, 524, 1588, 1580, 524, 1588, 1581, 524, 1588, 1582, 524, - 1588, 1605, 524, 1588, 1585, 524, 1587, 1585, 524, 1589, 1585, 524, 1590, - 1585, 525, 1588, 1580, 525, 1588, 1581, 525, 1588, 1582, 525, 1588, 1605, - 525, 1587, 1607, 525, 1588, 1607, 525, 1591, 1605, 526, 1587, 1580, 526, - 1587, 1581, 526, 1587, 1582, 526, 1588, 1580, 526, 1588, 1581, 526, 1588, - 1582, 526, 1591, 1605, 526, 1592, 1605, 524, 1575, 1611, 523, 1575, 1611, - 781, 1578, 1580, 1605, 780, 1578, 1581, 1580, 781, 1578, 1581, 1580, 781, - 1578, 1581, 1605, 781, 1578, 1582, 1605, 781, 1578, 1605, 1580, 781, - 1578, 1605, 1581, 781, 1578, 1605, 1582, 780, 1580, 1605, 1581, 781, - 1580, 1605, 1581, 780, 1581, 1605, 1610, 780, 1581, 1605, 1609, 781, - 1587, 1581, 1580, 781, 1587, 1580, 1581, 780, 1587, 1580, 1609, 780, - 1587, 1605, 1581, 781, 1587, 1605, 1581, 781, 1587, 1605, 1580, 780, - 1587, 1605, 1605, 781, 1587, 1605, 1605, 780, 1589, 1581, 1581, 781, - 1589, 1581, 1581, 780, 1589, 1605, 1605, 780, 1588, 1581, 1605, 781, - 1588, 1581, 1605, 780, 1588, 1580, 1610, 780, 1588, 1605, 1582, 781, - 1588, 1605, 1582, 780, 1588, 1605, 1605, 781, 1588, 1605, 1605, 780, - 1590, 1581, 1609, 780, 1590, 1582, 1605, 781, 1590, 1582, 1605, 780, - 1591, 1605, 1581, 781, 1591, 1605, 1581, 781, 1591, 1605, 1605, 780, - 1591, 1605, 1610, 780, 1593, 1580, 1605, 780, 1593, 1605, 1605, 781, - 1593, 1605, 1605, 780, 1593, 1605, 1609, 780, 1594, 1605, 1605, 780, - 1594, 1605, 1610, 780, 1594, 1605, 1609, 780, 1601, 1582, 1605, 781, - 1601, 1582, 1605, 780, 1602, 1605, 1581, 780, 1602, 1605, 1605, 780, - 1604, 1581, 1605, 780, 1604, 1581, 1610, 780, 1604, 1581, 1609, 781, - 1604, 1580, 1580, 780, 1604, 1580, 1580, 780, 1604, 1582, 1605, 781, - 1604, 1582, 1605, 780, 1604, 1605, 1581, 781, 1604, 1605, 1581, 781, - 1605, 1581, 1580, 781, 1605, 1581, 1605, 780, 1605, 1581, 1610, 781, - 1605, 1580, 1581, 781, 1605, 1580, 1605, 781, 1605, 1582, 1580, 781, - 1605, 1582, 1605, 781, 1605, 1580, 1582, 781, 1607, 1605, 1580, 781, - 1607, 1605, 1605, 781, 1606, 1581, 1605, 780, 1606, 1581, 1609, 780, - 1606, 1580, 1605, 781, 1606, 1580, 1605, 780, 1606, 1580, 1609, 780, - 1606, 1605, 1610, 780, 1606, 1605, 1609, 780, 1610, 1605, 1605, 781, - 1610, 1605, 1605, 780, 1576, 1582, 1610, 780, 1578, 1580, 1610, 780, - 1578, 1580, 1609, 780, 1578, 1582, 1610, 780, 1578, 1582, 1609, 780, - 1578, 1605, 1610, 780, 1578, 1605, 1609, 780, 1580, 1605, 1610, 780, - 1580, 1581, 1609, 780, 1580, 1605, 1609, 780, 1587, 1582, 1609, 780, - 1589, 1581, 1610, 780, 1588, 1581, 1610, 780, 1590, 1581, 1610, 780, - 1604, 1580, 1610, 780, 1604, 1605, 1610, 780, 1610, 1581, 1610, 780, - 1610, 1580, 1610, 780, 1610, 1605, 1610, 780, 1605, 1605, 1610, 780, - 1602, 1605, 1610, 780, 1606, 1581, 1610, 781, 1602, 1605, 1581, 781, - 1604, 1581, 1605, 780, 1593, 1605, 1610, 780, 1603, 1605, 1610, 781, - 1606, 1580, 1581, 780, 1605, 1582, 1610, 781, 1604, 1580, 1605, 780, - 1603, 1605, 1605, 780, 1604, 1580, 1605, 780, 1606, 1580, 1581, 780, - 1580, 1581, 1610, 780, 1581, 1580, 1610, 780, 1605, 1580, 1610, 780, - 1601, 1605, 1610, 780, 1576, 1581, 1610, 781, 1603, 1605, 1605, 781, - 1593, 1580, 1605, 781, 1589, 1605, 1605, 780, 1587, 1582, 1610, 780, - 1606, 1580, 1610, 779, 1589, 1604, 1746, 779, 1602, 1604, 1746, 1035, - 1575, 1604, 1604, 1607, 1035, 1575, 1603, 1576, 1585, 1035, 1605, 1581, - 1605, 1583, 1035, 1589, 1604, 1593, 1605, 1035, 1585, 1587, 1608, 1604, - 1035, 1593, 1604, 1610, 1607, 1035, 1608, 1587, 1604, 1605, 779, 1589, - 1604, 1609, 4619, 1589, 1604, 1609, 32, 1575, 1604, 1604, 1607, 32, 1593, - 1604, 1610, 1607, 32, 1608, 1587, 1604, 1605, 2059, 1580, 1604, 32, 1580, - 1604, 1575, 1604, 1607, 1035, 1585, 1740, 1575, 1604, 265, 44, 265, - 12289, 265, 12290, 265, 58, 265, 59, 265, 33, 265, 63, 265, 12310, 265, - 12311, 265, 8230, 265, 8229, 265, 8212, 265, 8211, 265, 95, 265, 95, 265, - 40, 265, 41, 265, 123, 265, 125, 265, 12308, 265, 12309, 265, 12304, 265, - 12305, 265, 12298, 265, 12299, 265, 12296, 265, 12297, 265, 12300, 265, - 12301, 265, 12302, 265, 12303, 265, 91, 265, 93, 258, 8254, 258, 8254, - 258, 8254, 258, 8254, 258, 95, 258, 95, 258, 95, 271, 44, 271, 12289, - 271, 46, 271, 59, 271, 58, 271, 63, 271, 33, 271, 8212, 271, 40, 271, 41, - 271, 123, 271, 125, 271, 12308, 271, 12309, 271, 35, 271, 38, 271, 42, - 271, 43, 271, 45, 271, 60, 271, 62, 271, 61, 271, 92, 271, 36, 271, 37, - 271, 64, 523, 32, 1611, 526, 1600, 1611, 523, 32, 1612, 523, 32, 1613, - 523, 32, 1614, 526, 1600, 1614, 523, 32, 1615, 526, 1600, 1615, 523, 32, - 1616, 526, 1600, 1616, 523, 32, 1617, 526, 1600, 1617, 523, 32, 1618, - 526, 1600, 1618, 267, 1569, 267, 1570, 268, 1570, 267, 1571, 268, 1571, - 267, 1572, 268, 1572, 267, 1573, 268, 1573, 267, 1574, 268, 1574, 269, - 1574, 270, 1574, 267, 1575, 268, 1575, 267, 1576, 268, 1576, 269, 1576, - 270, 1576, 267, 1577, 268, 1577, 267, 1578, 268, 1578, 269, 1578, 270, - 1578, 267, 1579, 268, 1579, 269, 1579, 270, 1579, 267, 1580, 268, 1580, - 269, 1580, 270, 1580, 267, 1581, 268, 1581, 269, 1581, 270, 1581, 267, - 1582, 268, 1582, 269, 1582, 270, 1582, 267, 1583, 268, 1583, 267, 1584, - 268, 1584, 267, 1585, 268, 1585, 267, 1586, 268, 1586, 267, 1587, 268, - 1587, 269, 1587, 270, 1587, 267, 1588, 268, 1588, 269, 1588, 270, 1588, - 267, 1589, 268, 1589, 269, 1589, 270, 1589, 267, 1590, 268, 1590, 269, - 1590, 270, 1590, 267, 1591, 268, 1591, 269, 1591, 270, 1591, 267, 1592, - 268, 1592, 269, 1592, 270, 1592, 267, 1593, 268, 1593, 269, 1593, 270, - 1593, 267, 1594, 268, 1594, 269, 1594, 270, 1594, 267, 1601, 268, 1601, - 269, 1601, 270, 1601, 267, 1602, 268, 1602, 269, 1602, 270, 1602, 267, - 1603, 268, 1603, 269, 1603, 270, 1603, 267, 1604, 268, 1604, 269, 1604, - 270, 1604, 267, 1605, 268, 1605, 269, 1605, 270, 1605, 267, 1606, 268, - 1606, 269, 1606, 270, 1606, 267, 1607, 268, 1607, 269, 1607, 270, 1607, - 267, 1608, 268, 1608, 267, 1609, 268, 1609, 267, 1610, 268, 1610, 269, - 1610, 270, 1610, 523, 1604, 1570, 524, 1604, 1570, 523, 1604, 1571, 524, - 1604, 1571, 523, 1604, 1573, 524, 1604, 1573, 523, 1604, 1575, 524, 1604, - 1575, 264, 33, 264, 34, 264, 35, 264, 36, 264, 37, 264, 38, 264, 39, 264, - 40, 264, 41, 264, 42, 264, 43, 264, 44, 264, 45, 264, 46, 264, 47, 264, - 48, 264, 49, 264, 50, 264, 51, 264, 52, 264, 53, 264, 54, 264, 55, 264, - 56, 264, 57, 264, 58, 264, 59, 264, 60, 264, 61, 264, 62, 264, 63, 264, - 64, 264, 65, 264, 66, 264, 67, 264, 68, 264, 69, 264, 70, 264, 71, 264, - 72, 264, 73, 264, 74, 264, 75, 264, 76, 264, 77, 264, 78, 264, 79, 264, - 80, 264, 81, 264, 82, 264, 83, 264, 84, 264, 85, 264, 86, 264, 87, 264, - 88, 264, 89, 264, 90, 264, 91, 264, 92, 264, 93, 264, 94, 264, 95, 264, - 96, 264, 97, 264, 98, 264, 99, 264, 100, 264, 101, 264, 102, 264, 103, - 264, 104, 264, 105, 264, 106, 264, 107, 264, 108, 264, 109, 264, 110, - 264, 111, 264, 112, 264, 113, 264, 114, 264, 115, 264, 116, 264, 117, - 264, 118, 264, 119, 264, 120, 264, 121, 264, 122, 264, 123, 264, 124, - 264, 125, 264, 126, 264, 10629, 264, 10630, 272, 12290, 272, 12300, 272, - 12301, 272, 12289, 272, 12539, 272, 12530, 272, 12449, 272, 12451, 272, - 12453, 272, 12455, 272, 12457, 272, 12515, 272, 12517, 272, 12519, 272, - 12483, 272, 12540, 272, 12450, 272, 12452, 272, 12454, 272, 12456, 272, - 12458, 272, 12459, 272, 12461, 272, 12463, 272, 12465, 272, 12467, 272, - 12469, 272, 12471, 272, 12473, 272, 12475, 272, 12477, 272, 12479, 272, - 12481, 272, 12484, 272, 12486, 272, 12488, 272, 12490, 272, 12491, 272, - 12492, 272, 12493, 272, 12494, 272, 12495, 272, 12498, 272, 12501, 272, - 12504, 272, 12507, 272, 12510, 272, 12511, 272, 12512, 272, 12513, 272, - 12514, 272, 12516, 272, 12518, 272, 12520, 272, 12521, 272, 12522, 272, - 12523, 272, 12524, 272, 12525, 272, 12527, 272, 12531, 272, 12441, 272, - 12442, 272, 12644, 272, 12593, 272, 12594, 272, 12595, 272, 12596, 272, - 12597, 272, 12598, 272, 12599, 272, 12600, 272, 12601, 272, 12602, 272, - 12603, 272, 12604, 272, 12605, 272, 12606, 272, 12607, 272, 12608, 272, - 12609, 272, 12610, 272, 12611, 272, 12612, 272, 12613, 272, 12614, 272, - 12615, 272, 12616, 272, 12617, 272, 12618, 272, 12619, 272, 12620, 272, - 12621, 272, 12622, 272, 12623, 272, 12624, 272, 12625, 272, 12626, 272, - 12627, 272, 12628, 272, 12629, 272, 12630, 272, 12631, 272, 12632, 272, - 12633, 272, 12634, 272, 12635, 272, 12636, 272, 12637, 272, 12638, 272, - 12639, 272, 12640, 272, 12641, 272, 12642, 272, 12643, 264, 162, 264, - 163, 264, 172, 264, 175, 264, 166, 264, 165, 264, 8361, 272, 9474, 272, - 8592, 272, 8593, 272, 8594, 272, 8595, 272, 9632, 272, 9675, 512, 55300, - 56473, 55300, 56506, 512, 55300, 56475, 55300, 56506, 512, 55300, 56485, - 55300, 56506, 512, 55300, 56625, 55300, 56615, 512, 55300, 56626, 55300, - 56615, 512, 55300, 57159, 55300, 57150, 512, 55300, 57159, 55300, 57175, - 512, 55301, 56505, 55301, 56506, 512, 55301, 56505, 55301, 56496, 512, - 55301, 56505, 55301, 56509, 512, 55301, 56760, 55301, 56751, 512, 55301, - 56761, 55301, 56751, 512, 55348, 56663, 55348, 56677, 512, 55348, 56664, - 55348, 56677, 512, 55348, 56671, 55348, 56686, 512, 55348, 56671, 55348, - 56687, 512, 55348, 56671, 55348, 56688, 512, 55348, 56671, 55348, 56689, - 512, 55348, 56671, 55348, 56690, 512, 55348, 56761, 55348, 56677, 512, - 55348, 56762, 55348, 56677, 512, 55348, 56763, 55348, 56686, 512, 55348, - 56764, 55348, 56686, 512, 55348, 56763, 55348, 56687, 512, 55348, 56764, - 55348, 56687, 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, - 71, 262, 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, - 79, 262, 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, - 87, 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, - 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, - 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, - 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, - 122, 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, - 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, - 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, - 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, - 102, 262, 103, 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, - 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, - 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, - 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, - 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, - 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, - 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, - 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, - 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, - 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 67, 262, 68, - 262, 71, 262, 74, 262, 75, 262, 78, 262, 79, 262, 80, 262, 81, 262, 83, - 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, - 262, 98, 262, 99, 262, 100, 262, 102, 262, 104, 262, 105, 262, 106, 262, - 107, 262, 108, 262, 109, 262, 110, 262, 112, 262, 113, 262, 114, 262, - 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, - 122, 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, - 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, - 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, - 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, - 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, - 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, - 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, - 262, 66, 262, 68, 262, 69, 262, 70, 262, 71, 262, 74, 262, 75, 262, 76, - 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 83, 262, 84, 262, 85, - 262, 86, 262, 87, 262, 88, 262, 89, 262, 97, 262, 98, 262, 99, 262, 100, - 262, 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, - 262, 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, - 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, - 262, 122, 262, 65, 262, 66, 262, 68, 262, 69, 262, 70, 262, 71, 262, 73, - 262, 74, 262, 75, 262, 76, 262, 77, 262, 79, 262, 83, 262, 84, 262, 85, - 262, 86, 262, 87, 262, 88, 262, 89, 262, 97, 262, 98, 262, 99, 262, 100, - 262, 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, - 262, 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, - 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, - 262, 122, 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, - 262, 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, - 262, 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, - 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, - 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, - 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, - 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, - 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, - 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, - 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, - 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, - 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, - 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, - 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, - 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, - 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, - 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, - 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, - 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, - 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, - 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, - 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, - 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, - 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, - 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, - 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, - 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, - 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, 262, 67, - 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, 262, 75, - 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, 262, 83, - 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, - 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, 104, 262, - 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, 111, 262, - 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, 262, - 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, 262, 67, 262, 68, - 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, 262, 75, 262, 76, - 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, 262, 83, 262, 84, - 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, - 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, - 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, - 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, - 120, 262, 121, 262, 122, 262, 305, 262, 567, 262, 913, 262, 914, 262, - 915, 262, 916, 262, 917, 262, 918, 262, 919, 262, 920, 262, 921, 262, - 922, 262, 923, 262, 924, 262, 925, 262, 926, 262, 927, 262, 928, 262, - 929, 262, 1012, 262, 931, 262, 932, 262, 933, 262, 934, 262, 935, 262, - 936, 262, 937, 262, 8711, 262, 945, 262, 946, 262, 947, 262, 948, 262, - 949, 262, 950, 262, 951, 262, 952, 262, 953, 262, 954, 262, 955, 262, - 956, 262, 957, 262, 958, 262, 959, 262, 960, 262, 961, 262, 962, 262, - 963, 262, 964, 262, 965, 262, 966, 262, 967, 262, 968, 262, 969, 262, - 8706, 262, 1013, 262, 977, 262, 1008, 262, 981, 262, 1009, 262, 982, 262, - 913, 262, 914, 262, 915, 262, 916, 262, 917, 262, 918, 262, 919, 262, - 920, 262, 921, 262, 922, 262, 923, 262, 924, 262, 925, 262, 926, 262, - 927, 262, 928, 262, 929, 262, 1012, 262, 931, 262, 932, 262, 933, 262, - 934, 262, 935, 262, 936, 262, 937, 262, 8711, 262, 945, 262, 946, 262, - 947, 262, 948, 262, 949, 262, 950, 262, 951, 262, 952, 262, 953, 262, - 954, 262, 955, 262, 956, 262, 957, 262, 958, 262, 959, 262, 960, 262, - 961, 262, 962, 262, 963, 262, 964, 262, 965, 262, 966, 262, 967, 262, - 968, 262, 969, 262, 8706, 262, 1013, 262, 977, 262, 1008, 262, 981, 262, - 1009, 262, 982, 262, 913, 262, 914, 262, 915, 262, 916, 262, 917, 262, - 918, 262, 919, 262, 920, 262, 921, 262, 922, 262, 923, 262, 924, 262, - 925, 262, 926, 262, 927, 262, 928, 262, 929, 262, 1012, 262, 931, 262, - 932, 262, 933, 262, 934, 262, 935, 262, 936, 262, 937, 262, 8711, 262, - 945, 262, 946, 262, 947, 262, 948, 262, 949, 262, 950, 262, 951, 262, - 952, 262, 953, 262, 954, 262, 955, 262, 956, 262, 957, 262, 958, 262, - 959, 262, 960, 262, 961, 262, 962, 262, 963, 262, 964, 262, 965, 262, - 966, 262, 967, 262, 968, 262, 969, 262, 8706, 262, 1013, 262, 977, 262, - 1008, 262, 981, 262, 1009, 262, 982, 262, 913, 262, 914, 262, 915, 262, - 916, 262, 917, 262, 918, 262, 919, 262, 920, 262, 921, 262, 922, 262, - 923, 262, 924, 262, 925, 262, 926, 262, 927, 262, 928, 262, 929, 262, - 1012, 262, 931, 262, 932, 262, 933, 262, 934, 262, 935, 262, 936, 262, - 937, 262, 8711, 262, 945, 262, 946, 262, 947, 262, 948, 262, 949, 262, - 950, 262, 951, 262, 952, 262, 953, 262, 954, 262, 955, 262, 956, 262, - 957, 262, 958, 262, 959, 262, 960, 262, 961, 262, 962, 262, 963, 262, - 964, 262, 965, 262, 966, 262, 967, 262, 968, 262, 969, 262, 8706, 262, - 1013, 262, 977, 262, 1008, 262, 981, 262, 1009, 262, 982, 262, 913, 262, - 914, 262, 915, 262, 916, 262, 917, 262, 918, 262, 919, 262, 920, 262, - 921, 262, 922, 262, 923, 262, 924, 262, 925, 262, 926, 262, 927, 262, - 928, 262, 929, 262, 1012, 262, 931, 262, 932, 262, 933, 262, 934, 262, - 935, 262, 936, 262, 937, 262, 8711, 262, 945, 262, 946, 262, 947, 262, - 948, 262, 949, 262, 950, 262, 951, 262, 952, 262, 953, 262, 954, 262, - 955, 262, 956, 262, 957, 262, 958, 262, 959, 262, 960, 262, 961, 262, - 962, 262, 963, 262, 964, 262, 965, 262, 966, 262, 967, 262, 968, 262, - 969, 262, 8706, 262, 1013, 262, 977, 262, 1008, 262, 981, 262, 1009, 262, - 982, 262, 988, 262, 989, 262, 48, 262, 49, 262, 50, 262, 51, 262, 52, - 262, 53, 262, 54, 262, 55, 262, 56, 262, 57, 262, 48, 262, 49, 262, 50, - 262, 51, 262, 52, 262, 53, 262, 54, 262, 55, 262, 56, 262, 57, 262, 48, - 262, 49, 262, 50, 262, 51, 262, 52, 262, 53, 262, 54, 262, 55, 262, 56, - 262, 57, 262, 48, 262, 49, 262, 50, 262, 51, 262, 52, 262, 53, 262, 54, - 262, 55, 262, 56, 262, 57, 262, 48, 262, 49, 262, 50, 262, 51, 262, 52, - 262, 53, 262, 54, 262, 55, 262, 56, 262, 57, 262, 1575, 262, 1576, 262, - 1580, 262, 1583, 262, 1608, 262, 1586, 262, 1581, 262, 1591, 262, 1610, - 262, 1603, 262, 1604, 262, 1605, 262, 1606, 262, 1587, 262, 1593, 262, - 1601, 262, 1589, 262, 1602, 262, 1585, 262, 1588, 262, 1578, 262, 1579, - 262, 1582, 262, 1584, 262, 1590, 262, 1592, 262, 1594, 262, 1646, 262, - 1722, 262, 1697, 262, 1647, 262, 1576, 262, 1580, 262, 1607, 262, 1581, - 262, 1610, 262, 1603, 262, 1604, 262, 1605, 262, 1606, 262, 1587, 262, - 1593, 262, 1601, 262, 1589, 262, 1602, 262, 1588, 262, 1578, 262, 1579, - 262, 1582, 262, 1590, 262, 1594, 262, 1580, 262, 1581, 262, 1610, 262, - 1604, 262, 1606, 262, 1587, 262, 1593, 262, 1589, 262, 1602, 262, 1588, - 262, 1582, 262, 1590, 262, 1594, 262, 1722, 262, 1647, 262, 1576, 262, - 1580, 262, 1607, 262, 1581, 262, 1591, 262, 1610, 262, 1603, 262, 1605, - 262, 1606, 262, 1587, 262, 1593, 262, 1601, 262, 1589, 262, 1602, 262, - 1588, 262, 1578, 262, 1579, 262, 1582, 262, 1590, 262, 1592, 262, 1594, - 262, 1646, 262, 1697, 262, 1575, 262, 1576, 262, 1580, 262, 1583, 262, - 1607, 262, 1608, 262, 1586, 262, 1581, 262, 1591, 262, 1610, 262, 1604, - 262, 1605, 262, 1606, 262, 1587, 262, 1593, 262, 1601, 262, 1589, 262, - 1602, 262, 1585, 262, 1588, 262, 1578, 262, 1579, 262, 1582, 262, 1584, - 262, 1590, 262, 1592, 262, 1594, 262, 1576, 262, 1580, 262, 1583, 262, - 1608, 262, 1586, 262, 1581, 262, 1591, 262, 1610, 262, 1604, 262, 1605, - 262, 1606, 262, 1587, 262, 1593, 262, 1601, 262, 1589, 262, 1602, 262, - 1585, 262, 1588, 262, 1578, 262, 1579, 262, 1582, 262, 1584, 262, 1590, - 262, 1592, 262, 1594, 514, 48, 46, 514, 48, 44, 514, 49, 44, 514, 50, 44, - 514, 51, 44, 514, 52, 44, 514, 53, 44, 514, 54, 44, 514, 55, 44, 514, 56, - 44, 514, 57, 44, 770, 40, 65, 41, 770, 40, 66, 41, 770, 40, 67, 41, 770, - 40, 68, 41, 770, 40, 69, 41, 770, 40, 70, 41, 770, 40, 71, 41, 770, 40, - 72, 41, 770, 40, 73, 41, 770, 40, 74, 41, 770, 40, 75, 41, 770, 40, 76, - 41, 770, 40, 77, 41, 770, 40, 78, 41, 770, 40, 79, 41, 770, 40, 80, 41, - 770, 40, 81, 41, 770, 40, 82, 41, 770, 40, 83, 41, 770, 40, 84, 41, 770, - 40, 85, 41, 770, 40, 86, 41, 770, 40, 87, 41, 770, 40, 88, 41, 770, 40, - 89, 41, 770, 40, 90, 41, 770, 12308, 83, 12309, 263, 67, 263, 82, 519, - 67, 68, 519, 87, 90, 266, 65, 266, 66, 266, 67, 266, 68, 266, 69, 266, - 70, 266, 71, 266, 72, 266, 73, 266, 74, 266, 75, 266, 76, 266, 77, 266, - 78, 266, 79, 266, 80, 266, 81, 266, 82, 266, 83, 266, 84, 266, 85, 266, - 86, 266, 87, 266, 88, 266, 89, 266, 90, 522, 72, 86, 522, 77, 86, 522, - 83, 68, 522, 83, 83, 778, 80, 80, 86, 522, 87, 67, 515, 77, 67, 515, 77, - 68, 522, 68, 74, 522, 12411, 12363, 522, 12467, 12467, 266, 12469, 266, - 25163, 266, 23383, 266, 21452, 266, 12487, 266, 20108, 266, 22810, 266, - 35299, 266, 22825, 266, 20132, 266, 26144, 266, 28961, 266, 26009, 266, - 21069, 266, 24460, 266, 20877, 266, 26032, 266, 21021, 266, 32066, 266, - 29983, 266, 36009, 266, 22768, 266, 21561, 266, 28436, 266, 25237, 266, - 25429, 266, 19968, 266, 19977, 266, 36938, 266, 24038, 266, 20013, 266, - 21491, 266, 25351, 266, 36208, 266, 25171, 266, 31105, 266, 31354, 266, - 21512, 266, 28288, 266, 26377, 266, 26376, 266, 30003, 266, 21106, 266, - 21942, 266, 37197, 770, 12308, 26412, 12309, 770, 12308, 19977, 12309, - 770, 12308, 20108, 12309, 770, 12308, 23433, 12309, 770, 12308, 28857, - 12309, 770, 12308, 25171, 12309, 770, 12308, 30423, 12309, 770, 12308, - 21213, 12309, 770, 12308, 25943, 12309, 263, 24471, 263, 21487, 256, - 20029, 256, 20024, 256, 20033, 256, 55360, 56610, 256, 20320, 256, 20398, - 256, 20411, 256, 20482, 256, 20602, 256, 20633, 256, 20711, 256, 20687, - 256, 13470, 256, 55361, 56890, 256, 20813, 256, 20820, 256, 20836, 256, - 20855, 256, 55361, 56604, 256, 13497, 256, 20839, 256, 20877, 256, 55361, - 56651, 256, 20887, 256, 20900, 256, 20172, 256, 20908, 256, 20917, 256, - 55396, 56799, 256, 20981, 256, 20995, 256, 13535, 256, 21051, 256, 21062, - 256, 21106, 256, 21111, 256, 13589, 256, 21191, 256, 21193, 256, 21220, - 256, 21242, 256, 21253, 256, 21254, 256, 21271, 256, 21321, 256, 21329, - 256, 21338, 256, 21363, 256, 21373, 256, 21375, 256, 21375, 256, 21375, - 256, 55362, 56876, 256, 28784, 256, 21450, 256, 21471, 256, 55362, 57187, - 256, 21483, 256, 21489, 256, 21510, 256, 21662, 256, 21560, 256, 21576, - 256, 21608, 256, 21666, 256, 21750, 256, 21776, 256, 21843, 256, 21859, - 256, 21892, 256, 21892, 256, 21913, 256, 21931, 256, 21939, 256, 21954, - 256, 22294, 256, 22022, 256, 22295, 256, 22097, 256, 22132, 256, 20999, - 256, 22766, 256, 22478, 256, 22516, 256, 22541, 256, 22411, 256, 22578, - 256, 22577, 256, 22700, 256, 55365, 56548, 256, 22770, 256, 22775, 256, - 22790, 256, 22810, 256, 22818, 256, 22882, 256, 55365, 57000, 256, 55365, - 57066, 256, 23020, 256, 23067, 256, 23079, 256, 23000, 256, 23142, 256, - 14062, 256, 14076, 256, 23304, 256, 23358, 256, 23358, 256, 55366, 56776, - 256, 23491, 256, 23512, 256, 23527, 256, 23539, 256, 55366, 57112, 256, - 23551, 256, 23558, 256, 24403, 256, 23586, 256, 14209, 256, 23648, 256, - 23662, 256, 23744, 256, 23693, 256, 55367, 56804, 256, 23875, 256, 55367, - 56806, 256, 23918, 256, 23915, 256, 23932, 256, 24033, 256, 24034, 256, - 14383, 256, 24061, 256, 24104, 256, 24125, 256, 24169, 256, 14434, 256, - 55368, 56707, 256, 14460, 256, 24240, 256, 24243, 256, 24246, 256, 24266, - 256, 55400, 57234, 256, 24318, 256, 55368, 57137, 256, 55368, 57137, 256, - 33281, 256, 24354, 256, 24354, 256, 14535, 256, 55372, 57016, 256, 55384, - 56794, 256, 24418, 256, 24427, 256, 14563, 256, 24474, 256, 24525, 256, - 24535, 256, 24569, 256, 24705, 256, 14650, 256, 14620, 256, 24724, 256, - 55369, 57044, 256, 24775, 256, 24904, 256, 24908, 256, 24910, 256, 24908, - 256, 24954, 256, 24974, 256, 25010, 256, 24996, 256, 25007, 256, 25054, - 256, 25074, 256, 25078, 256, 25104, 256, 25115, 256, 25181, 256, 25265, - 256, 25300, 256, 25424, 256, 55370, 57100, 256, 25405, 256, 25340, 256, - 25448, 256, 25475, 256, 25572, 256, 55370, 57329, 256, 25634, 256, 25541, - 256, 25513, 256, 14894, 256, 25705, 256, 25726, 256, 25757, 256, 25719, - 256, 14956, 256, 25935, 256, 25964, 256, 55372, 56330, 256, 26083, 256, - 26360, 256, 26185, 256, 15129, 256, 26257, 256, 15112, 256, 15076, 256, - 20882, 256, 20885, 256, 26368, 256, 26268, 256, 32941, 256, 17369, 256, - 26391, 256, 26395, 256, 26401, 256, 26462, 256, 26451, 256, 55372, 57283, - 256, 15177, 256, 26618, 256, 26501, 256, 26706, 256, 26757, 256, 55373, - 56429, 256, 26766, 256, 26655, 256, 26900, 256, 15261, 256, 26946, 256, - 27043, 256, 27114, 256, 27304, 256, 55373, 56995, 256, 27355, 256, 15384, - 256, 27425, 256, 55374, 56487, 256, 27476, 256, 15438, 256, 27506, 256, - 27551, 256, 27578, 256, 27579, 256, 55374, 56973, 256, 55367, 56587, 256, - 55374, 57082, 256, 27726, 256, 55375, 56508, 256, 27839, 256, 27853, 256, - 27751, 256, 27926, 256, 27966, 256, 28023, 256, 27969, 256, 28009, 256, - 28024, 256, 28037, 256, 55375, 56606, 256, 27956, 256, 28207, 256, 28270, - 256, 15667, 256, 28363, 256, 28359, 256, 55375, 57041, 256, 28153, 256, - 28526, 256, 55375, 57182, 256, 55375, 57230, 256, 28614, 256, 28729, 256, - 28702, 256, 28699, 256, 15766, 256, 28746, 256, 28797, 256, 28791, 256, - 28845, 256, 55361, 56613, 256, 28997, 256, 55376, 56931, 256, 29084, 256, - 55376, 57259, 256, 29224, 256, 29237, 256, 29264, 256, 55377, 56840, 256, - 29312, 256, 29333, 256, 55377, 57141, 256, 55378, 56340, 256, 29562, 256, - 29579, 256, 16044, 256, 29605, 256, 16056, 256, 16056, 256, 29767, 256, - 29788, 256, 29809, 256, 29829, 256, 29898, 256, 16155, 256, 29988, 256, - 55379, 56374, 256, 30014, 256, 55379, 56466, 256, 30064, 256, 55368, - 56735, 256, 30224, 256, 55379, 57249, 256, 55379, 57272, 256, 55380, - 56388, 256, 16380, 256, 16392, 256, 30452, 256, 55380, 56563, 256, 55380, - 56562, 256, 55380, 56601, 256, 55380, 56627, 256, 30494, 256, 30495, 256, - 30495, 256, 30538, 256, 16441, 256, 30603, 256, 16454, 256, 16534, 256, - 55381, 56349, 256, 30798, 256, 30860, 256, 30924, 256, 16611, 256, 55381, - 56870, 256, 31062, 256, 55381, 56986, 256, 55381, 57029, 256, 31119, 256, - 31211, 256, 16687, 256, 31296, 256, 31306, 256, 31311, 256, 55382, 56700, - 256, 55382, 56999, 256, 55382, 56999, 256, 31470, 256, 16898, 256, 55382, - 57259, 256, 31686, 256, 31689, 256, 16935, 256, 55383, 56448, 256, 31954, - 256, 17056, 256, 31976, 256, 31971, 256, 32000, 256, 55383, 57222, 256, - 32099, 256, 17153, 256, 32199, 256, 32258, 256, 32325, 256, 17204, 256, - 55384, 56872, 256, 55384, 56903, 256, 17241, 256, 55384, 57049, 256, - 32634, 256, 55384, 57150, 256, 32661, 256, 32762, 256, 32773, 256, 55385, - 56538, 256, 55385, 56611, 256, 32864, 256, 55385, 56744, 256, 32880, 256, - 55372, 57183, 256, 17365, 256, 32946, 256, 33027, 256, 17419, 256, 33086, - 256, 23221, 256, 55385, 57255, 256, 55385, 57269, 256, 55372, 57235, 256, - 55372, 57244, 256, 33281, 256, 33284, 256, 36766, 256, 17515, 256, 33425, - 256, 33419, 256, 33437, 256, 21171, 256, 33457, 256, 33459, 256, 33469, - 256, 33510, 256, 55386, 57148, 256, 33509, 256, 33565, 256, 33635, 256, - 33709, 256, 33571, 256, 33725, 256, 33767, 256, 33879, 256, 33619, 256, - 33738, 256, 33740, 256, 33756, 256, 55387, 56374, 256, 55387, 56683, 256, - 55387, 56533, 256, 17707, 256, 34033, 256, 34035, 256, 34070, 256, 55388, - 57290, 256, 34148, 256, 55387, 57132, 256, 17757, 256, 17761, 256, 55387, - 57265, 256, 55388, 56530, 256, 17771, 256, 34384, 256, 34396, 256, 34407, - 256, 34409, 256, 34473, 256, 34440, 256, 34574, 256, 34530, 256, 34681, - 256, 34600, 256, 34667, 256, 34694, 256, 17879, 256, 34785, 256, 34817, - 256, 17913, 256, 34912, 256, 34915, 256, 55389, 56935, 256, 35031, 256, - 35038, 256, 17973, 256, 35066, 256, 13499, 256, 55390, 56494, 256, 55390, - 56678, 256, 18110, 256, 18119, 256, 35488, 256, 35565, 256, 35722, 256, - 35925, 256, 55391, 56488, 256, 36011, 256, 36033, 256, 36123, 256, 36215, - 256, 55391, 57135, 256, 55362, 56324, 256, 36299, 256, 36284, 256, 36336, - 256, 55362, 56542, 256, 36564, 256, 36664, 256, 55393, 56786, 256, 55393, - 56813, 256, 37012, 256, 37105, 256, 37137, 256, 55393, 57134, 256, 37147, - 256, 37432, 256, 37591, 256, 37592, 256, 37500, 256, 37881, 256, 37909, - 256, 55394, 57338, 256, 38283, 256, 18837, 256, 38327, 256, 55395, 56695, - 256, 18918, 256, 38595, 256, 23986, 256, 38691, 256, 55396, 56645, 256, - 55396, 56858, 256, 19054, 256, 19062, 256, 38880, 256, 55397, 56330, 256, - 19122, 256, 55397, 56470, 256, 38923, 256, 38923, 256, 38953, 256, 55397, - 56758, 256, 39138, 256, 19251, 256, 39209, 256, 39335, 256, 39362, 256, - 39422, 256, 19406, 256, 55398, 57136, 256, 39698, 256, 40000, 256, 40189, - 256, 19662, 256, 19693, 256, 40295, 256, 55400, 56526, 256, 19704, 256, - 55400, 56581, 256, 55400, 56846, 256, 55400, 56977, 256, 40635, 256, - 19798, 256, 40697, 256, 40702, 256, 40709, 256, 40719, 256, 40726, 256, - 40763, 256, 55401, 56832, -}; - -/* index tables for the decomposition data */ -#define DECOMP_SHIFT1 6 -#define DECOMP_SHIFT2 4 -static const unsigned char decomp_index0[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 13, 14, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, 16, 5, 5, 5, 5, 17, 18, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 19, 20, - 5, 5, 5, 5, 5, 21, 22, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 23, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, -}; - -static const unsigned short decomp_index1[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, - 14, 0, 0, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 0, 0, 0, 0, 0, 0, 0, - 25, 0, 26, 27, 0, 0, 0, 0, 0, 28, 0, 0, 29, 30, 31, 32, 33, 34, 35, 0, - 36, 37, 38, 0, 39, 0, 40, 0, 41, 0, 0, 0, 0, 42, 43, 44, 45, 0, 0, 0, 0, - 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 0, 48, 0, 0, 0, - 0, 49, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 52, 0, 53, 0, 0, 0, 0, - 0, 0, 54, 55, 0, 0, 0, 0, 0, 56, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 58, 59, 0, 0, 0, 60, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, - 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, - 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 67, 0, 68, 0, 0, 69, 0, 0, 0, 70, - 71, 72, 73, 74, 75, 76, 77, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 81, 0, - 82, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 84, 85, 86, 87, 88, 89, 0, 90, 91, 92, 0, 0, 0, 0, - 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, - 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, - 123, 124, 125, 126, 127, 128, 129, 130, 0, 131, 132, 133, 134, 0, 0, 0, - 0, 0, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 0, 146, 0, - 0, 0, 147, 0, 148, 149, 150, 0, 151, 152, 153, 0, 154, 0, 0, 0, 155, 0, - 0, 0, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 157, - 158, 159, 160, 161, 162, 163, 164, 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 0, - 0, 0, 0, 0, 0, 167, 0, 0, 0, 0, 0, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 171, 0, 0, 0, 0, 0, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, - 182, 183, 184, 185, 186, 0, 0, 187, 0, 0, 188, 189, 190, 191, 192, 0, - 193, 194, 195, 196, 197, 0, 198, 0, 0, 0, 199, 200, 201, 202, 203, 204, - 205, 0, 0, 0, 0, 0, 0, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, - 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, - 230, 231, 232, 233, 234, 235, 236, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 239, 0, 0, - 0, 0, 0, 0, 0, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 243, 244, 245, 246, 247, - 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, - 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 0, 0, 272, 273, 274, - 275, 276, 277, 278, 279, 280, 281, 282, 283, 0, 284, 285, 286, 287, 288, - 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, - 303, 304, 305, 306, 0, 307, 308, 309, 310, 311, 312, 313, 314, 0, 0, 315, - 0, 316, 0, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, - 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, - 343, 344, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 345, 346, 0, 0, 0, 0, 0, 0, 0, - 347, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 348, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 350, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 351, 352, 0, 0, 0, 0, 353, 354, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, - 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, - 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, - 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, - 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 431, 432, 433, 434, 435, 0, 436, 0, - 0, 437, 0, 0, 0, 0, 0, 0, 438, 439, 440, 441, 442, 443, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 444, 445, - 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, - 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, - 474, 475, 476, 477, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -static const unsigned short decomp_index2[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, - 3, 0, 6, 0, 0, 0, 0, 8, 0, 0, 11, 13, 15, 18, 0, 0, 20, 23, 25, 0, 27, - 31, 35, 0, 39, 42, 45, 48, 51, 54, 0, 57, 60, 63, 66, 69, 72, 75, 78, 81, - 0, 84, 87, 90, 93, 96, 99, 0, 0, 102, 105, 108, 111, 114, 0, 0, 117, 120, - 123, 126, 129, 132, 0, 135, 138, 141, 144, 147, 150, 153, 156, 159, 0, - 162, 165, 168, 171, 174, 177, 0, 0, 180, 183, 186, 189, 192, 0, 195, 198, - 201, 204, 207, 210, 213, 216, 219, 222, 225, 228, 231, 234, 237, 240, - 243, 0, 0, 246, 249, 252, 255, 258, 261, 264, 267, 270, 273, 276, 279, - 282, 285, 288, 291, 294, 297, 300, 303, 0, 0, 306, 309, 312, 315, 318, - 321, 324, 327, 330, 0, 333, 336, 339, 342, 345, 348, 0, 351, 354, 357, - 360, 363, 366, 369, 372, 0, 0, 375, 378, 381, 384, 387, 390, 393, 0, 0, - 396, 399, 402, 405, 408, 411, 0, 0, 414, 417, 420, 423, 426, 429, 432, - 435, 438, 441, 444, 447, 450, 453, 456, 459, 462, 465, 0, 0, 468, 471, - 474, 477, 480, 483, 486, 489, 492, 495, 498, 501, 504, 507, 510, 513, - 516, 519, 522, 525, 528, 531, 534, 537, 539, 542, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 545, 548, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 551, 554, 557, 560, 563, 566, 569, 572, 575, 578, 581, 584, 587, - 590, 593, 596, 599, 602, 605, 608, 611, 614, 617, 620, 623, 0, 626, 629, - 632, 635, 638, 641, 0, 0, 644, 647, 650, 653, 656, 659, 662, 665, 668, - 671, 674, 677, 680, 683, 686, 689, 0, 0, 692, 695, 698, 701, 704, 707, - 710, 713, 716, 719, 722, 725, 728, 731, 734, 737, 740, 743, 746, 749, - 752, 755, 758, 761, 764, 767, 770, 773, 776, 779, 782, 785, 788, 791, - 794, 797, 0, 0, 800, 803, 0, 0, 0, 0, 0, 0, 806, 809, 812, 815, 818, 821, - 824, 827, 830, 833, 836, 839, 842, 845, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 848, 850, 852, 854, 856, 858, 860, 862, 864, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 866, 869, 872, 875, 878, 881, 0, 0, 884, 886, 888, - 890, 892, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 894, 896, 0, 898, 900, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 903, 0, 0, 0, 0, 0, 905, 0, 0, 0, - 908, 0, 0, 0, 0, 0, 910, 913, 916, 919, 921, 924, 927, 0, 930, 0, 933, - 936, 939, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 942, 945, 948, 951, 954, 957, 960, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 963, 966, 969, 972, 975, - 0, 978, 980, 982, 984, 987, 990, 992, 0, 0, 0, 0, 0, 0, 0, 0, 0, 994, - 996, 998, 0, 1000, 1002, 0, 0, 0, 1004, 0, 0, 0, 0, 0, 0, 1006, 1009, 0, - 1012, 0, 0, 0, 1015, 0, 0, 0, 0, 1018, 1021, 1024, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1027, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1030, 0, 0, - 0, 0, 0, 0, 1033, 1036, 0, 1039, 0, 0, 0, 1042, 0, 0, 0, 0, 1045, 1048, - 1051, 0, 0, 0, 0, 0, 0, 0, 1054, 1057, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1060, - 1063, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1066, 1069, 1072, 1075, 0, - 0, 1078, 1081, 0, 0, 1084, 1087, 1090, 1093, 1096, 1099, 0, 0, 1102, - 1105, 1108, 1111, 1114, 1117, 0, 0, 1120, 1123, 1126, 1129, 1132, 1135, - 1138, 1141, 1144, 1147, 1150, 1153, 0, 0, 1156, 1159, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1165, 1168, - 1171, 1174, 1177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1180, 1183, - 1186, 1189, 0, 0, 0, 0, 0, 0, 0, 1192, 0, 1195, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1198, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1201, 0, 0, 0, 0, 0, 0, 0, 1204, 0, 0, 1207, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1210, 1213, 1216, - 1219, 1222, 1225, 1228, 1231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1234, - 1237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1240, 1243, 0, 1246, - 0, 0, 0, 1249, 0, 0, 1252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1255, 1258, 1261, 0, 0, 1264, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1267, - 0, 0, 1270, 1273, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1276, - 1279, 0, 0, 0, 0, 0, 0, 1282, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1285, 1288, 1291, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1294, 0, 0, 0, 0, 0, 0, 0, 1297, 0, 0, 0, 0, 0, 0, 1300, 1303, 0, 1306, - 1309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1312, 1315, 1318, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1321, 0, 1324, 1327, 1330, 0, 0, 0, 0, - 1333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1336, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1339, 1342, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1345, 0, 0, 0, 0, 0, 0, 1347, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1350, 0, 0, 0, 0, 1353, 0, 0, 0, 0, 1356, 0, 0, - 0, 0, 1359, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1362, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1365, 0, 1368, 1371, 1374, 1377, 1380, 0, 0, 0, 0, 0, 0, 0, - 1383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1386, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1389, 0, 0, 0, 0, 1392, 0, 0, 0, 0, 1395, 0, 0, 0, 0, - 1398, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1401, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1404, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1407, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1409, 0, 1412, 0, 1415, 0, - 1418, 0, 1421, 0, 0, 0, 1424, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1427, 0, 1430, 0, 0, 1433, 1436, 0, 1439, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1442, 1444, 1446, 0, 1448, 1450, 1452, 1454, 1456, 1458, 1460, 1462, - 1464, 1466, 1468, 0, 1470, 1472, 1474, 1476, 1478, 1480, 1482, 1484, - 1486, 1488, 1490, 1492, 1494, 1496, 1498, 1500, 1502, 1504, 0, 1506, - 1508, 1510, 1512, 1514, 1516, 1518, 1520, 1522, 1524, 1526, 1528, 1530, - 1532, 1534, 1536, 1538, 1540, 1542, 1544, 1546, 1548, 1550, 1552, 1554, - 1556, 1558, 1560, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1562, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1564, 1566, 1568, 1570, - 1572, 1574, 1576, 1578, 1580, 1582, 1584, 1586, 1588, 1590, 1592, 1594, - 1596, 1598, 1600, 1602, 1604, 1606, 1608, 1610, 1612, 1614, 1616, 1618, - 1620, 1622, 1624, 1626, 1628, 1630, 1632, 1634, 1636, 1638, 1641, 1644, - 1647, 1650, 1653, 1656, 1659, 1662, 1665, 1668, 1671, 1674, 1677, 1680, - 1683, 1686, 1689, 1692, 1695, 1698, 1701, 1704, 1707, 1710, 1713, 1716, - 1719, 1722, 1725, 1728, 1731, 1734, 1737, 1740, 1743, 1746, 1749, 1752, - 1755, 1758, 1761, 1764, 1767, 1770, 1773, 1776, 1779, 1782, 1785, 1788, - 1791, 1794, 1797, 1800, 1803, 1806, 1809, 1812, 1815, 1818, 1821, 1824, - 1827, 1830, 1833, 1836, 1839, 1842, 1845, 1848, 1851, 1854, 1857, 1860, - 1863, 1866, 1869, 1872, 1875, 1878, 1881, 1884, 1887, 1890, 1893, 1896, - 1899, 1902, 1905, 1908, 1911, 1914, 1917, 1920, 1923, 1926, 1929, 1932, - 1935, 1938, 1941, 1944, 1947, 1950, 1953, 1956, 1959, 1962, 1965, 1968, - 1971, 1974, 1977, 1980, 1983, 1986, 1989, 1992, 1995, 1998, 2001, 2004, - 2007, 2010, 2013, 2016, 2019, 2022, 2025, 2028, 2031, 2034, 2037, 2040, - 2043, 2046, 2049, 2052, 2055, 2058, 2061, 2064, 2067, 2070, 2073, 2076, - 2079, 2082, 2085, 2088, 2091, 2094, 2097, 2100, 2103, 0, 0, 0, 0, 2106, - 2109, 2112, 2115, 2118, 2121, 2124, 2127, 2130, 2133, 2136, 2139, 2142, - 2145, 2148, 2151, 2154, 2157, 2160, 2163, 2166, 2169, 2172, 2175, 2178, - 2181, 2184, 2187, 2190, 2193, 2196, 2199, 2202, 2205, 2208, 2211, 2214, - 2217, 2220, 2223, 2226, 2229, 2232, 2235, 2238, 2241, 2244, 2247, 2250, - 2253, 2256, 2259, 2262, 2265, 2268, 2271, 2274, 2277, 2280, 2283, 2286, - 2289, 2292, 2295, 2298, 2301, 2304, 2307, 2310, 2313, 2316, 2319, 2322, - 2325, 2328, 2331, 2334, 2337, 2340, 2343, 2346, 2349, 2352, 2355, 2358, - 2361, 2364, 2367, 2370, 2373, 0, 0, 0, 0, 0, 0, 2376, 2379, 2382, 2385, - 2388, 2391, 2394, 2397, 2400, 2403, 2406, 2409, 2412, 2415, 2418, 2421, - 2424, 2427, 2430, 2433, 2436, 2439, 0, 0, 2442, 2445, 2448, 2451, 2454, - 2457, 0, 0, 2460, 2463, 2466, 2469, 2472, 2475, 2478, 2481, 2484, 2487, - 2490, 2493, 2496, 2499, 2502, 2505, 2508, 2511, 2514, 2517, 2520, 2523, - 2526, 2529, 2532, 2535, 2538, 2541, 2544, 2547, 2550, 2553, 2556, 2559, - 2562, 2565, 2568, 2571, 0, 0, 2574, 2577, 2580, 2583, 2586, 2589, 0, 0, - 2592, 2595, 2598, 2601, 2604, 2607, 2610, 2613, 0, 2616, 0, 2619, 0, - 2622, 0, 2625, 2628, 2631, 2634, 2637, 2640, 2643, 2646, 2649, 2652, - 2655, 2658, 2661, 2664, 2667, 2670, 2673, 2676, 2679, 2681, 2684, 2686, - 2689, 2691, 2694, 2696, 2699, 2701, 2704, 2706, 2709, 0, 0, 2711, 2714, - 2717, 2720, 2723, 2726, 2729, 2732, 2735, 2738, 2741, 2744, 2747, 2750, - 2753, 2756, 2759, 2762, 2765, 2768, 2771, 2774, 2777, 2780, 2783, 2786, - 2789, 2792, 2795, 2798, 2801, 2804, 2807, 2810, 2813, 2816, 2819, 2822, - 2825, 2828, 2831, 2834, 2837, 2840, 2843, 2846, 2849, 2852, 2855, 2858, - 2861, 2864, 2867, 0, 2870, 2873, 2876, 2879, 2882, 2885, 2887, 2890, - 2893, 2895, 2898, 2901, 2904, 2907, 2910, 0, 2913, 2916, 2919, 2922, - 2924, 2927, 2929, 2932, 2935, 2938, 2941, 2944, 2947, 2950, 0, 0, 2952, - 2955, 2958, 2961, 2964, 2967, 0, 2969, 2972, 2975, 2978, 2981, 2984, - 2987, 2989, 2992, 2995, 2998, 3001, 3004, 3007, 3010, 3012, 3015, 3018, - 3020, 0, 0, 3022, 3025, 3028, 0, 3031, 3034, 3037, 3040, 3042, 3045, - 3047, 3050, 3052, 0, 3055, 3057, 3059, 3061, 3063, 3065, 3067, 3069, - 3071, 3073, 3075, 0, 0, 0, 0, 0, 0, 3077, 0, 0, 0, 0, 0, 3079, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 3082, 3084, 3087, 0, 0, 0, 0, 0, 0, 0, 0, - 3091, 0, 0, 0, 3093, 3096, 0, 3100, 3103, 0, 0, 0, 0, 3107, 0, 3110, 0, - 0, 0, 0, 0, 0, 0, 0, 3113, 3116, 3119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3122, 0, 0, 0, 0, 0, 0, 0, 3127, 3129, 3131, 0, 0, 3133, 3135, - 3137, 3139, 3141, 3143, 3145, 3147, 3149, 3151, 3153, 3155, 3157, 3159, - 3161, 3163, 3165, 3167, 3169, 3171, 3173, 3175, 3177, 3179, 3181, 3183, - 3185, 0, 3187, 3189, 3191, 3193, 3195, 3197, 3199, 3201, 3203, 3205, - 3207, 3209, 3211, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3213, 0, 0, 0, 0, 0, - 0, 0, 3216, 3220, 3224, 3226, 0, 3229, 3233, 3237, 0, 3239, 3242, 3244, - 3246, 3248, 3250, 3252, 3254, 3256, 3258, 3260, 0, 3262, 3264, 0, 0, - 3267, 3269, 3271, 3273, 3275, 0, 0, 3277, 3280, 3284, 0, 3287, 0, 3289, - 0, 3291, 0, 3293, 3295, 3297, 3299, 0, 3301, 3303, 3305, 0, 3307, 3309, - 3311, 3313, 3315, 3317, 3319, 0, 3321, 3325, 3327, 3329, 3331, 3333, 0, - 0, 0, 0, 3335, 3337, 3339, 3341, 3343, 0, 0, 0, 0, 0, 0, 3345, 3349, - 3353, 3358, 3362, 3366, 3370, 3374, 3378, 3382, 3386, 3390, 3394, 3398, - 3402, 3406, 3409, 3411, 3414, 3418, 3421, 3423, 3426, 3430, 3435, 3438, - 3440, 3443, 3447, 3449, 3451, 3453, 3455, 3457, 3460, 3464, 3467, 3469, - 3472, 3476, 3481, 3484, 3486, 3489, 3493, 3495, 3497, 3499, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3501, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 3505, 3508, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3511, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3514, 3517, 3520, 0, 0, 0, 0, - 3523, 0, 0, 0, 0, 3526, 0, 0, 3529, 0, 0, 0, 0, 0, 0, 0, 3532, 0, 3535, - 0, 0, 0, 0, 0, 3538, 3541, 0, 3545, 3548, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3552, 0, 0, 3555, 0, 0, 3558, 0, 3561, 0, 0, 0, 0, 0, - 0, 3564, 0, 3567, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3570, 3573, 3576, 3579, - 3582, 0, 0, 3585, 3588, 0, 0, 3591, 3594, 0, 0, 0, 0, 0, 0, 3597, 3600, - 0, 0, 3603, 3606, 0, 0, 3609, 3612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3615, 3618, 3621, 3624, 3627, 3630, 3633, 3636, 0, 0, - 0, 0, 0, 0, 3639, 3642, 3645, 3648, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 3651, 3653, 0, 0, 0, 0, 0, 3655, 3657, 3659, 3661, 3663, 3665, 3667, - 3669, 3671, 3673, 3676, 3679, 3682, 3685, 3688, 3691, 3694, 3697, 3700, - 3703, 3706, 3710, 3714, 3718, 3722, 3726, 3730, 3734, 3738, 3742, 3747, - 3752, 3757, 3762, 3767, 3772, 3777, 3782, 3787, 3792, 3797, 3800, 3803, - 3806, 3809, 3812, 3815, 3818, 3821, 3824, 3828, 3832, 3836, 3840, 3844, - 3848, 3852, 3856, 3860, 3864, 3868, 3872, 3876, 3880, 3884, 3888, 3892, - 3896, 3900, 3904, 3908, 3912, 3916, 3920, 3924, 3928, 3932, 3936, 3940, - 3944, 3948, 3952, 3956, 3960, 3964, 3968, 3972, 3974, 3976, 3978, 3980, - 3982, 3984, 3986, 3988, 3990, 3992, 3994, 3996, 3998, 4000, 4002, 4004, - 4006, 4008, 4010, 4012, 4014, 4016, 4018, 4020, 4022, 4024, 4026, 4028, - 4030, 4032, 4034, 4036, 4038, 4040, 4042, 4044, 4046, 4048, 4050, 4052, - 4054, 4056, 4058, 4060, 4062, 4064, 4066, 4068, 4070, 4072, 4074, 4076, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4078, 0, 0, 0, 0, 0, - 0, 0, 4083, 4087, 4090, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 4094, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4097, - 4099, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4101, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4103, 0, 0, 0, 4105, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4107, 4109, 4111, 4113, 4115, 4117, 4119, 4121, - 4123, 4125, 4127, 4129, 4131, 4133, 4135, 4137, 4139, 4141, 4143, 4145, - 4147, 4149, 4151, 4153, 4155, 4157, 4159, 4161, 4163, 4165, 4167, 4169, - 4171, 4173, 4175, 4177, 4179, 4181, 4183, 4185, 4187, 4189, 4191, 4193, - 4195, 4197, 4199, 4201, 4203, 4205, 4207, 4209, 4211, 4213, 4215, 4217, - 4219, 4221, 4223, 4225, 4227, 4229, 4231, 4233, 4235, 4237, 4239, 4241, - 4243, 4245, 4247, 4249, 4251, 4253, 4255, 4257, 4259, 4261, 4263, 4265, - 4267, 4269, 4271, 4273, 4275, 4277, 4279, 4281, 4283, 4285, 4287, 4289, - 4291, 4293, 4295, 4297, 4299, 4301, 4303, 4305, 4307, 4309, 4311, 4313, - 4315, 4317, 4319, 4321, 4323, 4325, 4327, 4329, 4331, 4333, 4335, 4337, - 4339, 4341, 4343, 4345, 4347, 4349, 4351, 4353, 4355, 4357, 4359, 4361, - 4363, 4365, 4367, 4369, 4371, 4373, 4375, 4377, 4379, 4381, 4383, 4385, - 4387, 4389, 4391, 4393, 4395, 4397, 4399, 4401, 4403, 4405, 4407, 4409, - 4411, 4413, 4415, 4417, 4419, 4421, 4423, 4425, 4427, 4429, 4431, 4433, - 4435, 4437, 4439, 4441, 4443, 4445, 4447, 4449, 4451, 4453, 4455, 4457, - 4459, 4461, 4463, 4465, 4467, 4469, 4471, 4473, 4475, 4477, 4479, 4481, - 4483, 4485, 4487, 4489, 4491, 4493, 4495, 4497, 4499, 4501, 4503, 4505, - 4507, 4509, 4511, 4513, 4515, 4517, 4519, 4521, 4523, 4525, 4527, 4529, - 4531, 4533, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4535, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4537, 0, 4539, 4541, 4543, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4545, 0, 4548, 0, 4551, 0, - 4554, 0, 4557, 0, 4560, 0, 4563, 0, 4566, 0, 4569, 0, 4572, 0, 4575, 0, - 4578, 0, 0, 4581, 0, 4584, 0, 4587, 0, 0, 0, 0, 0, 0, 4590, 4593, 0, - 4596, 4599, 0, 4602, 4605, 0, 4608, 4611, 0, 4614, 4617, 0, 0, 0, 0, 0, - 0, 4620, 0, 0, 0, 0, 0, 0, 4623, 4626, 0, 4629, 4632, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 4635, 0, 4638, 0, 4641, 0, 4644, 0, 4647, 0, 4650, 0, - 4653, 0, 4656, 0, 4659, 0, 4662, 0, 4665, 0, 4668, 0, 0, 4671, 0, 4674, - 0, 4677, 0, 0, 0, 0, 0, 0, 4680, 4683, 0, 4686, 4689, 0, 4692, 4695, 0, - 4698, 4701, 0, 4704, 4707, 0, 0, 0, 0, 0, 0, 4710, 0, 0, 4713, 4716, - 4719, 4722, 0, 0, 0, 4725, 4728, 0, 4731, 4733, 4735, 4737, 4739, 4741, - 4743, 4745, 4747, 4749, 4751, 4753, 4755, 4757, 4759, 4761, 4763, 4765, - 4767, 4769, 4771, 4773, 4775, 4777, 4779, 4781, 4783, 4785, 4787, 4789, - 4791, 4793, 4795, 4797, 4799, 4801, 4803, 4805, 4807, 4809, 4811, 4813, - 4815, 4817, 4819, 4821, 4823, 4825, 4827, 4829, 4831, 4833, 4835, 4837, - 4839, 4841, 4843, 4845, 4847, 4849, 4851, 4853, 4855, 4857, 4859, 4861, - 4863, 4865, 4867, 4869, 4871, 4873, 4875, 4877, 4879, 4881, 4883, 4885, - 4887, 4889, 4891, 4893, 4895, 4897, 4899, 4901, 4903, 4905, 4907, 4909, - 4911, 4913, 4915, 4917, 0, 0, 0, 4919, 4921, 4923, 4925, 4927, 4929, - 4931, 4933, 4935, 4937, 4939, 4941, 4943, 4945, 4947, 4951, 4955, 4959, - 4963, 4967, 4971, 4975, 4979, 4983, 4987, 4991, 4995, 4999, 5003, 5008, - 5013, 5018, 5023, 5028, 5033, 5038, 5043, 5048, 5053, 5058, 5063, 5068, - 5073, 5078, 5086, 0, 5093, 5097, 5101, 5105, 5109, 5113, 5117, 5121, - 5125, 5129, 5133, 5137, 5141, 5145, 5149, 5153, 5157, 5161, 5165, 5169, - 5173, 5177, 5181, 5185, 5189, 5193, 5197, 5201, 5205, 5209, 5213, 5217, - 5221, 5225, 5229, 5233, 5237, 5239, 5241, 5243, 0, 0, 0, 0, 0, 0, 0, 0, - 5245, 5249, 5252, 5255, 5258, 5261, 5264, 5267, 5270, 5273, 5276, 5279, - 5282, 5285, 5288, 5291, 5294, 5296, 5298, 5300, 5302, 5304, 5306, 5308, - 5310, 5312, 5314, 5316, 5318, 5320, 5322, 5325, 5328, 5331, 5334, 5337, - 5340, 5343, 5346, 5349, 5352, 5355, 5358, 5361, 5364, 5370, 5375, 0, - 5378, 5380, 5382, 5384, 5386, 5388, 5390, 5392, 5394, 5396, 5398, 5400, - 5402, 5404, 5406, 5408, 5410, 5412, 5414, 5416, 5418, 5420, 5422, 5424, - 5426, 5428, 5430, 5432, 5434, 5436, 5438, 5440, 5442, 5444, 5446, 5448, - 5450, 5452, 5454, 5456, 5458, 5460, 5462, 5464, 5466, 5468, 5470, 5472, - 5474, 5476, 5479, 5482, 5485, 5488, 5491, 5494, 5497, 5500, 5503, 5506, - 5509, 5512, 5515, 5518, 5521, 5524, 5527, 5530, 5533, 5536, 5539, 5542, - 5545, 5548, 5552, 5556, 5560, 5563, 5567, 5570, 5574, 5576, 5578, 5580, - 5582, 5584, 5586, 5588, 5590, 5592, 5594, 5596, 5598, 5600, 5602, 5604, - 5606, 5608, 5610, 5612, 5614, 5616, 5618, 5620, 5622, 5624, 5626, 5628, - 5630, 5632, 5634, 5636, 5638, 5640, 5642, 5644, 5646, 5648, 5650, 5652, - 5654, 5656, 5658, 5660, 5662, 5664, 5666, 0, 5668, 5673, 5678, 5683, - 5687, 5692, 5696, 5700, 5706, 5711, 5715, 5719, 5723, 5728, 5733, 5737, - 5741, 5744, 5748, 5753, 5758, 5761, 5767, 5774, 5780, 5784, 5790, 5796, - 5801, 5805, 5809, 5813, 5818, 5824, 5829, 5833, 5837, 5841, 5844, 5847, - 5850, 5853, 5857, 5861, 5867, 5871, 5876, 5882, 5886, 5889, 5892, 5898, - 5903, 5909, 5913, 5919, 5922, 5926, 5930, 5934, 5938, 5942, 5947, 5951, - 5954, 5958, 5962, 5966, 5971, 5975, 5979, 5983, 5989, 5994, 5997, 6003, - 6006, 6011, 6016, 6020, 6024, 6028, 6033, 6036, 6040, 6045, 6048, 6054, - 6058, 6061, 6064, 6067, 6070, 6073, 6076, 6079, 6082, 6085, 6088, 6092, - 6096, 6100, 6104, 6108, 6112, 6116, 6120, 6124, 6128, 6132, 6136, 6140, - 6144, 6148, 6152, 6155, 6158, 6162, 6165, 6168, 6171, 6175, 6179, 6182, - 6185, 6188, 6191, 6194, 6199, 6202, 6205, 6208, 6211, 6214, 6217, 6220, - 6223, 6227, 6232, 6235, 6238, 6241, 6244, 6247, 6250, 6253, 6257, 6261, - 6265, 6269, 6272, 6275, 6278, 6281, 6284, 6287, 6290, 6293, 6296, 6299, - 6303, 6307, 6310, 6314, 6318, 6322, 6325, 6329, 6333, 6338, 6341, 6345, - 6349, 6353, 6357, 6363, 6370, 6373, 6376, 6379, 6382, 6385, 6388, 6391, - 6394, 6397, 6400, 6403, 6406, 6409, 6412, 6415, 6418, 6421, 6424, 6429, - 6432, 6435, 6438, 6443, 6447, 6450, 6453, 6456, 6459, 6462, 6465, 6468, - 6471, 6474, 6477, 6481, 6484, 6487, 6491, 6495, 6498, 6503, 6507, 6510, - 6513, 6516, 6519, 6523, 6527, 6530, 6533, 6536, 6539, 6542, 6545, 6548, - 6551, 6554, 6558, 6562, 6566, 6570, 6574, 6578, 6582, 6586, 6590, 6594, - 6598, 6602, 6606, 6610, 6614, 6618, 6622, 6626, 6630, 6634, 6638, 6642, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6646, 6648, 0, 0, 6650, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6652, 6654, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6656, 6658, 6660, - 6662, 6664, 6666, 6668, 6670, 6672, 6674, 6676, 6678, 6680, 6682, 6684, - 6686, 6688, 6690, 6692, 6694, 6696, 6698, 6700, 6702, 6704, 6706, 6708, - 6710, 6712, 6714, 6716, 6718, 6720, 6722, 6724, 6726, 6728, 6730, 6732, - 6734, 6736, 6738, 6740, 6742, 6744, 6746, 6748, 6750, 6752, 6754, 6756, - 6758, 6760, 6762, 6764, 6766, 6768, 6770, 6772, 6774, 6776, 6778, 6780, - 6782, 6784, 6786, 6788, 6790, 6792, 6794, 6796, 6798, 6800, 6802, 6804, - 6806, 6808, 6810, 6812, 6814, 6816, 6818, 6820, 6822, 6824, 6826, 6828, - 6830, 6832, 6834, 6836, 6838, 6840, 6842, 6844, 6846, 6848, 6850, 6852, - 6854, 6856, 6858, 6860, 6862, 6864, 6866, 6868, 6870, 6872, 6874, 6876, - 6878, 6880, 6882, 6884, 6886, 6888, 6890, 6892, 6894, 6896, 6898, 6900, - 6902, 6904, 6906, 6908, 6910, 6912, 6914, 6916, 6918, 6920, 6922, 6924, - 6926, 6928, 6930, 6932, 6934, 6936, 6938, 6940, 6942, 6944, 6946, 6948, - 6950, 6952, 6954, 6956, 6958, 6960, 6962, 6964, 6966, 6968, 6970, 6972, - 6974, 6976, 6978, 6980, 6982, 6984, 6986, 6988, 6990, 6992, 6994, 6996, - 6998, 7000, 7002, 7004, 7006, 7008, 7010, 7012, 7014, 7016, 7018, 7020, - 7022, 7024, 7026, 7028, 7030, 7032, 7034, 7036, 7038, 7040, 7042, 7044, - 7046, 7048, 7050, 7052, 7054, 7056, 7058, 7060, 7062, 7064, 7066, 7068, - 7070, 7072, 7074, 7076, 7078, 7080, 7082, 7084, 7086, 7088, 7090, 7092, - 7094, 7096, 7098, 7100, 7102, 7104, 7106, 7108, 7110, 7112, 7114, 7116, - 7118, 7120, 7122, 7124, 7126, 7128, 7130, 7132, 7134, 7136, 7138, 7140, - 7142, 7144, 7146, 7148, 7150, 7152, 7154, 7156, 7158, 7160, 7162, 7164, - 7166, 7168, 7170, 7172, 7174, 7176, 7178, 7180, 7182, 7184, 7186, 7188, - 7190, 7192, 7194, 7196, 7198, 7200, 7202, 0, 0, 7204, 0, 7206, 0, 0, - 7208, 7210, 7212, 7214, 7216, 7218, 7220, 7222, 7224, 7226, 0, 7228, 0, - 7230, 0, 0, 7232, 7234, 0, 0, 0, 7236, 7238, 7240, 7242, 7244, 7246, - 7248, 7250, 7252, 7254, 7256, 7258, 7260, 7262, 7264, 7266, 7268, 7270, - 7272, 7274, 7276, 7278, 7280, 7282, 7284, 7286, 7288, 7290, 7292, 7294, - 7296, 7298, 7300, 7302, 7304, 7306, 7308, 7310, 7312, 7314, 7316, 7318, - 7320, 7322, 7324, 7326, 7328, 7330, 7332, 7334, 7336, 7338, 7340, 7342, - 7344, 7346, 7348, 7350, 7352, 7354, 7356, 7358, 7360, 7362, 7364, 7366, - 7368, 7371, 0, 0, 7373, 7375, 7377, 7379, 7381, 7383, 7385, 7387, 7389, - 7391, 7393, 7395, 7397, 7399, 7401, 7403, 7405, 7407, 7409, 7411, 7413, - 7415, 7417, 7419, 7421, 7423, 7425, 7427, 7429, 7431, 7433, 7435, 7437, - 7439, 7441, 7443, 7445, 7447, 7449, 7451, 7453, 7455, 7457, 7459, 7461, - 7463, 7465, 7467, 7469, 7471, 7473, 7475, 7477, 7479, 7481, 7483, 7485, - 7487, 7489, 7491, 7493, 7495, 7497, 7499, 7501, 7503, 7505, 7507, 7509, - 7511, 7513, 7515, 7517, 7519, 7521, 7523, 7525, 7527, 7529, 7531, 7533, - 7535, 7537, 7539, 7541, 7543, 7545, 7547, 7549, 7551, 7553, 7555, 7557, - 7559, 7561, 7563, 7566, 7569, 7572, 7574, 7576, 7578, 7581, 7584, 7587, - 7589, 0, 0, 0, 0, 0, 0, 7591, 7594, 7597, 7600, 7604, 7608, 7611, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7614, 7617, 7620, 7623, 7626, 0, 0, 0, 0, - 0, 7629, 0, 7632, 7635, 7637, 7639, 7641, 7643, 7645, 7647, 7649, 7651, - 7653, 7655, 7658, 7661, 7664, 7667, 7670, 7673, 7676, 7679, 7682, 7685, - 7688, 7691, 0, 7694, 7697, 7700, 7703, 7706, 0, 7709, 0, 7712, 7715, 0, - 7718, 7721, 0, 7724, 7727, 7730, 7733, 7736, 7739, 7742, 7745, 7748, - 7751, 7754, 7756, 7758, 7760, 7762, 7764, 7766, 7768, 7770, 7772, 7774, - 7776, 7778, 7780, 7782, 7784, 7786, 7788, 7790, 7792, 7794, 7796, 7798, - 7800, 7802, 7804, 7806, 7808, 7810, 7812, 7814, 7816, 7818, 7820, 7822, - 7824, 7826, 7828, 7830, 7832, 7834, 7836, 7838, 7840, 7842, 7844, 7846, - 7848, 7850, 7852, 7854, 7856, 7858, 7860, 7862, 7864, 7866, 7868, 7870, - 7872, 7874, 7876, 7878, 7880, 7882, 7884, 7886, 7888, 7890, 7892, 7894, - 7896, 7898, 7900, 7902, 7904, 7906, 7908, 7910, 7912, 7914, 7916, 7918, - 7920, 7922, 7924, 7926, 7928, 7930, 7932, 7934, 7936, 7938, 7940, 7942, - 7944, 7946, 7948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 7950, 7952, 7954, 7956, 7958, 7960, 7962, 7964, 7966, 7968, 7970, 7972, - 7974, 7976, 7978, 7980, 7982, 7984, 7986, 7988, 7990, 7992, 7994, 7996, - 7999, 8002, 8005, 8008, 8011, 8014, 8017, 8020, 8023, 8026, 8029, 8032, - 8035, 8038, 8041, 8044, 8047, 8050, 8052, 8054, 8056, 8058, 8061, 8064, - 8067, 8070, 8073, 8076, 8079, 8082, 8085, 8088, 8091, 8094, 8097, 8100, - 8103, 8106, 8109, 8112, 8115, 8118, 8121, 8124, 8127, 8130, 8133, 8136, - 8139, 8142, 8145, 8148, 8151, 8154, 8157, 8160, 8163, 8166, 8169, 8172, - 8175, 8178, 8181, 8184, 8187, 8190, 8193, 8196, 8199, 8202, 8205, 8208, - 8211, 8214, 8217, 8220, 8223, 8226, 8229, 8232, 8235, 8238, 8241, 8244, - 8247, 8250, 8253, 8256, 8259, 8262, 8265, 8268, 8271, 8274, 8277, 8280, - 8283, 8286, 8289, 8292, 8295, 8298, 8301, 8304, 8307, 8310, 8313, 8316, - 8319, 8322, 8325, 8328, 8331, 8334, 8337, 8340, 8344, 8348, 8352, 8356, - 8360, 8364, 8367, 8370, 8373, 8376, 8379, 8382, 8385, 8388, 8391, 8394, - 8397, 8400, 8403, 8406, 8409, 8412, 8415, 8418, 8421, 8424, 8427, 8430, - 8433, 8436, 8439, 8442, 8445, 8448, 8451, 8454, 8457, 8460, 8463, 8466, - 8469, 8472, 8475, 8478, 8481, 8484, 8487, 8490, 8493, 8496, 8499, 8502, - 8505, 8508, 8511, 8514, 8517, 8520, 8523, 8526, 8529, 8532, 8535, 8538, - 8541, 8544, 8547, 8550, 8553, 8556, 8559, 8562, 8565, 8568, 8571, 8574, - 8577, 8580, 8583, 8586, 8589, 8592, 8595, 8598, 8601, 8604, 8607, 8610, - 8613, 8616, 8619, 8622, 8625, 8628, 8631, 8634, 8637, 8640, 8643, 8646, - 8649, 8652, 8655, 8658, 8661, 8664, 8667, 8670, 8673, 8676, 8679, 8682, - 8685, 8688, 8691, 8694, 8697, 8700, 8703, 8706, 8709, 8712, 8715, 8718, - 8721, 8724, 8727, 8730, 8733, 8736, 8739, 8742, 8745, 8748, 8751, 8754, - 8757, 8760, 8763, 8766, 8769, 8772, 8775, 8778, 8781, 8784, 8787, 8790, - 8794, 8798, 8802, 8805, 8808, 8811, 8814, 8817, 8820, 8823, 8826, 8829, - 8832, 8835, 8838, 8841, 8844, 8847, 8850, 8853, 8856, 8859, 8862, 8865, - 8868, 8871, 8874, 8877, 8880, 8883, 8886, 8889, 8892, 8895, 8898, 8901, - 8904, 8907, 8910, 8913, 8916, 8919, 8922, 8925, 8928, 8931, 8934, 8937, - 8940, 8943, 8946, 8949, 8952, 8955, 8958, 8961, 8964, 8967, 8970, 8973, - 8976, 8979, 8982, 8985, 8988, 8991, 8994, 8997, 9000, 9003, 9006, 9009, - 9012, 9015, 9018, 0, 0, 9021, 9025, 9029, 9033, 9037, 9041, 9045, 9049, - 9053, 9057, 9061, 9065, 9069, 9073, 9077, 9081, 9085, 9089, 9093, 9097, - 9101, 9105, 9109, 9113, 9117, 9121, 9125, 9129, 9133, 9137, 9141, 9145, - 9149, 9153, 9157, 9161, 9165, 9169, 9173, 9177, 9181, 9185, 9189, 9193, - 9197, 9201, 9205, 9209, 9213, 9217, 9221, 9225, 9229, 9233, 9237, 9241, - 9245, 9249, 9253, 9257, 9261, 9265, 9269, 9273, 0, 0, 9277, 9281, 9285, - 9289, 9293, 9297, 9301, 9305, 9309, 9313, 9317, 9321, 9325, 9329, 9333, - 9337, 9341, 9345, 9349, 9353, 9357, 9361, 9365, 9369, 9373, 9377, 9381, - 9385, 9389, 9393, 9397, 9401, 9405, 9409, 9413, 9417, 9421, 9425, 9429, - 9433, 9437, 9441, 9445, 9449, 9453, 9457, 9461, 9465, 9469, 9473, 9477, - 9481, 9485, 9489, 0, 0, 0, 0, 0, 0, 0, 0, 9493, 9497, 9501, 9506, 9511, - 9516, 9521, 9526, 9531, 9536, 9540, 9559, 9568, 0, 0, 0, 9573, 9575, - 9577, 9579, 9581, 9583, 9585, 9587, 9589, 9591, 0, 0, 0, 0, 0, 0, 9593, - 9595, 9597, 9599, 9601, 9603, 9605, 9607, 9609, 9611, 9613, 9615, 9617, - 9619, 9621, 9623, 9625, 9627, 9629, 9631, 9633, 0, 0, 9635, 9637, 9639, - 9641, 9643, 9645, 9647, 9649, 9651, 9653, 9655, 9657, 0, 9659, 9661, - 9663, 9665, 9667, 9669, 9671, 9673, 9675, 9677, 9679, 9681, 9683, 9685, - 9687, 9689, 9691, 9693, 9695, 0, 9697, 9699, 9701, 9703, 0, 0, 0, 0, - 9705, 9708, 9711, 0, 9714, 0, 9717, 9720, 9723, 9726, 9729, 9732, 9735, - 9738, 9741, 9744, 9747, 9749, 9751, 9753, 9755, 9757, 9759, 9761, 9763, - 9765, 9767, 9769, 9771, 9773, 9775, 9777, 9779, 9781, 9783, 9785, 9787, - 9789, 9791, 9793, 9795, 9797, 9799, 9801, 9803, 9805, 9807, 9809, 9811, - 9813, 9815, 9817, 9819, 9821, 9823, 9825, 9827, 9829, 9831, 9833, 9835, - 9837, 9839, 9841, 9843, 9845, 9847, 9849, 9851, 9853, 9855, 9857, 9859, - 9861, 9863, 9865, 9867, 9869, 9871, 9873, 9875, 9877, 9879, 9881, 9883, - 9885, 9887, 9889, 9891, 9893, 9895, 9897, 9899, 9901, 9903, 9905, 9907, - 9909, 9911, 9913, 9915, 9917, 9919, 9921, 9923, 9925, 9927, 9929, 9931, - 9933, 9935, 9937, 9939, 9941, 9943, 9945, 9947, 9949, 9951, 9953, 9955, - 9957, 9959, 9961, 9963, 9965, 9967, 9969, 9971, 9973, 9975, 9977, 9979, - 9981, 9984, 9987, 9990, 9993, 9996, 9999, 10002, 0, 0, 0, 0, 10005, - 10007, 10009, 10011, 10013, 10015, 10017, 10019, 10021, 10023, 10025, - 10027, 10029, 10031, 10033, 10035, 10037, 10039, 10041, 10043, 10045, - 10047, 10049, 10051, 10053, 10055, 10057, 10059, 10061, 10063, 10065, - 10067, 10069, 10071, 10073, 10075, 10077, 10079, 10081, 10083, 10085, - 10087, 10089, 10091, 10093, 10095, 10097, 10099, 10101, 10103, 10105, - 10107, 10109, 10111, 10113, 10115, 10117, 10119, 10121, 10123, 10125, - 10127, 10129, 10131, 10133, 10135, 10137, 10139, 10141, 10143, 10145, - 10147, 10149, 10151, 10153, 10155, 10157, 10159, 10161, 10163, 10165, - 10167, 10169, 10171, 10173, 10175, 10177, 10179, 10181, 10183, 10185, - 10187, 10189, 10191, 10193, 10195, 10197, 10199, 10201, 10203, 10205, - 10207, 10209, 10211, 10213, 10215, 10217, 10219, 10221, 10223, 10225, - 10227, 10229, 10231, 10233, 10235, 10237, 10239, 10241, 10243, 10245, - 10247, 10249, 10251, 10253, 10255, 10257, 10259, 10261, 10263, 10265, - 10267, 10269, 10271, 10273, 10275, 10277, 10279, 10281, 10283, 10285, - 10287, 10289, 10291, 10293, 10295, 10297, 10299, 10301, 10303, 10305, - 10307, 10309, 10311, 10313, 10315, 10317, 10319, 10321, 10323, 10325, - 10327, 10329, 10331, 10333, 10335, 10337, 10339, 10341, 10343, 10345, - 10347, 10349, 10351, 10353, 10355, 10357, 10359, 10361, 10363, 10365, - 10367, 10369, 10371, 10373, 10375, 10377, 10379, 10381, 10383, 0, 0, 0, - 10385, 10387, 10389, 10391, 10393, 10395, 0, 0, 10397, 10399, 10401, - 10403, 10405, 10407, 0, 0, 10409, 10411, 10413, 10415, 10417, 10419, 0, - 0, 10421, 10423, 10425, 0, 0, 0, 10427, 10429, 10431, 10433, 10435, - 10437, 10439, 0, 10441, 10443, 10445, 10447, 10449, 10451, 10453, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 10455, 0, 10460, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 10465, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 10470, 10475, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10480, 10485, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10490, 10495, 0, 10500, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 10505, 10510, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 10515, 10520, 10525, 10530, 10535, 10540, 10545, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10550, 10555, 10560, - 10565, 10570, 10575, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10580, - 10582, 10584, 10586, 10588, 10590, 10592, 10594, 10596, 10598, 10600, - 10602, 10604, 10606, 10608, 10610, 10612, 10614, 10616, 10618, 10620, - 10622, 10624, 10626, 10628, 10630, 10632, 10634, 10636, 10638, 10640, - 10642, 10644, 10646, 10648, 10650, 10652, 10654, 10656, 10658, 10660, - 10662, 10664, 10666, 10668, 10670, 10672, 10674, 10676, 10678, 10680, - 10682, 10684, 10686, 10688, 10690, 10692, 10694, 10696, 10698, 10700, - 10702, 10704, 10706, 10708, 10710, 10712, 10714, 10716, 10718, 10720, - 10722, 10724, 10726, 10728, 10730, 10732, 10734, 10736, 10738, 10740, - 10742, 10744, 10746, 10748, 0, 10750, 10752, 10754, 10756, 10758, 10760, - 10762, 10764, 10766, 10768, 10770, 10772, 10774, 10776, 10778, 10780, - 10782, 10784, 10786, 10788, 10790, 10792, 10794, 10796, 10798, 10800, - 10802, 10804, 10806, 10808, 10810, 10812, 10814, 10816, 10818, 10820, - 10822, 10824, 10826, 10828, 10830, 10832, 10834, 10836, 10838, 10840, - 10842, 10844, 10846, 10848, 10850, 10852, 10854, 10856, 10858, 10860, - 10862, 10864, 10866, 10868, 10870, 10872, 10874, 10876, 10878, 10880, - 10882, 10884, 10886, 10888, 10890, 0, 10892, 10894, 0, 0, 10896, 0, 0, - 10898, 10900, 0, 0, 10902, 10904, 10906, 10908, 0, 10910, 10912, 10914, - 10916, 10918, 10920, 10922, 10924, 10926, 10928, 10930, 10932, 0, 10934, - 0, 10936, 10938, 10940, 10942, 10944, 10946, 10948, 0, 10950, 10952, - 10954, 10956, 10958, 10960, 10962, 10964, 10966, 10968, 10970, 10972, - 10974, 10976, 10978, 10980, 10982, 10984, 10986, 10988, 10990, 10992, - 10994, 10996, 10998, 11000, 11002, 11004, 11006, 11008, 11010, 11012, - 11014, 11016, 11018, 11020, 11022, 11024, 11026, 11028, 11030, 11032, - 11034, 11036, 11038, 11040, 11042, 11044, 11046, 11048, 11050, 11052, - 11054, 11056, 11058, 11060, 11062, 11064, 11066, 11068, 11070, 11072, - 11074, 11076, 11078, 0, 11080, 11082, 11084, 11086, 0, 0, 11088, 11090, - 11092, 11094, 11096, 11098, 11100, 11102, 0, 11104, 11106, 11108, 11110, - 11112, 11114, 11116, 0, 11118, 11120, 11122, 11124, 11126, 11128, 11130, - 11132, 11134, 11136, 11138, 11140, 11142, 11144, 11146, 11148, 11150, - 11152, 11154, 11156, 11158, 11160, 11162, 11164, 11166, 11168, 11170, - 11172, 0, 11174, 11176, 11178, 11180, 0, 11182, 11184, 11186, 11188, - 11190, 0, 11192, 0, 0, 0, 11194, 11196, 11198, 11200, 11202, 11204, - 11206, 0, 11208, 11210, 11212, 11214, 11216, 11218, 11220, 11222, 11224, - 11226, 11228, 11230, 11232, 11234, 11236, 11238, 11240, 11242, 11244, - 11246, 11248, 11250, 11252, 11254, 11256, 11258, 11260, 11262, 11264, - 11266, 11268, 11270, 11272, 11274, 11276, 11278, 11280, 11282, 11284, - 11286, 11288, 11290, 11292, 11294, 11296, 11298, 11300, 11302, 11304, - 11306, 11308, 11310, 11312, 11314, 11316, 11318, 11320, 11322, 11324, - 11326, 11328, 11330, 11332, 11334, 11336, 11338, 11340, 11342, 11344, - 11346, 11348, 11350, 11352, 11354, 11356, 11358, 11360, 11362, 11364, - 11366, 11368, 11370, 11372, 11374, 11376, 11378, 11380, 11382, 11384, - 11386, 11388, 11390, 11392, 11394, 11396, 11398, 11400, 11402, 11404, - 11406, 11408, 11410, 11412, 11414, 11416, 11418, 11420, 11422, 11424, - 11426, 11428, 11430, 11432, 11434, 11436, 11438, 11440, 11442, 11444, - 11446, 11448, 11450, 11452, 11454, 11456, 11458, 11460, 11462, 11464, - 11466, 11468, 11470, 11472, 11474, 11476, 11478, 11480, 11482, 11484, - 11486, 11488, 11490, 11492, 11494, 11496, 11498, 11500, 11502, 11504, - 11506, 11508, 11510, 11512, 11514, 11516, 11518, 11520, 11522, 11524, - 11526, 11528, 11530, 11532, 11534, 11536, 11538, 11540, 11542, 11544, - 11546, 11548, 11550, 11552, 11554, 11556, 11558, 11560, 11562, 11564, - 11566, 11568, 11570, 11572, 11574, 11576, 11578, 11580, 11582, 11584, - 11586, 11588, 11590, 11592, 11594, 11596, 11598, 11600, 11602, 11604, - 11606, 11608, 11610, 11612, 11614, 11616, 11618, 11620, 11622, 11624, - 11626, 11628, 11630, 11632, 11634, 11636, 11638, 11640, 11642, 11644, - 11646, 11648, 11650, 11652, 11654, 11656, 11658, 11660, 11662, 11664, - 11666, 11668, 11670, 11672, 11674, 11676, 11678, 11680, 11682, 11684, - 11686, 11688, 11690, 11692, 11694, 11696, 11698, 11700, 11702, 11704, - 11706, 11708, 11710, 11712, 11714, 11716, 11718, 11720, 11722, 11724, - 11726, 11728, 11730, 11732, 11734, 11736, 11738, 11740, 11742, 11744, - 11746, 11748, 11750, 11752, 11754, 11756, 11758, 11760, 11762, 11764, - 11766, 11768, 11770, 11772, 11774, 11776, 11778, 11780, 11782, 11784, - 11786, 11788, 11790, 11792, 11794, 11796, 11798, 11800, 11802, 11804, - 11806, 11808, 11810, 11812, 11814, 11816, 11818, 11820, 11822, 11824, - 11826, 11828, 11830, 11832, 11834, 11836, 11838, 11840, 11842, 11844, - 11846, 11848, 11850, 11852, 11854, 11856, 11858, 11860, 11862, 11864, - 11866, 11868, 11870, 11872, 11874, 11876, 11878, 11880, 11882, 11884, - 11886, 0, 0, 11888, 11890, 11892, 11894, 11896, 11898, 11900, 11902, - 11904, 11906, 11908, 11910, 11912, 11914, 11916, 11918, 11920, 11922, - 11924, 11926, 11928, 11930, 11932, 11934, 11936, 11938, 11940, 11942, - 11944, 11946, 11948, 11950, 11952, 11954, 11956, 11958, 11960, 11962, - 11964, 11966, 11968, 11970, 11972, 11974, 11976, 11978, 11980, 11982, - 11984, 11986, 11988, 11990, 11992, 11994, 11996, 11998, 12000, 12002, - 12004, 12006, 12008, 12010, 12012, 12014, 12016, 12018, 12020, 12022, - 12024, 12026, 12028, 12030, 12032, 12034, 12036, 12038, 12040, 12042, - 12044, 12046, 12048, 12050, 12052, 12054, 12056, 12058, 12060, 12062, - 12064, 12066, 12068, 12070, 12072, 12074, 12076, 12078, 12080, 12082, - 12084, 12086, 12088, 12090, 12092, 12094, 12096, 12098, 12100, 12102, - 12104, 12106, 12108, 12110, 12112, 12114, 12116, 12118, 12120, 12122, - 12124, 12126, 12128, 12130, 12132, 12134, 12136, 12138, 12140, 12142, - 12144, 12146, 12148, 12150, 12152, 12154, 12156, 12158, 12160, 12162, - 12164, 12166, 12168, 12170, 12172, 12174, 12176, 12178, 12180, 12182, - 12184, 12186, 12188, 12190, 12192, 12194, 12196, 12198, 12200, 12202, - 12204, 12206, 12208, 12210, 12212, 12214, 12216, 12218, 12220, 12222, - 12224, 12226, 12228, 12230, 12232, 12234, 12236, 12238, 12240, 12242, - 12244, 12246, 12248, 12250, 12252, 12254, 12256, 12258, 12260, 12262, - 12264, 12266, 12268, 12270, 12272, 12274, 12276, 12278, 12280, 12282, - 12284, 12286, 12288, 12290, 12292, 12294, 12296, 12298, 12300, 12302, - 12304, 12306, 12308, 12310, 12312, 12314, 12316, 12318, 12320, 12322, - 12324, 12326, 12328, 12330, 12332, 12334, 12336, 12338, 12340, 12342, - 12344, 12346, 12348, 12350, 12352, 12354, 12356, 12358, 12360, 12362, - 12364, 12366, 12368, 12370, 12372, 12374, 12376, 12378, 12380, 12382, - 12384, 12386, 12388, 12390, 12392, 12394, 12396, 12398, 12400, 12402, - 12404, 12406, 12408, 12410, 12412, 12414, 12416, 12418, 12420, 12422, - 12424, 12426, 12428, 12430, 12432, 12434, 12436, 12438, 12440, 12442, - 12444, 12446, 12448, 12450, 12452, 12454, 12456, 12458, 12460, 12462, - 12464, 12466, 12468, 12470, 0, 0, 12472, 12474, 12476, 12478, 12480, - 12482, 12484, 12486, 12488, 12490, 12492, 12494, 12496, 12498, 12500, - 12502, 12504, 12506, 12508, 12510, 12512, 12514, 12516, 12518, 12520, - 12522, 12524, 12526, 12528, 12530, 12532, 12534, 12536, 12538, 12540, - 12542, 12544, 12546, 12548, 12550, 12552, 12554, 12556, 12558, 12560, - 12562, 12564, 12566, 12568, 12570, 12572, 12574, 12576, 12578, 0, 12580, - 12582, 12584, 12586, 12588, 12590, 12592, 12594, 12596, 12598, 12600, - 12602, 12604, 12606, 12608, 12610, 12612, 12614, 12616, 12618, 12620, - 12622, 12624, 12626, 12628, 12630, 12632, 0, 12634, 12636, 0, 12638, 0, - 0, 12640, 0, 12642, 12644, 12646, 12648, 12650, 12652, 12654, 12656, - 12658, 12660, 0, 12662, 12664, 12666, 12668, 0, 12670, 0, 12672, 0, 0, 0, - 0, 0, 0, 12674, 0, 0, 0, 0, 12676, 0, 12678, 0, 12680, 0, 12682, 12684, - 12686, 0, 12688, 12690, 0, 12692, 0, 0, 12694, 0, 12696, 0, 12698, 0, - 12700, 0, 12702, 0, 12704, 12706, 0, 12708, 0, 0, 12710, 12712, 12714, - 12716, 0, 12718, 12720, 12722, 12724, 12726, 12728, 12730, 0, 12732, - 12734, 12736, 12738, 0, 12740, 12742, 12744, 12746, 0, 12748, 0, 12750, - 12752, 12754, 12756, 12758, 12760, 12762, 12764, 12766, 12768, 0, 12770, - 12772, 12774, 12776, 12778, 12780, 12782, 12784, 12786, 12788, 12790, - 12792, 12794, 12796, 12798, 12800, 12802, 0, 0, 0, 0, 0, 12804, 12806, - 12808, 0, 12810, 12812, 12814, 12816, 12818, 0, 12820, 12822, 12824, - 12826, 12828, 12830, 12832, 12834, 12836, 12838, 12840, 12842, 12844, - 12846, 12848, 12850, 12852, 0, 0, 0, 0, 12854, 12857, 12860, 12863, - 12866, 12869, 12872, 12875, 12878, 12881, 12884, 0, 0, 0, 0, 0, 12887, - 12891, 12895, 12899, 12903, 12907, 12911, 12915, 12919, 12923, 12927, - 12931, 12935, 12939, 12943, 12947, 12951, 12955, 12959, 12963, 12967, - 12971, 12975, 12979, 12983, 12987, 12991, 12995, 12997, 12999, 13002, 0, - 13005, 13007, 13009, 13011, 13013, 13015, 13017, 13019, 13021, 13023, - 13025, 13027, 13029, 13031, 13033, 13035, 13037, 13039, 13041, 13043, - 13045, 13047, 13049, 13051, 13053, 13055, 13057, 13060, 13063, 13066, - 13069, 13073, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13076, 13079, 0, 0, 0, 0, - 13082, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13085, 13088, 13091, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13093, 13095, 13097, 13099, 13101, - 13103, 13105, 13107, 13109, 13111, 13113, 13115, 13117, 13119, 13121, - 13123, 13125, 13127, 13129, 13131, 13133, 13135, 13137, 13139, 13141, - 13143, 13145, 13147, 13149, 13151, 13153, 13155, 13157, 13159, 13161, - 13163, 13165, 13167, 13169, 13171, 13173, 13175, 13177, 13179, 0, 0, 0, - 0, 13181, 13185, 13189, 13193, 13197, 13201, 13205, 13209, 13213, 0, 0, - 0, 0, 0, 0, 0, 13217, 13219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 13221, 13223, 13225, 13227, 13230, 13232, 13234, 13236, 13238, 13240, - 13242, 13244, 13246, 13248, 13251, 13253, 13255, 13257, 13259, 13262, - 13264, 13266, 13268, 13271, 13273, 13275, 13277, 13279, 13281, 13284, - 13286, 13288, 13290, 13292, 13294, 13296, 13298, 13300, 13302, 13304, - 13306, 13308, 13310, 13312, 13314, 13316, 13318, 13320, 13322, 13324, - 13326, 13328, 13330, 13333, 13335, 13337, 13339, 13342, 13344, 13346, - 13348, 13350, 13352, 13354, 13356, 13358, 13360, 13362, 13364, 13366, - 13368, 13370, 13372, 13374, 13376, 13378, 13380, 13382, 13384, 13386, - 13388, 13390, 13392, 13394, 13396, 13398, 13400, 13402, 13404, 13406, - 13409, 13411, 13413, 13415, 13417, 13419, 13421, 13424, 13427, 13429, - 13431, 13433, 13435, 13437, 13439, 13441, 13443, 13445, 13447, 13450, - 13452, 13454, 13456, 13458, 13461, 13463, 13465, 13467, 13469, 13471, - 13473, 13475, 13477, 13479, 13482, 13484, 13487, 13489, 13491, 13493, - 13495, 13497, 13499, 13501, 13503, 13505, 13507, 13509, 13512, 13514, - 13516, 13518, 13520, 13522, 13525, 13527, 13530, 13533, 13535, 13537, - 13539, 13541, 13544, 13547, 13549, 13551, 13553, 13555, 13557, 13559, - 13561, 13563, 13565, 13567, 13569, 13572, 13574, 13576, 13578, 13580, - 13582, 13584, 13586, 13588, 13590, 13592, 13594, 13596, 13598, 13600, - 13602, 13604, 13606, 13608, 13610, 13613, 13615, 13617, 13619, 13621, - 13623, 13626, 13628, 13630, 13632, 13634, 13636, 13638, 13640, 13642, - 13644, 13646, 13648, 13651, 13653, 13655, 13657, 13659, 13661, 13663, - 13665, 13667, 13669, 13671, 13673, 13675, 13677, 13679, 13681, 13683, - 13685, 13687, 13690, 13692, 13694, 13696, 13698, 13700, 13703, 13705, - 13707, 13709, 13711, 13713, 13715, 13717, 13719, 13722, 13724, 13726, - 13728, 13731, 13733, 13735, 13737, 13739, 13741, 13743, 13746, 13749, - 13752, 13754, 13757, 13759, 13761, 13763, 13765, 13767, 13769, 13771, - 13773, 13775, 13777, 13780, 13782, 13784, 13786, 13788, 13790, 13792, - 13795, 13797, 13799, 13802, 13805, 13807, 13809, 13811, 13813, 13815, - 13817, 13819, 13821, 13823, 13826, 13828, 13831, 13833, 13836, 13838, - 13840, 13842, 13845, 13847, 13849, 13852, 13855, 13857, 13859, 13861, - 13863, 13865, 13867, 13869, 13871, 13873, 13875, 13877, 13879, 13881, - 13884, 13886, 13889, 13891, 13894, 13896, 13899, 13902, 13905, 13907, - 13909, 13911, 13914, 13917, 13920, 13923, 13925, 13927, 13929, 13931, - 13933, 13935, 13937, 13939, 13942, 13944, 13946, 13948, 13950, 13953, - 13955, 13958, 13961, 13963, 13965, 13967, 13969, 13971, 13973, 13976, - 13979, 13982, 13984, 13986, 13989, 13991, 13993, 13995, 13998, 14000, - 14002, 14004, 14006, 14008, 14011, 14013, 14015, 14017, 14019, 14021, - 14023, 14026, 14029, 14031, 14034, 14036, 14039, 14041, 14043, 14045, - 14048, 14051, 14053, 14056, 14058, 14061, 14063, 14065, 14067, 14069, - 14071, 14073, 14076, 14079, 14082, 14085, 14087, 14089, 14091, 14093, - 14095, 14097, 14099, 14101, 14103, 14105, 14107, 14109, 14112, 14114, - 14116, 14118, 14120, 14122, 14124, 14126, 14128, 14130, 14132, 14134, - 14136, 14139, 14142, 14145, 14147, 14149, 14151, 14153, 14156, 14158, - 14161, 14163, 14165, 14168, 14171, 14173, 14175, 14177, 14179, 14181, - 14183, 14185, 14187, 14189, 14191, 14193, 14195, 14197, 14199, 14201, - 14203, 14205, 14207, 14209, 14212, 14214, 14216, 14218, 14220, 14222, - 14225, 14228, 14230, 14232, 14234, 14236, 14238, 14240, 14243, 14245, - 14247, 14249, 14251, 14254, 14257, 14259, 14261, 14263, 14266, 14268, - 14270, 14273, 14276, 14278, 14280, 14282, 14285, 14287, 14289, 14291, - 14293, 14295, 14297, 14299, 14302, 14304, 14306, 14308, 14311, 14313, - 14315, 14317, 14319, 14322, 14325, 14327, 14329, 14331, 14334, 14336, - 14339, 14341, 14343, 14345, 14348, 14350, 14352, 14354, 14356, 14358, - 14360, 14362, 14365, 14367, 14369, 14371, 14373, 14375, 14377, 14380, - 14382, 14385, 14388, 14391, 14393, 14395, 14397, 14399, 14401, 14403, - 14405, 14407, 0, 0, -}; - -/* NFC pairs */ -#define COMP_SHIFT1 2 -#define COMP_SHIFT2 1 -static const unsigned short comp_index0[] = { - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4, - 5, 6, 7, 0, 0, 0, 0, 8, 0, 9, 10, 0, 0, 0, 11, 12, 13, 14, 0, 0, 0, 0, 0, - 15, 16, 17, 0, 0, 0, 0, 18, 19, 20, 21, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, - 23, 24, 25, 26, 0, 0, 0, 0, 27, 28, 29, 30, 0, 0, 0, 0, 31, 32, 33, 34, - 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 36, 0, 37, 38, 39, 0, 0, 0, 40, 41, 42, - 43, 0, 0, 0, 0, 44, 45, 46, 0, 0, 0, 0, 0, 47, 48, 49, 50, 0, 0, 0, 51, - 52, 53, 54, 0, 0, 0, 0, 55, 56, 0, 0, 0, 0, 0, 0, 57, 58, 59, 60, 0, 0, - 0, 0, 61, 62, 63, 0, 0, 0, 0, 0, 64, 65, 66, 67, 0, 0, 0, 68, 69, 70, 71, - 0, 0, 0, 0, 72, 0, 73, 0, 0, 0, 0, 0, 74, 0, 75, 0, 0, 0, 0, 0, 76, 0, 0, - 0, 0, 0, 0, 77, 78, 79, 0, 0, 0, 0, 0, 80, 81, 82, 83, 0, 0, 0, 0, 84, - 85, 86, 0, 0, 0, 0, 0, 87, 88, 0, 89, 0, 0, 0, 90, 91, 0, 92, 0, 0, 0, 0, - 0, 93, 94, 95, 0, 0, 0, 0, 96, 97, 98, 99, 0, 0, 0, 0, 100, 0, 0, 0, 0, - 0, 0, 101, 102, 0, 103, 0, 0, 0, 0, 104, 105, 106, 107, 0, 0, 0, 0, 108, - 109, 110, 111, 0, 0, 0, 0, 112, 113, 0, 0, 0, 0, 0, 114, 115, 116, 117, - 0, 0, 0, 0, 118, 119, 120, 121, 0, 0, 0, 0, 122, 0, 123, 0, 0, 0, 0, 124, - 125, 126, 127, 128, 0, 0, 0, 129, 130, 131, 132, 0, 0, 0, 0, 133, 134, 0, - 0, 0, 0, 0, 0, 135, 136, 137, 138, 0, 0, 0, 139, 140, 141, 142, 0, 0, 0, - 0, 0, 143, 144, 145, 0, 0, 0, 0, 146, 147, 148, 149, 0, 0, 0, 0, 150, 0, - 151, 0, 0, 0, 0, 152, 153, 154, 0, 0, 0, 0, 0, 0, 155, 0, 0, 0, 0, 0, 0, - 156, 157, 158, 0, 0, 0, 0, 0, 159, 160, 161, 162, 0, 0, 0, 163, 0, 0, 0, - 164, 0, 0, 0, 165, 166, 0, 0, 0, 0, 0, 0, 167, 0, 0, 0, 0, 0, 0, 0, 168, - 0, 0, 0, 0, 0, 0, 169, 170, 0, 0, 0, 0, 0, 0, 171, 0, 0, 0, 0, 0, 0, 0, - 172, 173, 0, 0, 0, 0, 0, 0, 174, 0, 0, 0, 0, 0, 0, 175, 176, 0, 0, 0, 0, - 0, 0, 177, 178, 0, 0, 0, 0, 0, 0, 179, 0, 0, 0, 0, 0, 0, 0, 180, 0, 0, 0, - 0, 0, 0, 181, 182, 183, 0, 0, 0, 0, 0, 184, 185, 0, 0, 0, 0, 0, 0, 186, - 0, 0, 0, 0, 0, 0, 0, 187, 0, 0, 0, 0, 0, 0, 188, 189, 0, 0, 0, 0, 0, 0, - 190, 0, 0, 0, 0, 0, 0, 0, 191, 192, 0, 0, 0, 0, 0, 0, 193, 0, 0, 0, 0, 0, - 0, 194, 195, 0, 0, 0, 0, 0, 0, 196, 197, 0, 0, 0, 0, 0, 0, 198, 0, 0, 0, - 0, 0, 0, 0, 199, 0, 0, 0, 0, 0, 0, 200, 201, 202, 0, 0, 0, 0, 0, 203, - 204, 0, 0, 0, 0, 0, 0, 205, 206, 0, 0, 0, 0, 0, 0, 207, 0, 0, 0, 0, 0, 0, - 208, 0, 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, - 0, 0, 211, 0, 0, 0, 0, 0, 0, 0, 212, 0, 0, 0, 0, 0, 0, 0, 213, 0, 0, 0, - 0, 0, 0, 0, 214, 0, 0, 0, 0, 0, 0, 215, 0, 0, 0, 0, 0, 0, 216, 0, 0, 0, - 0, 0, 0, 0, 0, 217, 0, 0, 0, 0, 0, 0, 0, 218, 0, 0, 0, 0, 0, 0, 219, 0, - 0, 0, 0, 0, 0, 220, 221, 222, 0, 0, 0, 0, 0, 223, 224, 225, 0, 0, 0, 0, - 0, 226, 227, 228, 0, 0, 0, 0, 0, 229, 230, 231, 0, 0, 0, 0, 0, 0, 232, 0, - 0, 0, 0, 0, 0, 233, 0, 0, 0, 0, 0, 0, 234, 0, 0, 0, 0, 0, 0, 0, 235, 0, - 0, 0, 0, 0, 0, 0, 236, 0, 0, 0, 0, 0, 0, 0, 237, 0, 0, 0, 0, 0, 0, 238, - 0, 0, 0, 0, 0, 0, 0, 239, 0, 0, 0, 0, 0, 0, 0, 240, 0, 0, 0, 0, 0, 0, 0, - 241, 0, 0, 0, 0, 0, 0, 242, 0, 243, 244, 0, 0, 0, 0, 245, 246, 0, 0, 0, - 0, 0, 247, 0, 248, 0, 249, 0, 0, 0, 250, 251, 252, 0, 0, 0, 0, 0, 253, 0, - 254, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 256, 257, 258, 0, 0, 0, 0, 0, - 259, 0, 260, 0, 261, 0, 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, 0, 0, 0, 263, 0, - 0, 0, 264, 265, 266, 0, 267, 0, 0, 0, 268, 0, 269, 0, 0, 0, 0, 0, 270, 0, - 271, 272, 0, 0, 0, 0, 273, 274, 0, 275, 0, 0, 0, 276, 0, 277, 0, 0, 0, 0, - 0, 0, 0, 278, 0, 0, 0, 0, 0, 279, 280, 281, 282, 0, 0, 0, 0, 283, 284, 0, - 285, 0, 0, 0, 286, 0, 0, 0, 287, 0, 0, 0, 288, 0, 0, 0, 289, 0, 0, 0, 0, - 0, 0, 290, 0, 0, 0, 0, 291, 0, 0, 0, 0, 0, 0, 0, 292, 0, 0, 0, 0, 0, 0, - 0, 293, 0, 0, 0, 0, 0, 0, 294, 0, 0, 0, 0, 0, 0, 0, 295, 0, 0, 0, 0, 0, - 0, 0, 296, 0, 0, 0, 0, 0, 0, 0, 297, 0, 0, 0, 0, 0, 0, 298, 299, 0, 0, 0, - 0, 0, 0, 300, 0, 0, 0, 0, 0, 0, 0, 301, 0, 0, 0, 0, 0, 0, 0, 302, 0, 0, - 0, 0, 0, 0, 0, 303, 0, 0, 0, 0, 0, 0, 304, 0, 0, 0, 0, 0, 0, 0, 305, 0, - 0, 0, 0, 0, 0, 0, 306, 0, 0, 0, 0, 0, 0, 307, 0, 0, 0, 0, 0, 0, 0, 308, - 0, 0, 0, 0, 0, 0, 0, 309, 0, 0, 0, 0, 0, 0, 0, 310, 0, 0, 0, 0, 0, 0, - 311, 312, 0, 0, 0, 0, 0, 0, 313, 0, 0, 0, 0, 0, 0, 0, 314, 0, 0, 0, 0, 0, - 0, 0, 315, 0, 0, 0, 0, 0, 0, 0, 316, 0, 0, 0, 0, 0, 0, 317, 0, 0, 0, 0, - 0, 0, 0, 318, 0, 0, 0, 0, 0, 0, 0, 319, 0, 0, 0, 0, 0, 0, 0, 320, 0, 0, - 0, 0, 0, 0, 0, 321, 0, 0, 0, 0, 0, 0, 322, 0, 0, 0, 0, 0, 0, 0, 323, 0, - 0, 0, 0, 0, 0, 0, 324, 0, 0, 0, 0, 0, 0, 325, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 326, 0, 0, 0, 0, 0, 0, 0, 327, 0, 0, 0, 0, 0, 0, 0, 328, 0, 0, 0, 0, - 0, 0, 329, 0, 0, 0, 0, 0, 0, 0, 330, 0, 0, 0, 0, 0, 0, 0, 331, 0, 0, 0, - 0, 0, 0, 0, 332, 0, 0, 0, 0, 0, 0, 0, 333, 0, 0, 0, 0, 0, 0, 334, 0, 0, - 0, 0, 0, 0, 0, 335, 0, 0, 0, 0, 0, 0, 0, 336, 337, 0, 0, 0, 0, 0, 0, 0, - 338, 0, 0, 0, 0, 0, 0, 339, 0, 0, 0, 0, 0, 0, 0, 340, 0, 0, 0, 0, 0, 0, - 0, 341, 0, 0, 0, 0, 0, 0, 0, 342, 0, 0, 0, 0, 0, 0, 0, 343, 0, 0, 0, 0, - 0, 0, 344, 0, 0, 0, 0, 0, 0, 0, 345, 346, 0, 0, 0, 0, 0, 0, 347, 0, 0, 0, - 0, 0, 0, 0, 348, 0, 0, 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 0, 0, 0, 350, 0, - 0, 0, 0, 0, 0, 0, 351, 0, 0, 0, 0, 0, 0, 0, 352, 0, 0, 0, 0, 0, 0, 353, - 0, 0, 0, 0, 0, 0, 0, 354, 0, 0, 0, 0, 0, 0, 0, 355, 0, 0, 0, 0, 0, 0, 0, - 356, 0, 0, 0, 0, 0, 0, 357, 0, 0, 0, 0, 0, 0, 0, 358, 0, 0, 0, 0, 0, 0, - 0, 359, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 361, 0, 362, 0, 0, 0, - 0, 0, 0, 0, 363, 0, 0, 0, 0, 0, 0, 0, 364, 0, 0, 0, 0, 0, 0, 0, 365, 0, - 0, 0, 0, 0, 0, 0, 366, 0, 0, 0, 0, 0, 0, 367, 0, 0, 0, 0, 0, 0, 0, 368, - 0, 0, 0, 0, 0, 0, 369, 370, 0, 0, 0, 0, 0, 0, 371, 0, 0, 0, 0, 0, 0, 0, - 372, 0, 0, 0, 0, 0, 0, 0, 373, 0, 0, 0, 0, 0, 0, 374, 0, 0, 0, 0, 0, 0, - 0, 375, 0, 0, 376, 0, 0, 0, 0, 377, 0, 0, 378, 0, 0, 0, 0, 0, 0, 0, 379, - 0, 0, 0, 0, 0, 0, 0, 380, 0, 0, 0, 0, 0, 0, 381, 0, 0, 0, 0, 0, 0, 0, - 382, 0, 0, 0, 0, 0, 0, 0, 383, 0, 0, 0, 0, 0, 0, 0, 384, 0, 0, 0, 385, 0, - 0, 386, 0, 0, 0, 0, 387, 0, 0, 388, 0, 0, 0, 0, 0, 0, 0, 389, 0, 0, 0, 0, - 0, 0, 0, 390, 0, 0, 0, 0, 0, 0, 391, 0, 0, 0, 0, 0, 0, 0, 392, 0, 0, 0, - 0, 0, 0, 0, 393, 0, 0, 0, 0, 0, 0, 0, 394, 0, 0, 0, 395, 0, 0, 0, 0, 0, - 0, 0, 396, 0, 0, 0, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, 0, 398, 0, 0, 0, 0, - 0, 0, 0, 399, 0, 0, 400, 0, 0, 0, 0, 401, 0, 0, 402, 0, 0, 0, 0, 0, 0, 0, - 403, 0, 0, 0, 0, 0, 0, 0, 404, 0, 0, 0, 0, 0, 0, 405, 0, 0, 0, 0, 0, 0, - 0, 406, 0, 0, 0, 0, 0, 0, 0, 407, 0, 0, 0, 0, 0, 0, 0, 408, 0, 0, 0, 409, - 0, 0, 410, 0, 0, 0, 0, 411, 0, 0, 412, 0, 0, 0, 0, 0, 0, 0, 413, 0, 0, 0, - 0, 0, 0, 0, 414, 0, 0, 0, 0, 0, 0, 415, 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, - 0, 0, 0, 0, 0, 417, 0, 0, 0, 0, 0, 0, 0, 418, 0, 0, 0, 419, 0, 0, 420, 0, - 0, 0, 0, 421, 0, 0, 422, 0, 0, 0, 423, 0, 0, 0, 424, 0, 0, 0, 425, 0, 0, - 0, 426, 0, 0, 0, 427, 0, 0, 0, 0, 0, 0, 0, 428, 0, 0, 0, 0, 0, 0, 429, 0, - 0, 0, 0, 0, 0, 0, 430, 0, 0, 0, 0, 0, 0, 0, 431, 0, 0, 432, 0, 0, 0, 0, - 433, 0, 0, 434, 0, 0, 0, 435, 0, 0, 0, 436, 0, 0, 0, 437, 0, 0, 0, 438, - 0, 0, 0, 439, 0, 0, 440, 0, 0, 0, 0, 0, 0, 0, 441, 0, 0, 0, 0, 0, 0, 0, - 442, 0, 0, 0, 0, 0, 0, 0, 443, 0, 0, 0, 0, 0, 0, 444, 0, 0, 0, 0, 0, 0, - 0, 445, 0, 0, 0, 0, 0, 0, 0, 446, 0, 0, 0, 447, 0, 0, 0, 448, 0, 0, 0, - 449, 0, 0, 450, 0, 0, 0, 0, 0, 0, 0, 451, 0, 0, 0, 0, 0, 0, 0, 452, 0, 0, - 0, 0, 0, 0, 0, 453, 0, 0, 0, 0, 0, 0, 454, 0, 0, 0, 0, 0, 0, 0, 455, 0, - 0, 0, 0, 0, 0, 0, 456, 0, 0, 0, 0, 0, 0, 0, 457, 0, 0, 0, 0, 0, 0, 458, - 0, 0, 0, 0, 0, 0, 0, 459, 0, 0, 0, 0, 0, 0, 0, 460, 0, 0, 0, 461, 0, 0, - 0, 462, 0, 0, 0, 0, 0, 0, 463, 0, 0, 0, 0, 0, 0, 0, 464, 0, 0, 0, 465, 0, - 0, 0, 466, 0, 0, 0, 0, 0, 0, 467, 0, 0, 0, 0, 0, 0, 0, 468, 0, 0, 0, 0, - 0, 0, 0, 469, 0, 0, 0, 0, 0, 0, 0, 470, 0, 0, 0, 0, 0, 0, 471, 0, 0, 0, - 0, 0, 0, 0, 472, 0, 0, 0, 0, 0, 0, 0, 473, 0, 0, 0, 0, 0, 0, 0, 474, 0, - 0, 0, 0, 0, 0, 475, 0, 0, 0, 0, 0, 0, 0, 476, 0, 0, 0, 0, 0, 0, 0, 477, - 0, 0, 0, 0, 0, 0, 0, 478, 0, 0, 0, 0, 0, 0, 479, 0, 0, 0, 0, 0, 0, 0, - 480, 0, 0, 0, 0, 0, 0, 0, 481, 0, 0, 0, 0, 0, 0, 0, 482, 0, 0, 0, 0, 0, - 0, 483, 0, 0, 0, 0, 0, 0, 0, 484, 0, 0, 0, 0, 0, 0, 0, 485, 0, 0, 0, 0, - 0, 0, 0, 486, 0, 0, 0, 0, 0, 0, 487, 0, 0, 0, 0, 0, 0, 0, 488, 0, 0, 0, - 0, 0, 0, 0, 489, 0, 0, 0, 0, 0, 0, 0, 490, 0, 0, 0, 0, 0, 0, 491, 0, 0, - 0, 0, 0, 0, 0, 492, 0, 0, 0, 0, 0, 0, 0, 493, 0, 0, 0, 0, 0, 0, 0, 494, - 0, 0, 0, 0, 0, 0, 495, 0, 0, 0, 0, 0, 0, 0, 496, 0, 0, 0, 0, 0, 0, 0, - 497, 0, 0, 0, 0, 0, 0, 0, 498, 0, 0, 0, 0, 0, 0, 499, 0, 0, 0, 0, 0, 0, - 0, 500, 0, 0, 0, 0, 0, 0, 0, 501, 0, 0, 0, 0, 0, 0, 0, 502, 0, 0, 0, 0, - 0, 0, 503, 0, 0, 0, 0, 0, 0, 0, 504, 0, 0, 0, 0, 0, 0, 0, 505, 0, 0, 0, - 0, 0, 0, 0, 506, 0, 0, 0, 0, 0, 0, 507, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 508, 0, 0, 0, 0, 0, 0, 0, 509, 0, 0, 0, 0, 0, 0, 0, 510, 0, 0, 0, 0, 0, - 0, 0, 511, 0, 0, 0, 0, 0, 0, 512, 0, 0, 0, 0, 0, 0, 0, 513, 0, 0, 0, 0, - 0, 0, 0, 514, 0, 0, 0, 0, 0, 0, 0, 515, 0, 0, 0, 0, 0, 0, 516, 0, 0, 0, - 0, 0, 0, 0, 517, 0, 0, 0, 0, 0, 0, 0, 518, 0, 0, 0, 0, 0, 0, 0, 519, 0, - 0, 0, 0, 0, 0, 520, 0, 0, 0, 0, 0, 0, 0, 521, 0, 0, 0, 0, 0, 0, 0, 522, - 0, 0, 0, 0, 0, 0, 0, 523, 0, 0, 0, 0, 0, 0, 524, 0, 0, 0, 0, 0, 0, 0, - 525, 0, 0, 0, 0, 0, 0, 0, 526, 0, 0, 0, 0, 0, 0, 0, 527, 0, 0, 0, 0, 0, - 0, 528, 0, 0, 0, 0, 0, 0, 0, 529, 0, 0, 0, 0, 0, 0, 0, 530, 0, 0, 0, 0, - 0, 0, 0, 531, 0, 0, 0, 0, 0, 0, 532, 0, 0, 0, 0, 0, 0, 0, 533, 0, 0, 0, - 0, 0, 0, 0, 534, 0, 0, 0, 0, 0, 0, 0, 535, 0, 0, 0, 0, 0, 0, 536, 0, 0, - 0, 0, 0, 0, 0, 537, 0, 0, 0, 0, 0, 0, 0, 538, 0, 0, 0, 0, 0, 0, 0, 539, - 0, 0, 0, 0, 0, 0, 540, 0, 0, 0, 0, 0, 0, 0, 541, 0, 0, 0, 0, 0, 0, 0, - 542, 0, 0, 0, 0, 0, 0, 0, 543, 0, 0, 0, 0, 0, 0, 544, 0, 0, 0, 0, 0, 0, - 0, 545, 0, 0, 0, 0, 0, 0, 0, 546, 0, 0, 0, 0, 0, 0, 0, 547, 0, 0, 0, 0, - 0, 0, 548, 0, 0, 0, 0, 0, 0, 0, 549, 0, 0, 0, 0, 0, 0, 0, 550, 0, 0, 0, - 0, 0, 0, 0, 551, 0, 0, 0, 0, 0, 0, 552, 0, 0, 0, 0, 0, 0, 0, 553, 0, 0, - 0, 0, 0, 0, 0, 554, 0, 0, 0, 0, 0, 0, 0, 555, 0, 0, 0, 0, 0, 0, 0, 556, - 0, 0, 0, 0, 0, 0, 557, 0, 0, 0, 0, 0, 0, 0, 558, 0, 0, 0, 0, 0, 0, 0, - 559, 0, 0, 0, 0, 0, 0, 0, 560, 0, 0, 0, 0, 0, 0, 0, 561, 0, 0, 0, 0, 0, - 0, 0, 562, 0, 0, 0, 0, 0, 0, 0, 563, 0, 0, 0, 0, 0, 0, 564, -}; - -static const unsigned short comp_index1[] = { - 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 0, 4, 5, 6, 7, 8, 9, 10, - 0, 11, 12, 0, 13, 0, 0, 0, 0, 0, 0, 14, 15, 0, 0, 0, 0, 16, 0, 0, 0, 0, - 0, 17, 18, 0, 19, 0, 20, 0, 0, 0, 0, 21, 0, 0, 0, 22, 0, 23, 0, 0, 24, 0, - 25, 26, 0, 27, 0, 28, 29, 30, 31, 32, 33, 34, 0, 35, 0, 36, 37, 38, 0, 0, - 0, 0, 0, 39, 0, 0, 0, 40, 41, 42, 43, 0, 44, 0, 0, 0, 0, 45, 0, 0, 0, 0, - 0, 46, 0, 47, 0, 48, 0, 0, 49, 0, 50, 0, 51, 0, 0, 52, 53, 54, 55, 56, - 57, 58, 0, 59, 0, 0, 60, 61, 0, 0, 0, 62, 0, 0, 0, 0, 0, 63, 64, 0, 0, - 65, 0, 66, 0, 0, 67, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 69, 0, 0, 70, 0, 71, - 72, 0, 73, 0, 74, 0, 0, 75, 0, 0, 0, 0, 76, 0, 0, 77, 78, 0, 79, 0, 80, - 0, 0, 81, 0, 82, 83, 0, 84, 0, 0, 0, 0, 0, 85, 86, 87, 88, 89, 90, 91, 0, - 92, 0, 0, 93, 0, 0, 0, 94, 0, 0, 95, 0, 0, 0, 96, 0, 0, 97, 0, 98, 99, 0, - 100, 0, 101, 0, 0, 102, 0, 103, 104, 0, 105, 0, 106, 0, 0, 107, 0, 108, - 0, 0, 0, 109, 0, 110, 0, 0, 111, 0, 112, 113, 0, 114, 0, 0, 0, 0, 0, 115, - 116, 117, 118, 119, 120, 121, 0, 122, 123, 0, 124, 125, 0, 0, 0, 126, 0, - 0, 127, 0, 0, 128, 129, 0, 130, 131, 0, 0, 0, 0, 0, 132, 0, 0, 0, 133, - 134, 135, 136, 137, 0, 0, 0, 138, 0, 0, 139, 140, 0, 141, 0, 142, 0, 0, - 143, 0, 0, 0, 0, 144, 0, 145, 146, 147, 148, 149, 150, 151, 0, 152, 153, - 0, 154, 0, 0, 155, 0, 0, 0, 0, 156, 157, 0, 0, 0, 0, 0, 158, 159, 0, 160, - 0, 161, 162, 0, 0, 0, 163, 0, 164, 0, 0, 165, 0, 166, 167, 0, 168, 0, - 169, 170, 171, 172, 173, 174, 175, 0, 176, 0, 177, 178, 179, 0, 0, 0, 0, - 0, 180, 0, 0, 0, 181, 182, 183, 184, 0, 185, 186, 0, 0, 0, 0, 0, 187, 0, - 188, 0, 189, 0, 0, 190, 0, 191, 0, 192, 193, 0, 194, 195, 196, 197, 198, - 199, 200, 0, 201, 0, 0, 202, 203, 0, 0, 0, 204, 0, 0, 0, 205, 0, 0, 0, 0, - 0, 206, 0, 0, 0, 0, 207, 0, 0, 208, 0, 209, 0, 0, 210, 0, 211, 0, 0, 0, - 0, 212, 0, 0, 213, 0, 214, 215, 0, 216, 0, 217, 0, 0, 218, 219, 0, 0, 0, - 0, 0, 0, 220, 221, 0, 222, 0, 223, 0, 0, 224, 0, 225, 226, 0, 227, 0, 0, - 0, 0, 0, 228, 229, 230, 231, 232, 233, 234, 0, 235, 0, 0, 236, 0, 0, 0, - 237, 0, 0, 238, 0, 0, 0, 239, 0, 0, 240, 0, 241, 242, 0, 243, 0, 244, 0, - 0, 245, 0, 0, 0, 0, 0, 246, 247, 0, 248, 0, 249, 0, 0, 250, 0, 251, 0, 0, - 0, 252, 0, 253, 0, 0, 254, 0, 255, 256, 0, 257, 0, 258, 259, 260, 261, - 262, 263, 264, 0, 265, 266, 0, 267, 268, 0, 0, 0, 269, 0, 0, 270, 0, 0, - 0, 0, 0, 0, 271, 272, 0, 273, 274, 0, 0, 0, 275, 0, 276, 0, 0, 0, 277, - 278, 279, 280, 281, 0, 0, 0, 282, 0, 0, 283, 284, 0, 285, 0, 286, 0, 0, - 287, 0, 0, 0, 0, 288, 0, 0, 0, 0, 0, 289, 0, 290, 0, 0, 0, 0, 291, 292, - 0, 0, 293, 0, 0, 0, 0, 294, 295, 0, 0, 0, 0, 0, 0, 296, 0, 297, 0, 0, 0, - 0, 298, 0, 0, 299, 300, 0, 0, 301, 0, 0, 302, 0, 0, 0, 0, 0, 0, 303, 304, - 0, 0, 305, 0, 0, 306, 0, 307, 308, 0, 0, 0, 0, 0, 309, 310, 0, 0, 0, 0, - 0, 0, 311, 0, 312, 0, 0, 313, 0, 0, 0, 0, 0, 314, 315, 0, 0, 316, 0, 0, - 0, 0, 317, 318, 0, 0, 0, 0, 0, 0, 319, 0, 320, 0, 0, 0, 0, 321, 0, 0, - 322, 323, 0, 0, 324, 0, 0, 325, 0, 0, 0, 0, 0, 0, 326, 327, 0, 0, 328, 0, - 0, 329, 0, 330, 331, 0, 0, 0, 0, 0, 332, 333, 0, 0, 0, 0, 0, 0, 334, 0, - 335, 0, 0, 336, 0, 0, 0, 0, 0, 337, 338, 0, 0, 339, 0, 0, 340, 341, 0, 0, - 342, 0, 0, 343, 0, 0, 0, 0, 0, 0, 344, 0, 0, 345, 0, 0, 346, 0, 0, 0, 0, - 0, 347, 0, 0, 348, 0, 0, 349, 0, 0, 350, 0, 0, 0, 351, 0, 0, 0, 0, 0, 0, - 352, 0, 353, 0, 0, 354, 0, 0, 0, 0, 0, 0, 355, 0, 0, 0, 356, 357, 0, 0, - 358, 0, 0, 0, 359, 0, 0, 360, 361, 0, 0, 362, 0, 0, 0, 363, 0, 0, 364, - 365, 0, 0, 366, 0, 0, 0, 367, 0, 0, 368, 369, 0, 0, 370, 0, 0, 0, 371, 0, - 0, 0, 372, 0, 0, 0, 373, 0, 0, 0, 0, 0, 0, 374, 0, 0, 375, 0, 0, 376, 0, - 0, 377, 0, 0, 0, 0, 0, 0, 378, 0, 0, 379, 0, 0, 380, 0, 0, 0, 0, 0, 381, - 0, 382, 0, 383, 384, 0, 0, 0, 0, 0, 0, 385, 386, 0, 0, 0, 0, 0, 0, 387, - 0, 0, 0, 388, 0, 0, 389, 0, 0, 390, 0, 0, 0, 0, 391, 0, 392, 393, 0, 0, - 0, 394, 0, 0, 0, 395, 0, 0, 396, 0, 0, 0, 0, 0, 0, 397, 0, 0, 0, 398, 0, - 399, 400, 0, 0, 0, 401, 0, 0, 0, 402, 0, 0, 403, 0, 0, 404, 0, 0, 0, 0, - 0, 0, 405, 0, 0, 406, 0, 0, 0, 0, 407, 0, 408, 0, 0, 0, 0, 409, 0, 0, - 410, 0, 0, 0, 0, 411, 0, 0, 412, 0, 0, 0, 413, 0, 0, 414, 0, 0, 0, 0, 0, - 0, 415, 416, 0, 417, 418, 0, 0, 0, 419, 0, 0, 420, 0, 0, 0, 0, 421, 0, 0, - 422, 0, 0, 423, 0, 0, 0, 424, 0, 425, 426, 0, 0, 0, 427, 0, 0, 0, 0, 0, - 0, 428, 429, 0, 0, 0, 0, 0, 0, 430, 0, 0, 431, 0, 0, 0, 0, 432, 0, 433, - 0, 0, 0, 0, 434, 0, 435, 0, 0, 0, 0, 0, 0, 436, 437, 0, 0, 438, 0, 0, - 439, 0, 440, 441, 0, 0, 0, 442, 0, 0, 443, 0, 444, 445, 0, 446, 447, 0, - 0, 448, 0, 0, 0, 449, 0, 450, 451, 0, 0, 0, 452, 0, 0, 0, 0, 0, 453, 0, - 454, 455, 0, 456, 457, 0, 0, 0, 0, 0, 0, 458, 0, 0, 459, 0, 460, 461, 0, - 0, 0, 462, 0, 0, 463, 0, 464, 465, 0, 466, 467, 0, 0, 468, 0, 0, 0, 469, - 0, 470, 471, 0, 0, 0, 472, 0, 0, 0, 0, 0, 473, 0, 474, 475, 0, 476, 477, - 0, 0, 0, 0, 0, 0, 478, 0, 0, 479, 0, 0, 480, 0, 0, 0, 0, 0, 481, 0, 0, - 482, 0, 0, 0, 483, 0, 0, 484, 0, 0, 485, 0, 0, 0, 0, 0, 0, 486, 0, 0, - 487, 488, 0, 489, 0, 0, 490, 0, 0, 0, 0, 0, 0, 491, 0, 0, 492, 0, 0, 493, - 0, 0, 0, 494, 0, 0, 495, 0, 0, 0, 0, 0, 0, 496, 0, 0, 0, 497, 0, 0, 0, - 498, 499, 0, 0, 0, 500, 0, 0, 0, 0, 0, 501, 502, 0, 503, 0, 0, 0, 504, 0, - 0, 0, 505, 0, 0, 506, 507, 0, 0, 0, 0, 0, 508, 0, 0, 0, 509, 510, 0, 0, - 0, 0, 0, 511, 0, 0, 0, 512, 513, 0, 514, 0, 0, 0, 0, 515, 0, 0, 516, 0, - 0, 517, 0, 0, 0, 0, 0, 0, 518, 0, 0, 519, 0, 0, 520, 0, 0, 521, 0, 0, 0, - 0, 0, 0, 522, 0, 0, 523, 0, 0, 524, 0, 0, 525, 0, 0, 0, 0, 0, 0, 526, 0, - 0, 0, 527, 0, 0, 528, 0, 0, 529, 0, 0, 530, 0, 0, 0, 531, 0, 0, 0, 0, 0, - 0, 532, 533, 534, 0, 0, 0, 0, 0, 535, 536, 0, 0, 0, 0, 0, 537, 0, 0, 538, - 0, 0, 539, 0, 0, 0, 0, 0, 0, 540, 0, 541, 0, 0, 0, 0, 0, 542, 543, 0, 0, - 0, 0, 0, 544, 0, 0, 545, 0, 0, 546, 0, 0, 0, 0, 0, 0, 547, 0, 0, 548, 0, - 0, 549, 0, 0, 550, 0, 0, 0, 0, 551, 0, 0, 0, 0, 0, 552, 553, 0, 0, 0, 0, - 0, 554, 0, 0, 555, 0, 0, 556, 0, 0, 0, 0, 0, 0, 557, 0, 0, 558, 0, 0, - 559, 0, 0, 560, 0, 0, 0, 0, 561, 0, 0, 562, 0, 0, 0, 0, 0, 0, 563, 0, 0, - 564, 0, 0, 565, 0, 0, 0, 0, 0, 566, 567, 0, 0, 0, 0, 0, 568, 0, 0, 569, - 0, 0, 570, 0, 0, 0, 0, 0, 0, 571, 0, 0, 572, 0, 0, 573, 0, 0, 574, 0, 0, - 0, 0, 575, 0, 0, 0, 0, 0, 576, 577, 0, 0, 0, 0, 0, 578, 0, 0, 579, 0, 0, - 580, 0, 0, 0, 0, 0, 0, 581, 0, 0, 582, 0, 0, 583, 0, 0, 584, 0, 0, 0, 0, - 585, 0, 0, 0, 0, 0, 586, 587, 0, 0, 0, 0, 0, 588, 0, 0, 0, 0, 589, 0, - 590, 0, 0, 0, 0, 591, 0, 592, 0, 0, 0, 0, 593, 0, 0, 594, 0, 0, 0, 0, 0, - 0, 595, 0, 0, 596, 0, 0, 597, 0, 0, 0, 0, 0, 598, 599, 0, 0, 0, 0, 0, - 600, 0, 0, 0, 0, 601, 0, 602, 0, 0, 0, 0, 603, 0, 604, 0, 0, 0, 0, 605, - 0, 0, 0, 0, 0, 606, 0, 0, 607, 0, 0, 608, 0, 0, 609, 0, 0, 0, 0, 0, 0, - 610, 0, 0, 611, 0, 0, 612, 0, 0, 0, 0, 613, 0, 614, 0, 0, 0, 0, 615, 0, - 0, 0, 0, 0, 616, 0, 0, 617, 0, 0, 618, 0, 0, 619, 0, 0, 0, 0, 0, 0, 620, - 0, 0, 621, 0, 0, 622, 0, 0, 623, 0, 0, 0, 0, 0, 0, 624, 0, 0, 625, 0, 0, - 626, 0, 0, 0, 0, 627, 0, 628, 0, 0, 0, 0, 0, 0, 629, 0, 0, 630, 0, 0, 0, - 0, 631, 0, 632, 0, 0, 0, 0, 0, 633, 0, 0, 634, 0, 0, 635, 0, 0, 636, 0, - 0, 0, 0, 0, 0, 637, 0, 0, 638, 0, 0, 639, 0, 0, 640, 0, 0, 0, 0, 0, 0, - 641, 0, 0, 642, 0, 0, 643, 0, 0, 644, 0, 0, 0, 0, 0, 0, 645, 0, 0, 646, - 0, 0, 647, 0, 0, 648, 0, 0, 0, 0, 0, 0, 649, 0, 0, 650, 0, 0, 651, 0, 0, - 652, 0, 0, 0, 0, 0, 0, 653, 0, 0, 654, 0, 0, 655, 0, 0, 656, 0, 0, 0, 0, - 0, 0, 657, 0, 0, 658, 0, 0, 659, 0, 0, 660, 0, 0, 0, 0, 0, 0, 661, 0, 0, - 662, 0, 0, 663, 0, 0, 664, 0, 0, 0, 0, 0, 0, 665, 0, 0, 666, 0, 0, 667, - 0, 0, 668, 0, 0, 0, 0, 0, 0, 669, 0, 0, 670, 0, 0, 671, 0, 0, 672, 0, 0, - 0, 0, 0, 0, 673, 0, 0, 0, 674, 0, 0, 675, 0, 0, 676, 0, 0, 677, 0, 0, 0, - 0, 0, 0, 678, 0, 0, 679, 0, 0, 680, 0, 0, 681, 0, 0, 0, 0, 0, 0, 682, 0, - 0, 683, 0, 0, 684, 0, 0, 685, 0, 0, 0, 0, 0, 0, 686, 0, 0, 687, 0, 0, - 688, 0, 0, 689, 0, 0, 0, 0, 0, 0, 690, 0, 0, 691, 0, 0, 692, 0, 0, 693, - 0, 0, 0, 0, 0, 0, 694, 0, 0, 695, 0, 0, 696, 0, 0, 697, 0, 0, 0, 0, 0, 0, - 698, 0, 0, 699, 0, 0, 700, 0, 0, 701, 0, 0, 0, 0, 0, 0, 702, 0, 0, 703, - 0, 0, 704, 0, 0, 705, 0, 0, 0, 0, 0, 0, 706, 0, 0, 707, 0, 0, 708, 0, 0, - 709, 0, 0, 0, 0, 0, 0, 710, 0, 0, 711, 0, 0, 712, 0, 0, 713, 0, 0, 0, 0, - 0, 0, 714, 0, 0, 715, 0, 0, 716, 0, 0, 717, 0, 0, 0, 0, 0, 0, 718, 0, 0, - 719, 0, 0, 720, 0, 0, 721, 0, 0, 0, 722, 0, 0, 0, 0, 0, 0, 723, 0, 0, - 724, 0, 0, 725, 0, 0, 726, 0, 0, 0, 727, 0, 0, 0, 728, 729, 0, 0, 730, 0, - 0, 0, 0, 0, 0, 731, -}; - -static const unsigned int comp_data[] = { - 0, 0, 0, 8814, 0, 8800, 0, 8815, 192, 193, 194, 195, 256, 258, 550, 196, - 7842, 197, 0, 461, 512, 514, 0, 7840, 0, 7680, 260, 0, 7682, 0, 0, 7684, - 7686, 0, 0, 262, 264, 0, 266, 0, 0, 268, 0, 199, 7690, 0, 0, 270, 0, - 7692, 0, 7696, 0, 7698, 7694, 0, 200, 201, 202, 7868, 274, 276, 278, 203, - 7866, 0, 0, 282, 516, 518, 0, 7864, 0, 552, 280, 7704, 0, 7706, 7710, 0, - 0, 500, 284, 0, 7712, 286, 288, 0, 0, 486, 0, 290, 292, 0, 7714, 7718, 0, - 542, 0, 7716, 0, 7720, 7722, 0, 204, 205, 206, 296, 298, 300, 304, 207, - 7880, 0, 0, 463, 520, 522, 0, 7882, 302, 0, 0, 7724, 308, 0, 0, 7728, 0, - 488, 0, 7730, 0, 310, 7732, 0, 0, 313, 0, 317, 0, 7734, 0, 315, 0, 7740, - 7738, 0, 0, 7742, 7744, 0, 0, 7746, 504, 323, 0, 209, 7748, 0, 0, 327, 0, - 7750, 0, 325, 0, 7754, 7752, 0, 210, 211, 212, 213, 332, 334, 558, 214, - 7886, 0, 336, 465, 524, 526, 416, 7884, 490, 0, 0, 7764, 7766, 0, 0, 340, - 7768, 0, 0, 344, 528, 530, 0, 7770, 0, 342, 7774, 0, 0, 346, 348, 0, - 7776, 0, 0, 352, 0, 7778, 536, 350, 7786, 0, 0, 356, 0, 7788, 538, 354, - 0, 7792, 7790, 0, 217, 218, 219, 360, 362, 364, 0, 220, 7910, 366, 368, - 467, 532, 534, 431, 7908, 7794, 0, 370, 7798, 0, 7796, 0, 7804, 0, 7806, - 7808, 7810, 372, 0, 7814, 7812, 0, 7816, 7818, 7820, 7922, 221, 374, - 7928, 562, 0, 7822, 376, 7926, 0, 0, 7924, 0, 377, 7824, 0, 379, 0, 0, - 381, 0, 7826, 7828, 0, 224, 225, 226, 227, 257, 259, 551, 228, 7843, 229, - 0, 462, 513, 515, 0, 7841, 0, 7681, 261, 0, 7683, 0, 0, 7685, 7687, 0, 0, - 263, 265, 0, 267, 0, 0, 269, 0, 231, 7691, 0, 0, 271, 0, 7693, 0, 7697, - 0, 7699, 7695, 0, 232, 233, 234, 7869, 275, 277, 279, 235, 7867, 0, 0, - 283, 517, 519, 0, 7865, 0, 553, 281, 7705, 0, 7707, 7711, 0, 0, 501, 285, - 0, 7713, 287, 289, 0, 0, 487, 0, 291, 293, 0, 7715, 7719, 0, 543, 0, - 7717, 0, 7721, 7723, 0, 7830, 0, 236, 237, 238, 297, 299, 301, 0, 239, - 7881, 0, 0, 464, 521, 523, 0, 7883, 303, 0, 0, 7725, 309, 0, 0, 496, 0, - 7729, 0, 489, 0, 7731, 0, 311, 7733, 0, 0, 314, 0, 318, 0, 7735, 0, 316, - 0, 7741, 7739, 0, 0, 7743, 7745, 0, 0, 7747, 505, 324, 0, 241, 7749, 0, - 0, 328, 0, 7751, 0, 326, 0, 7755, 7753, 0, 242, 243, 244, 245, 333, 335, - 559, 246, 7887, 0, 337, 466, 525, 527, 417, 7885, 491, 0, 0, 7765, 7767, - 0, 0, 341, 7769, 0, 0, 345, 529, 531, 0, 7771, 0, 343, 7775, 0, 0, 347, - 349, 0, 7777, 0, 0, 353, 0, 7779, 537, 351, 7787, 7831, 0, 357, 0, 7789, - 539, 355, 0, 7793, 7791, 0, 249, 250, 251, 361, 363, 365, 0, 252, 7911, - 367, 369, 468, 533, 535, 432, 7909, 7795, 0, 371, 7799, 0, 7797, 0, 7805, - 0, 7807, 7809, 7811, 373, 0, 7815, 7813, 0, 7832, 0, 7817, 7819, 7821, - 7923, 253, 375, 7929, 563, 0, 7823, 255, 7927, 7833, 0, 7925, 0, 378, - 7825, 0, 380, 0, 0, 382, 0, 7827, 7829, 0, 8173, 901, 8129, 0, 7846, - 7844, 0, 7850, 7848, 0, 478, 0, 0, 506, 0, 508, 482, 0, 0, 7688, 7872, - 7870, 0, 7876, 7874, 0, 0, 7726, 7890, 7888, 0, 7894, 7892, 0, 0, 7756, - 556, 0, 0, 7758, 554, 0, 0, 510, 475, 471, 469, 0, 0, 473, 7847, 7845, 0, - 7851, 7849, 0, 479, 0, 0, 507, 0, 509, 483, 0, 0, 7689, 7873, 7871, 0, - 7877, 7875, 0, 0, 7727, 7891, 7889, 0, 7895, 7893, 0, 0, 7757, 557, 0, 0, - 7759, 555, 0, 0, 511, 476, 472, 470, 0, 0, 474, 7856, 7854, 0, 7860, - 7858, 0, 7857, 7855, 0, 7861, 7859, 0, 7700, 7702, 7701, 7703, 7760, - 7762, 7761, 7763, 7780, 0, 7781, 0, 7782, 0, 7783, 0, 0, 7800, 0, 7801, - 0, 7802, 0, 7803, 7835, 0, 7900, 7898, 0, 7904, 7902, 0, 0, 7906, 7901, - 7899, 0, 7905, 7903, 0, 0, 7907, 7914, 7912, 0, 7918, 7916, 0, 0, 7920, - 7915, 7913, 0, 7919, 7917, 0, 0, 7921, 0, 494, 492, 0, 493, 0, 480, 0, - 481, 0, 0, 7708, 0, 7709, 560, 0, 561, 0, 0, 495, 8122, 902, 8121, 8120, - 7944, 7945, 0, 8124, 8136, 904, 7960, 7961, 8138, 905, 7976, 7977, 0, - 8140, 8154, 906, 8153, 8152, 0, 938, 7992, 7993, 8184, 908, 8008, 8009, - 0, 8172, 8170, 910, 8169, 8168, 0, 939, 0, 8025, 8186, 911, 8040, 8041, - 0, 8188, 0, 8116, 0, 8132, 8048, 940, 8113, 8112, 7936, 7937, 8118, 8115, - 8050, 941, 7952, 7953, 8052, 942, 7968, 7969, 8134, 8131, 8054, 943, - 8145, 8144, 0, 970, 7984, 7985, 8150, 0, 8056, 972, 8000, 8001, 8164, - 8165, 8058, 973, 8161, 8160, 0, 971, 8016, 8017, 8166, 0, 8060, 974, - 8032, 8033, 8182, 8179, 8146, 912, 8151, 0, 8162, 944, 8167, 0, 0, 8180, - 0, 979, 0, 980, 0, 1031, 0, 1232, 0, 1234, 0, 1027, 1024, 0, 0, 1238, 0, - 1025, 0, 1217, 0, 1244, 0, 1246, 1037, 0, 1250, 1049, 0, 1252, 0, 1036, - 0, 1254, 1262, 1038, 0, 1264, 1266, 0, 0, 1268, 0, 1272, 0, 1260, 0, - 1233, 0, 1235, 0, 1107, 1104, 0, 0, 1239, 0, 1105, 0, 1218, 0, 1245, 0, - 1247, 1117, 0, 1251, 1081, 0, 1253, 0, 1116, 0, 1255, 1263, 1118, 0, - 1265, 1267, 0, 0, 1269, 0, 1273, 0, 1261, 0, 1111, 1142, 0, 1143, 0, 0, - 1242, 0, 1243, 0, 1258, 0, 1259, 1570, 1571, 1573, 0, 0, 1572, 0, 1574, - 0, 1730, 0, 1747, 0, 1728, 0, 2345, 0, 2353, 0, 2356, 2507, 2508, 2891, - 2888, 2892, 0, 2964, 0, 0, 3018, 3020, 0, 0, 3019, 0, 3144, 0, 3264, - 3274, 3271, 3272, 0, 0, 3275, 0, 3402, 3404, 0, 0, 3403, 0, 3546, 3548, - 3550, 0, 3549, 4134, 0, 0, 6918, 0, 6920, 0, 6922, 0, 6924, 0, 6926, 0, - 6930, 0, 6971, 0, 6973, 0, 6976, 0, 6977, 0, 6979, 7736, 0, 7737, 0, - 7772, 0, 7773, 0, 7784, 0, 7785, 0, 7852, 0, 0, 7862, 7853, 0, 0, 7863, - 7878, 0, 7879, 0, 7896, 0, 7897, 0, 7938, 7940, 7942, 8064, 7939, 7941, - 7943, 8065, 0, 8066, 0, 8067, 0, 8068, 0, 8069, 0, 8070, 0, 8071, 7946, - 7948, 7950, 8072, 7947, 7949, 7951, 8073, 0, 8074, 0, 8075, 0, 8076, 0, - 8077, 0, 8078, 0, 8079, 7954, 7956, 7955, 7957, 7962, 7964, 7963, 7965, - 7970, 7972, 7974, 8080, 7971, 7973, 7975, 8081, 0, 8082, 0, 8083, 0, - 8084, 0, 8085, 0, 8086, 0, 8087, 7978, 7980, 7982, 8088, 7979, 7981, - 7983, 8089, 0, 8090, 0, 8091, 0, 8092, 0, 8093, 0, 8094, 0, 8095, 7986, - 7988, 7990, 0, 7987, 7989, 7991, 0, 7994, 7996, 7998, 0, 7995, 7997, - 7999, 0, 8002, 8004, 8003, 8005, 8010, 8012, 8011, 8013, 8018, 8020, - 8022, 0, 8019, 8021, 8023, 0, 8027, 8029, 8031, 0, 8034, 8036, 8038, - 8096, 8035, 8037, 8039, 8097, 0, 8098, 0, 8099, 0, 8100, 0, 8101, 0, - 8102, 0, 8103, 8042, 8044, 8046, 8104, 8043, 8045, 8047, 8105, 0, 8106, - 0, 8107, 0, 8108, 0, 8109, 0, 8110, 0, 8111, 0, 8114, 0, 8130, 0, 8178, - 0, 8119, 8141, 8142, 8143, 0, 0, 8135, 0, 8183, 8157, 8158, 8159, 0, 0, - 8602, 0, 8603, 0, 8622, 0, 8653, 0, 8655, 0, 8654, 0, 8708, 0, 8713, 0, - 8716, 0, 8740, 0, 8742, 0, 8769, 0, 8772, 0, 8775, 0, 8777, 0, 8813, 0, - 8802, 0, 8816, 0, 8817, 0, 8820, 0, 8821, 0, 8824, 0, 8825, 0, 8832, 0, - 8833, 0, 8928, 0, 8929, 0, 8836, 0, 8837, 0, 8840, 0, 8841, 0, 8930, 0, - 8931, 0, 8876, 0, 8877, 0, 8878, 0, 8879, 0, 8938, 0, 8939, 0, 8940, 0, - 8941, 12436, 0, 12364, 0, 12366, 0, 12368, 0, 12370, 0, 12372, 0, 12374, - 0, 12376, 0, 12378, 0, 12380, 0, 12382, 0, 12384, 0, 12386, 0, 12389, 0, - 12391, 0, 12393, 0, 12400, 12401, 12403, 12404, 12406, 12407, 12409, - 12410, 12412, 12413, 12446, 0, 12532, 0, 12460, 0, 12462, 0, 12464, 0, - 12466, 0, 12468, 0, 12470, 0, 12472, 0, 12474, 0, 12476, 0, 12478, 0, - 12480, 0, 12482, 0, 12485, 0, 12487, 0, 12489, 0, 12496, 12497, 12499, - 12500, 12502, 12503, 12505, 12506, 12508, 12509, 12535, 0, 12536, 0, - 12537, 0, 12538, 0, 12542, 0, 69786, 0, 69788, 0, 69803, 0, 0, 69934, 0, - 69935, 70475, 70476, 70844, 70843, 70846, 0, 0, 71098, 0, 71099, -}; - diff --git a/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh index 1dd0b3211e8..eb7776eecbe 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh @@ -7,13 +7,13 @@ * on file with this header: * * # emoji-data.txt - * # Date: 2018-02-07, 07:55:18 GMT - * # © 2018 Unicode®, Inc. + * # Date: 2020-01-28, 20:52:38 GMT + * # © 2020 Unicode®, Inc. * # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. * # For terms of use, see http://www.unicode.org/terms_of_use.html * # * # Emoji Data for UTS #51 - * # Version: 11.0 + * # Version: 13.0 * # * # For documentation and usage, see http://www.unicode.org/reports/tr51 */ @@ -23,88 +23,56 @@ #include "hb-unicode.hh" - -static const struct hb_unicode_range_t _hb_unicode_emoji_Extended_Pictographic_table[] = +static const uint8_t +_hb_emoji_u8[448] = { - {0x00A9, 0x00A9}, - {0x00AE, 0x00AE}, - {0x203C, 0x203C}, - {0x2049, 0x2049}, - {0x2122, 0x2122}, - {0x2139, 0x2139}, - {0x2194, 0x2199}, - {0x21A9, 0x21AA}, - {0x231A, 0x231B}, - {0x2328, 0x2328}, - {0x2388, 0x2388}, - {0x23CF, 0x23CF}, - {0x23E9, 0x23F3}, - {0x23F8, 0x23FA}, - {0x24C2, 0x24C2}, - {0x25AA, 0x25AB}, - {0x25B6, 0x25B6}, - {0x25C0, 0x25C0}, - {0x25FB, 0x25FE}, - {0x2600, 0x2605}, - {0x2607, 0x2612}, - {0x2614, 0x2685}, - {0x2690, 0x2705}, - {0x2708, 0x2712}, - {0x2714, 0x2714}, - {0x2716, 0x2716}, - {0x271D, 0x271D}, - {0x2721, 0x2721}, - {0x2728, 0x2728}, - {0x2733, 0x2734}, - {0x2744, 0x2744}, - {0x2747, 0x2747}, - {0x274C, 0x274C}, - {0x274E, 0x274E}, - {0x2753, 0x2755}, - {0x2757, 0x2757}, - {0x2763, 0x2767}, - {0x2795, 0x2797}, - {0x27A1, 0x27A1}, - {0x27B0, 0x27B0}, - {0x27BF, 0x27BF}, - {0x2934, 0x2935}, - {0x2B05, 0x2B07}, - {0x2B1B, 0x2B1C}, - {0x2B50, 0x2B50}, - {0x2B55, 0x2B55}, - {0x3030, 0x3030}, - {0x303D, 0x303D}, - {0x3297, 0x3297}, - {0x3299, 0x3299}, - {0x1F000, 0x1F0FF}, - {0x1F10D, 0x1F10F}, - {0x1F12F, 0x1F12F}, - {0x1F16C, 0x1F171}, - {0x1F17E, 0x1F17F}, - {0x1F18E, 0x1F18E}, - {0x1F191, 0x1F19A}, - {0x1F1AD, 0x1F1E5}, - {0x1F201, 0x1F20F}, - {0x1F21A, 0x1F21A}, - {0x1F22F, 0x1F22F}, - {0x1F232, 0x1F23A}, - {0x1F23C, 0x1F23F}, - {0x1F249, 0x1F3FA}, - {0x1F400, 0x1F53D}, - {0x1F546, 0x1F64F}, - {0x1F680, 0x1F6FF}, - {0x1F774, 0x1F77F}, - {0x1F7D5, 0x1F7FF}, - {0x1F80C, 0x1F80F}, - {0x1F848, 0x1F84F}, - {0x1F85A, 0x1F85F}, - {0x1F888, 0x1F88F}, - {0x1F8AE, 0x1F8FF}, - {0x1F90C, 0x1F93A}, - {0x1F93C, 0x1F945}, - {0x1F947, 0x1FFFD}, + 0, 0, 0, 0, 33, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84,118, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 7, 9, 10, 11, 0, + 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, + 7, 7, 7, 14, 15, 16, 17, 18, 19, 20, 7, 7, 7, 7, 7, 21, + 7, 7, 7, 7, 22, 23, 7, 7, 7, 24, 7, 14, 0, 25, 0, 26, + 27, 28, 29, 14, 30, 31, 7, 7, 7, 7, 7, 14, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 22, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,240, 1, 0, 2, 0, 0, + 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,254, 7, 3, + 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, + 159,255,243,255,255,255,255,255,255,255,255,255,255,255,255,255, + 31, 0,255,255,255,255,255,255, 31,255, 3, 0, 0, 0, 8, 0, + 0, 0, 24, 0,120, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 16, 0, 96, 0, 0, 8, 0, 0, 0, 0, + 255,255,255,255,255,255,255,127, 0, 96, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,240, 1, 64, 0, 0,254, 3, 0,224,255,255, + 255,255,255,255, 31, 0, 0, 0,254,127, 0, 0, 0, 0,252,115, + 0,254,255,255,255,255,255,255,255,255,255,255,255,255,255, 3, + 255,255,255,255,255,255,255, 31,192,255,255,255,255,255,255,255, + 255,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,240,127, + 0, 0,224,255,255,255,255,127, 0,112, 0, 0, 0, 0, 0, 0, + 0,127, 0,124, 0, 0, 0, 0, 0,127, 0, 0, 0,192,255,255, + 0,240,255,255,255,255,255,243,159,255,255,255,255,255,255,255, }; +static inline unsigned +_hb_emoji_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline unsigned +_hb_emoji_b1 (const uint8_t* a, unsigned i) +{ + return (a[i>>3]>>((i&7u)<<0))&1u; +} +static inline uint_fast8_t +_hb_emoji_is_Extended_Pictographic (unsigned u) +{ + return u<131069u?_hb_emoji_b1(192+_hb_emoji_u8,((_hb_emoji_u8[64+(((_hb_emoji_b4(_hb_emoji_u8,u>>6>>4))<<4)+((u>>6)&15u))])<<6)+((u)&63u)):0; +} + + #endif /* HB_UNICODE_EMOJI_TABLE_HH */ /* == End of generated table == */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-unicode.cc b/src/java.desktop/share/native/libharfbuzz/hb-unicode.cc index 47e6af647e3..8530289cbad 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-unicode.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-unicode.cc @@ -60,6 +60,7 @@ hb_unicode_combining_class_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, return HB_UNICODE_COMBINING_CLASS_NOT_REORDERED; } +#ifndef HB_DISABLE_DEPRECATED static unsigned int hb_unicode_eastasian_width_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, hb_codepoint_t unicode HB_UNUSED, @@ -67,6 +68,7 @@ hb_unicode_eastasian_width_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, { return 1; } +#endif static hb_unicode_general_category_t hb_unicode_general_category_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, @@ -113,6 +115,7 @@ hb_unicode_decompose_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, } +#ifndef HB_DISABLE_DEPRECATED static unsigned int hb_unicode_decompose_compatibility_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, hb_codepoint_t u HB_UNUSED, @@ -121,20 +124,23 @@ hb_unicode_decompose_compatibility_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED { return 0; } +#endif - -extern "C" hb_unicode_funcs_t *hb_glib_get_unicode_funcs (); -extern "C" hb_unicode_funcs_t *hb_icu_get_unicode_funcs (); -extern "C" hb_unicode_funcs_t *hb_ucdn_get_unicode_funcs (); +#if !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_GLIB) +#include "hb-glib.h" +#endif +#if !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_ICU) && defined(HAVE_ICU_BUILTIN) +#include "hb-icu.h" +#endif hb_unicode_funcs_t * hb_unicode_funcs_get_default () { -#if defined(HAVE_UCDN) - return hb_ucdn_get_unicode_funcs (); -#elif defined(HAVE_GLIB) +#if !defined(HB_NO_UNICODE_FUNCS) && !defined(HB_NO_UCD) + return hb_ucd_get_unicode_funcs (); +#elif !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_GLIB) return hb_glib_get_unicode_funcs (); -#elif defined(HAVE_ICU) && defined(HAVE_ICU_BUILTIN) +#elif !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_ICU) && defined(HAVE_ICU_BUILTIN) return hb_icu_get_unicode_funcs (); #else #define HB_UNICODE_FUNCS_NIL 1 @@ -144,7 +150,7 @@ hb_unicode_funcs_get_default () #if !defined(HB_NO_UNICODE_FUNCS) && defined(HB_UNICODE_FUNCS_NIL) #error "Could not find any Unicode functions implementation, you have to provide your own" -#error "Consider building hb-ucdn.c. If you absolutely want to build without any, check the code." +#error "Consider building hb-ucd.cc. If you absolutely want to build without any, check the code." #endif /** @@ -206,7 +212,7 @@ DEFINE_NULL_INSTANCE (hb_unicode_funcs_t) = hb_unicode_funcs_t * hb_unicode_funcs_get_empty () { - return const_cast (&Null(hb_unicode_funcs_t)); + return const_cast (&Null (hb_unicode_funcs_t)); } /** @@ -425,6 +431,7 @@ hb_unicode_decompose (hb_unicode_funcs_t *ufuncs, return ufuncs->decompose (ab, a, b); } +#ifndef HB_DISABLE_DEPRECATED /** * hb_unicode_decompose_compatibility: * @ufuncs: Unicode functions. @@ -445,8 +452,10 @@ hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs, { return ufuncs->decompose_compatibility (u, decomposed); } +#endif +#ifndef HB_NO_OT_SHAPE /* See hb-unicode.hh for details. */ const uint8_t _hb_modified_combining_class[256] = @@ -559,19 +568,19 @@ _hb_modified_combining_class[256] = 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, /* HB_UNICODE_COMBINING_CLASS_INVALID */ }; +#endif /* * Emoji */ +#ifndef HB_NO_EMOJI_SEQUENCES #include "hb-unicode-emoji-table.hh" bool _hb_unicode_is_emoji_Extended_Pictographic (hb_codepoint_t cp) { - return hb_bsearch (&cp, _hb_unicode_emoji_Extended_Pictographic_table, - ARRAY_LENGTH (_hb_unicode_emoji_Extended_Pictographic_table), - sizeof (hb_unicode_range_t), - hb_unicode_range_t::cmp); + return _hb_emoji_is_Extended_Pictographic (cp); } +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-unicode.hh b/src/java.desktop/share/native/libharfbuzz/hb-unicode.hh index 24b03c20184..40f6a5a7f43 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-unicode.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-unicode.hh @@ -42,19 +42,19 @@ extern HB_INTERNAL const uint8_t _hb_modified_combining_class[256]; #define HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS \ HB_UNICODE_FUNC_IMPLEMENT (combining_class) \ - HB_UNICODE_FUNC_IMPLEMENT (eastasian_width) \ + HB_IF_NOT_DEPRECATED (HB_UNICODE_FUNC_IMPLEMENT (eastasian_width)) \ HB_UNICODE_FUNC_IMPLEMENT (general_category) \ HB_UNICODE_FUNC_IMPLEMENT (mirroring) \ HB_UNICODE_FUNC_IMPLEMENT (script) \ HB_UNICODE_FUNC_IMPLEMENT (compose) \ HB_UNICODE_FUNC_IMPLEMENT (decompose) \ - HB_UNICODE_FUNC_IMPLEMENT (decompose_compatibility) \ + HB_IF_NOT_DEPRECATED (HB_UNICODE_FUNC_IMPLEMENT (decompose_compatibility)) \ /* ^--- Add new callbacks here */ /* Simple callbacks are those taking a hb_codepoint_t and returning a hb_codepoint_t */ #define HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE \ HB_UNICODE_FUNC_IMPLEMENT (hb_unicode_combining_class_t, combining_class) \ - HB_UNICODE_FUNC_IMPLEMENT (unsigned int, eastasian_width) \ + HB_IF_NOT_DEPRECATED (HB_UNICODE_FUNC_IMPLEMENT (unsigned int, eastasian_width)) \ HB_UNICODE_FUNC_IMPLEMENT (hb_unicode_general_category_t, general_category) \ HB_UNICODE_FUNC_IMPLEMENT (hb_codepoint_t, mirroring) \ HB_UNICODE_FUNC_IMPLEMENT (hb_script_t, script) \ @@ -89,7 +89,11 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE unsigned int decompose_compatibility (hb_codepoint_t u, hb_codepoint_t *decomposed) { +#ifdef HB_DISABLE_DEPRECATED + unsigned int ret = 0; +#else unsigned int ret = func.decompose_compatibility (this, u, decomposed, user_data.decompose_compatibility); +#endif if (ret == 1 && u == decomposed[0]) { decomposed[0] = 0; return 0; @@ -101,9 +105,6 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE unsigned int modified_combining_class (hb_codepoint_t u) { - /* XXX This hack belongs to the Myanmar shaper. */ - if (unlikely (u == 0x1037u)) u = 0x103Au; - /* XXX This hack belongs to the USE shaper (for Tai Tham): * Reorder SAKOT to ensure it comes after any tone marks. */ if (unlikely (u == 0x1A60u)) return 254; @@ -322,11 +323,11 @@ DECLARE_NULL_INSTANCE (hb_unicode_funcs_t); * * Modify Telugu length marks (ccc=84, ccc=91). * These are the only matras in the main Indic scripts range that have - * a non-zero ccc. That makes them reorder with the Halant that is - * ccc=9. Just zero them, we don't need them in our Indic shaper. + * a non-zero ccc. That makes them reorder with the Halant (ccc=9). + * Assign 4 and 5, which are otherwise unassigned. */ -#define HB_MODIFIED_COMBINING_CLASS_CCC84 0 /* length mark */ -#define HB_MODIFIED_COMBINING_CLASS_CCC91 0 /* ai length mark */ +#define HB_MODIFIED_COMBINING_CLASS_CCC84 4 /* length mark */ +#define HB_MODIFIED_COMBINING_CLASS_CCC91 5 /* ai length mark */ /* Thai * @@ -391,4 +392,7 @@ HB_INTERNAL bool _hb_unicode_is_emoji_Extended_Pictographic (hb_codepoint_t cp); +extern "C" HB_INTERNAL hb_unicode_funcs_t *hb_ucd_get_unicode_funcs (); + + #endif /* HB_UNICODE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-vector.hh b/src/java.desktop/share/native/libharfbuzz/hb-vector.hh index b438675b9dc..4a7a7a734d1 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-vector.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-vector.hh @@ -38,103 +38,141 @@ struct hb_vector_t typedef Type item_t; static constexpr unsigned item_size = hb_static_size (Type); - HB_NO_COPY_ASSIGN_TEMPLATE (hb_vector_t, Type); hb_vector_t () { init (); } + hb_vector_t (const hb_vector_t &o) + { + init (); + alloc (o.length); + hb_copy (o, *this); + } + hb_vector_t (hb_vector_t &&o) + { + allocated = o.allocated; + length = o.length; + arrayZ = o.arrayZ; + o.init (); + } ~hb_vector_t () { fini (); } - unsigned int length; private: int allocated; /* == -1 means allocation failed. */ - Type *arrayZ_; public: + unsigned int length; + public: + Type *arrayZ; void init () { allocated = length = 0; - arrayZ_ = nullptr; + arrayZ = nullptr; } void fini () { - if (arrayZ_) - free (arrayZ_); + free (arrayZ); init (); } void fini_deep () { - Type *array = arrayZ(); unsigned int count = length; for (unsigned int i = 0; i < count; i++) - array[i].fini (); + arrayZ[i].fini (); + fini (); + } + + void reset () { resize (0); } + + hb_vector_t& operator = (const hb_vector_t &o) + { + reset (); + alloc (o.length); + hb_copy (o, *this); + return *this; + } + hb_vector_t& operator = (hb_vector_t &&o) + { fini (); + allocated = o.allocated; + length = o.length; + arrayZ = o.arrayZ; + o.init (); + return *this; } - const Type * arrayZ () const { return arrayZ_; } - Type * arrayZ () { return arrayZ_; } + hb_bytes_t as_bytes () const + { return hb_bytes_t ((const char *) arrayZ, length * item_size); } + + bool operator == (const hb_vector_t &o) const { return as_array () == o.as_array (); } + bool operator != (const hb_vector_t &o) const { return !(*this == o); } + uint32_t hash () const { return as_array ().hash (); } Type& operator [] (int i_) { unsigned int i = (unsigned int) i_; if (unlikely (i >= length)) return Crap (Type); - return arrayZ()[i]; + return arrayZ[i]; } const Type& operator [] (int i_) const { unsigned int i = (unsigned int) i_; if (unlikely (i >= length)) - return Null(Type); - return arrayZ()[i]; + return Null (Type); + return arrayZ[i]; } - explicit_operator bool () const { return length; } + Type& tail () { return (*this)[length - 1]; } + const Type& tail () const { return (*this)[length - 1]; } + + explicit operator bool () const { return length; } + unsigned get_size () const { return length * item_size; } + + /* Sink interface. */ + template + hb_vector_t& operator << (T&& v) { push (hb_forward (v)); return *this; } - hb_array_t as_array () - { return hb_array (arrayZ(), length); } - hb_array_t as_array () const - { return hb_array (arrayZ(), length); } + hb_array_t< Type> as_array () { return hb_array (arrayZ, length); } + hb_array_t as_array () const { return hb_array (arrayZ, length); } + + /* Iterator. */ + typedef hb_array_t iter_t; + typedef hb_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } hb_array_t sub_array (unsigned int start_offset, unsigned int count) const - { return as_array ().sub_array (start_offset, count);} + { return as_array ().sub_array (start_offset, count); } hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const - { return as_array ().sub_array (start_offset, count);} + { return as_array ().sub_array (start_offset, count); } hb_array_t sub_array (unsigned int start_offset, unsigned int count) - { return as_array ().sub_array (start_offset, count);} + { return as_array ().sub_array (start_offset, count); } hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) - { return as_array ().sub_array (start_offset, count);} + { return as_array ().sub_array (start_offset, count); } hb_sorted_array_t as_sorted_array () - { return hb_sorted_array (arrayZ(), length); } + { return hb_sorted_array (arrayZ, length); } hb_sorted_array_t as_sorted_array () const - { return hb_sorted_array (arrayZ(), length); } - - hb_array_t sorted_sub_array (unsigned int start_offset, unsigned int count) const - { return as_sorted_array ().sorted_sub_array (start_offset, count);} - hb_array_t sorted_sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const - { return as_sorted_array ().sorted_sub_array (start_offset, count);} - hb_array_t sorted_sub_array (unsigned int start_offset, unsigned int count) - { return as_sorted_array ().sorted_sub_array (start_offset, count);} - hb_array_t sorted_sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) - { return as_sorted_array ().sorted_sub_array (start_offset, count);} + { return hb_sorted_array (arrayZ, length); } - template explicit_operator T * () { return arrayZ(); } - template explicit_operator const T * () const { return arrayZ(); } - operator hb_array_t () { return as_array (); } - operator hb_array_t () const { return as_array (); } + template explicit operator T * () { return arrayZ; } + template explicit operator const T * () const { return arrayZ; } - Type * operator + (unsigned int i) { return arrayZ() + i; } - const Type * operator + (unsigned int i) const { return arrayZ() + i; } + Type * operator + (unsigned int i) { return arrayZ + i; } + const Type * operator + (unsigned int i) const { return arrayZ + i; } Type *push () { if (unlikely (!resize (length + 1))) - return &Crap(Type); - return &arrayZ()[length - 1]; + return &Crap (Type); + return &arrayZ[length - 1]; } - Type *push (const Type& v) + template + Type *push (T&& v) { Type *p = push (); - *p = v; + *p = hb_forward (v); return p; } @@ -161,7 +199,7 @@ struct hb_vector_t (new_allocated < (unsigned) allocated) || hb_unsigned_mul_overflows (new_allocated, sizeof (Type)); if (likely (!overflows)) - new_array = (Type *) realloc (arrayZ_, new_allocated * sizeof (Type)); + new_array = (Type *) realloc (arrayZ, new_allocated * sizeof (Type)); if (unlikely (!new_array)) { @@ -169,7 +207,7 @@ struct hb_vector_t return false; } - arrayZ_ = new_array; + arrayZ = new_array; allocated = new_allocated; return true; @@ -182,25 +220,24 @@ struct hb_vector_t return false; if (size > length) - memset (arrayZ() + length, 0, (size - length) * sizeof (*arrayZ())); + memset (arrayZ + length, 0, (size - length) * sizeof (*arrayZ)); length = size; return true; } - void pop () + Type pop () { - if (!length) return; - length--; + if (!length) return Null (Type); + return hb_move (arrayZ[--length]); /* Does this move actually work? */ } void remove (unsigned int i) { if (unlikely (i >= length)) return; - Type *array = arrayZ(); - memmove (static_cast (&array[i]), - static_cast (&array[i + 1]), + memmove (static_cast (&arrayZ[i]), + static_cast (&arrayZ[i + 1]), (length - i - 1) * sizeof (Type)); length--; } @@ -215,19 +252,17 @@ struct hb_vector_t template Type *find (T v) { - Type *array = arrayZ(); for (unsigned int i = 0; i < length; i++) - if (array[i] == v) - return &array[i]; + if (arrayZ[i] == v) + return &arrayZ[i]; return nullptr; } template const Type *find (T v) const { - const Type *array = arrayZ(); for (unsigned int i = 0; i < length; i++) - if (array[i] == v) - return &array[i]; + if (arrayZ[i] == v) + return &arrayZ[i]; return nullptr; } @@ -242,19 +277,37 @@ struct hb_vector_t template const Type *lsearch (const T &x, const Type *not_found = nullptr) const { return as_array ().lsearch (x, not_found); } + template + bool lfind (const T &x, unsigned *pos = nullptr) const + { return as_array ().lfind (x, pos); } +}; + +template +struct hb_sorted_vector_t : hb_vector_t +{ + hb_sorted_array_t< Type> as_array () { return hb_sorted_array (this->arrayZ, this->length); } + hb_sorted_array_t as_array () const { return hb_sorted_array (this->arrayZ, this->length); } + + /* Iterator. */ + typedef hb_sorted_array_t const_iter_t; + typedef hb_sorted_array_t< Type> iter_t; + const_iter_t iter () const { return as_array (); } + const_iter_t citer () const { return as_array (); } + iter_t iter () { return as_array (); } + operator iter_t () { return iter (); } + operator const_iter_t () const { return iter (); } template Type *bsearch (const T &x, Type *not_found = nullptr) - { return as_sorted_array ().bsearch (x, not_found); } + { return as_array ().bsearch (x, not_found); } template const Type *bsearch (const T &x, const Type *not_found = nullptr) const - { return as_sorted_array ().bsearch (x, not_found); } + { return as_array ().bsearch (x, not_found); } template bool bfind (const T &x, unsigned int *i = nullptr, - hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE, - unsigned int to_store = (unsigned int) -1) const - { return as_sorted_array ().bfind (x, i, not_found, to_store); } + hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array ().bfind (x, i, not_found, to_store); } }; - #endif /* HB_VECTOR_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-version.h b/src/java.desktop/share/native/libharfbuzz/hb-version.h index 01248794ff8..b9ab5f55f1b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-version.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-version.h @@ -37,10 +37,10 @@ HB_BEGIN_DECLS #define HB_VERSION_MAJOR 2 -#define HB_VERSION_MINOR 3 -#define HB_VERSION_MICRO 1 +#define HB_VERSION_MINOR 7 +#define HB_VERSION_MICRO 2 -#define HB_VERSION_STRING "2.3.1" +#define HB_VERSION_STRING "2.7.2" #define HB_VERSION_ATLEAST(major,minor,micro) \ ((major)*10000+(minor)*100+(micro) <= \ diff --git a/src/java.desktop/share/native/libharfbuzz/hb.h b/src/java.desktop/share/native/libharfbuzz/hb.h index c5e7072fbac..360686ca687 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb.h +++ b/src/java.desktop/share/native/libharfbuzz/hb.h @@ -32,12 +32,14 @@ #include "hb-buffer.h" #include "hb-common.h" #include "hb-deprecated.h" +#include "hb-draw.h" #include "hb-face.h" #include "hb-font.h" #include "hb-map.h" #include "hb-set.h" #include "hb-shape.h" #include "hb-shape-plan.h" +#include "hb-style.h" #include "hb-unicode.h" #include "hb-version.h" diff --git a/src/java.desktop/share/native/libharfbuzz/hb.hh b/src/java.desktop/share/native/libharfbuzz/hb.hh index f2d39068663..8f0ba2ee054 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb.hh @@ -29,8 +29,9 @@ #ifndef HB_HH #define HB_HH + #ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC -#if defined(_MSC_VER) +#ifdef _MSC_VER #pragma warning( disable: 4068 ) /* Unknown pragma */ #endif #if defined(__GNUC__) || defined(__clang__) @@ -65,9 +66,12 @@ #pragma GCC diagnostic error "-Wcast-align" #pragma GCC diagnostic error "-Wcast-function-type" #pragma GCC diagnostic error "-Wdelete-non-virtual-dtor" +#pragma GCC diagnostic error "-Wembedded-directive" +#pragma GCC diagnostic error "-Wextra-semi-stmt" #pragma GCC diagnostic error "-Wformat-security" #pragma GCC diagnostic error "-Wimplicit-function-declaration" #pragma GCC diagnostic error "-Winit-self" +#pragma GCC diagnostic error "-Winjected-class-name" #pragma GCC diagnostic error "-Wmissing-braces" #pragma GCC diagnostic error "-Wmissing-declarations" #pragma GCC diagnostic error "-Wmissing-prototypes" @@ -93,13 +97,17 @@ /* Warning. To be investigated if happens. */ #ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_WARNING #pragma GCC diagnostic warning "-Wbuiltin-macro-redefined" +#pragma GCC diagnostic warning "-Wdeprecated" +#pragma GCC diagnostic warning "-Wdeprecated-declarations" #pragma GCC diagnostic warning "-Wdisabled-optimization" +#pragma GCC diagnostic warning "-Wdouble-promotion" #pragma GCC diagnostic warning "-Wformat=2" #pragma GCC diagnostic warning "-Wignored-pragma-optimize" #pragma GCC diagnostic warning "-Wlogical-op" #pragma GCC diagnostic warning "-Wmaybe-uninitialized" #pragma GCC diagnostic warning "-Wmissing-format-attribute" #pragma GCC diagnostic warning "-Wundef" +#pragma GCC diagnostic warning "-Wunused-but-set-variable" #endif /* Ignored currently, but should be fixed at some point. */ @@ -120,14 +128,15 @@ #pragma GCC diagnostic ignored "-Wpacked" // Erratic impl in clang #pragma GCC diagnostic ignored "-Wstrict-aliasing" #pragma GCC diagnostic ignored "-Wtype-limits" +#pragma GCC diagnostic ignored "-Wc++11-compat" // only gcc raises it #endif #endif #endif -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif + +#include "hb-config.hh" + /* * Following added based on what AC_USE_SYSTEM_EXTENSIONS adds to @@ -166,20 +175,30 @@ #include "hb-aat.h" #define HB_AAT_H_IN -#include "hb-aat.h" - +#include #include +#include #include #include #include #include -#include #include #include #if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__) +#ifdef __MINGW32_VERSION +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#else #include #endif +#endif + +#ifdef _WIN32 +#include +#include +#endif #define HB_PASTE1(a,b) a##b #define HB_PASTE(a,b) HB_PASTE1(a,b) @@ -187,10 +206,15 @@ /* Compile-time custom allocator support. */ -#if defined(hb_malloc_impl) \ - && defined(hb_calloc_impl) \ - && defined(hb_realloc_impl) \ - && defined(hb_free_impl) +#if !defined(HB_CUSTOM_MALLOC) \ + && defined(hb_malloc_impl) \ + && defined(hb_calloc_impl) \ + && defined(hb_realloc_impl) \ + && defined(hb_free_impl) +#define HB_CUSTOM_MALLOC +#endif + +#ifdef HB_CUSTOM_MALLOC extern "C" void* hb_malloc_impl(size_t size); extern "C" void* hb_calloc_impl(size_t nmemb, size_t size); extern "C" void* hb_realloc_impl(void *ptr, size_t size); @@ -199,14 +223,6 @@ extern "C" void hb_free_impl(void *ptr); #define calloc hb_calloc_impl #define realloc hb_realloc_impl #define free hb_free_impl - -#if defined(hb_memalign_impl) -extern "C" int hb_memalign_impl(void **memptr, size_t alignment, size_t size); -#define posix_memalign hb_memalign_impl -#else -#undef HAVE_POSIX_MEMALIGN -#endif - #endif @@ -214,58 +230,6 @@ extern "C" int hb_memalign_impl(void **memptr, size_t alignment, size_t size); * Compiler attributes */ -#if __cplusplus < 201103L - -#ifndef nullptr -#define nullptr NULL -#endif - -#ifndef constexpr -#define constexpr const -#endif - -#ifndef static_assert -#define static_assert(e, msg) \ - HB_UNUSED typedef int HB_PASTE(static_assertion_failed_at_line_, __LINE__) [(e) ? 1 : -1] -#endif // static_assert - -#if defined(__GNUC__) -#if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)) -#define thread_local __thread -#endif -#else -#define thread_local -#endif - -template -struct _hb_alignof -{ - struct s - { - char c; - T t; - }; - static constexpr size_t value = offsetof (s, t); -}; -#ifndef alignof -#define alignof(x) (_hb_alignof::value) -#endif - -/* https://github.com/harfbuzz/harfbuzz/issues/1127 */ -#ifndef explicit_operator -#define explicit_operator operator -#endif - -#else /* __cplusplus >= 201103L */ - -/* https://github.com/harfbuzz/harfbuzz/issues/1127 */ -#ifndef explicit_operator -#define explicit_operator explicit operator -#endif - -#endif /* __cplusplus < 201103L */ - - #if (defined(__GNUC__) || defined(__clang__)) && defined(__OPTIMIZE__) #define likely(expr) (__builtin_expect (!!(expr), 1)) #define unlikely(expr) (__builtin_expect (!!(expr), 0)) @@ -288,7 +252,7 @@ struct _hb_alignof #define HB_CONST_FUNC #define HB_PRINTF_FUNC(format_idx, arg_idx) #endif -#if defined(__GNUC__) && (__GNUC__ >= 4) +#if defined(__GNUC__) && (__GNUC__ >= 4) || (__clang__) #define HB_UNUSED __attribute__((unused)) #elif defined(_MSC_VER) /* https://github.com/harfbuzz/harfbuzz/issues/635 */ #define HB_UNUSED __pragma(warning(suppress: 4100 4101)) @@ -311,6 +275,13 @@ struct _hb_alignof # endif #endif +/* https://github.com/harfbuzz/harfbuzz/issues/1651 */ +#if defined(__clang__) && __clang_major__ < 10 +#define static_const static +#else +#define static_const static const +#endif + #if defined(__GNUC__) && (__GNUC__ >= 3) #define HB_FUNC __PRETTY_FUNCTION__ #elif defined(_MSC_VER) @@ -357,7 +328,20 @@ struct _hb_alignof # define HB_FALLTHROUGH /* FALLTHROUGH */ #endif -#if defined(__clang__) +/* A tag to enforce use of return value for a function */ +#if __cplusplus >= 201703L +# define HB_NODISCARD [[nodiscard]] +#elif defined(__GNUC__) || defined(__clang__) +# define HB_NODISCARD __attribute__((warn_unused_result)) +#elif defined(_MSC_VER) +# define HB_NODISCARD _Check_return_ +#else +# define HB_NODISCARD +#endif +#define hb_success_t HB_NODISCARD bool + +/* https://github.com/harfbuzz/harfbuzz/issues/1852 */ +#if defined(__clang__) && !(defined(_AIX) && (defined(__IBMCPP__) || defined(__ibmxl__))) /* Disable certain sanitizer errors. */ /* https://github.com/harfbuzz/harfbuzz/issues/1247 */ #define HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW __attribute__((no_sanitize("signed-integer-overflow"))) @@ -374,7 +358,7 @@ struct _hb_alignof # undef _WIN32_WINNT # endif # ifndef _WIN32_WINNT -# if !defined(WINAPI_FAMILY) || !(WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) # define _WIN32_WINNT 0x0600 # endif # endif @@ -388,19 +372,35 @@ struct _hb_alignof # if defined(_WIN32_WCE) /* Some things not defined on Windows CE. */ # define vsnprintf _vsnprintf -# define getenv(Name) nullptr +# ifndef HB_NO_GETENV +# define HB_NO_GETENV +# endif # if _WIN32_WCE < 0x800 -# define setlocale(Category, Locale) "C" -static int errno = 0; /* Use something better? */ +# define HB_NO_SETLOCALE +# define HB_NO_ERRNO +# endif +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# ifndef HB_NO_GETENV +# define HB_NO_GETENV # endif -# elif defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) -# define getenv(Name) nullptr # endif # if defined(_MSC_VER) && _MSC_VER < 1900 # define snprintf _snprintf # endif #endif +#ifdef HB_NO_GETENV +#define getenv(Name) nullptr +#endif + +#ifndef HB_NO_ERRNO +# include +#else +static int HB_UNUSED _hb_errno = 0; +# undef errno +# define errno _hb_errno +#endif + #if defined(HAVE_ATEXIT) && !defined(HB_USE_ATEXIT) /* atexit() is only safe to be called from shared libraries on certain * platforms. Whitelist. @@ -459,87 +459,13 @@ static_assert ((sizeof (hb_position_t) == 4), ""); static_assert ((sizeof (hb_mask_t) == 4), ""); static_assert ((sizeof (hb_var_int_t) == 4), ""); - -#if __cplusplus >= 201103L - -/* We only enable these with C++11 or later, since earlier language - * does not allow structs with constructors in unions, and we need - * those. */ - -#define HB_NO_COPY_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) -#define HB_NO_COPY_ASSIGN_TEMPLATE(TypeName, T) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) -#define HB_NO_COPY_ASSIGN_TEMPLATE2(TypeName, T1, T2) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) -#define HB_NO_CREATE_COPY_ASSIGN(TypeName) \ - TypeName(); \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) -#define HB_NO_CREATE_COPY_ASSIGN_TEMPLATE(TypeName, T) \ - TypeName(); \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) -#define HB_NO_CREATE_COPY_ASSIGN_TEMPLATE2(TypeName, T1, T2) \ - TypeName(); \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) - -#else /* __cpluspplus >= 201103L */ - -#define HB_NO_COPY_ASSIGN(TypeName) static_assert (true, "") -#define HB_NO_COPY_ASSIGN_TEMPLATE(TypeName, T) static_assert (true, "") -#define HB_NO_COPY_ASSIGN_TEMPLATE2(TypeName, T1, T2) static_assert (true, "") -#define HB_NO_CREATE_COPY_ASSIGN(TypeName) static_assert (true, "") -#define HB_NO_CREATE_COPY_ASSIGN_TEMPLATE(TypeName, T) static_assert (true, "") -#define HB_NO_CREATE_COPY_ASSIGN_TEMPLATE2(TypeName, T1, T2) static_assert (true, "") - -#endif /* __cpluspplus >= 201103L */ - - -/* - * Compiler-assisted vectorization parameters. - */ - -/* - * Disable vectorization for now. To correctly use them, we should - * use posix_memalign() to allocate in hb_vector_t. Otherwise, can - * cause misaligned access. - * - * https://bugs.chromium.org/p/chromium/issues/detail?id=860184 - */ -#if !defined(HB_VECTOR_SIZE) -# define HB_VECTOR_SIZE 0 -#endif - -/* The `vector_size' attribute was introduced in gcc 3.1. */ -#if !defined(HB_VECTOR_SIZE) -# if defined( __GNUC__ ) && ( __GNUC__ >= 4 ) -# define HB_VECTOR_SIZE 128 -# else -# define HB_VECTOR_SIZE 0 -# endif -#endif -static_assert (0 == (HB_VECTOR_SIZE & (HB_VECTOR_SIZE - 1)), "HB_VECTOR_SIZE is not power of 2."); -static_assert (0 == (HB_VECTOR_SIZE % 64), "HB_VECTOR_SIZE is not multiple of 64."); -#if HB_VECTOR_SIZE -typedef uint64_t hb_vector_size_impl_t __attribute__((vector_size (HB_VECTOR_SIZE / 8))); -#else -typedef uint64_t hb_vector_size_impl_t; -#endif - - -/* HB_NDEBUG disables some sanity checks that are very safe to disable and - * should be disabled in production systems. If NDEBUG is defined, enable - * HB_NDEBUG; but if it's desirable that normal assert()s (which are very - * light-weight) to be enabled, then HB_DEBUG can be defined to disable - * the costlier checks. */ -#ifdef NDEBUG -#define HB_NDEBUG 1 -#endif +#define HB_DELETE_COPY_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete +#define HB_DELETE_CREATE_COPY_ASSIGN(TypeName) \ + TypeName() = delete; \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete /* Flags */ @@ -579,47 +505,107 @@ typedef uint64_t hb_vector_size_impl_t; /* Size signifying variable-sized array */ -#define VAR 1 - - -/* fallback for round() */ -static inline double -_hb_round (double x) -{ - if (x >= 0) - return floor (x + 0.5); - else - return ceil (x - 0.5); -} -#if !defined (HAVE_ROUND) && !defined (HAVE_DECL_ROUND) -#define round(x) _hb_round(x) +#ifndef HB_VAR_ARRAY +#define HB_VAR_ARRAY 1 #endif +static inline float +_hb_roundf (float x) { return floorf (x + .5f); } +#define roundf(x) _hb_roundf(x) -/* fallback for posix_memalign() */ -static inline int -_hb_memalign(void **memptr, size_t alignment, size_t size) -{ - if (unlikely (0 != (alignment & (alignment - 1)) || - !alignment || - 0 != (alignment & (sizeof (void *) - 1)))) - return EINVAL; - - char *p = (char *) malloc (size + alignment - 1); - if (unlikely (!p)) - return ENOMEM; +/* Endian swap, used in Windows related backends */ +static inline uint16_t hb_uint16_swap (const uint16_t v) +{ return (v >> 8) | (v << 8); } +static inline uint32_t hb_uint32_swap (const uint32_t v) +{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); } - size_t off = (size_t) p & (alignment - 1); - if (off) - p += alignment - off; +/* + * Big-endian integers. Here because fundamental. + */ - *memptr = (void *) p; +template struct BEInt; - return 0; -} -#if !defined(posix_memalign) && !defined(HAVE_POSIX_MEMALIGN) -#define posix_memalign _hb_memalign -#endif +template +struct BEInt +{ + public: + BEInt& operator = (Type V) + { + v = V; + return *this; + } + operator Type () const { return v; } + private: uint8_t v; +}; +template +struct BEInt +{ + public: + BEInt& operator = (Type V) + { + v[0] = (V >> 8) & 0xFF; + v[1] = (V ) & 0xFF; + return *this; + } + operator Type () const + { +#if ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \ + defined(__BYTE_ORDER) && \ + (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN) + /* Spoon-feed the compiler a big-endian integer with alignment 1. + * https://github.com/harfbuzz/harfbuzz/pull/1398 */ + struct __attribute__((packed)) packed_uint16_t { uint16_t v; }; +#if __BYTE_ORDER == __LITTLE_ENDIAN + return __builtin_bswap16 (((packed_uint16_t *) this)->v); +#else /* __BYTE_ORDER == __BIG_ENDIAN */ + return ((packed_uint16_t *) this)->v; +#endif +#endif + return (v[0] << 8) + + (v[1] ); + } + private: uint8_t v[2]; +}; +template +struct BEInt +{ + public: + BEInt& operator = (Type V) + { + v[0] = (V >> 16) & 0xFF; + v[1] = (V >> 8) & 0xFF; + v[2] = (V ) & 0xFF; + return *this; + } + operator Type () const + { + return (v[0] << 16) + + (v[1] << 8) + + (v[2] ); + } + private: uint8_t v[3]; +}; +template +struct BEInt +{ + public: + BEInt& operator = (Type V) + { + v[0] = (V >> 24) & 0xFF; + v[1] = (V >> 16) & 0xFF; + v[2] = (V >> 8) & 0xFF; + v[3] = (V ) & 0xFF; + return *this; + } + operator Type () const + { + return (v[0] << 24) + + (v[1] << 16) + + (v[2] << 8) + + (v[3] ); + } + private: uint8_t v[4]; +}; /* @@ -630,28 +616,18 @@ _hb_memalign(void **memptr, size_t alignment, size_t size) #define HB_SCRIPT_MYANMAR_ZAWGYI ((hb_script_t) HB_TAG ('Q','a','a','g')) -/* Some really basic things everyone wants. */ -template struct hb_remove_const { typedef T value; }; -template struct hb_remove_const { typedef T value; }; -#define hb_remove_const(T) hb_remove_const::value -template struct hb_remove_reference { typedef T value; }; -template struct hb_remove_reference { typedef T value; }; -#define hb_remove_reference(T) hb_remove_reference::value -template struct hb_remove_pointer { typedef T value; }; -template struct hb_remove_pointer { typedef T value; }; -#define hb_remove_pointer(T) hb_remove_pointer::value - - /* Headers we include for everyone. Keep topologically sorted by dependency. * They express dependency amongst themselves, but no other file should include * them directly.*/ -#include "hb-atomic.hh" +#include "hb-meta.hh" #include "hb-mutex.hh" -#include "hb-null.hh" -#include "hb-dsalgs.hh" // Requires: hb-null -#include "hb-iter.hh" // Requires: hb-null -#include "hb-debug.hh" // Requires: hb-atomic hb-dsalgs -#include "hb-array.hh" // Requires: hb-dsalgs hb-iter hb-null +#include "hb-number.hh" +#include "hb-atomic.hh" // Requires: hb-meta +#include "hb-null.hh" // Requires: hb-meta +#include "hb-algs.hh" // Requires: hb-meta hb-null hb-number +#include "hb-iter.hh" // Requires: hb-algs hb-meta +#include "hb-debug.hh" // Requires: hb-algs hb-atomic +#include "hb-array.hh" // Requires: hb-algs hb-iter hb-null #include "hb-vector.hh" // Requires: hb-array hb-null #include "hb-object.hh" // Requires: hb-atomic hb-mutex hb-vector diff --git a/src/java.desktop/share/native/libjavajpeg/jmemnobs.c b/src/java.desktop/share/native/libjavajpeg/jmemnobs.c index 884c5a16830..ac455985aa2 100644 --- a/src/java.desktop/share/native/libjavajpeg/jmemnobs.c +++ b/src/java.desktop/share/native/libjavajpeg/jmemnobs.c @@ -70,13 +70,16 @@ jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject) /* * This routine computes the total memory space available for allocation. - * Here we always say, "we got all you want bud!" */ GLOBAL(size_t) jpeg_mem_available (j_common_ptr cinfo, size_t min_bytes_needed, size_t max_bytes_needed, size_t already_allocated) { + if (cinfo->mem->max_memory_to_use) + return cinfo->mem->max_memory_to_use - already_allocated; + + /* Here we say, "we got all you want bud!" */ return max_bytes_needed; } diff --git a/src/java.desktop/share/native/libjsound/MidiOutDevice.c b/src/java.desktop/share/native/libjsound/MidiOutDevice.c index 7411564cc5f..3738eba9381 100644 --- a/src/java.desktop/share/native/libjsound/MidiOutDevice.c +++ b/src/java.desktop/share/native/libjsound/MidiOutDevice.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -133,7 +133,7 @@ Java_com_sun_media_sound_MidiOutDevice_nSendLongMessage(JNIEnv* e, jobject thisO } /* "continuation" sysex messages start with F7 (instead of F0), but are sent without the F7. */ - if (data[0] == 0xF7) { + if (data[0] == 0xF7 && size > 1) { data++; size--; } diff --git a/src/java.desktop/unix/classes/sun/java2d/xr/XRPMBlitLoops.java b/src/java.desktop/unix/classes/sun/java2d/xr/XRPMBlitLoops.java index 3704367a9f2..abff8138ca8 100644 --- a/src/java.desktop/unix/classes/sun/java2d/xr/XRPMBlitLoops.java +++ b/src/java.desktop/unix/classes/sun/java2d/xr/XRPMBlitLoops.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,12 +34,12 @@ import java.awt.geom.*; import java.lang.ref.*; -public class XRPMBlitLoops { +public final class XRPMBlitLoops { static WeakReference argbTmpPM = new WeakReference(null); static WeakReference rgbTmpPM = new WeakReference(null); - public XRPMBlitLoops() { + private XRPMBlitLoops() { } public static void register() { @@ -355,19 +355,13 @@ class XrSwToPMBlit extends Blit { } public void Blit(SurfaceData src, SurfaceData dst, Composite comp, Region clip, int sx, int sy, int dx, int dy, int w, int h) { - // If the blit is write-only (putimge), no need for a temporary VI. - if (CompositeType.SrcOverNoEa.equals(comp) && (src.getTransparency() == Transparency.OPAQUE)) { - Blit opaqueSwToSurfaceBlit = Blit.getFromCache(src.getSurfaceType(), CompositeType.SrcNoEa, dst.getSurfaceType()); - opaqueSwToSurfaceBlit.Blit(src, dst, comp, clip, sx, sy, dx, dy, w, h); - } else { - try { - SunToolkit.awtLock(); + try { + SunToolkit.awtLock(); - XRSurfaceData vImgSurface = XRPMBlitLoops.cacheToTmpSurface(src, (XRSurfaceData) dst, w, h, sx, sy); - pmToSurfaceBlit.Blit(vImgSurface, dst, comp, clip, 0, 0, dx, dy, w, h); - } finally { - SunToolkit.awtUnlock(); - } + XRSurfaceData vImgSurface = XRPMBlitLoops.cacheToTmpSurface(src, (XRSurfaceData) dst, w, h, sx, sy); + pmToSurfaceBlit.Blit(vImgSurface, dst, comp, clip, 0, 0, dx, dy, w, h); + } finally { + SunToolkit.awtUnlock(); } } } diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/awt_GraphicsEnv.c b/src/java.desktop/unix/native/libawt_xawt/awt/awt_GraphicsEnv.c index 1d47520a23d..dc9c2a0eb0e 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/awt_GraphicsEnv.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/awt_GraphicsEnv.c @@ -23,6 +23,10 @@ * questions. */ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include "jni_util.h" #include "awt_p.h" #include "awt.h" @@ -31,14 +35,12 @@ #include #include #include -#ifndef HEADLESS #include #include #ifndef NO_XRANDR #include #endif #include "GLXGraphicsConfig.h" -#endif /* !HEADLESS */ #include #include @@ -54,8 +56,6 @@ #include #include "Trace.h" -#ifndef HEADLESS - int awt_numScreens; /* Xinerama-aware number of screens */ AwtScreenDataPtr x11Screens; @@ -66,12 +66,6 @@ AwtScreenDataPtr x11Screens; */ static jboolean glxRequested = JNI_FALSE; -#endif /* !HEADLESS */ - -#ifdef HEADLESS -#define Display void -#endif /* HEADLESS */ - Display *awt_display; jclass tkClass = NULL; @@ -92,9 +86,7 @@ jboolean awtLockInited = JNI_FALSE; struct X11GraphicsConfigIDs x11GraphicsConfigIDs; -#ifndef HEADLESS int awtCreateX11Colormap(AwtGraphicsConfigDataPtr adata); -#endif /* HEADLESS */ static char *x11GraphicsConfigClassName = "sun/awt/X11GraphicsConfig"; @@ -134,8 +126,6 @@ Java_sun_awt_X11GraphicsConfig_initIDs (JNIEnv *env, jclass cls) CHECK_NULL(x11GraphicsConfigIDs.bitsPerPixel); } -#ifndef HEADLESS - /* * XIOErrorHandler */ @@ -593,8 +583,6 @@ getAllConfigs (JNIEnv *env, int screen, AwtScreenDataPtr screenDataPtr) { AWT_UNLOCK (); } -#ifndef HEADLESS - /* * Checks if Xinerama is running and perform Xinerama-related initialization. */ @@ -656,7 +644,6 @@ static void xineramaInit(void) { DTRACE_PRINTLN1("\ncouldn't open shared library: %s\n", dlerror()); } } -#endif /* HEADLESS */ Display * awt_init_Display(JNIEnv *env, jobject this) @@ -733,7 +720,6 @@ awt_init_Display(JNIEnv *env, jobject this) return dpy; } -#endif /* !HEADLESS */ /* * Class: sun_awt_X11GraphicsEnvironment @@ -744,14 +730,9 @@ JNIEXPORT jint JNICALL Java_sun_awt_X11GraphicsEnvironment_getDefaultScreenNum( JNIEnv *env, jobject this) { -#ifdef HEADLESS - return (jint)0; -#else return DefaultScreen(awt_display); -#endif /* !HEADLESS */ } -#ifndef HEADLESS static void ensureConfigsInited(JNIEnv* env, int screen) { if (x11Screens[screen].numConfigs == 0) { if (env == NULL) { @@ -760,19 +741,12 @@ static void ensureConfigsInited(JNIEnv* env, int screen) { getAllConfigs (env, screen, &(x11Screens[screen])); } } -#endif -#ifdef HEADLESS -void* getDefaultConfig(int screen) { - return NULL; -} -#else AwtGraphicsConfigDataPtr getDefaultConfig(int screen) { ensureConfigsInited(NULL, screen); return x11Screens[screen].defaultConfig; } -#endif /* !HEADLESS */ /* * Class: sun_awt_X11GraphicsEnvironment @@ -783,10 +757,8 @@ JNIEXPORT void JNICALL Java_sun_awt_X11GraphicsEnvironment_initDisplay(JNIEnv *env, jobject this, jboolean glxReq) { -#ifndef HEADLESS glxRequested = glxReq; (void) awt_init_Display(env, this); -#endif /* !HEADLESS */ } /* @@ -797,7 +769,6 @@ Java_sun_awt_X11GraphicsEnvironment_initDisplay(JNIEnv *env, jobject this, JNIEXPORT jboolean JNICALL Java_sun_awt_X11GraphicsEnvironment_initGLX(JNIEnv *env, jclass x11ge) { -#ifndef HEADLESS jboolean glxAvailable; AWT_LOCK(); @@ -805,9 +776,6 @@ Java_sun_awt_X11GraphicsEnvironment_initGLX(JNIEnv *env, jclass x11ge) AWT_UNLOCK(); return glxAvailable; -#else - return JNI_FALSE; -#endif /* !HEADLESS */ } /* @@ -818,11 +786,7 @@ Java_sun_awt_X11GraphicsEnvironment_initGLX(JNIEnv *env, jclass x11ge) JNIEXPORT jint JNICALL Java_sun_awt_X11GraphicsEnvironment_getNumScreens(JNIEnv *env, jobject this) { -#ifdef HEADLESS - return (jint)0; -#else return awt_numScreens; -#endif /* !HEADLESS */ } /* @@ -833,11 +797,7 @@ Java_sun_awt_X11GraphicsEnvironment_getNumScreens(JNIEnv *env, jobject this) JNIEXPORT jlong JNICALL Java_sun_awt_X11GraphicsDevice_getDisplay(JNIEnv *env, jobject this) { -#ifdef HEADLESS - return NULL; -#else return ptr_to_jlong(awt_display); -#endif /* !HEADLESS */ } #ifdef MITSHM @@ -964,11 +924,7 @@ JNIEXPORT jstring JNICALL Java_sun_awt_X11GraphicsEnvironment_getDisplayString (JNIEnv *env, jobject this) { -#ifdef HEADLESS - return (jstring)NULL; -#else return (*env)->NewStringUTF(env, DisplayString(awt_display)); -#endif /* HEADLESS */ } @@ -981,12 +937,8 @@ JNIEXPORT jint JNICALL Java_sun_awt_X11GraphicsDevice_getNumConfigs( JNIEnv *env, jobject this, jint screen) { -#ifdef HEADLESS - return (jint)0; -#else ensureConfigsInited(env, screen); return x11Screens[screen].numConfigs; -#endif /* !HEADLESS */ } /* @@ -998,9 +950,6 @@ JNIEXPORT jint JNICALL Java_sun_awt_X11GraphicsDevice_getConfigVisualId( JNIEnv *env, jobject this, jint index, jint screen) { -#ifdef HEADLESS - return (jint)0; -#else int visNum; ensureConfigsInited(env, screen); @@ -1009,7 +958,6 @@ JNIEnv *env, jobject this, jint index, jint screen) } else { return ((jint)x11Screens[screen].configs[index]->awt_visInfo.visualid); } -#endif /* !HEADLESS */ } /* @@ -1021,9 +969,6 @@ JNIEXPORT jint JNICALL Java_sun_awt_X11GraphicsDevice_getConfigDepth( JNIEnv *env, jobject this, jint index, jint screen) { -#ifdef HEADLESS - return (jint)0; -#else int visNum; ensureConfigsInited(env, screen); @@ -1032,7 +977,6 @@ JNIEnv *env, jobject this, jint index, jint screen) } else { return ((jint)x11Screens[screen].configs[index]->awt_visInfo.depth); } -#endif /* !HEADLESS */ } /* @@ -1044,9 +988,6 @@ JNIEXPORT jint JNICALL Java_sun_awt_X11GraphicsDevice_getConfigColormap( JNIEnv *env, jobject this, jint index, jint screen) { -#ifdef HEADLESS - return (jint)0; -#else int visNum; ensureConfigsInited(env, screen); @@ -1055,7 +996,6 @@ JNIEnv *env, jobject this, jint index, jint screen) } else { return ((jint)x11Screens[screen].configs[index]->awt_cmap); } -#endif /* !HEADLESS */ } /* @@ -1067,7 +1007,6 @@ JNIEXPORT void JNICALL Java_sun_awt_X11GraphicsDevice_resetNativeData (JNIEnv *env, jclass x11gd, jint screen) { -#ifndef HEADLESS /* * Reset references to the various configs; the actual native config data * will be free'd later by the Disposer mechanism when the Java-level @@ -1081,7 +1020,6 @@ Java_sun_awt_X11GraphicsDevice_resetNativeData } x11Screens[screen].defaultConfig = NULL; x11Screens[screen].numConfigs = 0; -#endif /* !HEADLESS */ } /* @@ -1093,7 +1031,6 @@ JNIEXPORT void JNICALL Java_sun_awt_X11GraphicsConfig_dispose (JNIEnv *env, jclass x11gc, jlong configData) { -#ifndef HEADLESS AwtGraphicsConfigDataPtr aData = (AwtGraphicsConfigDataPtr) jlong_to_ptr(configData); @@ -1135,7 +1072,6 @@ Java_sun_awt_X11GraphicsConfig_dispose } free(aData); -#endif /* !HEADLESS */ } /* @@ -1147,12 +1083,8 @@ JNIEXPORT jdouble JNICALL Java_sun_awt_X11GraphicsConfig_getXResolution( JNIEnv *env, jobject this, jint screen) { -#ifdef HEADLESS - return (jdouble)0; -#else return ((DisplayWidth(awt_display, screen) * 25.4) / DisplayWidthMM(awt_display, screen)); -#endif /* !HEADLESS */ } /* @@ -1164,12 +1096,8 @@ JNIEXPORT jdouble JNICALL Java_sun_awt_X11GraphicsConfig_getYResolution( JNIEnv *env, jobject this, jint screen) { -#ifdef HEADLESS - return (jdouble)0; -#else return ((DisplayHeight(awt_display, screen) * 25.4) / DisplayHeightMM(awt_display, screen)); -#endif /* !HEADLESS */ } @@ -1182,16 +1110,12 @@ JNIEXPORT jint JNICALL Java_sun_awt_X11GraphicsConfig_getNumColors( JNIEnv *env, jobject this) { -#ifdef HEADLESS - return (jint)0; -#else AwtGraphicsConfigData *adata; adata = (AwtGraphicsConfigData *) JNU_GetLongFieldAsPtr(env, this, x11GraphicsConfigIDs.aData); return adata->awt_num_colors; -#endif /* !HEADLESS */ } /* @@ -1203,7 +1127,6 @@ JNIEXPORT void JNICALL Java_sun_awt_X11GraphicsConfig_init( JNIEnv *env, jobject this, jint visualNum, jint screen) { -#ifndef HEADLESS AwtGraphicsConfigData *adata = NULL; AwtScreenData asd = x11Screens[screen]; int i, n; @@ -1244,11 +1167,8 @@ JNIEnv *env, jobject this, jint visualNum, jint screen) (*env)->SetIntField(env, this, x11GraphicsConfigIDs.bitsPerPixel, (jint)tempImage->bits_per_pixel); XDestroyImage(tempImage); -#endif /* !HEADLESS */ } - - /* * Class: sun_awt_X11GraphicsConfig * Method: makeColorModel @@ -1258,9 +1178,6 @@ JNIEXPORT jobject JNICALL Java_sun_awt_X11GraphicsConfig_makeColorModel( JNIEnv *env, jobject this) { -#ifdef HEADLESS - return NULL; -#else AwtGraphicsConfigData *adata; jobject colorModel; @@ -1289,7 +1206,6 @@ JNIEnv *env, jobject this) AWT_UNLOCK (); return colorModel; -#endif /* !HEADLESS */ } @@ -1301,9 +1217,6 @@ JNIEnv *env, jobject this) JNIEXPORT jobject JNICALL Java_sun_awt_X11GraphicsConfig_pGetBounds(JNIEnv *env, jobject this, jint screen) { -#ifdef HEADLESS - return NULL; -#else jclass clazz; jmethodID mid; jobject bounds = NULL; @@ -1362,7 +1275,6 @@ Java_sun_awt_X11GraphicsConfig_pGetBounds(JNIEnv *env, jobject this, jint screen } } return bounds; -#endif /* !HEADLESS */ } /* @@ -1438,15 +1350,11 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_X11GraphicsConfig_isTranslucencyCapable (JNIEnv *env, jobject this, jlong configData) { -#ifdef HEADLESS - return JNI_FALSE; -#else AwtGraphicsConfigDataPtr aData = (AwtGraphicsConfigDataPtr)jlong_to_ptr(configData); if (aData == NULL) { return JNI_FALSE; } return aData->isTranslucencySupported ? JNI_TRUE : JNI_FALSE; -#endif } /* @@ -1457,9 +1365,6 @@ Java_sun_awt_X11GraphicsConfig_isTranslucencyCapable JNIEXPORT jboolean JNICALL Java_sun_awt_X11GraphicsDevice_isDBESupported(JNIEnv *env, jobject this) { -#ifdef HEADLESS - return JNI_FALSE; -#else int opcode = 0, firstEvent = 0, firstError = 0; jboolean ret; @@ -1468,7 +1373,6 @@ Java_sun_awt_X11GraphicsDevice_isDBESupported(JNIEnv *env, jobject this) &opcode, &firstEvent, &firstError); AWT_FLUSH_UNLOCK(); return ret; -#endif /* !HEADLESS */ } /* @@ -1480,7 +1384,6 @@ JNIEXPORT void JNICALL Java_sun_awt_X11GraphicsDevice_getDoubleBufferVisuals(JNIEnv *env, jobject this, jint screen) { -#ifndef HEADLESS jclass clazz; jmethodID midAddVisual; Window rootWindow; @@ -1515,7 +1418,6 @@ Java_sun_awt_X11GraphicsDevice_getDoubleBufferVisuals(JNIEnv *env, break; } } -#endif /* !HEADLESS */ } /* @@ -1527,19 +1429,13 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_X11GraphicsEnvironment_pRunningXinerama(JNIEnv *env, jobject this) { -#ifdef HEADLESS - return JNI_FALSE; -#else return usingXinerama ? JNI_TRUE : JNI_FALSE; -#endif /* HEADLESS */ } /** * Begin DisplayMode/FullScreen support */ -#ifndef HEADLESS - #ifndef NO_XRANDR #define BIT_DEPTH_MULTI java_awt_DisplayMode_BIT_DEPTH_MULTI @@ -1777,7 +1673,6 @@ X11GD_SetFullscreenMode(Window win, jboolean enabled) &event); XSync(awt_display, False); } -#endif /* !HEADLESS */ /* * Class: sun_awt_X11GraphicsDevice @@ -1788,7 +1683,7 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_X11GraphicsDevice_initXrandrExtension (JNIEnv *env, jclass x11gd) { -#if defined(HEADLESS) || defined(NO_XRANDR) +#if defined(NO_XRANDR) return JNI_FALSE; #else int opcode = 0, firstEvent = 0, firstError = 0; @@ -1803,7 +1698,7 @@ Java_sun_awt_X11GraphicsDevice_initXrandrExtension AWT_FLUSH_UNLOCK(); return ret; -#endif /* HEADLESS */ +#endif /* NO_XRANDR */ } /* @@ -1815,7 +1710,7 @@ JNIEXPORT jobject JNICALL Java_sun_awt_X11GraphicsDevice_getCurrentDisplayMode (JNIEnv* env, jclass x11gd, jint screen) { -#if defined(HEADLESS) || defined(NO_XRANDR) +#if defined(NO_XRANDR) return NULL; #else XRRScreenConfiguration *config; @@ -1898,7 +1793,7 @@ Java_sun_awt_X11GraphicsDevice_getCurrentDisplayMode AWT_FLUSH_UNLOCK(); return displayMode; -#endif /* HEADLESS */ +#endif /* NO_XRANDR */ } /* @@ -1911,7 +1806,7 @@ Java_sun_awt_X11GraphicsDevice_enumDisplayModes (JNIEnv* env, jclass x11gd, jint screen, jobject arrayList) { -#if !defined(HEADLESS) && !defined(NO_XRANDR) +#if !defined(NO_XRANDR) AWT_LOCK(); @@ -1986,7 +1881,7 @@ Java_sun_awt_X11GraphicsDevice_enumDisplayModes } AWT_FLUSH_UNLOCK(); -#endif /* !HEADLESS */ +#endif /* !NO_XRANDR */ } /* @@ -1999,7 +1894,7 @@ Java_sun_awt_X11GraphicsDevice_configDisplayMode (JNIEnv* env, jclass x11gd, jint screen, jint width, jint height, jint refreshRate) { -#if !defined(HEADLESS) && !defined(NO_XRANDR) +#if !defined(NO_XRANDR) jboolean success = JNI_FALSE; XRRScreenConfiguration *config; Drawable root; @@ -2069,7 +1964,7 @@ Java_sun_awt_X11GraphicsDevice_configDisplayMode if (!success && !(*env)->ExceptionCheck(env)) { JNU_ThrowInternalError(env, "Could not set display mode"); } -#endif /* !HEADLESS */ +#endif /* !NO_XRANDR */ } /* @@ -2082,14 +1977,12 @@ Java_sun_awt_X11GraphicsDevice_enterFullScreenExclusive (JNIEnv* env, jclass x11gd, jlong window) { -#ifndef HEADLESS Window win = (Window)window; AWT_LOCK(); XSync(awt_display, False); /* ensures window is visible first */ X11GD_SetFullscreenMode(win, JNI_TRUE); AWT_UNLOCK(); -#endif /* !HEADLESS */ } /* @@ -2102,13 +1995,11 @@ Java_sun_awt_X11GraphicsDevice_exitFullScreenExclusive (JNIEnv* env, jclass x11gd, jlong window) { -#ifndef HEADLESS Window win = (Window)window; AWT_LOCK(); X11GD_SetFullscreenMode(win, JNI_FALSE); AWT_UNLOCK(); -#endif /* !HEADLESS */ } /** diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/awt_Insets.c b/src/java.desktop/unix/native/libawt_xawt/awt/awt_Insets.c index cc4c68cddff..1e8d409ede0 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/awt_Insets.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/awt_Insets.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,10 @@ * questions. */ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include "java_awt_Insets.h" JNIEXPORT void JNICALL diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/awt_MenuComponent.h b/src/java.desktop/unix/native/libawt_xawt/awt/awt_MenuComponent.h index f254e0cc195..19dd1f590cb 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/awt_MenuComponent.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/awt_MenuComponent.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,10 @@ * questions. */ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include "jni_util.h" struct MenuComponentIDs { diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/awt_UNIXToolkit.c b/src/java.desktop/unix/native/libawt_xawt/awt/awt_UNIXToolkit.c index 62b13da078c..0387ee2af6f 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/awt_UNIXToolkit.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/awt_UNIXToolkit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,10 @@ * questions. */ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include #include #include @@ -32,11 +36,8 @@ #include #include "sun_awt_UNIXToolkit.h" -#ifndef HEADLESS #include "awt.h" #include "gtk_interface.h" -#endif /* !HEADLESS */ - static jclass this_class = NULL; static jmethodID icon_upcall_method = NULL; @@ -49,11 +50,7 @@ static jmethodID icon_upcall_method = NULL; */ JNIEXPORT jboolean JNICALL Java_sun_awt_UNIXToolkit_check_1gtk(JNIEnv *env, jclass klass, jint version) { -#ifndef HEADLESS return (jboolean)gtk_check_version(version); -#else - return JNI_FALSE; -#endif /* !HEADLESS */ } @@ -65,11 +62,7 @@ Java_sun_awt_UNIXToolkit_check_1gtk(JNIEnv *env, jclass klass, jint version) { JNIEXPORT jboolean JNICALL Java_sun_awt_UNIXToolkit_load_1gtk(JNIEnv *env, jclass klass, jint version, jboolean verbose) { -#ifndef HEADLESS return (jboolean)gtk_load(env, version, verbose); -#else - return JNI_FALSE; -#endif /* !HEADLESS */ } @@ -81,11 +74,7 @@ Java_sun_awt_UNIXToolkit_load_1gtk(JNIEnv *env, jclass klass, jint version, JNIEXPORT jboolean JNICALL Java_sun_awt_UNIXToolkit_unload_1gtk(JNIEnv *env, jclass klass) { -#ifndef HEADLESS return (jboolean)gtk->unload(); -#else - return JNI_FALSE; -#endif /* !HEADLESS */ } jboolean init_method(JNIEnv *env, jobject this) @@ -111,7 +100,6 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_UNIXToolkit_load_1gtk_1icon(JNIEnv *env, jobject this, jstring filename) { -#ifndef HEADLESS int len; jsize jlen; char *filename_str = NULL; @@ -142,9 +130,6 @@ Java_sun_awt_UNIXToolkit_load_1gtk_1icon(JNIEnv *env, jobject this, free(filename_str); return result; -#else /* HEADLESS */ - return JNI_FALSE; -#endif /* !HEADLESS */ } /* @@ -159,7 +144,6 @@ Java_sun_awt_UNIXToolkit_load_1stock_1icon(JNIEnv *env, jobject this, jint widget_type, jstring stock_id, jint icon_size, jint text_direction, jstring detail) { -#ifndef HEADLESS int len; jsize jlen; char *stock_id_str = NULL; @@ -206,9 +190,6 @@ Java_sun_awt_UNIXToolkit_load_1stock_1icon(JNIEnv *env, jobject this, free(detail_str); return result; -#else /* HEADLESS */ - return JNI_FALSE; -#endif /* !HEADLESS */ } /* @@ -219,11 +200,9 @@ Java_sun_awt_UNIXToolkit_load_1stock_1icon(JNIEnv *env, jobject this, JNIEXPORT void JNICALL Java_sun_awt_UNIXToolkit_nativeSync(JNIEnv *env, jobject this) { -#ifndef HEADLESS AWT_LOCK(); XSync(awt_display, False); AWT_UNLOCK(); -#endif /* !HEADLESS */ } /* @@ -275,9 +254,5 @@ Java_sun_awt_UNIXToolkit_gtkCheckVersionImpl(JNIEnv *env, jobject this, JNIEXPORT jint JNICALL Java_sun_awt_UNIXToolkit_get_1gtk_1version(JNIEnv *env, jclass klass) { -#ifndef HEADLESS return gtk ? gtk->version : GTK_ANY; -#else - return GTK_ANY; -#endif /* !HEADLESS */ } diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/canvas.h b/src/java.desktop/unix/native/libawt_xawt/awt/canvas.h index f35decb65dd..919d880ee2b 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/canvas.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/canvas.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,11 +22,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #ifndef _CANVAS_H_ #define _CANVAS_H_ -#ifndef HEADLESS KeySym awt_getX11KeySym(jint awtKey); -#endif /* !HEADLESS */ #endif /* _CANVAS_H_ */ diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c b/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c index 4995044dc49..6fe9797ecd3 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c @@ -22,6 +22,11 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include #include #include diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h b/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h index 18e53496899..d786117ce78 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,11 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #ifndef _GTK2_INTERFACE_H #define _GTK2_INTERFACE_H diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c index 886437e2a79..39f17a985af 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c @@ -22,6 +22,11 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include #include #include @@ -703,7 +708,7 @@ static int gtk3_unload() */ static void flush_gtk_event_loop() { - while((*fp_g_main_context_iteration)(NULL)); + while((*fp_g_main_context_iteration)(NULL, FALSE)); } /* diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h index 8b461ff335e..19a1e2ddaf5 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,11 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #ifndef _GTK3_INTERFACE_H #define _GTK3_INTERFACE_H @@ -396,7 +401,7 @@ static void (*fp_g_object_set)(gpointer object, const gchar *first_property_name, ...); -static gboolean (*fp_g_main_context_iteration)(GMainContext *context); +static gboolean (*fp_g_main_context_iteration)(GMainContext *context, gboolean may_block); static gboolean (*fp_g_str_has_prefix)(const gchar *str, const gchar *prefix); static gchar** (*fp_g_strsplit)(const gchar *string, const gchar *delimiter, gint max_tokens); diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.c b/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.c index 0b78024b434..c8573ed3fbf 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,11 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include #include #include "jvm_md.h" diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h b/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h index a2021ff02b7..98097e18301 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,11 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #ifndef _GTK_INTERFACE_H #define _GTK_INTERFACE_H diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/list.c b/src/java.desktop/unix/native/libawt_xawt/awt/list.c index abbfe7c72c0..fc3e02103b3 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/list.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/list.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -63,6 +63,10 @@ from The Open Group. ----------------------------------------------------------------------- **/ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include #include diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/list.h b/src/java.desktop/unix/native/libawt_xawt/awt/list.h index ff538ccfe07..bbae6d319ed 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/list.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/list.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -63,6 +63,10 @@ from The Open Group. -------------------------------------------------------------------- **/ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #ifndef LIST_DEF #define LIST_DEF diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/multiVis.c b/src/java.desktop/unix/native/libawt_xawt/awt/multiVis.c index 7f89010715a..e2e3cd11809 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/multiVis.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/multiVis.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -63,6 +63,10 @@ from The Open Group. ------------------------------------------------------------------------ **/ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include #include #include diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/multiVis.h b/src/java.desktop/unix/native/libawt_xawt/awt/multiVis.h index a505cd1a457..9c8149ae3fc 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/multiVis.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/multiVis.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -63,6 +63,10 @@ from The Open Group. ------------------------------------------------------------------------ **/ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + extern int GetMultiVisualRegions( Display *, Window, int, int, unsigned int, unsigned int, int *, int *, XVisualInfo **, int *, diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/sun_awt_X11_GtkFileDialogPeer.c b/src/java.desktop/unix/native/libawt_xawt/awt/sun_awt_X11_GtkFileDialogPeer.c index 556fe252538..0700f545eb1 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/sun_awt_X11_GtkFileDialogPeer.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/sun_awt_X11_GtkFileDialogPeer.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,10 @@ * questions. */ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include #include #include diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c b/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c index ef52c7aaf25..b507ae7a4c5 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c @@ -23,6 +23,10 @@ * questions. */ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include "gtk_interface.h" #include "com_sun_java_swing_plaf_gtk_GTKEngine.h" #include @@ -346,10 +350,8 @@ Java_com_sun_java_swing_plaf_gtk_GTKEngine_nativeFinishPainting( JNIEXPORT void JNICALL Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1switch_1theme( JNIEnv *env, jobject this) { - // Note that flush_gtk_event_loop takes care of locks (7053002) - gtk->gdk_threads_enter(); + // Note that gtk->flush_event_loop takes care of locks (7053002), gdk_threads_enter/gdk_threads_leave should not be used. gtk->flush_event_loop(); - gtk->gdk_threads_leave(); } /* diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKStyle.c b/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKStyle.c index a977f6bee2e..5b39fbd571d 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKStyle.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKStyle.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,10 @@ * questions. */ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include #include #include "gtk_interface.h" diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/wsutils.h b/src/java.desktop/unix/native/libawt_xawt/awt/wsutils.h index 52666a26bef..0daa569f527 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/wsutils.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/wsutils.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -70,6 +70,10 @@ from The Open Group. * ******************************************************************************/ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + typedef unsigned long Pixel; /* This is the actual structure returned by the X server describing the diff --git a/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRBackendNative.c b/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRBackendNative.c index bd6023d89b8..4709f8bb399 100644 --- a/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRBackendNative.c +++ b/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRBackendNative.c @@ -23,6 +23,10 @@ * questions. */ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include "X11SurfaceData.h" #include #include @@ -249,7 +253,6 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_X11GraphicsEnvironment_initXRender (JNIEnv *env, jclass x11ge, jboolean verbose, jboolean ignoreLinuxVersion) { -#ifndef HEADLESS static jboolean xrenderAvailable = JNI_FALSE; static jboolean firstTime = JNI_TRUE; @@ -267,9 +270,6 @@ Java_sun_awt_X11GraphicsEnvironment_initXRender firstTime = JNI_FALSE; } return xrenderAvailable; -#else - return JNI_FALSE; -#endif /* !HEADLESS */ } diff --git a/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRSurfaceData.c b/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRSurfaceData.c index 60b4d5b71e3..53068bd19d4 100644 --- a/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRSurfaceData.c +++ b/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRSurfaceData.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,10 @@ * questions. */ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include "GraphicsPrimitiveMgr.h" #include "Region.h" #include "Trace.h" @@ -43,20 +47,16 @@ #include #include -#ifndef HEADLESS jfieldID pictID; jfieldID xidID; jfieldID blitMaskPMID; jfieldID blitMaskPictID; -#endif /* !HEADLESS */ JNIEXPORT void JNICALL Java_sun_java2d_xr_XRSurfaceData_initXRPicture(JNIEnv *env, jobject xsd, jlong pXSData, jint pictFormat) { -#ifndef HEADLESS - X11SDOps *xsdo; XRenderPictFormat *fmt; @@ -78,13 +78,11 @@ JNIEXPORT void JNICALL (*env)->SetIntField (env, xsd, pictID, xsdo->xrPic); (*env)->SetIntField (env, xsd, xidID, xsdo->drawable); -#endif /* !HEADLESS */ } JNIEXPORT void JNICALL Java_sun_java2d_xr_XRSurfaceData_initIDs(JNIEnv *env, jclass xsd) { -#ifndef HEADLESS J2dTraceLn(J2D_TRACE_INFO, "in XRSurfaceData_initIDs"); pictID = (*env)->GetFieldID(env, xsd, "picture", "I"); @@ -97,7 +95,6 @@ Java_sun_java2d_xr_XRSurfaceData_initIDs(JNIEnv *env, jclass xsd) } XShared_initIDs(env, JNI_FALSE); -#endif /* !HEADLESS */ } @@ -107,7 +104,6 @@ Java_sun_java2d_xr_XRSurfaceData_XRInitSurface(JNIEnv *env, jclass xsd, jint width, jint height, jlong drawable, jint pictFormat) { -#ifndef HEADLESS X11SDOps *xsdo; J2dTraceLn(J2D_TRACE_INFO, "in XRSurfaceData_initSurface"); @@ -118,7 +114,6 @@ Java_sun_java2d_xr_XRSurfaceData_XRInitSurface(JNIEnv *env, jclass xsd, } XShared_initSurface(env, xsdo, depth, width, height, drawable); -#endif /* !HEADLESS */ } @@ -127,7 +122,6 @@ JNIEXPORT void JNICALL Java_sun_java2d_xr_XRSurfaceData_freeXSDOPicture(JNIEnv *env, jobject xsd, jlong pXSData) { -#ifndef HEADLESS X11SDOps *xsdo; J2dTraceLn(J2D_TRACE_INFO, "in XRSurfaceData_freeXSDOPicture"); @@ -141,5 +135,4 @@ Java_sun_java2d_xr_XRSurfaceData_freeXSDOPicture(JNIEnv *env, jobject xsd, XRenderFreePicture(awt_display, xsdo->xrPic); xsdo->xrPic = None; } -#endif /* !HEADLESS */ } diff --git a/src/java.desktop/unix/native/libawt_xawt/xawt/XToolkit.c b/src/java.desktop/unix/native/libawt_xawt/xawt/XToolkit.c index 102c42ca5b2..ac8d1fc154b 100644 --- a/src/java.desktop/unix/native/libawt_xawt/xawt/XToolkit.c +++ b/src/java.desktop/unix/native/libawt_xawt/xawt/XToolkit.c @@ -23,6 +23,10 @@ * questions. */ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include #include #include @@ -70,12 +74,9 @@ struct ComponentIDs componentIDs; struct MenuComponentIDs menuComponentIDs; -#ifndef HEADLESS - extern Display* awt_init_Display(JNIEnv *env, jobject this); extern void freeNativeStringArray(char **array, jsize length); extern char** stringArrayToNative(JNIEnv *env, jobjectArray array, jsize * ret_length); -#endif /* !HEADLESS */ /* This function gets called from the static initializer for FileDialog.java to initialize the fieldIDs for fields that may be accessed from C */ @@ -300,11 +301,7 @@ Java_java_awt_TextField_initIDs } JNIEXPORT jboolean JNICALL AWTIsHeadless() { -#ifdef HEADLESS - return JNI_TRUE; -#else return JNI_FALSE; -#endif } JNIEXPORT void JNICALL Java_java_awt_Dialog_initIDs (JNIEnv *env, jclass cls) diff --git a/src/java.desktop/unix/native/libawt_xawt/xawt/XWindow.c b/src/java.desktop/unix/native/libawt_xawt/xawt/XWindow.c index 461d0a44f55..be4b692b043 100644 --- a/src/java.desktop/unix/native/libawt_xawt/xawt/XWindow.c +++ b/src/java.desktop/unix/native/libawt_xawt/xawt/XWindow.c @@ -86,7 +86,6 @@ jfieldID graphicsConfigID; extern jobject currentX11InputMethodInstance; extern Boolean awt_x11inputmethod_lookupString(XKeyPressedEvent *, KeySym *); Boolean awt_UseType4Patch = False; -/* how about HEADLESS */ Boolean awt_ServerDetected = False; Boolean awt_XKBDetected = False; Boolean awt_IsXsun = False; diff --git a/src/java.desktop/unix/native/libawt_xawt/xawt/XlibWrapper.c b/src/java.desktop/unix/native/libawt_xawt/xawt/XlibWrapper.c index fba687ec120..19173eb6052 100644 --- a/src/java.desktop/unix/native/libawt_xawt/xawt/XlibWrapper.c +++ b/src/java.desktop/unix/native/libawt_xawt/xawt/XlibWrapper.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,10 @@ * questions. */ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include "awt.h" #include "awt_util.h" #include "jni.h" diff --git a/src/java.desktop/unix/native/libawt_xawt/xawt/awt_Desktop.c b/src/java.desktop/unix/native/libawt_xawt/xawt/awt_Desktop.c index 96acfb59d11..1e0b67316ae 100644 --- a/src/java.desktop/unix/native/libawt_xawt/xawt/awt_Desktop.c +++ b/src/java.desktop/unix/native/libawt_xawt/xawt/awt_Desktop.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,10 @@ * questions. */ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include "jni_util.h" #include "gtk_interface.h" #include "gnome_interface.h" diff --git a/src/java.desktop/unix/native/libawt_xawt/xawt/awt_Taskbar.c b/src/java.desktop/unix/native/libawt_xawt/xawt/awt_Taskbar.c index 9b415476f83..b5f267b9cf3 100644 --- a/src/java.desktop/unix/native/libawt_xawt/xawt/awt_Taskbar.c +++ b/src/java.desktop/unix/native/libawt_xawt/xawt/awt_Taskbar.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,10 @@ * questions. */ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include #include "jvm_md.h" #include diff --git a/src/java.desktop/unix/native/libawt_xawt/xawt/awt_Taskbar.h b/src/java.desktop/unix/native/libawt_xawt/xawt/awt_Taskbar.h index 0a574615d10..ec62bd452b6 100644 --- a/src/java.desktop/unix/native/libawt_xawt/xawt/awt_Taskbar.h +++ b/src/java.desktop/unix/native/libawt_xawt/xawt/awt_Taskbar.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,10 @@ * questions. */ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #ifndef AWT_TASKBAR_H #define AWT_TASKBAR_H diff --git a/src/java.desktop/unix/native/libawt_xawt/xawt/gnome_interface.c b/src/java.desktop/unix/native/libawt_xawt/xawt/gnome_interface.c index 46c96e13680..76d6ed4dd81 100644 --- a/src/java.desktop/unix/native/libawt_xawt/xawt/gnome_interface.c +++ b/src/java.desktop/unix/native/libawt_xawt/xawt/gnome_interface.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,10 @@ * questions. */ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #include "gnome_interface.h" GNOME_URL_SHOW_TYPE *gnome_url_show = NULL; diff --git a/src/java.desktop/unix/native/libawt_xawt/xawt/gnome_interface.h b/src/java.desktop/unix/native/libawt_xawt/xawt/gnome_interface.h index 86ea796ba7e..422e3a08f70 100644 --- a/src/java.desktop/unix/native/libawt_xawt/xawt/gnome_interface.h +++ b/src/java.desktop/unix/native/libawt_xawt/xawt/gnome_interface.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,10 @@ * questions. */ +#ifdef HEADLESS + #error This file should not be included in headless library +#endif + #ifndef _GNOME_INTERFACE_H #define _GNOME_INTERFACE_H #include "gtk_interface.h" diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonListener.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonListener.java index a78298cada2..11b0a8de24e 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonListener.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,23 +23,13 @@ * questions. */ - package com.sun.java.swing.plaf.windows; -import java.beans.PropertyChangeEvent; - -import javax.swing.*; -import javax.swing.plaf.basic.*; +import javax.swing.AbstractButton; +import javax.swing.plaf.basic.BasicButtonListener; /** - * Button Listener - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. + * Button Listener. * * @author Rich Schiavi */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java index 89a2a18e115..6b3d9b004bf 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,31 +25,38 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.basic.*; -import javax.swing.border.*; -import javax.swing.plaf.*; -import javax.swing.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Rectangle; + +import javax.swing.AbstractButton; +import javax.swing.ButtonModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JRadioButton; +import javax.swing.JToolBar; +import javax.swing.LookAndFeel; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicButtonUI; +import javax.swing.plaf.basic.BasicGraphicsUtils; -import java.awt.*; - -import static com.sun.java.swing.plaf.windows.TMSchema.*; -import static com.sun.java.swing.plaf.windows.TMSchema.Part.*; -import static com.sun.java.swing.plaf.windows.XPStyle.Skin; import sun.awt.AppContext; +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.State; +import static com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows button. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Jeff Dinkins - * */ public class WindowsButtonUI extends BasicButtonUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java index 99966cb210e..c7007bec3db 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,24 +25,21 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; -import javax.swing.*; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Rectangle; + +import javax.swing.ButtonModel; +import javax.swing.JComponent; +import javax.swing.JMenuItem; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI; import com.sun.java.swing.plaf.windows.TMSchema.Part; import com.sun.java.swing.plaf.windows.TMSchema.State; - /** * Windows check box menu item. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java index 91c919c8711..65b1131a351 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,23 +25,15 @@ package com.sun.java.swing.plaf.windows; -import sun.awt.AppContext; - -import javax.swing.plaf.basic.*; -import javax.swing.*; -import javax.swing.plaf.*; +import javax.swing.AbstractButton; +import javax.swing.JComponent; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; -import java.awt.*; +import sun.awt.AppContext; /** * Windows check box. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Jeff Dinkins */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java index 5141fa399da..6a2c9eeae31 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,13 +27,6 @@ /** * Implements the Windows95/98/ME/NT/2000 Look and Feel. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @since 1.5 */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java index 0de8f38d43e..13f7e6e7d47 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,38 +25,56 @@ package com.sun.java.swing.plaf.windows; -import java.beans.PropertyChangeListener; +import java.awt.Color; +import java.awt.Component; +import java.awt.ComponentOrientation; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.KeyboardFocusManager; +import java.awt.LayoutManager; +import java.awt.Rectangle; +import java.awt.event.KeyListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; import java.beans.PropertyChangeEvent; -import javax.swing.plaf.basic.*; -import javax.swing.plaf.*; -import javax.swing.border.*; -import javax.swing.*; -import java.awt.event.*; -import java.awt.*; +import java.beans.PropertyChangeListener; -import static com.sun.java.swing.plaf.windows.TMSchema.Part; -import static com.sun.java.swing.plaf.windows.TMSchema.State; -import static com.sun.java.swing.plaf.windows.XPStyle.Skin; +import javax.swing.ButtonModel; +import javax.swing.ComboBoxEditor; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.ListCellRenderer; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicComboBoxEditor; +import javax.swing.plaf.basic.BasicComboBoxRenderer; +import javax.swing.plaf.basic.BasicComboBoxUI; +import javax.swing.plaf.basic.BasicComboPopup; +import javax.swing.plaf.basic.ComboPopup; +import com.sun.java.swing.plaf.windows.WindowsBorders.DashedBorder; import sun.swing.DefaultLookup; import sun.swing.StringUIClientPropertyKey; -import com.sun.java.swing.plaf.windows.WindowsBorders.DashedBorder; +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.State; +import static com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows combo box. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Tom Santos * @author Igor Kushnirskiy */ - public class WindowsComboBoxUI extends BasicComboBoxUI { private static final MouseListener rolloverListener = diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java index 38618a82150..564ae5b9f71 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,23 +25,16 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; -import javax.swing.*; -import javax.swing.border.*; - +import java.awt.BorderLayout; +import java.awt.Dimension; +import javax.swing.JComponent; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicDesktopIconUI; /** * Windows icon for a minimized window on the desktop. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsDesktopIconUI extends BasicDesktopIconUI { private int width; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java index f9949fce4ca..3ceea2d31dc 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,20 +25,12 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.*; -import javax.swing.plaf.basic.*; +import javax.swing.JComponent; import javax.swing.plaf.ComponentUI; -import java.awt.event.*; +import javax.swing.plaf.basic.BasicDesktopPaneUI; /** * Windows desktop pane. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author David Kloba */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java index 0e2a1bcdc1b..2f5c45633d4 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,21 +25,13 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; -import javax.swing.*; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicEditorPaneUI; import javax.swing.text.Caret; - /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsEditorPaneUI extends BasicEditorPaneUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java index 3d8b523a238..86a587aabd3 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,28 +25,39 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.*; -import javax.swing.plaf.ButtonUI; -import javax.swing.plaf.UIResource; - -import java.awt.*; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Stroke; import java.io.Serializable; -import static com.sun.java.swing.plaf.windows.TMSchema.*; -import static com.sun.java.swing.plaf.windows.XPStyle.Skin; +import javax.swing.AbstractButton; +import javax.swing.ButtonModel; +import javax.swing.Icon; +import javax.swing.JCheckBox; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JInternalFrame; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.plaf.ButtonUI; +import javax.swing.plaf.UIResource; import sun.swing.MenuItemCheckIconFactory; import sun.swing.SwingUtilities2; +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.State; +import static com.sun.java.swing.plaf.windows.XPStyle.Skin; + /** * Factory object that can vend Icons appropriate for the Windows {@literal L & F}. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author David Kloba * @author Georges Saab diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java index 66b038d2ec5..19169f6e424 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,25 +25,25 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; -import java.beans.*; -import javax.swing.*; -import javax.swing.border.*; -import javax.swing.plaf.basic.*; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Insets; + +import javax.swing.DesktopManager; +import javax.swing.JComponent; +import javax.swing.JInternalFrame; +import javax.swing.LookAndFeel; +import javax.swing.UIManager; +import javax.swing.border.AbstractBorder; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicInternalFrameUI; -import static com.sun.java.swing.plaf.windows.TMSchema.*; +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsInternalFrameUI extends BasicInternalFrameUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java index 0f3c4562a55..93f69f49c63 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,31 +25,20 @@ package com.sun.java.swing.plaf.windows; -import sun.swing.SwingUtilities2; -import sun.awt.AppContext; - import java.awt.Color; import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.UIManager; - import javax.swing.plaf.ComponentUI; - import javax.swing.plaf.basic.BasicLabelUI; - +import sun.awt.AppContext; +import sun.swing.SwingUtilities2; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsLabelUI extends BasicLabelUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java index d3ab14f8d00..91a5732fc9e 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,58 +40,75 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.HeadlessException; +import java.awt.Image; +import java.awt.Insets; +import java.awt.KeyboardFocusManager; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; import java.awt.image.BufferedImage; +import java.awt.image.FilteredImageSource; import java.awt.image.ImageFilter; import java.awt.image.ImageProducer; -import java.awt.image.FilteredImageSource; import java.awt.image.RGBImageFilter; +import java.security.AccessController; -import javax.swing.plaf.*; -import javax.swing.*; -import javax.swing.plaf.basic.*; -import javax.swing.border.*; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JRootPane; +import javax.swing.JTextField; +import javax.swing.LayoutStyle; +import javax.swing.LookAndFeel; +import javax.swing.MenuSelectionManager; +import javax.swing.SwingConstants; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.BorderUIResource; +import javax.swing.plaf.ColorUIResource; +import javax.swing.plaf.FontUIResource; +import javax.swing.plaf.InsetsUIResource; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicBorders; +import javax.swing.plaf.basic.BasicLookAndFeel; import javax.swing.text.DefaultEditorKit; -import static javax.swing.UIDefaults.LazyValue; - -import java.awt.Font; -import java.awt.Color; -import java.awt.event.ActionEvent; - -import java.security.AccessController; -import sun.awt.SunToolkit; +import com.sun.java.swing.plaf.windows.WindowsIconFactory.VistaMenuItemCheckIconFactory; import sun.awt.OSInfo; +import sun.awt.SunToolkit; import sun.awt.shell.ShellFolder; import sun.font.FontUtilities; import sun.security.action.GetPropertyAction; - import sun.swing.DefaultLayoutStyle; import sun.swing.ImageIconUIResource; +import sun.swing.StringUIClientPropertyKey; import sun.swing.SwingAccessor; -import sun.swing.icon.SortArrowIcon; import sun.swing.SwingUtilities2; -import sun.swing.StringUIClientPropertyKey; +import sun.swing.icon.SortArrowIcon; import sun.swing.plaf.windows.ClassicSortArrowIcon; -import static com.sun.java.swing.plaf.windows.TMSchema.*; +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.Prop; +import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; - -import com.sun.java.swing.plaf.windows.WindowsIconFactory.VistaMenuItemCheckIconFactory; +import static javax.swing.UIDefaults.LazyValue; /** * Implements the Windows95/98/NT/2000 Look and Feel. * UI classes not implemented specifically for Windows will * default to those implemented in Basic. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. - * - * @author unattributed */ @SuppressWarnings("serial") // Superclass is not serializable across versions public class WindowsLookAndFeel extends BasicLookAndFeel diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java index af73b1948f0..23df6b67849 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,32 +25,35 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.basic.*; -import javax.swing.*; -import javax.swing.plaf.ActionMapUIResource; -import javax.swing.plaf.ComponentUI; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.HierarchyEvent; import java.awt.event.HierarchyListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; -import java.awt.event.WindowStateListener; -import java.awt.*; +import javax.swing.AbstractAction; +import javax.swing.ActionMap; +import javax.swing.JComponent; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JRootPane; +import javax.swing.MenuElement; +import javax.swing.MenuSelectionManager; +import javax.swing.SwingUtilities; +import javax.swing.plaf.ActionMapUIResource; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicMenuBarUI; -import com.sun.java.swing.plaf.windows.TMSchema.*; -import com.sun.java.swing.plaf.windows.XPStyle.*; +import com.sun.java.swing.plaf.windows.TMSchema.Part; +import com.sun.java.swing.plaf.windows.TMSchema.State; +import com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsMenuBarUI extends BasicMenuBarUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java index 3b895ba4d4f..09fad9c98a6 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,33 +25,34 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; +import java.awt.Color; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Rectangle; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import javax.swing.*; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; +import javax.swing.ButtonModel; +import javax.swing.JComponent; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicMenuItemUI; + +import com.sun.java.swing.plaf.windows.TMSchema.Part; +import com.sun.java.swing.plaf.windows.TMSchema.State; +import com.sun.java.swing.plaf.windows.XPStyle.Skin; import sun.swing.MenuItemCheckIconFactory; import sun.swing.MenuItemLayoutHelper; import sun.swing.SwingUtilities2; -import com.sun.java.swing.plaf.windows.TMSchema.*; -import com.sun.java.swing.plaf.windows.XPStyle.*; - /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Igor Kushnirskiy */ - public class WindowsMenuItemUI extends BasicMenuItemUI { /** * The instance of {@code PropertyChangeListener}. diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java index 803952b8011..4195b4e85ca 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,26 +25,30 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; import java.awt.event.MouseEvent; +import javax.swing.ButtonModel; +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.MenuElement; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.event.MouseInputListener; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicMenuUI; -import javax.swing.event.MouseInputListener; -import javax.swing.*; import com.sun.java.swing.plaf.windows.TMSchema.Part; import com.sun.java.swing.plaf.windows.TMSchema.State; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsMenuUI extends BasicMenuUI { protected Integer menuBarHeight; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java index b163d88d577..2755cc76c96 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2000, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,23 +25,10 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.*; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - - +import javax.swing.plaf.basic.BasicOptionPaneUI; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsOptionPaneUI extends BasicOptionPaneUI { } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java index aef2a218323..baf0997ceff 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,22 +25,13 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; -import javax.swing.*; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicPasswordFieldUI; import javax.swing.text.Caret; - - /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsPasswordFieldUI extends BasicPasswordFieldUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java index 06dde67d7a9..87e89fca3dc 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,31 +28,31 @@ import java.awt.Component; import java.awt.Graphics; import java.awt.Insets; -import java.awt.KeyEventPostProcessor; -import java.awt.KeyboardFocusManager; import java.awt.Window; -import java.awt.event.KeyEvent; -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; -import sun.swing.StringUIClientPropertyKey; +import javax.swing.JComponent; +import javax.swing.JPopupMenu; +import javax.swing.JRootPane; +import javax.swing.MenuElement; +import javax.swing.MenuSelectionManager; +import javax.swing.Popup; +import javax.swing.PopupFactory; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicPopupMenuUI; import com.sun.java.swing.plaf.windows.TMSchema.Part; import com.sun.java.swing.plaf.windows.TMSchema.State; import com.sun.java.swing.plaf.windows.XPStyle.Skin; +import sun.swing.StringUIClientPropertyKey; + import static sun.swing.SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Igor Kushnirskiy */ @@ -108,6 +108,7 @@ public void stateChanged(ChangeEvent ev) { WindowsGraphicsUtils.repaintMnemonicsInWindow(win); } } + repaintRoot = null; } else { Component c = (Component)path[0]; if (c instanceof JPopupMenu) c = ((JPopupMenu)c).getInvoker(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupWindow.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupWindow.java index 89ac13b66c4..bf01850297b 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupWindow.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupWindow.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,11 +22,13 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package com.sun.java.swing.plaf.windows; -import javax.swing.JWindow; -import java.awt.Window; import java.awt.Graphics; +import java.awt.Window; + +import javax.swing.JWindow; /** * A class which tags a window with a particular semantic usage, @@ -39,13 +41,6 @@ * Note that support for transition effects may be supported with a * different mechanism in the future and so this class is * package-private and targeted for Swing implementation use only. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Amy Fowler */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java index 45947fc2b7e..e53cc798e61 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,24 +25,28 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.basic.*; -import javax.swing.plaf.*; -import javax.swing.*; -import java.awt.*; - -import static com.sun.java.swing.plaf.windows.TMSchema.*; +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Rectangle; + +import javax.swing.JComponent; +import javax.swing.JProgressBar; +import javax.swing.LookAndFeel; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicProgressBarUI; + +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.Prop; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; - /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Michael C. Albers */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java index cce34eb9a65..584a0f1622b 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,23 +25,21 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; -import javax.swing.*; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Rectangle; + +import javax.swing.ButtonModel; +import javax.swing.JComponent; +import javax.swing.JMenuItem; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI; import com.sun.java.swing.plaf.windows.TMSchema.Part; import com.sun.java.swing.plaf.windows.TMSchema.State; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java index 36b5413e9bb..dd0a93c95a1 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,24 +25,23 @@ package com.sun.java.swing.plaf.windows; -import sun.awt.AppContext; - -import javax.swing.plaf.basic.*; -import javax.swing.*; -import javax.swing.plaf.*; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; -import java.awt.*; +import javax.swing.AbstractButton; +import javax.swing.JComponent; +import javax.swing.LookAndFeel; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicGraphicsUtils; +import javax.swing.plaf.basic.BasicRadioButtonUI; +import sun.awt.AppContext; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsRadioButtonUI extends BasicRadioButtonUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java index f6f1bcf15b9..a3dd04362b9 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,28 +25,35 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; -import java.awt.event.*; -import java.awt.image.*; -import java.lang.ref.*; -import java.util.*; -import javax.swing.plaf.basic.*; -import javax.swing.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.IndexColorModel; +import java.lang.ref.WeakReference; +import java.util.HashMap; + +import javax.swing.ButtonModel; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JScrollBar; +import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicArrowButton; +import javax.swing.plaf.basic.BasicScrollBarUI; -import static com.sun.java.swing.plaf.windows.TMSchema.*; +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.Prop; +import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; - /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsScrollBarUI extends BasicScrollBarUI { private Grid thumbGrid; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java index 86e5a0c9c86..6c89eabc894 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,20 +25,10 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.basic.*; -import javax.swing.*; - - +import javax.swing.plaf.basic.BasicScrollPaneUI; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsScrollPaneUI extends BasicScrollPaneUI {} diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java index 1bae3f8f441..ceaf878cbca 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,26 +25,24 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; import java.awt.event.MouseEvent; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; -import javax.swing.*; +import javax.swing.JComponent; +import javax.swing.JSlider; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicSliderUI; -import static com.sun.java.swing.plaf.windows.TMSchema.*; +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.Prop; +import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; - /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsSliderUI extends BasicSliderUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java index 327162e62bf..04eeb726f90 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,22 +25,16 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; -import javax.swing.JSplitPane; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; + import javax.swing.UIManager; -import javax.swing.plaf.basic.BasicSplitPaneUI; import javax.swing.plaf.basic.BasicSplitPaneDivider; - +import javax.swing.plaf.basic.BasicSplitPaneUI; /** * Divider used for Windows split pane. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Jeff Dinkins */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java index 3b85bbe660c..9305e46c36f 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,23 +25,13 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.basic.*; -import javax.swing.*; - -import javax.swing.plaf.basic.BasicSplitPaneUI; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicSplitPaneDivider; -import javax.swing.plaf.*; - +import javax.swing.plaf.basic.BasicSplitPaneUI; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsSplitPaneUI extends BasicSplitPaneUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java index eb8080ba3c9..9b4e22102eb 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,28 +25,28 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; - -import javax.swing.plaf.basic.*; -import javax.swing.plaf.*; -import javax.swing.*; -import java.util.Set; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.KeyboardFocusManager; +import java.awt.Rectangle; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; import java.util.HashSet; -import java.awt.event.*; +import java.util.Set; -import static com.sun.java.swing.plaf.windows.TMSchema.*; -import static com.sun.java.swing.plaf.windows.XPStyle.Skin; +import javax.swing.JComponent; +import javax.swing.KeyStroke; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicTabbedPaneUI; +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.State; +import static com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsTabbedPaneUI extends BasicTabbedPaneUI { /** diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java index ed8f90d9847..6aca81225ea 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,22 +25,13 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; -import javax.swing.*; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicTextAreaUI; import javax.swing.text.Caret; - - /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsTextAreaUI extends BasicTextAreaUI { /** diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java index 4e5b3d6c1ae..be3e6276161 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,17 +25,23 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; -import java.awt.event.*; -import java.beans.PropertyChangeEvent; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.BasicTextFieldUI; -import javax.swing.text.*; -import javax.swing.*; -import javax.swing.plaf.UIResource; -import sun.swing.DefaultLookup; - +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Rectangle; +import javax.swing.BoundedRangeModel; +import javax.swing.JComponent; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.TextUI; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicTextFieldUI; +import javax.swing.text.BadLocationException; +import javax.swing.text.Caret; +import javax.swing.text.DefaultCaret; +import javax.swing.text.Highlighter; +import javax.swing.text.Position; /** * Provides the Windows look and feel for a text field. This @@ -53,13 +59,6 @@ *

  • Ctrl-left-arrow and ctrl-right-arrow act like home and * end respectively. * - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Timothy Prinzing */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java index f17b2c49599..372563f74f4 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,21 +25,13 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; -import javax.swing.*; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicTextPaneUI; import javax.swing.text.Caret; - /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsTextPaneUI extends BasicTextPaneUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextUI.java index 58e9dfe5dbf..b73b38385f2 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,21 +29,22 @@ import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; -import javax.swing.plaf.basic.*; -import javax.swing.*; + import javax.swing.plaf.TextUI; import javax.swing.plaf.UIResource; -import javax.swing.text.*; +import javax.swing.plaf.basic.BasicTextUI; +import javax.swing.text.BadLocationException; +import javax.swing.text.Caret; +import javax.swing.text.DefaultCaret; +import javax.swing.text.DefaultHighlighter; +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; +import javax.swing.text.LayeredHighlighter; +import javax.swing.text.Position; +import javax.swing.text.View; /** * Windows text rendering. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public abstract class WindowsTextUI extends BasicTextUI { /** diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java index afbc825fb20..feee0c0aaef 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,27 +25,23 @@ package com.sun.java.swing.plaf.windows; -import sun.awt.AppContext; - -import javax.swing.plaf.basic.*; -import javax.swing.border.*; -import javax.swing.plaf.*; -import javax.swing.*; - -import java.awt.*; -import java.beans.PropertyChangeEvent; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; +import javax.swing.AbstractButton; +import javax.swing.JComponent; +import javax.swing.LookAndFeel; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicGraphicsUtils; +import javax.swing.plaf.basic.BasicToggleButtonUI; +import sun.awt.AppContext; /** * A Windows toggle button. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Jeff Dinkins */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java index 8849b786f6f..5bef0e075a4 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,31 +25,26 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; -import java.awt.event.*; - -import java.io.*; -import java.util.*; - -import javax.swing.plaf.basic.*; -import javax.swing.*; -import javax.swing.plaf.*; - -import javax.swing.tree.*; - -import static com.sun.java.swing.plaf.windows.TMSchema.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.io.Serializable; + +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JTree; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicTreeUI; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.TreeCellRenderer; + +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; - /** * A Windows tree. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Scott Violet */ @@ -118,14 +113,7 @@ protected TreeCellRenderer createDefaultCellRenderer() { } /** - * The minus sign button icon - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. + * The minus sign button icon. */ @SuppressWarnings("serial") // Same-version serialization only public static class ExpandedIcon implements Icon, Serializable { @@ -172,13 +160,6 @@ public int getIconHeight() { /** * The plus sign button icon - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ @SuppressWarnings("serial") // Superclass is not serializable across versions public static class CollapsedIcon extends ExpandedIcon { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/XPStyle.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/XPStyle.java index 2c60209186f..71bb48a9a16 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/XPStyle.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/XPStyle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -593,8 +593,7 @@ void paintSkin(Graphics g, int dx, int dy, int dw, int dh, State state) { if (XPStyle.getXP() == null) { return; } - if (ThemeReader.isGetThemeTransitionDurationDefined() - && component instanceof JComponent + if (component instanceof JComponent && SwingUtilities.getAncestorOfClass(CellRendererPane.class, component) == null) { AnimationController.paintSkin((JComponent) component, this, diff --git a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java index b4962f2a5d7..52a36786064 100644 --- a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java +++ b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java @@ -467,7 +467,7 @@ public synchronized void setDisplayMode(DisplayMode dm) { // display mode Rectangle screenBounds = getDefaultConfiguration().getBounds(); w.setBounds(screenBounds.x, screenBounds.y, - dm.getWidth(), dm.getHeight()); + screenBounds.width, screenBounds.height); // Note: no call to replaceSurfaceData is required here since // replacement will be caused by an upcoming display change event } else { diff --git a/src/java.desktop/windows/classes/sun/awt/windows/ThemeReader.java b/src/java.desktop/windows/classes/sun/awt/windows/ThemeReader.java index 167d4d5b910..482a92fd885 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/ThemeReader.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/ThemeReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,13 +35,6 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -/* !!!! WARNING !!!! - * This class has to be in sync with - * src/solaris/classes/sun/awt/windows/ThemeReader.java - * while we continue to build WinL&F on solaris - */ - - /** * Implements Theme Support for Windows XP. * @@ -299,8 +292,6 @@ public static long getThemeTransitionDuration(String widget, int part, } } - public static native boolean isGetThemeTransitionDurationDefined(); - private static native Insets getThemeBackgroundContentMargins(long theme, int part, int state, int boundingWidth, int boundingHeight); diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java index d8114aed453..c07d116abfb 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java @@ -533,7 +533,11 @@ public void run() { @Override public boolean updateGraphicsData(GraphicsConfiguration gc) { + var old = getGraphicsConfiguration().getDefaultTransform(); winGraphicsConfig = (Win32GraphicsConfig)gc; + if (gc != null && !old.equals(gc.getDefaultTransform())) { + syncBounds(); // the bounds of the peer depend on the DPI + } try { replaceSurfaceData(); } catch (InvalidPipeException e) { @@ -542,6 +546,14 @@ public boolean updateGraphicsData(GraphicsConfiguration gc) { return false; } + /** + * Make sure that the native peer's coordinates are in sync with the target. + */ + void syncBounds() { + Rectangle r = ((Component) target).getBounds(); + setBounds(r.x, r.y, r.width, r.height, SET_BOUNDS); + } + //This will return null for Components not yet added to a Container @Override public ColorModel getColorModel() { diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WDialogPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WDialogPeer.java index ea4fc4aaa41..930064d5981 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WDialogPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WDialogPeer.java @@ -36,6 +36,8 @@ import sun.awt.AWTAccessor; import sun.awt.im.InputMethodManager; +import static sun.java2d.SunGraphicsEnvironment.toUserSpace; + final class WDialogPeer extends WWindowPeer implements DialogPeer { // Toolkit & peer internals @@ -117,8 +119,8 @@ public Dimension getMinimumSize() { if (((Dialog)target).isUndecorated()) { return super.getMinimumSize(); } - return new Dimension(scaleDownX(getSysMinWidth()), - scaleDownY(getSysMinHeight())); + return toUserSpace(getGraphicsConfiguration(), + getSysMinWidth(), getSysMinHeight()); } @Override diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WFramePeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WFramePeer.java index eaa830fc8af..8671f25f308 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WFramePeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WFramePeer.java @@ -38,7 +38,9 @@ import sun.awt.im.InputMethodManager; import sun.security.action.GetPropertyAction; -import static sun.java2d.SunGraphicsEnvironment.convertToDeviceSpace; +import static sun.java2d.SunGraphicsEnvironment.getGCDeviceBounds; +import static sun.java2d.SunGraphicsEnvironment.toDeviceSpaceAbs; +import static sun.java2d.SunGraphicsEnvironment.toUserSpace; class WFramePeer extends WWindowPeer implements FramePeer { @@ -97,10 +99,9 @@ public final void setMaximizedBounds(Rectangle b) { */ private Rectangle adjustMaximizedBounds(Rectangle bounds) { // All calculations should be done in the device space - bounds = convertToDeviceSpace(bounds); - + bounds = toDeviceSpaceAbs(bounds); GraphicsConfiguration gc = getGraphicsConfiguration(); - Rectangle currentDevBounds = convertToDeviceSpace(gc, gc.getBounds()); + Rectangle currentDevBounds = getGCDeviceBounds(gc); // Prepare data for WM_GETMINMAXINFO message. // ptMaxPosition should be in coordinate system of the current monitor, // not the main monitor, or monitor on which we maximize the window. @@ -148,13 +149,13 @@ public void reshape(int x, int y, int width, int height) { @Override public final Dimension getMinimumSize() { + GraphicsConfiguration gc = getGraphicsConfiguration(); Dimension d = new Dimension(); if (!((Frame)target).isUndecorated()) { - d.setSize(scaleDownX(getSysMinWidth()), - scaleDownY(getSysMinHeight())); + d.setSize(toUserSpace(gc, getSysMinWidth(), getSysMinHeight())); } - if (((Frame)target).getMenuBar() != null) { - d.height += scaleDownY(getSysMenuHeight()); + if (((Frame) target).getMenuBar() != null) { + d.height += toUserSpace(gc, 0, getSysMenuHeight()).height; } return d; } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WRobotPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WRobotPeer.java index 4d17500e079..ad924dd6e1f 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WRobotPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WRobotPeer.java @@ -29,14 +29,14 @@ import java.awt.Rectangle; import java.awt.peer.RobotPeer; -import sun.java2d.SunGraphicsEnvironment; +import static sun.java2d.SunGraphicsEnvironment.toDeviceSpaceAbs; final class WRobotPeer implements RobotPeer { public native void mouseMoveImpl(int x, int y); @Override public void mouseMove(int x, int y) { - Point point = SunGraphicsEnvironment.convertToDeviceSpace(x, y); + Point point = toDeviceSpaceAbs(x, y); mouseMoveImpl(point.x, point.y); } @Override @@ -64,5 +64,10 @@ public int getRGBPixel(int x, int y) { return pixelArray; } + @Override + public boolean useAbsoluteCoordinates() { + return true; + } + private native void getRGBPixels(int x, int y, int width, int height, int[] pixelArray); } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java index 5db663c6f7f..6c3d059457d 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java @@ -63,10 +63,11 @@ import sun.awt.Win32GraphicsConfig; import sun.awt.Win32GraphicsDevice; import sun.awt.Win32GraphicsEnvironment; -import sun.java2d.SunGraphicsEnvironment; import sun.java2d.pipe.Region; import sun.util.logging.PlatformLogger; +import static sun.java2d.SunGraphicsEnvironment.toUserSpace; + public class WWindowPeer extends WPanelPeer implements WindowPeer, DisplayChangedListener { @@ -108,8 +109,6 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, * WindowStateEvent is posted to the EventQueue. */ private WindowListener windowListener; - private float scaleX; - private float scaleY; /** * Initialize JNI field IDs @@ -222,8 +221,6 @@ void initialize() { GraphicsConfiguration gc = getGraphicsConfiguration(); Win32GraphicsDevice gd = (Win32GraphicsDevice) gc.getDevice(); gd.addDisplayChangedListener(this); - scaleX = gd.getDefaultScaleX(); - scaleY = gd.getDefaultScaleY(); initActiveWindowsTracking((Window)target); @@ -310,6 +307,12 @@ public void show() { } } + @Override + final void syncBounds() { + // Windows will take care of the top-level window/frame/dialog, and + // update the location/size when DPI changes. + } + // Synchronize the insets members (here & in helper) with actual window // state. native void updateInsets(Insets i); @@ -438,9 +441,10 @@ public void updateMinimumSize() { minimumSize = ((Component)target).getMinimumSize(); } if (minimumSize != null) { - int w = Math.max(minimumSize.width, scaleDownX(getSysMinWidth())); - int h = Math.max(minimumSize.height, scaleDownY(getSysMinHeight())); - setMinSize(w, h); + Dimension sysMin = toUserSpace(getGraphicsConfiguration(), + getSysMinWidth(), getSysMinHeight()); + setMinSize(Math.max(minimumSize.width, sysMin.width), + Math.max(minimumSize.height, sysMin.height)); } else { setMinSize(0, 0); } @@ -598,21 +602,6 @@ public void updateGC() { AWTAccessor.getComponentAccessor(). setGraphicsConfiguration((Component)target, winGraphicsConfig); - - checkDPIChange(oldDev, newDev); - } - - private void checkDPIChange(Win32GraphicsDevice oldDev, - Win32GraphicsDevice newDev) { - float newScaleX = newDev.getDefaultScaleX(); - float newScaleY = newDev.getDefaultScaleY(); - - if (scaleX != newScaleX || scaleY != newScaleY) { - windowDPIChange(oldDev.getScreen(), scaleX, scaleY, - newDev.getScreen(), newScaleX, newScaleY); - scaleX = newScaleX; - scaleY = newScaleY; - } } /** @@ -666,77 +655,9 @@ boolean isTargetUndecorated() { return true; } - // These are the peer bounds. They get updated at: - // 1. the WWindowPeer.setBounds() method. - // 2. the native code (on WM_SIZE/WM_MOVE) - private volatile int sysX = 0; - private volatile int sysY = 0; - private volatile int sysW = 0; - private volatile int sysH = 0; - @Override public native void repositionSecurityWarning(); - @Override - public void setBounds(int x, int y, int width, int height, int op) { - sysX = x; - sysY = y; - sysW = width; - sysH = height; - - int cx = x + width / 2; - int cy = y + height / 2; - GraphicsConfiguration current = getGraphicsConfiguration(); - GraphicsConfiguration other = SunGraphicsEnvironment - .getGraphicsConfigurationAtPoint(current, cx, cy); - if (!current.equals(other)) { - AffineTransform tx = other.getDefaultTransform(); - double otherScaleX = tx.getScaleX(); - double otherScaleY = tx.getScaleY(); - initScales(); - if (scaleX != otherScaleX || scaleY != otherScaleY) { - x = (int) Math.floor(x * otherScaleX / scaleX); - y = (int) Math.floor(y * otherScaleY / scaleY); - } - } - - super.setBounds(x, y, width, height, op); - } - - private void initScales() { - - if (scaleX >= 1 && scaleY >= 1) { - return; - } - - GraphicsConfiguration gc = getGraphicsConfiguration(); - if (gc instanceof Win32GraphicsConfig) { - Win32GraphicsDevice gd = ((Win32GraphicsConfig) gc).getDevice(); - scaleX = gd.getDefaultScaleX(); - scaleY = gd.getDefaultScaleY(); - } else { - AffineTransform tx = gc.getDefaultTransform(); - scaleX = (float) tx.getScaleX(); - scaleY = (float) tx.getScaleY(); - } - } - - final int scaleUpX(int x) { - return Region.clipRound(x * scaleX); - } - - final int scaleUpY(int y) { - return Region.clipRound(y * scaleY); - } - - final int scaleDownX(int x) { - return Region.clipRound(x / scaleX); - } - - final int scaleDownY(int y) { - return Region.clipRound(y / scaleY); - } - @Override public void print(Graphics g) { // We assume we print the whole frame, @@ -905,9 +826,6 @@ private void updateWindow(boolean repaint) { } } - native void windowDPIChange(int prevScreen, float prevScaleX, float prevScaleY, - int newScreen, float newScaleX, float newScaleY); - /* * The method maps the list of the active windows to the window's AppContext, * then the method registers ActiveWindowListener, GuiDisposedListener listeners; diff --git a/src/java.desktop/windows/native/libawt/java2d/windows/GDIWindowSurfaceData.cpp b/src/java.desktop/windows/native/libawt/java2d/windows/GDIWindowSurfaceData.cpp index bada561fe8c..804a8efb8e9 100644 --- a/src/java.desktop/windows/native/libawt/java2d/windows/GDIWindowSurfaceData.cpp +++ b/src/java.desktop/windows/native/libawt/java2d/windows/GDIWindowSurfaceData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -125,9 +125,14 @@ void SetupThreadGraphicsInfo(JNIEnv *env, GDIWinSDOps *wsdo) { // First, init the HDC object AwtComponent *comp = GDIWindowSurfaceData_GetComp(env, wsdo); if (comp == NULL) { + // wsdo->invalid is set by GDIWindowSurfaceData_GetComp return; } hDC = comp->GetDCFromComponent(); + if (hDC == NULL) { + wsdo->invalid = JNI_TRUE; + return; + } if (hDC != NULL && wsdo->device != NULL) { ::SelectObject(hDC, nullbrush); ::SelectObject(hDC, nullpen); diff --git a/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp b/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp index 37fbc914519..560764e8b28 100644 --- a/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp +++ b/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -106,8 +106,8 @@ Java_sun_awt_windows_WMouseInfoPeer_fillPointWithCoords(JNIEnv *env, jclass cls, yID = env->GetFieldID(pointClass, "y", "I"); CHECK_NULL_RETURN(yID, (jint)0); - int x = (device == NULL) ? pt.x : device->ScaleDownX(pt.x); - int y = (device == NULL) ? pt.y : device->ScaleDownY(pt.y); + int x = (device == NULL) ? pt.x : device->ScaleDownAbsX(pt.x); + int y = (device == NULL) ? pt.y : device->ScaleDownAbsY(pt.y); env->SetIntField(point, xID, x); env->SetIntField(point, yID, y); diff --git a/src/java.desktop/windows/native/libawt/windows/ThemeReader.cpp b/src/java.desktop/windows/native/libawt/windows/ThemeReader.cpp index 8a4461be409..002b502fafb 100644 --- a/src/java.desktop/windows/native/libawt/windows/ThemeReader.cpp +++ b/src/java.desktop/windows/native/libawt/windows/ThemeReader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,51 +24,13 @@ */ #include "sun_awt_windows_ThemeReader.h" -#include #include "awt.h" #include "awt_Toolkit.h" -#include "awt_Object.h" -#include "awt_Component.h" #include "math.h" -// Important note about VC6 and VC7 (or XP Platform SDK) ! -// -// These type definitions have been imported from UxTheme.h -// They have been imported instead of including them, because -// currently we don't require Platform SDK for building J2SE and -// VC6 includes do not have UxTheme.h. When we move to VC7 -// we should remove these imports and just include -// -// Uncomment these when we start using VC 7 (or XP Platform SDK) -// -// #include -// #incldue - - -// Remove everyting inside this ifdef when we start using VC 7 (or XP Platform SDK) -#ifndef _UXTHEME_H_ -typedef HANDLE HTHEME; // handle to a section of theme data for class - -typedef enum { - TS_MIN, - TS_TRUE, - TS_DRAW -} THEME_SIZE; - - -// Remove these when we start using VC 7 (or XP Platform SDK) -typedef struct _MARGINS -{ - int cxLeftWidth; // width of left border that retains its size - int cxRightWidth; // width of right border that retains its size - int cyTopHeight; // height of top border that retains its size - int cyBottomHeight; // height of bottom border that retains its size -} MARGINS, *PMARGINS; - -#define TMT_TRANSPARENT 2201 -#endif // _UXTHEME_H_ +#include #if defined(_MSC_VER) && _MSC_VER >= 1800 # define ROUND_TO_INT(num) ((int) round(num)) @@ -119,7 +81,7 @@ typedef HRESULT (__stdcall *PFNGETTHEMEENUMVALUE)(HTHEME hTheme, int iPartId, typedef HRESULT (__stdcall *PFNGETTHEMEINT)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, int *val); typedef HRESULT (__stdcall *PFNGETTHEMEPARTSIZE)(HTHEME hTheme, HDC hdc, - int iPartId, int iStateId, RECT *prc, THEME_SIZE eSize, SIZE *size); + int iPartId, int iStateId, RECT *prc, THEMESIZE eSize, SIZE *size); typedef HRESULT (__stdcall *PFNGETTHEMEPOSITION)(HTHEME hTheme, int iPartId, int iStateId, int propID, POINT *point); @@ -134,25 +96,24 @@ typedef HRESULT (__stdcall *PFNGETTHEMETRANSITIONDURATION) (HTHEME hTheme, int iPartId, int iStateIdFrom, int iStateIdTo, int iPropId, DWORD *pdwDuration); -static PFNOPENTHEMEDATA OpenThemeData = NULL; -static PFNDRAWTHEMEBACKGROUND DrawThemeBackground = NULL; -static PFNCLOSETHEMEDATA CloseThemeData = NULL; -static PFNDRAWTHEMETEXT DrawThemeText = NULL; -static PFNGETTHEMEBACKGROUNDCONTENTRECT GetThemeBackgroundContentRect = NULL; -static PFNGETTHEMEMARGINS GetThemeMargins = NULL; -static PFNISTHEMEPARTDEFINED IsThemePartDefined = NULL; -static PFNGETTHEMEBOOL GetThemeBool=NULL; -static PFNGETTHEMESYSBOOL GetThemeSysBool=NULL; -static PFNGETTHEMECOLOR GetThemeColor=NULL; -static PFNGETTHEMEENUMVALUE GetThemeEnumValue = NULL; -static PFNGETTHEMEINT GetThemeInt = NULL; -static PFNGETTHEMEPARTSIZE GetThemePartSize = NULL; -static PFNGETTHEMEPOSITION GetThemePosition = NULL; -static PFNSETWINDOWTHEME SetWindowTheme = NULL; +static PFNOPENTHEMEDATA OpenThemeDataFunc = NULL; +static PFNDRAWTHEMEBACKGROUND DrawThemeBackgroundFunc = NULL; +static PFNCLOSETHEMEDATA CloseThemeDataFunc = NULL; +static PFNDRAWTHEMETEXT DrawThemeTextFunc = NULL; +static PFNGETTHEMEBACKGROUNDCONTENTRECT GetThemeBackgroundContentRectFunc = NULL; +static PFNGETTHEMEMARGINS GetThemeMarginsFunc = NULL; +static PFNISTHEMEPARTDEFINED IsThemePartDefinedFunc = NULL; +static PFNGETTHEMEBOOL GetThemeBoolFunc=NULL; +static PFNGETTHEMESYSBOOL GetThemeSysBoolFunc=NULL; +static PFNGETTHEMECOLOR GetThemeColorFunc=NULL; +static PFNGETTHEMEENUMVALUE GetThemeEnumValueFunc = NULL; +static PFNGETTHEMEINT GetThemeIntFunc = NULL; +static PFNGETTHEMEPARTSIZE GetThemePartSizeFunc = NULL; +static PFNGETTHEMEPOSITION GetThemePositionFunc = NULL; +static PFNSETWINDOWTHEME SetWindowThemeFunc = NULL; static PFNISTHEMEBACKGROUNDPARTIALLYTRANSPARENT - IsThemeBackgroundPartiallyTransparent = NULL; -//this function might not exist on Windows XP -static PFNGETTHEMETRANSITIONDURATION GetThemeTransitionDuration = NULL; + IsThemeBackgroundPartiallyTransparentFunc = NULL; +static PFNGETTHEMETRANSITIONDURATION GetThemeTransitionDurationFunc = NULL; BOOL InitThemes() { @@ -161,67 +122,67 @@ BOOL InitThemes() { DTRACE_PRINTLN1("InitThemes hModThemes = %x\n", hModThemes); if(hModThemes) { DTRACE_PRINTLN("Loaded UxTheme.dll\n"); - OpenThemeData = (PFNOPENTHEMEDATA)GetProcAddress(hModThemes, + OpenThemeDataFunc = (PFNOPENTHEMEDATA)GetProcAddress(hModThemes, "OpenThemeData"); - DrawThemeBackground = (PFNDRAWTHEMEBACKGROUND)GetProcAddress( + DrawThemeBackgroundFunc = (PFNDRAWTHEMEBACKGROUND)GetProcAddress( hModThemes, "DrawThemeBackground"); - CloseThemeData = (PFNCLOSETHEMEDATA)GetProcAddress( + CloseThemeDataFunc = (PFNCLOSETHEMEDATA)GetProcAddress( hModThemes, "CloseThemeData"); - DrawThemeText = (PFNDRAWTHEMETEXT)GetProcAddress( + DrawThemeTextFunc = (PFNDRAWTHEMETEXT)GetProcAddress( hModThemes, "DrawThemeText"); - GetThemeBackgroundContentRect = (PFNGETTHEMEBACKGROUNDCONTENTRECT) + GetThemeBackgroundContentRectFunc = (PFNGETTHEMEBACKGROUNDCONTENTRECT) GetProcAddress(hModThemes, "GetThemeBackgroundContentRect"); - GetThemeMargins = (PFNGETTHEMEMARGINS)GetProcAddress( + GetThemeMarginsFunc = (PFNGETTHEMEMARGINS)GetProcAddress( hModThemes, "GetThemeMargins"); - IsThemePartDefined = (PFNISTHEMEPARTDEFINED)GetProcAddress( + IsThemePartDefinedFunc = (PFNISTHEMEPARTDEFINED)GetProcAddress( hModThemes, "IsThemePartDefined"); - GetThemeBool = (PFNGETTHEMEBOOL)GetProcAddress( + GetThemeBoolFunc = (PFNGETTHEMEBOOL)GetProcAddress( hModThemes, "GetThemeBool"); - GetThemeSysBool = (PFNGETTHEMESYSBOOL)GetProcAddress(hModThemes, + GetThemeSysBoolFunc = (PFNGETTHEMESYSBOOL)GetProcAddress(hModThemes, "GetThemeSysBool"); - GetThemeColor = (PFNGETTHEMECOLOR)GetProcAddress(hModThemes, + GetThemeColorFunc = (PFNGETTHEMECOLOR)GetProcAddress(hModThemes, "GetThemeColor"); - GetThemeEnumValue = (PFNGETTHEMEENUMVALUE)GetProcAddress(hModThemes, + GetThemeEnumValueFunc = (PFNGETTHEMEENUMVALUE)GetProcAddress(hModThemes, "GetThemeEnumValue"); - GetThemeInt = (PFNGETTHEMEINT)GetProcAddress(hModThemes, "GetThemeInt"); - GetThemePosition = (PFNGETTHEMEPOSITION)GetProcAddress(hModThemes, + GetThemeIntFunc = (PFNGETTHEMEINT)GetProcAddress(hModThemes, "GetThemeInt"); + GetThemePositionFunc = (PFNGETTHEMEPOSITION)GetProcAddress(hModThemes, "GetThemePosition"); - GetThemePartSize = (PFNGETTHEMEPARTSIZE)GetProcAddress(hModThemes, + GetThemePartSizeFunc = (PFNGETTHEMEPARTSIZE)GetProcAddress(hModThemes, "GetThemePartSize"); - SetWindowTheme = (PFNSETWINDOWTHEME)GetProcAddress(hModThemes, + SetWindowThemeFunc = (PFNSETWINDOWTHEME)GetProcAddress(hModThemes, "SetWindowTheme"); - IsThemeBackgroundPartiallyTransparent = + IsThemeBackgroundPartiallyTransparentFunc = (PFNISTHEMEBACKGROUNDPARTIALLYTRANSPARENT)GetProcAddress(hModThemes, "IsThemeBackgroundPartiallyTransparent"); - //this function might not exist - GetThemeTransitionDuration = + GetThemeTransitionDurationFunc = (PFNGETTHEMETRANSITIONDURATION)GetProcAddress(hModThemes, "GetThemeTransitionDuration"); - if(OpenThemeData - && DrawThemeBackground - && CloseThemeData - && DrawThemeText - && GetThemeBackgroundContentRect - && GetThemeMargins - && IsThemePartDefined - && GetThemeBool - && GetThemeSysBool - && GetThemeColor - && GetThemeEnumValue - && GetThemeInt - && GetThemePartSize - && GetThemePosition - && SetWindowTheme - && IsThemeBackgroundPartiallyTransparent + if(OpenThemeDataFunc + && DrawThemeBackgroundFunc + && CloseThemeDataFunc + && DrawThemeTextFunc + && GetThemeBackgroundContentRectFunc + && GetThemeMarginsFunc + && IsThemePartDefinedFunc + && GetThemeBoolFunc + && GetThemeSysBoolFunc + && GetThemeColorFunc + && GetThemeEnumValueFunc + && GetThemeIntFunc + && GetThemePartSizeFunc + && GetThemePositionFunc + && SetWindowThemeFunc + && IsThemeBackgroundPartiallyTransparentFunc + && GetThemeTransitionDurationFunc ) { DTRACE_PRINTLN("Loaded function pointers.\n"); // We need to make sure we can load the Theme. This may not be // the case on a WinXP machine with classic mode enabled. - HTHEME hTheme = OpenThemeData(AwtToolkit::GetInstance().GetHWnd(), L"Button"); + HTHEME hTheme = OpenThemeDataFunc(AwtToolkit::GetInstance().GetHWnd(), L"Button"); if(hTheme) { DTRACE_PRINTLN("Loaded Theme data.\n"); - CloseThemeData(hTheme); + CloseThemeDataFunc(hTheme); return TRUE; } } else { @@ -290,7 +251,7 @@ JNIEXPORT jlong JNICALL Java_sun_awt_windows_ThemeReader_openTheme } // We need to open the Theme on a Window that will stick around. // The best one for that purpose is the Toolkit window. - HTHEME htheme = OpenThemeData(AwtToolkit::GetInstance().GetHWnd(), str); + HTHEME htheme = OpenThemeDataFunc(AwtToolkit::GetInstance().GetHWnd(), str); JNU_ReleaseStringPlatformChars(env, widget, str); return (jlong) htheme; } @@ -308,7 +269,7 @@ JNIEXPORT void JNICALL Java_sun_awt_windows_ThemeReader_setWindowTheme str = (LPCTSTR) JNU_GetStringPlatformChars(env, subAppName, NULL); } // We need to set the Window theme on the same theme that we opened it with. - HRESULT hres = SetWindowTheme(AwtToolkit::GetInstance().GetHWnd(), str, NULL); + HRESULT hres = SetWindowThemeFunc(AwtToolkit::GetInstance().GetHWnd(), str, NULL); assert_result(hres, env); if (subAppName != NULL) { JNU_ReleaseStringPlatformChars(env, subAppName, str); @@ -323,7 +284,7 @@ JNIEXPORT void JNICALL Java_sun_awt_windows_ThemeReader_setWindowTheme JNIEXPORT void JNICALL Java_sun_awt_windows_ThemeReader_closeTheme (JNIEnv *env, jclass klass, jlong theme) { - HRESULT hres = CloseThemeData((HTHEME)theme); + HRESULT hres = CloseThemeDataFunc((HTHEME)theme); assert_result(hres, env); } @@ -474,7 +435,7 @@ JNIEXPORT void JNICALL Java_sun_awt_windows_ThemeReader_paintBackground ZeroMemory(pSrcBits,(BITS_PER_PIXEL>>3)*w*h); - HRESULT hres = DrawThemeBackground(hTheme, memDC, part, state, &rect, NULL); + HRESULT hres = DrawThemeBackgroundFunc(hTheme, memDC, part, state, &rect, NULL); assert_result(hres, env); if (SUCCEEDED(hres)) { // Make sure GDI is done. @@ -482,7 +443,7 @@ JNIEXPORT void JNICALL Java_sun_awt_windows_ThemeReader_paintBackground // Copy the resulting pixels to our Java BufferedImage. pDstBits = (int *)env->GetPrimitiveArrayCritical(array, 0); BOOL transparent = FALSE; - transparent = IsThemeBackgroundPartiallyTransparent(hTheme,part,state); + transparent = IsThemeBackgroundPartiallyTransparentFunc(hTheme, part, state); copyDIBToBufferedImage(pDstBits, pSrcBits, transparent, w, h, stride); env->ReleasePrimitiveArrayCritical(array, pDstBits, 0); } @@ -530,7 +491,7 @@ JNIEXPORT jobject JNICALL Java_sun_awt_windows_ThemeReader_getThemeMargins HTHEME hTheme = (HTHEME) theme; if (hTheme != NULL) { - HRESULT hres = GetThemeMargins(hTheme, NULL, part, state, property, NULL, &margins); + HRESULT hres = GetThemeMarginsFunc(hTheme, NULL, part, state, property, NULL, &margins); assert_result(hres, env); if (FAILED(hres)) { return NULL; @@ -551,7 +512,7 @@ JNIEXPORT jobject JNICALL Java_sun_awt_windows_ThemeReader_getThemeMargins JNIEXPORT jboolean JNICALL Java_sun_awt_windows_ThemeReader_isThemePartDefined (JNIEnv *env, jclass klass, jlong theme, jint part, jint state) { HTHEME hTheme = (HTHEME) theme; - return JNI_IS_TRUE(IsThemePartDefined(hTheme, part, state)); + return JNI_IS_TRUE(IsThemePartDefinedFunc(hTheme, part, state)); } /* @@ -567,7 +528,7 @@ JNIEXPORT jobject JNICALL Java_sun_awt_windows_ThemeReader_getColor if (hTheme != NULL) { COLORREF color=0; - if (GetThemeColor(hTheme, part, state, type, &color) != S_OK) { + if (GetThemeColorFunc(hTheme, part, state, type, &color) != S_OK) { return NULL; } @@ -613,7 +574,7 @@ JNIEXPORT jint JNICALL Java_sun_awt_windows_ThemeReader_getInt HTHEME hTheme = (HTHEME) theme; int retVal = -1; if (hTheme != NULL) { - HRESULT hres = GetThemeInt(hTheme, part, state, prop, &retVal); + HRESULT hres = GetThemeIntFunc(hTheme, part, state, prop, &retVal); assert_result(hres, env); } return retVal; @@ -629,7 +590,7 @@ JNIEXPORT jint JNICALL Java_sun_awt_windows_ThemeReader_getEnum HTHEME hTheme = (HTHEME) theme; int retVal = -1; if (hTheme != NULL) { - HRESULT hres = GetThemeEnumValue(hTheme, part, state, prop, &retVal); + HRESULT hres = GetThemeEnumValueFunc(hTheme, part, state, prop, &retVal); assert_result(hres, env); } return retVal; @@ -645,7 +606,7 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_windows_ThemeReader_getBoolean HTHEME hTheme = (HTHEME) theme; BOOL retVal = FALSE; if (hTheme != NULL) { - HRESULT hres = GetThemeBool(hTheme, part, state, prop, &retVal); + HRESULT hres = GetThemeBoolFunc(hTheme, part, state, prop, &retVal); assert_result(hres, env); } return JNI_IS_TRUE(retVal); @@ -660,7 +621,7 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_windows_ThemeReader_getSysBoolean (JNIEnv *env, jclass klass, jlong theme, jint prop) { HTHEME hTheme = (HTHEME)theme; if (hTheme != NULL) { - return JNI_IS_TRUE(GetThemeSysBool(hTheme, prop)); + return JNI_IS_TRUE(GetThemeSysBoolFunc(hTheme, prop)); } return JNI_FALSE; } @@ -676,7 +637,7 @@ JNIEXPORT jobject JNICALL Java_sun_awt_windows_ThemeReader_getPoint POINT point; if (hTheme != NULL) { - if (GetThemePosition(hTheme, part, state, prop, &point) != S_OK) { + if (GetThemePositionFunc(hTheme, part, state, prop, &point) != S_OK) { return NULL; } @@ -723,7 +684,7 @@ JNIEXPORT jobject JNICALL Java_sun_awt_windows_ThemeReader_getPosition POINT point; - HRESULT hres = GetThemePosition(hTheme, part, state, prop, &point); + HRESULT hres = GetThemePositionFunc(hTheme, part, state, prop, &point); assert_result(hres, env); if (FAILED(hres)) { return NULL; @@ -789,7 +750,7 @@ JNIEXPORT jobject JNICALL Java_sun_awt_windows_ThemeReader_getPartSize if (theme != NULL) { SIZE size; - if (SUCCEEDED(GetThemePartSize((HTHEME)theme, NULL, part, state, + if (SUCCEEDED(GetThemePartSizeFunc((HTHEME)theme, NULL, part, state, NULL, TS_TRUE, &size)) && (env->EnsureLocalCapacity(2) >= 0)) { static jmethodID dimMID = NULL; @@ -833,9 +794,10 @@ jint boundingWidth, jint boundingHeight) { boundingRect.right = boundingWidth; boundingRect.bottom = boundingHeight; RECT contentRect; - if (SUCCEEDED(GetThemeBackgroundContentRect((HTHEME) hTheme, NULL, part, - state, &boundingRect, - &contentRect))) { + if (SUCCEEDED(GetThemeBackgroundContentRectFunc((HTHEME) hTheme, NULL, + part, state, + &boundingRect, + &contentRect))) { return newInsets(env, contentRect.top, contentRect.left, boundingHeight - contentRect.bottom, @@ -855,23 +817,10 @@ Java_sun_awt_windows_ThemeReader_getThemeTransitionDuration (JNIEnv *env, jclass klass, jlong theme, jint part, jint stateFrom, jint stateTo, jint propId) { jlong rv = -1; - if (GetThemeTransitionDuration != NULL) { - DWORD duration = 0; - if (SUCCEEDED(GetThemeTransitionDuration((HTHEME) theme, part, - stateFrom, stateTo, propId, &duration))) { - rv = duration; - } + DWORD duration = 0; + if (SUCCEEDED(GetThemeTransitionDurationFunc((HTHEME) theme, part, + stateFrom, stateTo, propId, &duration))) { + rv = duration; } return rv; } - -/* - * Class: sun_awt_windows_ThemeReader - * Method: isGetThemeTransitionDurationDefined - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL -Java_sun_awt_windows_ThemeReader_isGetThemeTransitionDurationDefined -(JNIEnv *env, jclass klass) { - return (GetThemeTransitionDuration != NULL) ? JNI_TRUE : JNI_FALSE; -} diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Clipboard.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Clipboard.cpp index 12f47a2e732..2aadcd5a08c 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Clipboard.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Clipboard.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,9 +42,7 @@ jobject AwtClipboard::theCurrentClipboard; BOOL AwtClipboard::isGettingOwnership = FALSE; volatile jmethodID AwtClipboard::handleContentsChangedMID; -volatile BOOL AwtClipboard::skipInitialWmDrawClipboardMsg = TRUE; volatile BOOL AwtClipboard::isClipboardViewerRegistered = FALSE; -volatile HWND AwtClipboard::hwndNextViewer = NULL; #define GALLOCFLG (GMEM_DDESHARE | GMEM_MOVEABLE | GMEM_ZEROINIT) @@ -59,27 +57,11 @@ void AwtClipboard::LostOwnership(JNIEnv *env) { } } -void AwtClipboard::WmChangeCbChain(WPARAM wParam, LPARAM lParam) { - if ((HWND)wParam == hwndNextViewer) { - hwndNextViewer = (HWND)lParam; - } else if (hwndNextViewer != NULL) { - ::SendMessage(hwndNextViewer, WM_CHANGECBCHAIN, wParam, lParam); - } -} - -void AwtClipboard::WmDrawClipboard(JNIEnv *env, WPARAM wParam, LPARAM lParam) { - if (skipInitialWmDrawClipboardMsg) { - // skipping the first contents change notification as it comes - // immediately after registering the clipboard viewer window - // and it is not caused by an actual contents change. - skipInitialWmDrawClipboardMsg = FALSE; - return; - } +void AwtClipboard::WmClipboardUpdate(JNIEnv *env) { if (theCurrentClipboard != NULL) { env->CallVoidMethod(theCurrentClipboard, handleContentsChangedMID); DASSERT(!safe_ExceptionOccurred(env)); } - ::SendMessage(hwndNextViewer, WM_DRAWCLIPBOARD, wParam, lParam); } void AwtClipboard::RegisterClipboardViewer(JNIEnv *env, jobject jclipboard) { @@ -96,7 +78,7 @@ void AwtClipboard::RegisterClipboardViewer(JNIEnv *env, jobject jclipboard) { env->GetMethodID(cls, "handleContentsChanged", "()V"); DASSERT(AwtClipboard::handleContentsChangedMID != NULL); - hwndNextViewer = ::SetClipboardViewer(AwtToolkit::GetInstance().GetHWnd()); + ::AddClipboardFormatListener(AwtToolkit::GetInstance().GetHWnd()); isClipboardViewerRegistered = TRUE; } @@ -104,10 +86,8 @@ void AwtClipboard::UnregisterClipboardViewer(JNIEnv *env) { TRY; if (isClipboardViewerRegistered) { - ::ChangeClipboardChain(AwtToolkit::GetInstance().GetHWnd(), AwtClipboard::hwndNextViewer); - AwtClipboard::hwndNextViewer = NULL; + ::RemoveClipboardFormatListener(AwtToolkit::GetInstance().GetHWnd()); isClipboardViewerRegistered = FALSE; - skipInitialWmDrawClipboardMsg = TRUE; } CATCH_BAD_ALLOC; diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Clipboard.h b/src/java.desktop/windows/native/libawt/windows/awt_Clipboard.h index a9e9d71d544..7821dacdb74 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Clipboard.h +++ b/src/java.desktop/windows/native/libawt/windows/awt_Clipboard.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,7 @@ class AwtClipboard { private: static BOOL isGettingOwnership; - // handle to the next window in the clipboard viewer chain - static volatile HWND hwndNextViewer; static volatile BOOL isClipboardViewerRegistered; - static volatile BOOL skipInitialWmDrawClipboardMsg; static volatile jmethodID handleContentsChangedMID; public: @@ -57,8 +54,7 @@ class AwtClipboard { } static void LostOwnership(JNIEnv *env); - static void WmChangeCbChain(WPARAM wparam, LPARAM lparam); - static void WmDrawClipboard(JNIEnv *env, WPARAM wparam, LPARAM lparam); + static void WmClipboardUpdate(JNIEnv *env); static void RegisterClipboardViewer(JNIEnv *env, jobject jclipboard); static void UnregisterClipboardViewer(JNIEnv *env); }; diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp index 9e15a77514a..1ed97ba94d2 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp @@ -603,7 +603,7 @@ AwtComponent::CreateHWnd(JNIEnv *env, LPCWSTR title, /* * Fix for 4046446. */ - SetWindowPos(GetHWnd(), 0, x, y, w, h, SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOACTIVATE); + Reshape(x, y, w, h); /* Set default colors. */ m_colorForeground = colorForeground; @@ -1087,6 +1087,7 @@ void SpyWinMessage(HWND hwnd, UINT message, LPCTSTR szComment) { WIN_MSG(WM_DESTROY) WIN_MSG(WM_MOVE) WIN_MSG(WM_SIZE) + WIN_MSG(WM_DPICHANGED) WIN_MSG(WM_ACTIVATE) WIN_MSG(WM_SETFOCUS) WIN_MSG(WM_KILLFOCUS) @@ -1505,9 +1506,9 @@ LRESULT AwtComponent::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) case WM_SIZE: { RECT r; - // fix 4128317 : use GetClientRect for full 32-bit int precision and + // fix 4128317 : use GetWindowRect for full 32-bit int precision and // to avoid negative client area dimensions overflowing 16-bit params - robi - ::GetClientRect( GetHWnd(), &r ); + ::GetWindowRect(GetHWnd(), &r); mr = WmSize(static_cast(wParam), r.right - r.left, r.bottom - r.top); //mr = WmSize(wParam, LOWORD(lParam), HIWORD(lParam)); SetCompositionWindow(r); @@ -3888,8 +3889,8 @@ void AwtComponent::OpenCandidateWindow(int x, int y) } HWND hTop = GetTopLevelParentForWindow(hWnd); ::ClientToScreen(hTop, &p); - int sx = ScaleUpX(x) - p.x; - int sy = ScaleUpY(y) - p.y; + int sx = ScaleUpAbsX(x) - p.x; + int sy = ScaleUpAbsY(y) - p.y; if (!m_bitsCandType) { SetCandidateWindow(m_bitsCandType, sx, sy); return; @@ -4767,34 +4768,71 @@ void AwtComponent::FillAlpha(void *bitmapBits, SIZE &size, BYTE alpha) } } +int AwtComponent::GetScreenImOn() { + HWND hWindow = GetAncestor(GetHWnd(), GA_ROOT); + AwtComponent *comp = AwtComponent::GetComponent(hWindow); + if (comp && comp->IsTopLevel()) { + return comp->GetScreenImOn(); + } + return AwtWin32GraphicsDevice::DeviceIndexForWindow(hWindow); +} + int AwtComponent::ScaleUpX(int x) { - int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd()); + int screen = GetScreenImOn(); Devices::InstanceAccess devices; AwtWin32GraphicsDevice* device = devices->GetDevice(screen); return device == NULL ? x : device->ScaleUpX(x); } +int AwtComponent::ScaleUpAbsX(int x) { + int screen = GetScreenImOn(); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice* device = devices->GetDevice(screen); + return device == NULL ? x : device->ScaleUpAbsX(x); +} + int AwtComponent::ScaleUpY(int y) { - int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd()); + int screen = GetScreenImOn(); Devices::InstanceAccess devices; AwtWin32GraphicsDevice* device = devices->GetDevice(screen); return device == NULL ? y : device->ScaleUpY(y); } +int AwtComponent::ScaleUpAbsY(int y) { + int screen = GetScreenImOn(); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice* device = devices->GetDevice(screen); + return device == NULL ? y : device->ScaleUpAbsY(y); +} + int AwtComponent::ScaleDownX(int x) { - int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd()); + int screen = GetScreenImOn(); Devices::InstanceAccess devices; AwtWin32GraphicsDevice* device = devices->GetDevice(screen); return device == NULL ? x : device->ScaleDownX(x); } +int AwtComponent::ScaleDownAbsX(int x) { + int screen = GetScreenImOn(); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice* device = devices->GetDevice(screen); + return device == NULL ? x : device->ScaleDownAbsX(x); +} + int AwtComponent::ScaleDownY(int y) { - int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd()); + int screen = GetScreenImOn(); Devices::InstanceAccess devices; AwtWin32GraphicsDevice* device = devices->GetDevice(screen); return device == NULL ? y : device->ScaleDownY(y); } +int AwtComponent::ScaleDownAbsY(int y) { + int screen = GetScreenImOn(); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice* device = devices->GetDevice(screen); + return device == NULL ? y : device->ScaleDownAbsY(y); +} + jintArray AwtComponent::CreatePrintedPixels(SIZE &loc, SIZE &size, int alpha) { JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); @@ -5090,7 +5128,7 @@ void AwtComponent::SendMouseEvent(jint id, jlong when, jint x, jint y, id, when, modifiers, ScaleDownX(x + insets.left), ScaleDownY(y + insets.top), - ScaleDownX(xAbs), ScaleDownY(yAbs), + ScaleDownAbsX(xAbs), ScaleDownAbsY(yAbs), clickCount, popupTrigger, button); if (safe_ExceptionOccurred(env)) { @@ -5163,8 +5201,8 @@ AwtComponent::SendMouseWheelEvent(jint id, jlong when, jint x, jint y, id, when, modifiers, ScaleDownX(x + insets.left), ScaleDownY(y + insets.top), - ScaleDownX(xAbs), - ScaleDownY(yAbs), + ScaleDownAbsX(xAbs), + ScaleDownAbsY(yAbs), clickCount, popupTrigger, scrollType, scrollAmount, roundedWheelRotation, preciseWheelRotation); @@ -5674,8 +5712,8 @@ jobject AwtComponent::_GetLocationOnScreen(void *param) RECT rect; VERIFY(::GetWindowRect(p->GetHWnd(),&rect)); result = JNU_NewObjectByName(env, "java/awt/Point", "(II)V", - p->ScaleDownX(rect.left), - p->ScaleDownY(rect.top)); + p->ScaleDownAbsX(rect.left), + p->ScaleDownAbsY(rect.top)); } ret: env->DeleteGlobalRef(self); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Component.h b/src/java.desktop/windows/native/libawt/windows/awt_Component.h index 1e3e0769273..d950e78abda 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Component.h +++ b/src/java.desktop/windows/native/libawt/windows/awt_Component.h @@ -275,6 +275,7 @@ class AwtComponent : public AwtObject { /* * methods on this component */ + virtual int GetScreenImOn(); virtual void Show(); virtual void Hide(); virtual void Reshape(int x, int y, int w, int h); @@ -755,9 +756,13 @@ class AwtComponent : public AwtObject { virtual void FillAlpha(void *bitmapBits, SIZE &size, BYTE alpha); int ScaleUpX(int x); + int ScaleUpAbsX(int x); int ScaleUpY(int y); + int ScaleUpAbsY(int y); int ScaleDownX(int x); + int ScaleDownAbsX(int x); int ScaleDownY(int y); + int ScaleDownAbsY(int y); private: /* A bitmask keeps the button's numbers as MK_LBUTTON, MK_MBUTTON, MK_RBUTTON diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Cursor.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Cursor.cpp index 642c9e9b14c..4a1a4c08131 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Cursor.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Cursor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -475,8 +475,8 @@ Java_sun_awt_windows_WGlobalCursorManager_getCursorPos(JNIEnv *env, int screen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(monitor); Devices::InstanceAccess devices; AwtWin32GraphicsDevice *device = devices->GetDevice(screen); - int x = (device == NULL) ? p.x : device->ScaleDownX(p.x); - int y = (device == NULL) ? p.y : device->ScaleDownY(p.y); + int x = (device == NULL) ? p.x : device->ScaleDownAbsX(p.x); + int y = (device == NULL) ? p.y : device->ScaleDownAbsY(p.y); env->SetIntField(point, AwtCursor::pointXID, x); env->SetIntField(point, AwtCursor::pointYID, y); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_DnDDS.cpp b/src/java.desktop/windows/native/libawt/windows/awt_DnDDS.cpp index 390b9250aba..0f9fb662bbf 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_DnDDS.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_DnDDS.cpp @@ -1174,14 +1174,14 @@ HRESULT __stdcall AwtDragSource::GetProcessId(FORMATETC __RPC_FAR *pFormatEtc, S return S_OK; } -static void ScaleDown(POINT &pt) { +static void ScaleDownAbs(POINT &pt) { HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY); int screen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(monitor); Devices::InstanceAccess devices; AwtWin32GraphicsDevice *device = devices->GetDevice(screen); if (device) { - pt.x = device->ScaleDownX(pt.x); - pt.y = device->ScaleDownY(pt.y); + pt.x = device->ScaleDownAbsX(pt.x); + pt.y = device->ScaleDownAbsY(pt.y); } } @@ -1190,7 +1190,7 @@ DECLARE_JAVA_CLASS(dSCClazz, "sun/awt/windows/WDragSourceContextPeer") void AwtDragSource::call_dSCenter(JNIEnv* env, jobject self, jint targetActions, jint modifiers, POINT pt) { - ScaleDown(pt); + ScaleDownAbs(pt); DECLARE_VOID_JAVA_METHOD(dSCenter, dSCClazz, "dragEnter", "(IIII)V"); DASSERT(!JNU_IsNull(env, self)); env->CallVoidMethod(self, dSCenter, targetActions, modifiers, pt.x, pt.y); @@ -1203,7 +1203,7 @@ AwtDragSource::call_dSCenter(JNIEnv* env, jobject self, jint targetActions, void AwtDragSource::call_dSCmotion(JNIEnv* env, jobject self, jint targetActions, jint modifiers, POINT pt) { - ScaleDown(pt); + ScaleDownAbs(pt); DECLARE_VOID_JAVA_METHOD(dSCmotion, dSCClazz, "dragMotion", "(IIII)V"); DASSERT(!JNU_IsNull(env, self)); env->CallVoidMethod(self, dSCmotion, targetActions, modifiers, pt.x, pt.y); @@ -1216,7 +1216,7 @@ AwtDragSource::call_dSCmotion(JNIEnv* env, jobject self, jint targetActions, void AwtDragSource::call_dSCchanged(JNIEnv* env, jobject self, jint targetActions, jint modifiers, POINT pt) { - ScaleDown(pt); + ScaleDownAbs(pt); DECLARE_VOID_JAVA_METHOD(dSCchanged, dSCClazz, "operationChanged", "(IIII)V"); DASSERT(!JNU_IsNull(env, self)); @@ -1229,7 +1229,7 @@ AwtDragSource::call_dSCchanged(JNIEnv* env, jobject self, jint targetActions, void AwtDragSource::call_dSCexit(JNIEnv* env, jobject self, POINT pt) { - ScaleDown(pt); + ScaleDownAbs(pt); DECLARE_VOID_JAVA_METHOD(dSCexit, dSCClazz, "dragExit", "(II)V"); DASSERT(!JNU_IsNull(env, self)); env->CallVoidMethod(self, dSCexit, pt.x, pt.y); @@ -1242,7 +1242,7 @@ AwtDragSource::call_dSCexit(JNIEnv* env, jobject self, POINT pt) { void AwtDragSource::call_dSCddfinished(JNIEnv* env, jobject self, jboolean success, jint operations, POINT pt) { - ScaleDown(pt); + ScaleDownAbs(pt); DECLARE_VOID_JAVA_METHOD(dSCddfinished, dSCClazz, "dragDropFinished", "(ZIII)V"); DASSERT(!JNU_IsNull(env, self)); env->CallVoidMethod(self, dSCddfinished, success, operations, pt.x, pt.y); @@ -1255,7 +1255,7 @@ AwtDragSource::call_dSCddfinished(JNIEnv* env, jobject self, jboolean success, void AwtDragSource::call_dSCmouseMoved(JNIEnv* env, jobject self, jint targetActions, jint modifiers, POINT pt) { - ScaleDown(pt); + ScaleDownAbs(pt); DECLARE_VOID_JAVA_METHOD(dSCmouseMoved, dSCClazz, "dragMouseMoved", "(IIII)V"); DASSERT(!JNU_IsNull(env, self)); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_FileDialog.cpp b/src/java.desktop/windows/native/libawt/windows/awt_FileDialog.cpp index 119b2c3680c..e8128372ceb 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_FileDialog.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_FileDialog.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -631,18 +631,18 @@ Java_sun_awt_windows_WFileDialogPeer_toBack(JNIEnv *env, jobject peer) CATCH_BAD_ALLOC; } -int ScaleDownX(int x, HWND hwnd) { +int ScaleDownAbsX(int x, HWND hwnd) { int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(hwnd); Devices::InstanceAccess devices; AwtWin32GraphicsDevice* device = devices->GetDevice(screen); - return device == NULL ? x : device->ScaleDownX(x); + return device == NULL ? x : device->ScaleDownAbsX(x); } -int ScaleDownY(int y, HWND hwnd) { +int ScaleDownAbsY(int y, HWND hwnd) { int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(hwnd); Devices::InstanceAccess devices; AwtWin32GraphicsDevice* device = devices->GetDevice(screen); - return device == NULL ? y : device->ScaleDownY(y); + return device == NULL ? y : device->ScaleDownAbsY(y); } jobject AwtFileDialog::_GetLocationOnScreen(void *param) @@ -657,7 +657,8 @@ jobject AwtFileDialog::_GetLocationOnScreen(void *param) RECT rect; VERIFY(::GetWindowRect(hwnd, &rect)); result = JNU_NewObjectByName(env, "java/awt/Point", "(II)V", - ScaleDownX(rect.left, hwnd), ScaleDownY(rect.top, hwnd)); + ScaleDownAbsX(rect.left, hwnd), + ScaleDownAbsY(rect.top, hwnd)); } if (result != NULL) diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp index 8f2fcd84f6f..d8873b6bec2 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp @@ -328,17 +328,13 @@ AwtFrame* AwtFrame::Create(jobject self, jobject parent) frame->CreateHWnd(env, L"", style, exStyle, - 0, 0, 0, 0, + x, y, width, height, hwndParent, NULL, ::GetSysColor(COLOR_WINDOWTEXT), ::GetSysColor(COLOR_WINDOWFRAME), self); - /* - * Reshape here instead of during create, so that a - * WM_NCCALCSIZE is sent. - */ - frame->Reshape(x, y, width, height); + frame->RecalcNonClient(); } } } catch (...) { diff --git a/src/java.desktop/windows/native/libawt/windows/awt_List.cpp b/src/java.desktop/windows/native/libawt/windows/awt_List.cpp index 69484f024d7..a04f6182a5b 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_List.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_List.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -288,14 +288,17 @@ void AwtList::SetMultiSelect(BOOL ms) { UnsubclassHWND(); AwtToolkit::DestroyComponentHWND(m_hwnd); - CreateHWnd(env, L"", style, exStyle, - rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, + CreateHWnd(env, L"", style, exStyle, 0, 0, 0, 0, parentHWnd, NULL, ::GetSysColor(COLOR_WINDOWTEXT), ::GetSysColor(COLOR_WINDOW), peer); + SetWindowPos(GetHWnd(), 0, + rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, + SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOACTIVATE); + SendListMessage(WM_SETFONT, (WPARAM)font, (LPARAM)FALSE); SendListMessage(LB_SETITEMHEIGHT, 0, MAKELPARAM(itemHeight, 0)); SendListMessage(LB_RESETCONTENT); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp index 7a7148d888d..1ab9c6f6278 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp @@ -1067,12 +1067,8 @@ LRESULT CALLBACK AwtToolkit::WndProc(HWND hWnd, UINT message, AwtClipboard::LostOwnership((JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2)); return 0; } - case WM_CHANGECBCHAIN: { - AwtClipboard::WmChangeCbChain(wParam, lParam); - return 0; - } - case WM_DRAWCLIPBOARD: { - AwtClipboard::WmDrawClipboard((JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2), wParam, lParam); + case WM_CLIPBOARDUPDATE: { + AwtClipboard::WmClipboardUpdate((JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2)); return 0; } case WM_AWT_LIST_SETMULTISELECT: { diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp index 7d13fbe2d87..463c1fdee10 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -99,16 +99,12 @@ JNIEXPORT jobject JNICALL AwtWin32GraphicsDevice *device = devices->GetDevice(screen); if (TRUE == MonitorBounds(AwtWin32GraphicsDevice::GetMonitor(screen), &rRW)) { - - int x = (device == NULL) ? rRW.left : device->ScaleDownX(rRW.left); - int y = (device == NULL) ? rRW.top : device->ScaleDownY(rRW.top); int w = (device == NULL) ? rRW.right - rRW.left : device->ScaleDownX(rRW.right - rRW.left); int h = (device == NULL) ? rRW.bottom - rRW.top : device->ScaleDownY(rRW.bottom - rRW.top); - bounds = env->NewObject(clazz, mid, x, y, w, h); - + bounds = env->NewObject(clazz, mid, rRW.left, rRW.top, w, h); } else { // 4910760 - don't return a null bounds, return the bounds of the diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp index 2b6af9d8f2c..79e7b9d1050 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp @@ -77,6 +77,7 @@ AwtWin32GraphicsDevice::AwtWin32GraphicsDevice(int screen, this->devicesArray = arr; this->scaleX = 1; this->scaleY = 1; + disableScaleAutoRefresh = FALSE; javaDevice = NULL; colorData = new ImgColorData; colorData->grayscale = GS_NOTGRAY; @@ -633,21 +634,45 @@ int AwtWin32GraphicsDevice::ScaleUpX(int x) return ClipRound(x * scaleX); } +int AwtWin32GraphicsDevice::ScaleUpAbsX(int x) +{ + LONG screen = pMonitorInfo->rcMonitor.left; + return screen + ClipRound((x - screen) * scaleX); +} + int AwtWin32GraphicsDevice::ScaleUpY(int y) { return ClipRound(y * scaleY); } +int AwtWin32GraphicsDevice::ScaleUpAbsY(int y) +{ + LONG screen = pMonitorInfo->rcMonitor.top; + return screen + ClipRound((y - screen) * scaleY); +} + int AwtWin32GraphicsDevice::ScaleDownX(int x) { return ClipRound(x / scaleX); } +int AwtWin32GraphicsDevice::ScaleDownAbsX(int x) +{ + LONG screen = pMonitorInfo->rcMonitor.left; + return screen + ClipRound((x - screen) / scaleX); +} + int AwtWin32GraphicsDevice::ScaleDownY(int y) { return ClipRound(y / scaleY); } +int AwtWin32GraphicsDevice::ScaleDownAbsY(int y) +{ + LONG screen = pMonitorInfo->rcMonitor.top; + return screen + ClipRound((y - screen) / scaleY); +} + int AwtWin32GraphicsDevice::ClipRound(double value) { value -= 0.5; @@ -666,11 +691,13 @@ int AwtWin32GraphicsDevice::ClipRound(double value) void AwtWin32GraphicsDevice::InitDesktopScales() { - float dpiX = -1.0f; - float dpiY = -1.0f; - GetScreenDpi(GetMonitor(), &dpiX, &dpiY); - if (dpiX > 0 && dpiY > 0) { - SetScale(dpiX / 96, dpiY / 96); + if (!disableScaleAutoRefresh) { + float dpiX = -1.0f; + float dpiY = -1.0f; + GetScreenDpi(GetMonitor(), &dpiX, &dpiY); + if (dpiX > 0 && dpiY > 0) { + SetScale(dpiX / 96, dpiY / 96); + } } } @@ -694,6 +721,11 @@ void AwtWin32GraphicsDevice::DisableOffscreenAcceleration() // REMIND: noop for now } +void AwtWin32GraphicsDevice::DisableScaleAutoRefresh() +{ + disableScaleAutoRefresh = TRUE; +} + /** * Invalidates the GraphicsDevice object associated with this * device by disabling offscreen acceleration and calling @@ -754,6 +786,21 @@ void AwtWin32GraphicsDevice::ResetAllMonitorInfo() } } +/** + * This function updates the scale factor for all monitors on the system. + */ +void AwtWin32GraphicsDevice::ResetAllDesktopScales() +{ + if (!Devices::GetInstance()){ + return; + } + Devices::InstanceAccess devices; + int devicesNum = devices->GetNumDevices(); + for (int deviceIndex = 0; deviceIndex < devicesNum; deviceIndex++) { + devices->GetDevice(deviceIndex)->InitDesktopScales(); + } +} + void AwtWin32GraphicsDevice::DisableOffscreenAccelerationForDevice( HMONITOR hMonitor) { @@ -1393,6 +1440,7 @@ JNIEXPORT void JNICALL AwtWin32GraphicsDevice *device = devices->GetDevice(screen); if (device != NULL ) { + device->DisableScaleAutoRefresh(); device->SetScale(scaleX, scaleY); } } @@ -1441,4 +1489,3 @@ Java_sun_awt_Win32GraphicsDevice_initNativeScale device->InitDesktopScales(); } } - diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h index d5fc0d57150..f298aad68da 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h +++ b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -65,15 +65,20 @@ class AwtWin32GraphicsDevice { int GetDeviceIndex() { return screen; } void Release(); void DisableOffscreenAcceleration(); + void DisableScaleAutoRefresh(); void Invalidate(JNIEnv *env); void InitDesktopScales(); void SetScale(float scaleX, float scaleY); float GetScaleX(); float GetScaleY(); int ScaleUpX(int x); + int ScaleUpAbsX(int x); int ScaleUpY(int y); + int ScaleUpAbsY(int x); int ScaleDownX(int x); + int ScaleDownAbsX(int x); int ScaleDownY(int y); + int ScaleDownAbsY(int y); static int DeviceIndexForWindow(HWND hWnd); static jobject GetColorModel(JNIEnv *env, jboolean dynamic, @@ -88,6 +93,7 @@ class AwtWin32GraphicsDevice { static HMONITOR GetMonitor(int deviceIndex); static LPMONITORINFO GetMonitorInfo(int deviceIndex); static void ResetAllMonitorInfo(); + static void ResetAllDesktopScales(); static BOOL IsPrimaryPalettized() { return primaryPalettized; } static int GetDefaultDeviceIndex() { return primaryIndex; } static void DisableOffscreenAccelerationForDevice(HMONITOR hMonitor); @@ -117,6 +123,7 @@ class AwtWin32GraphicsDevice { Devices *devicesArray; float scaleX; float scaleY; + BOOL disableScaleAutoRefresh; static HDC MakeDCFromMonitor(HMONITOR); static int ClipRound(double value); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp index e73c2615a07..dbca3d778c8 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp @@ -153,17 +153,6 @@ struct SetFullScreenExclusiveModeStateStruct { jboolean isFSEMState; }; -// struct for _WindowDPIChange() method -struct ScaleStruct { - jobject window; - jint prevScreen; - jfloat prevScaleX; - jfloat prevScaleY; - jint screen; - jfloat scaleX; - jfloat scaleY; -}; - struct OverrideHandle { jobject frame; HWND handle; @@ -179,10 +168,6 @@ jfieldID AwtWindow::autoRequestFocusID; jfieldID AwtWindow::securityWarningWidthID; jfieldID AwtWindow::securityWarningHeightID; -jfieldID AwtWindow::sysXID; -jfieldID AwtWindow::sysYID; -jfieldID AwtWindow::sysWID; -jfieldID AwtWindow::sysHID; jfieldID AwtWindow::windowTypeID; jmethodID AwtWindow::notifyWindowStateChangedMID; @@ -1128,20 +1113,19 @@ AwtWindow* AwtWindow::Create(jobject self, jobject parent) // specify WS_EX_TOOLWINDOW to remove parentless windows from taskbar exStyle |= WS_EX_TOOLWINDOW; } + jint x = env->GetIntField(target, AwtComponent::xID); + jint y = env->GetIntField(target, AwtComponent::yID); + jint width = env->GetIntField(target, AwtComponent::widthID); + jint height = env->GetIntField(target, AwtComponent::heightID); + window->CreateHWnd(env, L"", style, exStyle, - 0, 0, 0, 0, + x, y, width, height, (awtParent != NULL) ? awtParent->GetHWnd() : NULL, NULL, ::GetSysColor(COLOR_WINDOWTEXT), ::GetSysColor(COLOR_WINDOW), self); - - jint x = env->GetIntField(target, AwtComponent::xID); - jint y = env->GetIntField(target, AwtComponent::yID); - jint width = env->GetIntField(target, AwtComponent::widthID); - jint height = env->GetIntField(target, AwtComponent::heightID); - /* * Initialize icon as inherited from parent if it exists */ @@ -1151,13 +1135,7 @@ AwtWindow* AwtWindow::Create(jobject self, jobject parent) window->m_iconInherited = TRUE; } window->DoUpdateIcon(); - - - /* - * Reshape here instead of during create, so that a WM_NCCALCSIZE - * is sent. - */ - window->Reshape(x, y, width, height); + window->RecalcNonClient(); } } catch (...) { env->DeleteLocalRef(target); @@ -1215,6 +1193,48 @@ void AwtWindow::moveToDefaultLocation() { VERIFY(::SetWindowPos(GetHWnd(), NULL, defLoc.left, defLoc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER)); } +/** + * Override AwtComponent::Reshape() to handle absolute screen coordinates used + * by the top-level windows. + */ +void AwtWindow::Reshape(int x, int y, int w, int h) { + if (IsEmbeddedFrame()) { + // Not the "real" top level window + return AwtComponent::Reshape(x, y, w, h); + } + // Yes, use x,y in user's space to find the nearest monitor in device space. + POINT pt = {x + w / 2, y + h / 2}; + Devices::InstanceAccess devices; + HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + int screen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(monitor); + AwtWin32GraphicsDevice *device = devices->GetDevice(screen); + // Try to set the correct size and jump to the correct location, even if it is + // on the different monitor. Note that for the "size" we use the current + // monitor, so the WM_DPICHANGED will adjust it for the "target" monitor. + int scaleUpAbsX = device == NULL ? x : device->ScaleUpAbsX(x); + int scaleUpAbsY = device == NULL ? y : device->ScaleUpAbsY(y); + ReshapeNoScale(scaleUpAbsX, scaleUpAbsY, ScaleUpX(w), ScaleUpY(h)); + // The window manager may tweak the size for different reasons, so try + // to make sure our window has the correct size in the user's space. + // NOOP if the size was changed already or changing is in progress. + RECT rc; + ::GetWindowRect(GetHWnd(), &rc); + ReshapeNoScale(rc.left, rc.top, ScaleUpX(w), ScaleUpY(h)); + // the window manager may ignore our "SetWindowPos" request, in this, + // case the WmMove/WmSize will not come and we need to manually resync + // the "java.awt.Window" locations, because "java.awt.Window" already + // uses location ignored by the window manager. + ::GetWindowRect(GetHWnd(), &rc); + if (x != ScaleDownAbsX(rc.left) || y != ScaleDownAbsY(rc.top)) { + WmMove(rc.left, rc.top); + } + int userW = ScaleDownX(rc.right - rc.left); + int userH = ScaleDownY(rc.bottom - rc.top); + if (w != userW || h != userH) { + WmSize(SIZENORMAL, rc.right - rc.left, rc.bottom - rc.top); + } +} + void AwtWindow::Show() { m_visible = true; @@ -1774,6 +1794,15 @@ MsgRouting AwtWindow::WmShowWindow(BOOL show, UINT status) return AwtCanvas::WmShowWindow(show, status); } +void AwtWindow::WmDPIChanged(const LPARAM &lParam) { + // need to update the scales now, otherwise the ReshapeNoScale() will + // calculate the bounds wrongly + AwtWin32GraphicsDevice::ResetAllDesktopScales(); + RECT *r = (RECT *) lParam; + ReshapeNoScale(r->left, r->top, r->right - r->left, r->bottom - r->top); + CheckIfOnNewScreen(true); +} + /* * Override AwtComponent's move handling to first update the * java AWT target's position fields directly, since Windows @@ -1789,30 +1818,21 @@ MsgRouting AwtWindow::WmMove(int x, int y) // NOTE: See also AwtWindow::Reshape return mrDoDefault; } - - if (m_screenNum == -1) { - // Set initial value - m_screenNum = GetScreenImOn(); - } - else { - CheckIfOnNewScreen(); - } + // Check for the new screen and update the java peer + CheckIfOnNewScreen(false); // postpone if different DPI /* Update the java AWT target component's fields directly */ JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); if (env->EnsureLocalCapacity(1) < 0) { return mrConsume; } - jobject peer = GetPeer(env); - jobject target = env->GetObjectField(peer, AwtObject::targetID); + jobject target = GetTarget(env); RECT rect; ::GetWindowRect(GetHWnd(), &rect); - (env)->SetIntField(target, AwtComponent::xID, ScaleDownX(rect.left)); - (env)->SetIntField(target, AwtComponent::yID, ScaleDownY(rect.top)); - (env)->SetIntField(peer, AwtWindow::sysXID, ScaleDownX(rect.left)); - (env)->SetIntField(peer, AwtWindow::sysYID, ScaleDownY(rect.top)); + (env)->SetIntField(target, AwtComponent::xID, ScaleDownAbsX(rect.left)); + (env)->SetIntField(target, AwtComponent::yID, ScaleDownAbsY(rect.top)); SendComponentEvent(java_awt_event_ComponentEvent_COMPONENT_MOVED); env->DeleteLocalRef(target); @@ -1857,13 +1877,22 @@ MsgRouting AwtWindow::WmSizing() MsgRouting AwtWindow::WmEnterSizeMove() { m_winSizeMove = TRUE; + // Below is a workaround, see CheckWindowDPIChange + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice* device = devices->GetDevice(m_screenNum); + if (device) { + prevScaleRec.screen = m_screenNum; + prevScaleRec.scaleX = device->GetScaleX(); + prevScaleRec.scaleY = device->GetScaleY(); + } + // Above is a workaround return mrDoDefault; } MsgRouting AwtWindow::WmExitSizeMove() { m_winSizeMove = FALSE; - CheckWindowDPIChange(); + CheckWindowDPIChange(); // workaround return mrDoDefault; } @@ -1880,6 +1909,8 @@ MsgRouting AwtWindow::WmSize(UINT type, int w, int h) UpdateSecurityWarningVisibility(); return mrDoDefault; } + // Check for the new screen and update the java peer + CheckIfOnNewScreen(false); // postpone if different DPI JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); if (env->EnsureLocalCapacity(1) < 0) @@ -1887,15 +1918,8 @@ MsgRouting AwtWindow::WmSize(UINT type, int w, int h) jobject target = GetTarget(env); // fix 4167248 : ensure the insets are up-to-date before using BOOL insetsChanged = UpdateInsets(NULL); - int newWidth = w + m_insets.left + m_insets.right; - int newHeight = h + m_insets.top + m_insets.bottom; - - (env)->SetIntField(target, AwtComponent::widthID, ScaleDownX(newWidth)); - (env)->SetIntField(target, AwtComponent::heightID, ScaleDownY(newHeight)); - - jobject peer = GetPeer(env); - (env)->SetIntField(peer, AwtWindow::sysWID, ScaleDownX(newWidth)); - (env)->SetIntField(peer, AwtWindow::sysHID, ScaleDownY(newHeight)); + (env)->SetIntField(target, AwtComponent::widthID, ScaleDownX(w)); + (env)->SetIntField(target, AwtComponent::heightID, ScaleDownY(h)); if (!AwtWindow::IsResizing()) { WindowResized(); @@ -1977,6 +2001,11 @@ LRESULT AwtWindow::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) LRESULT retValue = 0L; switch(message) { + case WM_DPICHANGED: { + WmDPIChanged(lParam); + mr = mrConsume; + break; + } case WM_GETICON: mr = WmGetIcon(wParam, retValue); break; @@ -2120,14 +2149,28 @@ int AwtWindow::GetScreenImOn() { return scrnNum; } -/* Check to see if we've been moved onto another screen. +/* + * Check to see if we've been moved onto another screen. * If so, update internal data, surfaces, etc. */ - -void AwtWindow::CheckIfOnNewScreen() { +void AwtWindow::CheckIfOnNewScreen(BOOL force) { int curScrn = GetScreenImOn(); if (curScrn != m_screenNum) { // we've been moved + // if moved from one monitor to another with different DPI, we should + // update the m_screenNum only if the size was updated as well in the + // WM_DPICHANGED. + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice* oldDevice = devices->GetDevice(m_screenNum); + AwtWin32GraphicsDevice* newDevice = devices->GetDevice(curScrn); + if (!force && m_winSizeMove && oldDevice && newDevice) { + if (oldDevice->GetScaleX() != newDevice->GetScaleX() + || oldDevice->GetScaleY() != newDevice->GetScaleY()) { + // scales are different, wait for WM_DPICHANGED + return; + } + } + JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); jclass peerCls = env->GetObjectClass(m_peerObject); @@ -2149,65 +2192,37 @@ void AwtWindow::CheckIfOnNewScreen() { } } +// The shared code is not ready to the top-level window which crosses a few +// monitors with different DPI. Popup windows will start to use wrong screen, +// will be placed in the wrong place and will use the wrong size, see 8249164 +// So we will "JUMP TO" the new screen. void AwtWindow::CheckWindowDPIChange() { - - if (prevScaleRec.screen != -1 ) { - float prevScaleX = prevScaleRec.scaleX; - float prevScaleY = prevScaleRec.scaleY; - - if (prevScaleX >= 1 && prevScaleY >= 1) { - Devices::InstanceAccess devices; - AwtWin32GraphicsDevice* device = devices->GetDevice(m_screenNum); - if (device) { - float scaleX = device->GetScaleX(); - float scaleY = device->GetScaleY(); - if (prevScaleX != scaleX || prevScaleY != scaleY) { - WindowDPIChange(prevScaleRec.screen, prevScaleX, prevScaleY, - m_screenNum, scaleX, scaleY); - } - } - } - prevScaleRec.screen = -1; - } -} - -void AwtWindow::WindowDPIChange(int prevScreen, - float prevScaleX, float prevScaleY, - int screen, float scaleX, - float scaleY) -{ - int x; - int y; - int w; - int h; - RECT rect; - - if (prevScaleX == scaleX && prevScaleY == scaleY) { - return; - } - - ::GetWindowRect(GetHWnd(), &rect); - x = rect.left; - y = rect.top; - w = (rect.right - rect.left) * scaleX / prevScaleX; - h = (rect.bottom - rect.top) * scaleY / prevScaleY; - - if (prevScreen != screen) { + if (prevScaleRec.screen != -1 && prevScaleRec.screen != m_screenNum) { Devices::InstanceAccess devices; - AwtWin32GraphicsDevice* device = devices->GetDevice(screen); + AwtWin32GraphicsDevice *device = devices->GetDevice(m_screenNum); if (device) { - RECT bounds; - if (MonitorBounds(device->GetMonitor(), &bounds)) { - x = x < bounds.left ? bounds.left : x; - y = y < bounds.top ? bounds.top : y; - - x = (x + w > bounds.right) ? bounds.right - w : x; - y = (y + h > bounds.bottom) ? bounds.bottom - h : y; + if (prevScaleRec.scaleX != device->GetScaleX() + || prevScaleRec.scaleY != device->GetScaleY()) { + RECT rect; + ::GetWindowRect(GetHWnd(), &rect); + int x = rect.left; + int y = rect.top; + int w = rect.right - rect.left; + int h = rect.bottom - rect.top; + RECT bounds; + if (MonitorBounds(device->GetMonitor(), &bounds)) { + x = x < bounds.left ? bounds.left : x; + y = y < bounds.top ? bounds.top : y; + x = (x + w > bounds.right) ? bounds.right - w : x; + y = (y + h > bounds.bottom) ? bounds.bottom - h : y; + } + ReshapeNoScale(x, y, w, h); } } + prevScaleRec.screen = -1; + prevScaleRec.scaleX = -1.0f; + prevScaleRec.scaleY = -1.0f; } - - ReshapeNoScale(x, y, w, h); } BOOL AwtWindow::IsFocusableWindow() { @@ -2582,15 +2597,11 @@ void AwtWindow::_ReshapeFrame(void *param) { env->SetIntField(target, AwtComponent::widthID, w = minWidth); - env->SetIntField(peer, AwtWindow::sysWID, - w); } if (h < minHeight) { env->SetIntField(target, AwtComponent::heightID, h = minHeight); - env->SetIntField(peer, AwtWindow::sysHID, - h); } } env->DeleteLocalRef(target); @@ -3257,39 +3268,6 @@ void AwtWindow::_GetNativeWindowSize(void* param) { env->DeleteGlobalRef(self); } -void AwtWindow::_WindowDPIChange(void* param) -{ - JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); - - ScaleStruct *ss = (ScaleStruct *)param; - jobject self = ss->window; - jint prevScreen = ss->prevScreen; - jfloat prevScaleX = ss->prevScaleX; - jfloat prevScaleY = ss->prevScaleY; - jint screen = ss->screen; - jfloat scaleX = ss->scaleX; - jfloat scaleY = ss->scaleY; - - PDATA pData; - JNI_CHECK_PEER_GOTO(self, ret); - AwtWindow *window = (AwtWindow *)pData; - - if (window->m_winSizeMove) { - if (window->prevScaleRec.screen == -1) { - window->prevScaleRec.screen = prevScreen; - window->prevScaleRec.scaleX = prevScaleX; - window->prevScaleRec.scaleY = prevScaleY; - } - } - else { - window->WindowDPIChange(prevScreen, prevScaleX, prevScaleY, - screen, scaleX, scaleY); - } - -ret: - env->DeleteGlobalRef(self); - delete ss; -} extern "C" int getSystemMetricValue(int msgType); extern "C" { @@ -3347,11 +3325,6 @@ Java_sun_awt_windows_WWindowPeer_initIDs(JNIEnv *env, jclass cls) { TRY; - CHECK_NULL(AwtWindow::sysXID = env->GetFieldID(cls, "sysX", "I")); - CHECK_NULL(AwtWindow::sysYID = env->GetFieldID(cls, "sysY", "I")); - CHECK_NULL(AwtWindow::sysWID = env->GetFieldID(cls, "sysW", "I")); - CHECK_NULL(AwtWindow::sysHID = env->GetFieldID(cls, "sysH", "I")); - AwtWindow::windowTypeID = env->GetFieldID(cls, "windowType", "Ljava/awt/Window$Type;"); @@ -3991,33 +3964,6 @@ Java_sun_awt_windows_WWindowPeer_repositionSecurityWarning(JNIEnv *env, CATCH_BAD_ALLOC; } -/* -* Class: sun_awt_windows_WWindowPeer -* Method: windowDPIChange -* Signature: (IFFIFF)V -*/ -JNIEXPORT void JNICALL -Java_sun_awt_windows_WWindowPeer_windowDPIChange(JNIEnv *env, jobject self, - jint prevScreen, jfloat prevScaleX, jfloat prevScaleY, - jint screen, jfloat scaleX, jfloat scaleY) -{ - TRY; - - ScaleStruct *ss = new ScaleStruct; - ss->window = env->NewGlobalRef(self); - ss->prevScreen = prevScreen; - ss->prevScaleX = prevScaleX; - ss->prevScaleY = prevScaleY; - ss->screen = screen; - ss->scaleX = scaleX; - ss->scaleY = scaleY; - - AwtToolkit::GetInstance().InvokeFunction(AwtWindow::_WindowDPIChange, ss); - // global refs and ss are deleted in _WindowDPIChange - - CATCH_BAD_ALLOC; -} - /* * Class: sun_awt_windows_WLightweightFramePeer * Method: overrideNativeHandle diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Window.h b/src/java.desktop/windows/native/libawt/windows/awt_Window.h index 38f012864df..6e035cc8f38 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Window.h +++ b/src/java.desktop/windows/native/libawt/windows/awt_Window.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,12 +58,6 @@ class AwtWindow : public AwtCanvas { static jfieldID securityWarningHeightID; /* sun.awt.windows.WWindowPeer field and method IDs */ - // The coordinates at the peer. - static jfieldID sysXID; - static jfieldID sysYID; - static jfieldID sysWID; - static jfieldID sysHID; - static jfieldID windowTypeID; static jmethodID notifyWindowStateChangedMID; @@ -129,6 +123,7 @@ class AwtWindow : public AwtCanvas { return FALSE; } + virtual void Reshape(int x, int y, int w, int h); virtual void Invalidate(RECT* r); virtual void Show(); virtual void SetResizable(BOOL isResizable); @@ -136,7 +131,7 @@ class AwtWindow : public AwtCanvas { virtual void RecalcNonClient(); virtual void RedrawNonClient(); virtual int GetScreenImOn(); - virtual void CheckIfOnNewScreen(); + virtual void CheckIfOnNewScreen(BOOL force); virtual void Grab(); virtual void Ungrab(); virtual void Ungrab(BOOL doPost); @@ -248,7 +243,6 @@ class AwtWindow : public AwtCanvas { static void _RepositionSecurityWarning(void* param); static void _SetFullScreenExclusiveModeState(void* param); static void _GetNativeWindowSize(void* param); - static void _WindowDPIChange(void* param); static void _OverrideHandle(void *param); inline static BOOL IsResizing() { @@ -409,8 +403,7 @@ class AwtWindow : public AwtCanvas { void InitOwner(AwtWindow *owner); void CheckWindowDPIChange(); - void WindowDPIChange(int prevScreen, float prevScaleX, float prevScaleY, - int newScreen, float scaleX, float scaleY); + void WmDPIChanged(const LPARAM &lParam); Type m_windowType; void InitType(JNIEnv *env, jobject peer); diff --git a/src/java.desktop/windows/native/libawt/windows/awtmsg.h b/src/java.desktop/windows/native/libawt/windows/awtmsg.h index ba8a4492591..c4f85fad41c 100644 --- a/src/java.desktop/windows/native/libawt/windows/awtmsg.h +++ b/src/java.desktop/windows/native/libawt/windows/awtmsg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,6 +41,10 @@ extern const UINT SYSCOMMAND_IMM; * See winuser.h for details. */ +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 +#endif //WM_DPICHANGED + #ifndef WM_MOUSEWHEEL #define WM_MOUSEWHEEL 0x020A #endif //WM_MOUSEWHEEL diff --git a/src/java.instrument/share/classes/java/lang/instrument/ClassFileTransformer.java b/src/java.instrument/share/classes/java/lang/instrument/ClassFileTransformer.java index aff31855422..6df7a2ae697 100644 --- a/src/java.instrument/share/classes/java/lang/instrument/ClassFileTransformer.java +++ b/src/java.instrument/share/classes/java/lang/instrument/ClassFileTransformer.java @@ -192,7 +192,6 @@ public interface ClassFileTransformer { * or {@code null} if no transform is performed * * @revised 9 - * @spec JPMS */ default byte[] transform( ClassLoader loader, @@ -231,7 +230,6 @@ public interface ClassFileTransformer { * or {@code null} if no transform is performed * * @since 9 - * @spec JPMS */ default byte[] transform( Module module, diff --git a/src/java.instrument/share/classes/java/lang/instrument/Instrumentation.java b/src/java.instrument/share/classes/java/lang/instrument/Instrumentation.java index cd57f0188e1..e16ea972345 100644 --- a/src/java.instrument/share/classes/java/lang/instrument/Instrumentation.java +++ b/src/java.instrument/share/classes/java/lang/instrument/Instrumentation.java @@ -729,7 +729,6 @@ public interface Instrumentation { * * @see #isModifiableModule(Module) * @since 9 - * @spec JPMS */ void redefineModule(Module module, Set extraReads, @@ -750,7 +749,6 @@ void redefineModule(Module module, * @throws NullPointerException if the module is {@code null} * * @since 9 - * @spec JPMS */ boolean isModifiableModule(Module module); } diff --git a/src/java.instrument/share/classes/java/lang/instrument/UnmodifiableModuleException.java b/src/java.instrument/share/classes/java/lang/instrument/UnmodifiableModuleException.java index be037b21cf6..462ee8b71ff 100644 --- a/src/java.instrument/share/classes/java/lang/instrument/UnmodifiableModuleException.java +++ b/src/java.instrument/share/classes/java/lang/instrument/UnmodifiableModuleException.java @@ -30,7 +30,6 @@ * * @see Instrumentation#redefineModule * @since 9 - * @spec JPMS */ public class UnmodifiableModuleException extends RuntimeException { diff --git a/src/java.logging/share/classes/java/util/logging/LogManager.java b/src/java.logging/share/classes/java/util/logging/LogManager.java index 7c301f8ad7c..a181910e94b 100644 --- a/src/java.logging/share/classes/java/util/logging/LogManager.java +++ b/src/java.logging/share/classes/java/util/logging/LogManager.java @@ -1920,7 +1920,7 @@ public void updateConfiguration(Function *

  • * - * + * * * - * + * * * - * + * * * - * + * * * - * + * * - - - - - +
    Class
    +
    Description
    """); checkOutput("allpackages-index.html", true, """ -
    - - - - - - """); +
    Package Summary
    +
    +
    Package
    +
    Description
    + """); checkOutput("allclasses-index.html", found, """ -
    setInnerHTMLsetOuterHTML
    *
    *

    Paragraph 1

    diff --git a/src/java.desktop/share/classes/sun/awt/AppContext.java b/src/java.desktop/share/classes/sun/awt/AppContext.java index cc4547ddc02..b8d902801a9 100644 --- a/src/java.desktop/share/classes/sun/awt/AppContext.java +++ b/src/java.desktop/share/classes/sun/awt/AppContext.java @@ -208,7 +208,7 @@ public boolean isDisposed() { * number is 1. If so, it returns the sole AppContext without * checking Thread.currentThread(). */ - private static final AtomicInteger numAppContexts = new AtomicInteger(0); + private static final AtomicInteger numAppContexts = new AtomicInteger(); /* diff --git a/src/java.desktop/share/classes/sun/awt/dnd/SunDragSourceContextPeer.java b/src/java.desktop/share/classes/sun/awt/dnd/SunDragSourceContextPeer.java index 5aa3d8c1987..4dc0fd434d8 100644 --- a/src/java.desktop/share/classes/sun/awt/dnd/SunDragSourceContextPeer.java +++ b/src/java.desktop/share/classes/sun/awt/dnd/SunDragSourceContextPeer.java @@ -75,7 +75,7 @@ public abstract class SunDragSourceContextPeer implements DragSourceContextPeer private int sourceActions; private static volatile boolean dragDropInProgress = false; - private static boolean discardingMouseEvents = false; + private static volatile boolean discardingMouseEvents = false; /* * dispatch constants diff --git a/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java b/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java index 0f3f195fb19..c5ce7ebd315 100644 --- a/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java +++ b/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java @@ -27,6 +27,7 @@ import java.awt.AWTError; import java.awt.Color; +import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; @@ -349,53 +350,124 @@ public static GraphicsConfiguration getGraphicsConfigurationAtPoint( } /** - * Converts coordinates from the user's space to the device space using - * appropriate device transformation. + * Returns the bounds of the graphics configuration in device space. + * + * @param config the graphics configuration which bounds are requested + * @return the bounds of the area covered by this + * {@code GraphicsConfiguration} in device space (pixels) + */ + public static Rectangle getGCDeviceBounds(GraphicsConfiguration config) { + AffineTransform tx = config.getDefaultTransform(); + Rectangle bounds = config.getBounds(); + bounds.width *= tx.getScaleX(); + bounds.height *= tx.getScaleY(); + return bounds; + } + + /** + * Converts the size (w, h) from the device space to the user's space using + * passed graphics configuration. + * + * @param gc the graphics configuration to be used for transformation + * @param w the width in the device space + * @param h the height in the device space + * @return the size in the user's space + */ + public static Dimension toUserSpace(GraphicsConfiguration gc, + int w, int h) { + AffineTransform tx = gc.getDefaultTransform(); + return new Dimension( + Region.clipRound(w / tx.getScaleX()), + Region.clipRound(h / tx.getScaleY()) + ); + } + + /** + * Converts absolute coordinates from the user's space to the device space + * using appropriate device transformation. * - * @param x coordinate in the user space - * @param y coordinate in the user space - * @return the point which uses device space(pixels) + * @param x absolute coordinate in the user's space + * @param y absolute coordinate in the user's space + * @return the point which uses device space (pixels) */ - public static Point convertToDeviceSpace(double x, double y) { + public static Point toDeviceSpaceAbs(int x, int y) { GraphicsConfiguration gc = getLocalGraphicsEnvironment() - .getDefaultScreenDevice().getDefaultConfiguration(); + .getDefaultScreenDevice().getDefaultConfiguration(); gc = getGraphicsConfigurationAtPoint(gc, x, y); + return toDeviceSpaceAbs(gc, x, y, 0, 0).getLocation(); + } + /** + * Converts the rectangle from the user's space to the device space using + * appropriate device transformation. + * + * @param rect the rectangle in the user's space + * @return the rectangle which uses device space (pixels) + */ + public static Rectangle toDeviceSpaceAbs(Rectangle rect) { + GraphicsConfiguration gc = getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + gc = getGraphicsConfigurationAtPoint(gc, rect.x, rect.y); + return toDeviceSpaceAbs(gc, rect.x, rect.y, rect.width, rect.height); + } + + /** + * Converts absolute coordinates (x, y) and the size (w, h) from the user's + * space to the device space using passed graphics configuration. + * + * @param gc the graphics configuration to be used for transformation + * @param x absolute coordinate in the user's space + * @param y absolute coordinate in the user's space + * @param w the width in the user's space + * @param h the height in the user's space + * @return the rectangle which uses device space (pixels) + */ + public static Rectangle toDeviceSpaceAbs(GraphicsConfiguration gc, + int x, int y, int w, int h) { AffineTransform tx = gc.getDefaultTransform(); - x = Region.clipRound(x * tx.getScaleX()); - y = Region.clipRound(y * tx.getScaleY()); - return new Point((int) x, (int) y); + Rectangle screen = gc.getBounds(); + return new Rectangle( + screen.x + Region.clipRound((x - screen.x) * tx.getScaleX()), + screen.y + Region.clipRound((y - screen.y) * tx.getScaleY()), + Region.clipRound(w * tx.getScaleX()), + Region.clipRound(h * tx.getScaleY()) + ); } /** - * Converts bounds from the user's space to the device space using + * Converts coordinates from the user's space to the device space using * appropriate device transformation. * - * @param bounds the rectangle in the user space - * @return the rectangle which uses device space(pixels) + * @param x coordinate in the user's space + * @param y coordinate in the user's space + * @return the point which uses device space (pixels) */ - public static Rectangle convertToDeviceSpace(Rectangle bounds) { + public static Point toDeviceSpace(int x, int y) { GraphicsConfiguration gc = getLocalGraphicsEnvironment() .getDefaultScreenDevice().getDefaultConfiguration(); - gc = getGraphicsConfigurationAtPoint(gc, bounds.x, bounds.y); - return convertToDeviceSpace(gc, bounds); + gc = getGraphicsConfigurationAtPoint(gc, x, y); + return toDeviceSpace(gc, x, y, 0, 0).getLocation(); } /** - * Converts bounds from the user's space to the device space using - * appropriate device transformation of the passed graphics configuration. + * Converts coordinates (x, y) and the size (w, h) from the user's + * space to the device space using passed graphics configuration. * - * @param bounds the rectangle in the user space - * @return the rectangle which uses device space(pixels) + * @param gc the graphics configuration to be used for transformation + * @param x coordinate in the user's space + * @param y coordinate in the user's space + * @param w the width in the user's space + * @param h the height in the user's space + * @return the rectangle which uses device space (pixels) */ - public static Rectangle convertToDeviceSpace(GraphicsConfiguration gc, - Rectangle bounds) { + public static Rectangle toDeviceSpace(GraphicsConfiguration gc, + int x, int y, int w, int h) { AffineTransform tx = gc.getDefaultTransform(); return new Rectangle( - Region.clipRound(bounds.x * tx.getScaleX()), - Region.clipRound(bounds.y * tx.getScaleY()), - Region.clipRound(bounds.width * tx.getScaleX()), - Region.clipRound(bounds.height * tx.getScaleY()) + Region.clipRound(x * tx.getScaleX()), + Region.clipRound(y * tx.getScaleY()), + Region.clipRound(w * tx.getScaleX()), + Region.clipRound(h * tx.getScaleY()) ); } } diff --git a/src/java.desktop/share/classes/sun/java2d/cmm/CMSManager.java b/src/java.desktop/share/classes/sun/java2d/cmm/CMSManager.java index fea14af51ed..4d9f09c5905 100644 --- a/src/java.desktop/share/classes/sun/java2d/cmm/CMSManager.java +++ b/src/java.desktop/share/classes/sun/java2d/cmm/CMSManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,14 +25,11 @@ package sun.java2d.cmm; +import java.awt.color.CMMException; import java.awt.color.ColorSpace; import java.awt.color.ICC_Profile; -import java.awt.color.CMMException; -import java.awt.image.BufferedImage; -import java.awt.image.Raster; -import java.awt.image.WritableRaster; import java.security.AccessController; -import java.security.PrivilegedAction; + import sun.security.action.GetPropertyAction; public class CMSManager { @@ -103,11 +100,6 @@ public Profile loadProfile(byte[] data) { return p; } - public void freeProfile(Profile p) { - System.err.printf(cName + ".freeProfile(ID=%s)\n", p.toString()); - tcmm.freeProfile(p); - } - public int getProfileSize(Profile p) { System.err.print(cName + ".getProfileSize(ID=" + p + ")"); int size = tcmm.getProfileSize(p); diff --git a/src/java.desktop/share/classes/sun/java2d/cmm/PCMM.java b/src/java.desktop/share/classes/sun/java2d/cmm/PCMM.java index d147f0a42dc..0526abafeca 100644 --- a/src/java.desktop/share/classes/sun/java2d/cmm/PCMM.java +++ b/src/java.desktop/share/classes/sun/java2d/cmm/PCMM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,6 @@ public interface PCMM { /* methods invoked from ICC_Profile */ public Profile loadProfile(byte[] data); - public void freeProfile(Profile p); public int getProfileSize(Profile p); public void getProfileData(Profile p, byte[] data); public void getTagData(Profile p, int tagSignature, byte[] data); diff --git a/src/java.desktop/share/classes/sun/java2d/cmm/ProfileDeferralMgr.java b/src/java.desktop/share/classes/sun/java2d/cmm/ProfileDeferralMgr.java index 49b6808baa4..d19b6f6da68 100644 --- a/src/java.desktop/share/classes/sun/java2d/cmm/ProfileDeferralMgr.java +++ b/src/java.desktop/share/classes/sun/java2d/cmm/ProfileDeferralMgr.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,23 +59,6 @@ public static void registerDeferral(ProfileActivator pa) { } - /** - * Removes a ProfileActivator object from the vector of ProfileActivator - * objects whose activate method will be called if the CMM needs to be - * activated. - */ - public static void unregisterDeferral(ProfileActivator pa) { - - if (!deferring) { - return; - } - if (aVector == null) { - return; - } - aVector.removeElement(pa); - return; - } - /** * Removes a ProfileActivator object from the vector of ProfileActivator * objects whose activate method will be called if the CMM needs to be diff --git a/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java b/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java index a9e6adbbed9..32232d02798 100644 --- a/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java +++ b/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ import java.awt.color.CMMException; import java.awt.color.ICC_Profile; + import sun.java2d.cmm.ColorTransform; import sun.java2d.cmm.PCMM; import sun.java2d.cmm.Profile; @@ -56,12 +57,6 @@ private LCMSProfile getLcmsProfile(Profile p) { throw new CMMException("Invalid profile: " + p); } - - @Override - public void freeProfile(Profile p) { - // we use disposer, so this method does nothing - } - @Override public int getProfileSize(final Profile p) { synchronized (p) { diff --git a/src/java.desktop/share/classes/sun/java2d/pipe/RegionClipSpanIterator.java b/src/java.desktop/share/classes/sun/java2d/pipe/RegionClipSpanIterator.java index 40ff927d301..c409ae1b98d 100644 --- a/src/java.desktop/share/classes/sun/java2d/pipe/RegionClipSpanIterator.java +++ b/src/java.desktop/share/classes/sun/java2d/pipe/RegionClipSpanIterator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -377,15 +377,4 @@ public void skipDownTo(int y) { public long getNativeIterator() { return 0; } - - /* - * Cleans out all internal data structures. - */ - //public native void dispose(); - - @SuppressWarnings("deprecation") - protected void finalize() { - //dispose(); - } - } diff --git a/src/java.desktop/share/classes/sun/java2d/pipe/RegionSpanIterator.java b/src/java.desktop/share/classes/sun/java2d/pipe/RegionSpanIterator.java index 47f1ee1fa39..9741b14ee23 100644 --- a/src/java.desktop/share/classes/sun/java2d/pipe/RegionSpanIterator.java +++ b/src/java.desktop/share/classes/sun/java2d/pipe/RegionSpanIterator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,17 +47,6 @@ public class RegionSpanIterator implements SpanIterator { // Is the associated Region rectangular? boolean isrect; -/* - REMIND: For native implementation - long pData; // Private storage of rect info - - static { - initIDs(); - } - - public static native void initIDs(); -*/ - /** * Constructs an instance based on the given Region */ @@ -197,14 +186,4 @@ public void skipDownTo(int y) { public long getNativeIterator() { return 0; } - - /* - * Cleans out all internal data structures. - * REMIND: Native implementation - public native void dispose(); - - protected void finalize() { - dispose(); - } - */ } diff --git a/src/java.desktop/share/classes/sun/print/CustomMediaTray.java b/src/java.desktop/share/classes/sun/print/CustomMediaTray.java index 394aba988c1..7410dfd9786 100644 --- a/src/java.desktop/share/classes/sun/print/CustomMediaTray.java +++ b/src/java.desktop/share/classes/sun/print/CustomMediaTray.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ import javax.print.attribute.standard.Media; import java.util.ArrayList; -class CustomMediaTray extends MediaTray { +public class CustomMediaTray extends MediaTray { private static ArrayList customStringTable = new ArrayList<>(); private static ArrayList customEnumTable = new ArrayList<>(); private String choiceName; diff --git a/src/java.desktop/share/legal/harfbuzz.md b/src/java.desktop/share/legal/harfbuzz.md index 16698bc86ac..465bcf5be3c 100644 --- a/src/java.desktop/share/legal/harfbuzz.md +++ b/src/java.desktop/share/legal/harfbuzz.md @@ -1,16 +1,18 @@ -## Harfbuzz v2.3.1 +## Harfbuzz v2.7.2 ### Harfbuzz License -http://cgit.freedesktop.org/harfbuzz/tree/COPYING +https://github.com/harfbuzz/harfbuzz/blob/master/COPYING
     
    -HarfBuzz is licensed under the so-called "Old MIT" license. Details follow.
    +HarfBuzz is licensed under the so-called "Old MIT" license.  Details follow.
     For parts of HarfBuzz that are licensed under different licenses see individual
     files names COPYING in subdirectories where applicable.
     
    -Copyright © 2010,2011,2012  Google, Inc.
    +Copyright © 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020  Google, Inc.
    +Copyright © 2018,2019,2020  Ebrahim Byagowi
    +Copyright © 2019,2020  Facebook, Inc. 
     Copyright © 2012  Mozilla Foundation
     Copyright © 2011  Codethink Limited
     Copyright © 2008,2010  Nokia Corporation and/or its subsidiary(-ies)
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-fdsc-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-fdsc-table.hh
    deleted file mode 100644
    index 4ee7353346d..00000000000
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-fdsc-table.hh
    +++ /dev/null
    @@ -1,126 +0,0 @@
    -/*
    - * Copyright © 2018  Ebrahim Byagowi
    - *
    - *  This is part of HarfBuzz, a text shaping library.
    - *
    - * Permission is hereby granted, without written agreement and without
    - * license or royalty fees, to use, copy, modify, and distribute this
    - * software and its documentation for any purpose, provided that the
    - * above copyright notice and the following two paragraphs appear in
    - * all copies of this software.
    - *
    - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
    - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
    - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
    - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
    - * DAMAGE.
    - *
    - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
    - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    - * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
    - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
    - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
    - */
    -
    -#ifndef HB_AAT_FDSC_TABLE_HH
    -#define HB_AAT_FDSC_TABLE_HH
    -
    -#include "hb-aat-layout-common.hh"
    -#include "hb-open-type.hh"
    -
    -/*
    - * fdsc -- Font descriptors
    - * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6fdsc.html
    - */
    -#define HB_AAT_TAG_fdsc HB_TAG('f','d','s','c')
    -
    -
    -namespace AAT {
    -
    -
    -struct FontDescriptor
    -{
    -  bool has_data () const { return tag; }
    -
    -  int cmp (hb_tag_t a) const { return tag.cmp (a); }
    -
    -  float get_value () const { return u.value.to_float (); }
    -
    -  enum non_alphabetic_value_t {
    -    Alphabetic          = 0,
    -    Dingbats            = 1,
    -    PiCharacters        = 2,
    -    Fleurons            = 3,
    -    DecorativeBorders   = 4,
    -    InternationalSymbols= 5,
    -    MathSymbols         = 6
    -  };
    -
    -  bool sanitize (hb_sanitize_context_t *c) const
    -  {
    -    TRACE_SANITIZE (this);
    -    return_trace (c->check_struct (this));
    -  }
    -
    -  protected:
    -  Tag           tag;            /* The 4-byte table tag name. */
    -  union {
    -  Fixed         value;          /* The value for the descriptor tag. */
    -  HBUINT32      nalfType;       /* If the tag is `nalf`, see non_alphabetic_value_t */
    -  } u;
    -  public:
    -  DEFINE_SIZE_STATIC (8);
    -};
    -
    -struct fdsc
    -{
    -  static constexpr hb_tag_t tableTag = HB_AAT_TAG_fdsc;
    -
    -  enum {
    -    Weight       = HB_TAG ('w','g','h','t'),
    -                                /* Percent weight relative to regular weight.
    -                                 * (defaul value: 1.0) */
    -    Width        = HB_TAG ('w','d','t','h'),
    -                                /* Percent width relative to regular width.
    -                                 * (default value: 1.0) */
    -    Slant        = HB_TAG ('s','l','n','t'),
    -                                /* Angle of slant in degrees, where positive
    -                                 * is clockwise from straight up.
    -                                 * (default value: 0.0) */
    -    OpticalSize  = HB_TAG ('o','p','s','z'),
    -                                /* Point size the font was designed for.
    -                                 * (default value: 12.0) */
    -    NonAlphabetic= HB_TAG ('n','a','l','f')
    -                                /* These values are treated as integers,
    -                                 * not fixed32s. 0 means alphabetic, and greater
    -                                 * integers mean the font is non-alphabetic (e.g. symbols).
    -                                 * (default value: 0) */
    -  };
    -
    -  const FontDescriptor &get_descriptor (hb_tag_t style) const
    -  { return descriptors.lsearch (style); }
    -
    -  bool sanitize (hb_sanitize_context_t *c) const
    -  {
    -    TRACE_SANITIZE (this);
    -    return_trace (c->check_struct (this) &&
    -                  descriptors.sanitize (c));
    -  }
    -
    -  protected:
    -  Fixed         version;        /* Version number of the font descriptors
    -                                 * table (0x00010000 for the current version). */
    -  LArrayOf
    -                descriptors;    /* List of tagged-coordinate pairs style descriptors
    -                                 * that will be included to characterize this font.
    -                                 * Each descriptor consists of a  pair.
    -                                 * These pairs are located in the gxFontDescriptor
    -                                 * array that follows. */
    -  public:
    -  DEFINE_SIZE_ARRAY (8, descriptors);
    -};
    -
    -} /* namespace AAT */
    -
    -
    -#endif /* HB_AAT_FDSC_TABLE_HH */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-ankr-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-ankr-table.hh
    index f8495f384b9..90dd949a50a 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-ankr-table.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-ankr-table.hh
    @@ -66,7 +66,7 @@ struct ankr
       {
         const NNOffsetTo *offset = (this+lookupTable).get_value (glyph_id, num_glyphs);
         if (!offset)
    -      return Null(Anchor);
    +      return Null (Anchor);
         const GlyphAnchors &anchors = &(this+anchorData) + *offset;
         return anchors[i];
       }
    @@ -76,13 +76,14 @@ struct ankr
         TRACE_SANITIZE (this);
         return_trace (likely (c->check_struct (this) &&
                               version == 0 &&
    +                          c->check_range (this, anchorData) &&
                               lookupTable.sanitize (c, this, &(this+anchorData))));
       }
     
       protected:
       HBUINT16      version;        /* Version number (set to zero) */
       HBUINT16      flags;          /* Flags (currently unused; set to zero) */
    -  LOffsetTo > >
    +  LOffsetTo>>
                     lookupTable;    /* Offset to the table's lookup table */
       LNNOffsetTo
                     anchorData;     /* Offset to the glyph data table */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh
    index 746da3ae5bc..7dcf1c3bd9d 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh
    @@ -82,7 +82,7 @@ struct BaselineTableFormat2Part
       }
     
       protected:
    -  GlyphID       stdGlyph;       /* The specific glyph index number in this
    +  HBGlyphID     stdGlyph;       /* The specific glyph index number in this
                                      * font that is used to set the baseline values.
                                      * This is the standard glyph.
                                      * This glyph must contain a set of control points
    @@ -101,11 +101,11 @@ struct BaselineTableFormat3Part
       bool sanitize (hb_sanitize_context_t *c) const
       {
         TRACE_SANITIZE (this);
    -    return_trace (c->check_struct (this) && lookupTable.sanitize (c));
    +    return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c)));
       }
     
       protected:
    -  GlyphID       stdGlyph;       /* ditto */
    +  HBGlyphID     stdGlyph;       /* ditto */
       HBUINT16      ctlPoints[32];  /* ditto */
       Lookup
                     lookupTable;    /* Lookup table that maps glyphs to their
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh
    index 7c8e3cec16f..e1dcd6f7102 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh
    @@ -93,8 +93,8 @@ struct LookupSegmentSingle
         return_trace (c->check_struct (this) && value.sanitize (c, base));
       }
     
    -  GlyphID       last;           /* Last GlyphID in this segment */
    -  GlyphID       first;          /* First GlyphID in this segment */
    +  HBGlyphID     last;           /* Last GlyphID in this segment */
    +  HBGlyphID     first;          /* First GlyphID in this segment */
       T             value;          /* The lookup value (only one) */
       public:
       DEFINE_SIZE_STATIC (4 + T::static_size);
    @@ -125,7 +125,7 @@ struct LookupFormat2
     
       protected:
       HBUINT16      format;         /* Format identifier--format = 2 */
    -  VarSizedBinSearchArrayOf >
    +  VarSizedBinSearchArrayOf>
                     segments;       /* The actual segments. These must already be sorted,
                                      * according to the first word in each one (the last
                                      * glyph in each segment). */
    @@ -153,18 +153,18 @@ struct LookupSegmentArray
                       first <= last &&
                       valuesZ.sanitize (c, base, last - first + 1));
       }
    -  template 
    -  bool sanitize (hb_sanitize_context_t *c, const void *base, T2 user_data) const
    +  template 
    +  bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const
       {
         TRACE_SANITIZE (this);
         return_trace (c->check_struct (this) &&
                       first <= last &&
    -                  valuesZ.sanitize (c, base, last - first + 1, user_data));
    +                  valuesZ.sanitize (c, base, last - first + 1, hb_forward (ds)...));
       }
     
    -  GlyphID       last;           /* Last GlyphID in this segment */
    -  GlyphID       first;          /* First GlyphID in this segment */
    -  NNOffsetTo >
    +  HBGlyphID     last;           /* Last GlyphID in this segment */
    +  HBGlyphID     first;          /* First GlyphID in this segment */
    +  NNOffsetTo>
                     valuesZ;        /* A 16-bit offset from the start of
                                      * the table to the data. */
       public:
    @@ -196,7 +196,7 @@ struct LookupFormat4
     
       protected:
       HBUINT16      format;         /* Format identifier--format = 4 */
    -  VarSizedBinSearchArrayOf >
    +  VarSizedBinSearchArrayOf>
                     segments;       /* The actual segments. These must already be sorted,
                                      * according to the first word in each one (the last
                                      * glyph in each segment). */
    @@ -222,7 +222,7 @@ struct LookupSingle
         return_trace (c->check_struct (this) && value.sanitize (c, base));
       }
     
    -  GlyphID       glyph;          /* Last GlyphID */
    +  HBGlyphID     glyph;          /* Last GlyphID */
       T             value;          /* The lookup value (only one) */
       public:
       DEFINE_SIZE_STATIC (2 + T::static_size);
    @@ -253,7 +253,7 @@ struct LookupFormat6
     
       protected:
       HBUINT16      format;         /* Format identifier--format = 6 */
    -  VarSizedBinSearchArrayOf >
    +  VarSizedBinSearchArrayOf>
                     entries;        /* The actual entries, sorted by glyph index. */
       public:
       DEFINE_SIZE_ARRAY (8, entries);
    @@ -284,7 +284,7 @@ struct LookupFormat8
     
       protected:
       HBUINT16      format;         /* Format identifier--format = 8 */
    -  GlyphID       firstGlyph;     /* First glyph index included in the trimmed array. */
    +  HBGlyphID     firstGlyph;     /* First glyph index included in the trimmed array. */
       HBUINT16      glyphCount;     /* Total number of glyphs (equivalent to the last
                                      * glyph minus the value of firstGlyph plus 1). */
       UnsizedArrayOf
    @@ -303,7 +303,7 @@ struct LookupFormat10
       const typename T::type get_value_or_null (hb_codepoint_t glyph_id) const
       {
         if (!(firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount))
    -      return Null(T);
    +      return Null (T);
     
         const HBUINT8 *p = &valueArrayZ[(glyph_id - firstGlyph) * valueSize];
     
    @@ -326,7 +326,7 @@ struct LookupFormat10
       protected:
       HBUINT16      format;         /* Format identifier--format = 8 */
       HBUINT16      valueSize;      /* Byte size of each value. */
    -  GlyphID       firstGlyph;     /* First glyph index included in the trimmed array. */
    +  HBGlyphID     firstGlyph;     /* First glyph index included in the trimmed array. */
       HBUINT16      glyphCount;     /* Total number of glyphs (equivalent to the last
                                      * glyph minus the value of firstGlyph plus 1). */
       UnsizedArrayOf
    @@ -358,7 +358,7 @@ struct Lookup
           case 10: return u.format10.get_value_or_null (glyph_id);
           default:
           const T *v = get_value (glyph_id, num_glyphs);
    -      return v ? *v : Null(T);
    +      return v ? *v : Null (T);
         }
       }
     
    @@ -418,15 +418,11 @@ struct Lookup
     } /* Close namespace. */
     /* Ugly hand-coded null objects for template Lookup<> :(. */
     extern HB_INTERNAL const unsigned char _hb_Null_AAT_Lookup[2];
    -template <>
    -/*static*/ inline const AAT::Lookup& Null > ()
    -{ return *reinterpret_cast *> (_hb_Null_AAT_Lookup); }
    -template <>
    -/*static*/ inline const AAT::Lookup& Null > ()
    -{ return *reinterpret_cast *> (_hb_Null_AAT_Lookup); }
    -template <>
    -/*static*/ inline const AAT::Lookup >& Null > > ()
    -{ return *reinterpret_cast > *> (_hb_Null_AAT_Lookup); }
    +template 
    +struct Null> {
    +  static AAT::Lookup const & get_null ()
    +  { return *reinterpret_cast *> (_hb_Null_AAT_Lookup); }
    +};
     namespace AAT {
     
     enum { DELETED_GLYPH = 0xFFFF };
    @@ -514,7 +510,7 @@ struct StateTable
       const Entry &get_entry (int state, unsigned int klass) const
       {
         if (unlikely (klass >= nClasses))
    -      klass = StateTable >::CLASS_OUT_OF_BOUNDS;
    +      klass = StateTable>::CLASS_OUT_OF_BOUNDS;
     
         const HBUSHORT *states = (this+stateArrayTable).arrayZ;
         const Entry *entries = (this+entryTable).arrayZ;
    @@ -580,7 +576,7 @@ struct StateTable
               if (unlikely (stop > states))
                 return_trace (false);
               for (const HBUSHORT *p = states; stop < p; p--)
    -            num_entries = MAX (num_entries, *(p - 1) + 1);
    +            num_entries = hb_max (num_entries, *(p - 1) + 1);
               state_neg = min_state;
             }
           }
    @@ -601,7 +597,7 @@ struct StateTable
               if (unlikely (stop < states))
                 return_trace (false);
               for (const HBUSHORT *p = &states[state_pos * num_classes]; p < stop; p++)
    -            num_entries = MAX (num_entries, *p + 1);
    +            num_entries = hb_max (num_entries, *p + 1);
               state_pos = max_state + 1;
             }
           }
    @@ -615,8 +611,8 @@ struct StateTable
             for (const Entry *p = &entries[entry]; p < stop; p++)
             {
               int newState = new_state (p->newState);
    -          min_state = MIN (min_state, newState);
    -          max_state = MAX (max_state, newState);
    +          min_state = hb_min (min_state, newState);
    +          max_state = hb_max (max_state, newState);
             }
             entry = num_entries;
           }
    @@ -635,7 +631,7 @@ struct StateTable
                     classTable;     /* Offset to the class table. */
       NNOffsetTo, HBUINT>
                     stateArrayTable;/* Offset to the state array. */
    -  NNOffsetTo >, HBUINT>
    +  NNOffsetTo>, HBUINT>
                     entryTable;     /* Offset to the entry array. */
     
       public:
    @@ -662,7 +658,7 @@ struct ClassTable
         return_trace (c->check_struct (this) && classArray.sanitize (c));
       }
       protected:
    -  GlyphID               firstGlyph;     /* First glyph index included in the trimmed array. */
    +  HBGlyphID             firstGlyph;     /* First glyph index included in the trimmed array. */
       ArrayOf      classArray;     /* The class codes (indexed by glyph index minus
                                              * firstGlyph). */
       public:
    @@ -682,7 +678,7 @@ struct ObsoleteTypes
                                          const void *base,
                                          const T *array)
       {
    -    return (offset - ((const char *) array - (const char *) base)) / sizeof (T);
    +    return (offset - ((const char *) array - (const char *) base)) / T::static_size;
       }
       template 
       static unsigned int byteOffsetToIndex (unsigned int offset,
    @@ -824,12 +820,11 @@ struct hb_aat_apply_context_t :
     
       /* Unused. For debug tracing only. */
       unsigned int lookup_index;
    -  unsigned int debug_depth;
     
       HB_INTERNAL hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_,
                                           hb_font_t *font_,
                                           hb_buffer_t *buffer_,
    -                                      hb_blob_t *blob = const_cast (&Null(hb_blob_t)));
    +                                      hb_blob_t *blob = const_cast (&Null (hb_blob_t)));
     
       HB_INTERNAL ~hb_aat_apply_context_t ();
     
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh
    index 910a94f0bc3..06c48d2f64a 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh
    @@ -47,17 +47,16 @@ struct SettingName
       hb_aat_layout_feature_selector_t get_selector () const
       { return (hb_aat_layout_feature_selector_t) (unsigned) setting; }
     
    -  void get_info (hb_aat_layout_feature_selector_info_t *s,
    -                        hb_aat_layout_feature_selector_t default_selector) const
    +  hb_aat_layout_feature_selector_info_t get_info (hb_aat_layout_feature_selector_t default_selector) const
       {
    -    s->name_id = nameIndex;
    -
    -    s->enable = (hb_aat_layout_feature_selector_t) (unsigned int) setting;
    -    s->disable = default_selector == HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID ?
    -                 (hb_aat_layout_feature_selector_t) (s->enable + 1) :
    -                 default_selector;
    -
    -    s->reserved = 0;
    +    return {
    +      nameIndex,
    +      (hb_aat_layout_feature_selector_t) (unsigned int) setting,
    +      default_selector == HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID
    +        ? (hb_aat_layout_feature_selector_t) (setting + 1)
    +        : default_selector,
    +      0
    +    };
       }
     
       bool sanitize (hb_sanitize_context_t *c) const
    @@ -117,9 +116,10 @@ struct FeatureName
     
         if (selectors_count)
         {
    -      hb_array_t arr = settings_table.sub_array (start_offset, selectors_count);
    -      for (unsigned int i = 0; i < arr.length; i++)
    -        settings_table[start_offset + i].get_info (&selectors[i], default_selector);
    +      + settings_table.sub_array (start_offset, selectors_count)
    +      | hb_map ([=] (const SettingName& setting) { return setting.get_info (default_selector); })
    +      | hb_sink (hb_array (selectors, *selectors_count))
    +      ;
         }
         return settings_table.length;
       }
    @@ -129,6 +129,11 @@ struct FeatureName
     
       hb_ot_name_id_t get_feature_name_id () const { return nameIndex; }
     
    +  bool is_exclusive () const { return featureFlags & Exclusive; }
    +
    +  /* A FeatureName with no settings is meaningless */
    +  bool has_data () const { return nSettings; }
    +
       bool sanitize (hb_sanitize_context_t *c, const void *base) const
       {
         TRACE_SANITIZE (this);
    @@ -139,7 +144,7 @@ struct FeatureName
       protected:
       HBUINT16      feature;        /* Feature type. */
       HBUINT16      nSettings;      /* The number of records in the setting name array. */
    -  LOffsetTo, false>
    +  LNNOffsetTo>
                     settingTableZ;  /* Offset in bytes from the beginning of this table to
                                      * this feature's setting name array. The actual type of
                                      * record this offset refers to will depend on the
    @@ -162,21 +167,21 @@ struct feat
                                       unsigned int                 *count,
                                       hb_aat_layout_feature_type_t *features) const
       {
    -    unsigned int feature_count = featureNameCount;
    -    if (count && *count)
    +    if (count)
         {
    -      unsigned int len = MIN (feature_count - start_offset, *count);
    -      for (unsigned int i = 0; i < len; i++)
    -        features[i] = namesZ[i + start_offset].get_feature_type ();
    -      *count = len;
    +      + namesZ.as_array (featureNameCount).sub_array (start_offset, count)
    +      | hb_map (&FeatureName::get_feature_type)
    +      | hb_sink (hb_array (features, *count))
    +      ;
         }
         return featureNameCount;
       }
     
    +  bool exposes_feature (hb_aat_layout_feature_type_t feature_type) const
    +  { return get_feature (feature_type).has_data (); }
    +
       const FeatureName& get_feature (hb_aat_layout_feature_type_t feature_type) const
    -  {
    -    return namesZ.bsearch (featureNameCount, feature_type);
    -  }
    +  { return namesZ.bsearch (featureNameCount, feature_type); }
     
       hb_ot_name_id_t get_feature_name_id (hb_aat_layout_feature_type_t feature) const
       { return get_feature (feature).get_feature_name_id (); }
    @@ -209,7 +214,7 @@ struct feat
       SortedUnsizedArrayOf
                     namesZ;         /* The feature name array. */
       public:
    -  DEFINE_SIZE_STATIC (24);
    +  DEFINE_SIZE_ARRAY (12, namesZ);
     };
     
     } /* namespace AAT */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh
    index c3817ea0b9c..7ebd6a5ec5e 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh
    @@ -70,9 +70,9 @@ struct DecompositionAction
     
       ActionSubrecordHeader
                     header;
    -  Fixed         lowerLimit;     /* If the distance factor is less than this value,
    +  HBFixed       lowerLimit;     /* If the distance factor is less than this value,
                                      * then the ligature is decomposed. */
    -  Fixed         upperLimit;     /* If the distance factor is greater than this value,
    +  HBFixed       upperLimit;     /* If the distance factor is greater than this value,
                                      * then the ligature is decomposed. */
       HBUINT16      order;          /* Numerical order in which this ligature will
                                      * be decomposed; you may want infrequent ligatures
    @@ -100,7 +100,7 @@ struct UnconditionalAddGlyphAction
       protected:
       ActionSubrecordHeader
                     header;
    -  GlyphID       addGlyph;       /* Glyph that should be added if the distance factor
    +  HBGlyphID     addGlyph;       /* Glyph that should be added if the distance factor
                                      * is growing. */
     
       public:
    @@ -118,14 +118,14 @@ struct ConditionalAddGlyphAction
       protected:
       ActionSubrecordHeader
                     header;
    -  Fixed         substThreshold; /* Distance growth factor (in ems) at which
    +  HBFixed       substThreshold; /* Distance growth factor (in ems) at which
                                      * this glyph is replaced and the growth factor
                                      * recalculated. */
    -  GlyphID       addGlyph;       /* Glyph to be added as kashida. If this value is
    +  HBGlyphID     addGlyph;       /* Glyph to be added as kashida. If this value is
                                      * 0xFFFF, no extra glyph will be added. Note that
                                      * generally when a glyph is added, justification
                                      * will need to be redone. */
    -  GlyphID       substGlyph;     /* Glyph to be substituted for this glyph if the
    +  HBGlyphID     substGlyph;     /* Glyph to be substituted for this glyph if the
                                      * growth factor equals or exceeds the value of
                                      * substThreshold. */
       public:
    @@ -146,13 +146,13 @@ struct DuctileGlyphAction
       HBUINT32      variationAxis;  /* The 4-byte tag identifying the ductile axis.
                                      * This would normally be 0x64756374 ('duct'),
                                      * but you may use any axis the font contains. */
    -  Fixed         minimumLimit;   /* The lowest value for the ductility axis tha
    +  HBFixed       minimumLimit;   /* The lowest value for the ductility axis tha
                                      * still yields an acceptable appearance. Normally
                                      * this will be 1.0. */
    -  Fixed         noStretchValue; /* This is the default value that corresponds to
    +  HBFixed       noStretchValue; /* This is the default value that corresponds to
                                      * no change in appearance. Normally, this will
                                      * be 1.0. */
    -  Fixed         maximumLimit;   /* The highest value for the ductility axis that
    +  HBFixed       maximumLimit;   /* The highest value for the ductility axis that
                                      * still yields an acceptable appearance. */
       public:
       DEFINE_SIZE_STATIC (22);
    @@ -170,7 +170,7 @@ struct RepeatedAddGlyphAction
       ActionSubrecordHeader
                     header;
       HBUINT16      flags;          /* Currently unused; set to 0. */
    -  GlyphID       glyph;          /* Glyph that should be added if the distance factor
    +  HBGlyphID     glyph;          /* Glyph that should be added if the distance factor
                                      * is growing. */
       public:
       DEFINE_SIZE_STATIC (10);
    @@ -271,14 +271,14 @@ struct JustWidthDeltaEntry
       };
     
       protected:
    -  Fixed         beforeGrowLimit;/* The ratio by which the advance width of the
    +  HBFixed       beforeGrowLimit;/* The ratio by which the advance width of the
                                      * glyph is permitted to grow on the left or top side. */
    -  Fixed         beforeShrinkLimit;
    +  HBFixed       beforeShrinkLimit;
                                     /* The ratio by which the advance width of the
                                      * glyph is permitted to shrink on the left or top side. */
    -  Fixed         afterGrowLimit; /* The ratio by which the advance width of the glyph
    +  HBFixed       afterGrowLimit; /* The ratio by which the advance width of the glyph
                                      * is permitted to shrink on the left or top side. */
    -  Fixed         afterShrinkLimit;
    +  HBFixed       afterShrinkLimit;
                                     /* The ratio by which the advance width of the glyph
                                      * is at most permitted to shrink on the right or
                                      * bottom side. */
    @@ -371,7 +371,7 @@ struct JustificationHeader
                                      * of postcompensation subtable (set to zero if none).
                                      *
                                      * The postcompensation subtable, if present in the font. */
    -  Lookup >
    +  Lookup>
                     lookupTable;    /* Lookup table associating glyphs with width delta
                                      * clusters. See the description of Width Delta Clusters
                                      * table for details on how to interpret the lookup values. */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh
    index b5519480e23..76e1da06f3e 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh
    @@ -82,8 +82,8 @@ struct KernPair
       }
     
       protected:
    -  GlyphID       left;
    -  GlyphID       right;
    +  HBGlyphID     left;
    +  HBGlyphID     right;
       FWORD         value;
       public:
       DEFINE_SIZE_STATIC (6);
    @@ -229,9 +229,7 @@ struct KerxSubTableFormat1
     
         bool is_actionable (StateTableDriver *driver HB_UNUSED,
                             const Entry &entry)
    -    {
    -      return Format1EntryT::performAction (entry);
    -    }
    +    { return Format1EntryT::performAction (entry); }
         void transition (StateTableDriver *driver,
                          const Entry &entry)
         {
    @@ -251,7 +249,7 @@ struct KerxSubTableFormat1
     
           if (Format1EntryT::performAction (entry) && depth)
           {
    -        unsigned int tuple_count = MAX (1u, table->header.tuple_count ());
    +        unsigned int tuple_count = hb_max (1u, table->header.tuple_count ());
     
             unsigned int kern_idx = Format1EntryT::kernActionIndex (entry);
             kern_idx = Types::byteOffsetToIndex (kern_idx, &table->machine, kernAction.arrayZ);
    @@ -281,35 +279,28 @@ struct KerxSubTableFormat1
     
               hb_glyph_position_t &o = buffer->pos[idx];
     
    -          /* Testing shows that CoreText only applies kern (cross-stream or not)
    -           * if none has been applied by previous subtables.  That is, it does
    -           * NOT seem to accumulate as otherwise implied by specs. */
    -
    -          /* The following flag is undocumented in the spec, but described
    -           * in the 'kern' table example. */
    -          if (v == -0x8000)
    -          {
    -            o.attach_type() = ATTACH_TYPE_NONE;
    -            o.attach_chain() = 0;
    -            o.x_offset = o.y_offset = 0;
    -          }
    -          else if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
    +          if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
               {
                 if (crossStream)
                 {
    -              if (buffer->pos[idx].attach_type() && !buffer->pos[idx].y_offset)
    +              /* The following flag is undocumented in the spec, but described
    +               * in the 'kern' table example. */
    +              if (v == -0x8000)
                   {
    -                o.y_offset = c->font->em_scale_y (v);
    +                o.attach_type() = ATTACH_TYPE_NONE;
    +                o.attach_chain() = 0;
    +                o.y_offset = 0;
    +              }
    +              else if (o.attach_type())
    +              {
    +                o.y_offset += c->font->em_scale_y (v);
                     buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
                   }
                 }
                 else if (buffer->info[idx].mask & kern_mask)
                 {
    -              if (!buffer->pos[idx].x_offset)
    -              {
    -                buffer->pos[idx].x_advance += c->font->em_scale_x (v);
    -                buffer->pos[idx].x_offset += c->font->em_scale_x (v);
    -              }
    +              o.x_advance += c->font->em_scale_x (v);
    +              o.x_offset += c->font->em_scale_x (v);
                 }
               }
               else
    @@ -317,19 +308,22 @@ struct KerxSubTableFormat1
                 if (crossStream)
                 {
                   /* CoreText doesn't do crossStream kerning in vertical.  We do. */
    -              if (buffer->pos[idx].attach_type() && !buffer->pos[idx].x_offset)
    +              if (v == -0x8000)
                   {
    -                o.x_offset = c->font->em_scale_x (v);
    +                o.attach_type() = ATTACH_TYPE_NONE;
    +                o.attach_chain() = 0;
    +                o.x_offset = 0;
    +              }
    +              else if (o.attach_type())
    +              {
    +                o.x_offset += c->font->em_scale_x (v);
                     buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
                   }
                 }
                 else if (buffer->info[idx].mask & kern_mask)
                 {
    -              if (!buffer->pos[idx].y_offset)
    -              {
    -                buffer->pos[idx].y_advance += c->font->em_scale_y (v);
    -                buffer->pos[idx].y_offset += c->font->em_scale_y (v);
    -              }
    +              o.y_advance += c->font->em_scale_y (v);
    +              o.y_offset += c->font->em_scale_y (v);
                 }
               }
             }
    @@ -392,7 +386,7 @@ struct KerxSubTableFormat2
     
         const UnsizedArrayOf &arrayZ = this+array;
         unsigned int kern_idx = l + r;
    -    kern_idx = Types::offsetToIndex (kern_idx, this, &arrayZ);
    +    kern_idx = Types::offsetToIndex (kern_idx, this, arrayZ.arrayZ);
         const FWORD *v = &arrayZ[kern_idx];
         if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
     
    @@ -488,7 +482,7 @@ struct KerxSubTableFormat4
         };
     
         driver_context_t (const KerxSubTableFormat4 *table,
    -                             hb_aat_apply_context_t *c_) :
    +                      hb_aat_apply_context_t *c_) :
             c (c_),
             action_type ((table->flags & ActionType) >> 30),
             ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))),
    @@ -497,9 +491,7 @@ struct KerxSubTableFormat4
     
         bool is_actionable (StateTableDriver *driver HB_UNUSED,
                             const Entry &entry)
    -    {
    -      return entry.data.ankrActionIndex != 0xFFFF;
    -    }
    +    { return entry.data.ankrActionIndex != 0xFFFF; }
         void transition (StateTableDriver *driver,
                          const Entry &entry)
         {
    @@ -512,11 +504,13 @@ struct KerxSubTableFormat4
             {
               case 0: /* Control Point Actions.*/
               {
    -            /* indexed into glyph outline. */
    -            const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex];
    +            /* Indexed into glyph outline. */
    +            /* Each action (record in ankrData) contains two 16-bit fields, so we must
    +               double the ankrActionIndex to get the correct offset here. */
    +            const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2];
                 if (!c->sanitizer.check_array (data, 2)) return;
    -            HB_UNUSED unsigned int markControlPoint = *data++;
    -            HB_UNUSED unsigned int currControlPoint = *data++;
    +            unsigned int markControlPoint = *data++;
    +            unsigned int currControlPoint = *data++;
                 hb_position_t markX = 0;
                 hb_position_t markY = 0;
                 hb_position_t currX = 0;
    @@ -538,8 +532,10 @@ struct KerxSubTableFormat4
     
               case 1: /* Anchor Point Actions. */
               {
    -           /* Indexed into 'ankr' table. */
    -            const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex];
    +            /* Indexed into 'ankr' table. */
    +            /* Each action (record in ankrData) contains two 16-bit fields, so we must
    +               double the ankrActionIndex to get the correct offset here. */
    +            const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2];
                 if (!c->sanitizer.check_array (data, 2)) return;
                 unsigned int markAnchorPoint = *data++;
                 unsigned int currAnchorPoint = *data++;
    @@ -557,7 +553,9 @@ struct KerxSubTableFormat4
     
               case 2: /* Control Point Coordinate Actions. */
               {
    -            const FWORD *data = (const FWORD *) &ankrData[entry.data.ankrActionIndex];
    +            /* Each action contains four 16-bit fields, so we multiply the ankrActionIndex
    +               by 4 to get the correct offset for the given action. */
    +            const FWORD *data = (const FWORD *) &ankrData[entry.data.ankrActionIndex * 4];
                 if (!c->sanitizer.check_array (data, 4)) return;
                 int markX = *data++;
                 int markY = *data++;
    @@ -628,7 +626,7 @@ struct KerxSubTableFormat6
       bool is_long () const { return flags & ValuesAreLong; }
     
       int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
    -                          hb_aat_apply_context_t *c) const
    +                   hb_aat_apply_context_t *c) const
       {
         unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
         if (is_long ())
    @@ -712,18 +710,18 @@ struct KerxSubTableFormat6
       {
         struct Long
         {
    -      LNNOffsetTo >            rowIndexTable;
    -      LNNOffsetTo >            columnIndexTable;
    -      LNNOffsetTo >     array;
    +      LNNOffsetTo>             rowIndexTable;
    +      LNNOffsetTo>             columnIndexTable;
    +      LNNOffsetTo>      array;
         } l;
         struct Short
         {
    -      LNNOffsetTo >            rowIndexTable;
    -      LNNOffsetTo >            columnIndexTable;
    -      LNNOffsetTo >       array;
    +      LNNOffsetTo>             rowIndexTable;
    +      LNNOffsetTo>             columnIndexTable;
    +      LNNOffsetTo>        array;
         } s;
       } u;
    -  LNNOffsetTo >   vector;
    +  LNNOffsetTo>    vector;
       public:
       DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 24);
     };
    @@ -733,8 +731,8 @@ struct KerxSubTableHeader
     {
       typedef ExtendedTypes Types;
     
    -  unsigned int tuple_count () const { return tupleCount; }
    -  bool is_horizontal () const       { return !(coverage & Vertical); }
    +  unsigned   tuple_count () const { return tupleCount; }
    +  bool     is_horizontal () const { return !(coverage & Vertical); }
     
       enum Coverage
       {
    @@ -771,17 +769,17 @@ struct KerxSubTable
       unsigned int get_size () const { return u.header.length; }
       unsigned int get_type () const { return u.header.coverage & u.header.SubtableType; }
     
    -  template 
    -  typename context_t::return_t dispatch (context_t *c) const
    +  template 
    +  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
       {
         unsigned int subtable_type = get_type ();
         TRACE_DISPATCH (this, subtable_type);
         switch (subtable_type) {
    -    case 0:     return_trace (c->dispatch (u.format0));
    -    case 1:     return_trace (c->dispatch (u.format1));
    -    case 2:     return_trace (c->dispatch (u.format2));
    -    case 4:     return_trace (c->dispatch (u.format4));
    -    case 6:     return_trace (c->dispatch (u.format6));
    +    case 0:     return_trace (c->dispatch (u.format0, hb_forward (ds)...));
    +    case 1:     return_trace (c->dispatch (u.format1, hb_forward (ds)...));
    +    case 2:     return_trace (c->dispatch (u.format2, hb_forward (ds)...));
    +    case 4:     return_trace (c->dispatch (u.format4, hb_forward (ds)...));
    +    case 6:     return_trace (c->dispatch (u.format6, hb_forward (ds)...));
         default:    return_trace (c->default_return_value ());
         }
       }
    @@ -891,7 +889,7 @@ struct KerxTable
           reverse = bool (st->u.header.coverage & st->u.header.Backwards) !=
                     HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
     
    -      if (!c->buffer->message (c->font, "start %c%c%c%c subtable %d", HB_UNTAG (thiz()->tableTag), c->lookup_index))
    +      if (!c->buffer->message (c->font, "start subtable %d", c->lookup_index))
             goto skip;
     
           if (!seenCrossStream &&
    @@ -923,7 +921,7 @@ struct KerxTable
           if (reverse)
             c->buffer->reverse ();
     
    -      (void) c->buffer->message (c->font, "end %c%c%c%c subtable %d", HB_UNTAG (thiz()->tableTag), c->lookup_index);
    +      (void) c->buffer->message (c->font, "end subtable %d", c->lookup_index);
     
         skip:
           st = &StructAfter (*st);
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-lcar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-lcar-table.hh
    deleted file mode 100644
    index 58f1ee02fce..00000000000
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-lcar-table.hh
    +++ /dev/null
    @@ -1,93 +0,0 @@
    -/*
    - * Copyright © 2018  Ebrahim Byagowi
    - *
    - *  This is part of HarfBuzz, a text shaping library.
    - *
    - * Permission is hereby granted, without written agreement and without
    - * license or royalty fees, to use, copy, modify, and distribute this
    - * software and its documentation for any purpose, provided that the
    - * above copyright notice and the following two paragraphs appear in
    - * all copies of this software.
    - *
    - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
    - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
    - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
    - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
    - * DAMAGE.
    - *
    - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
    - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    - * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
    - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
    - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
    - */
    -#ifndef HB_AAT_LAYOUT_LCAR_TABLE_HH
    -#define HB_AAT_LAYOUT_LCAR_TABLE_HH
    -
    -#include "hb-open-type.hh"
    -#include "hb-aat-layout-common.hh"
    -
    -/*
    - * lcar -- Ligature caret
    - * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6lcar.html
    - */
    -#define HB_AAT_TAG_lcar HB_TAG('l','c','a','r')
    -
    -
    -namespace AAT {
    -
    -typedef ArrayOf LigCaretClassEntry;
    -
    -struct lcar
    -{
    -  static constexpr hb_tag_t tableTag = HB_AAT_TAG_lcar;
    -
    -  unsigned int get_lig_carets (hb_font_t      *font,
    -                               hb_direction_t  direction,
    -                               hb_codepoint_t  glyph,
    -                               unsigned int    start_offset,
    -                               unsigned int   *caret_count /* IN/OUT */,
    -                               hb_position_t  *caret_array /* OUT */) const
    -  {
    -    const OffsetTo* entry_offset = lookup.get_value (glyph,
    -                                                                         font->face->get_num_glyphs ());
    -    const LigCaretClassEntry& array = entry_offset ? this+*entry_offset : Null (LigCaretClassEntry);
    -    if (caret_count)
    -    {
    -      hb_array_t arr = array.sub_array (start_offset, caret_count);
    -      unsigned int count = arr.length;
    -      for (unsigned int i = 0; i < count; ++i)
    -        switch (format)
    -        {
    -        case 0: caret_array[i] = font->em_scale_dir (arr[i], direction); break;
    -        case 1:
    -          hb_position_t x, y;
    -          font->get_glyph_contour_point_for_origin (glyph, arr[i], direction, &x, &y);
    -          caret_array[i] = HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y;
    -          break;
    -        }
    -    }
    -    return array.len;
    -  }
    -
    -  bool sanitize (hb_sanitize_context_t *c) const
    -  {
    -    TRACE_SANITIZE (this);
    -    return_trace (likely (c->check_struct (this) &&
    -                          version.major == 1 &&
    -                          lookup.sanitize (c, this)));
    -  }
    -
    -  protected:
    -  FixedVersion<>version;        /* Version number of the ligature caret table */
    -  HBUINT16      format;         /* Format of the ligature caret table. */
    -  Lookup >
    -                lookup;         /* data Lookup table associating glyphs */
    -
    -  public:
    -  DEFINE_SIZE_MIN (8);
    -};
    -
    -} /* namespace AAT */
    -
    -#endif /* HB_AAT_LAYOUT_LCAR_TABLE_HH */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh
    index f52d2ab301b..a0d137836b1 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh
    @@ -88,7 +88,7 @@ struct RearrangementSubtable
             start = buffer->idx;
     
           if (flags & MarkLast)
    -        end = MIN (buffer->idx + 1, buffer->len);
    +        end = hb_min (buffer->idx + 1, buffer->len);
     
           if ((flags & Verb) && start < end)
           {
    @@ -117,14 +117,14 @@ struct RearrangementSubtable
             };
     
             unsigned int m = map[flags & Verb];
    -        unsigned int l = MIN (2, m >> 4);
    -        unsigned int r = MIN (2, m & 0x0F);
    +        unsigned int l = hb_min (2u, m >> 4);
    +        unsigned int r = hb_min (2u, m & 0x0F);
             bool reverse_l = 3 == (m >> 4);
             bool reverse_r = 3 == (m & 0x0F);
     
             if (end - start >= l + r)
             {
    -          buffer->merge_clusters (start, MIN (buffer->idx + 1, buffer->len));
    +          buffer->merge_clusters (start, hb_min (buffer->idx + 1, buffer->len));
               buffer->merge_clusters (start, end);
     
               hb_glyph_info_t *info = buffer->info;
    @@ -240,46 +240,46 @@ struct ContextualSubtable
           if (buffer->idx == buffer->len && !mark_set)
             return;
     
    -      const GlyphID *replacement;
    +      const HBGlyphID *replacement;
     
           replacement = nullptr;
           if (Types::extended)
           {
             if (entry.data.markIndex != 0xFFFF)
             {
    -          const Lookup &lookup = subs[entry.data.markIndex];
    +          const Lookup &lookup = subs[entry.data.markIndex];
               replacement = lookup.get_value (buffer->info[mark].codepoint, driver->num_glyphs);
             }
           }
           else
           {
             unsigned int offset = entry.data.markIndex + buffer->info[mark].codepoint;
    -        const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs;
    +        const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs;
             replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)];
             if (!replacement->sanitize (&c->sanitizer) || !*replacement)
               replacement = nullptr;
           }
           if (replacement)
           {
    -        buffer->unsafe_to_break (mark, MIN (buffer->idx + 1, buffer->len));
    +        buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len));
             buffer->info[mark].codepoint = *replacement;
             ret = true;
           }
     
           replacement = nullptr;
    -      unsigned int idx = MIN (buffer->idx, buffer->len - 1);
    +      unsigned int idx = hb_min (buffer->idx, buffer->len - 1);
           if (Types::extended)
           {
             if (entry.data.currentIndex != 0xFFFF)
             {
    -          const Lookup &lookup = subs[entry.data.currentIndex];
    +          const Lookup &lookup = subs[entry.data.currentIndex];
               replacement = lookup.get_value (buffer->info[idx].codepoint, driver->num_glyphs);
             }
           }
           else
           {
             unsigned int offset = entry.data.currentIndex + buffer->info[idx].codepoint;
    -        const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs;
    +        const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs;
             replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)];
             if (!replacement->sanitize (&c->sanitizer) || !*replacement)
               replacement = nullptr;
    @@ -304,7 +304,7 @@ struct ContextualSubtable
         bool mark_set;
         unsigned int mark;
         const ContextualSubtable *table;
    -    const UnsizedOffsetListOf, HBUINT, false> &subs;
    +    const UnsizedOffsetListOf, HBUINT, false> &subs;
       };
     
       bool apply (hb_aat_apply_context_t *c) const
    @@ -337,9 +337,9 @@ struct ContextualSubtable
           const EntryData &data = entries[i].data;
     
           if (data.markIndex != 0xFFFF)
    -        num_lookups = MAX (num_lookups, 1 + data.markIndex);
    +        num_lookups = hb_max (num_lookups, 1 + data.markIndex);
           if (data.currentIndex != 0xFFFF)
    -        num_lookups = MAX (num_lookups, 1 + data.currentIndex);
    +        num_lookups = hb_max (num_lookups, 1 + data.currentIndex);
         }
     
         return_trace (substitutionTables.sanitize (c, this, num_lookups));
    @@ -348,7 +348,7 @@ struct ContextualSubtable
       protected:
       StateTable
                     machine;
    -  NNOffsetTo, HBUINT, false>, HBUINT>
    +  NNOffsetTo, HBUINT, false>, HBUINT>
                     substitutionTables;
       public:
       DEFINE_SIZE_STATIC (20);
    @@ -520,7 +520,7 @@ struct LigatureSubtable
               if (action & (LigActionStore | LigActionLast))
               {
                 ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ);
    -            const GlyphID &ligatureData = ligature[ligature_idx];
    +            const HBGlyphID &ligatureData = ligature[ligature_idx];
                 if (unlikely (!ligatureData.sanitize (&c->sanitizer))) break;
                 hb_codepoint_t lig = ligatureData;
     
    @@ -554,7 +554,7 @@ struct LigatureSubtable
         const LigatureSubtable *table;
         const UnsizedArrayOf &ligAction;
         const UnsizedArrayOf &component;
    -    const UnsizedArrayOf &ligature;
    +    const UnsizedArrayOf &ligature;
         unsigned int match_length;
         unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
       };
    @@ -586,7 +586,7 @@ struct LigatureSubtable
                     ligAction;      /* Offset to the ligature action table. */
       NNOffsetTo, HBUINT>
                     component;      /* Offset to the component table. */
    -  NNOffsetTo, HBUINT>
    +  NNOffsetTo, HBUINT>
                     ligature;       /* Offset to the actual ligature lists. */
       public:
       DEFINE_SIZE_STATIC (28);
    @@ -606,7 +606,7 @@ struct NoncontextualSubtable
         unsigned int count = c->buffer->len;
         for (unsigned int i = 0; i < count; i++)
         {
    -      const GlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
    +      const HBGlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
           if (replacement)
           {
             info[i].codepoint = *replacement;
    @@ -624,7 +624,7 @@ struct NoncontextualSubtable
       }
     
       protected:
    -  Lookup       substitute;
    +  Lookup     substitute;
       public:
       DEFINE_SIZE_MIN (2);
     };
    @@ -725,8 +725,9 @@ struct InsertionSubtable
           if (entry.data.markedInsertIndex != 0xFFFF)
           {
             unsigned int count = (flags & MarkedInsertCount);
    +        if (unlikely ((buffer->max_ops -= count) <= 0)) return;
             unsigned int start = entry.data.markedInsertIndex;
    -        const GlyphID *glyphs = &insertionAction[start];
    +        const HBGlyphID *glyphs = &insertionAction[start];
             if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0;
     
             bool before = flags & MarkedInsertBefore;
    @@ -744,7 +745,7 @@ struct InsertionSubtable
     
             buffer->move_to (end + count);
     
    -        buffer->unsafe_to_break_from_outbuffer (mark, MIN (buffer->idx + 1, buffer->len));
    +        buffer->unsafe_to_break_from_outbuffer (mark, hb_min (buffer->idx + 1, buffer->len));
           }
     
           if (flags & SetMark)
    @@ -753,8 +754,9 @@ struct InsertionSubtable
           if (entry.data.currentInsertIndex != 0xFFFF)
           {
             unsigned int count = (flags & CurrentInsertCount) >> 5;
    +        if (unlikely ((buffer->max_ops -= count) <= 0)) return;
             unsigned int start = entry.data.currentInsertIndex;
    -        const GlyphID *glyphs = &insertionAction[start];
    +        const HBGlyphID *glyphs = &insertionAction[start];
             if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0;
     
             bool before = flags & CurrentInsertBefore;
    @@ -793,7 +795,7 @@ struct InsertionSubtable
         private:
         hb_aat_apply_context_t *c;
         unsigned int mark;
    -    const UnsizedArrayOf &insertionAction;
    +    const UnsizedArrayOf &insertionAction;
       };
     
       bool apply (hb_aat_apply_context_t *c) const
    @@ -819,7 +821,7 @@ struct InsertionSubtable
       protected:
       StateTable
                     machine;
    -  NNOffsetTo, HBUINT>
    +  NNOffsetTo, HBUINT>
                     insertionAction;        /* Byte offset from stateHeader to the start of
                                              * the insertion glyph table. */
       public:
    @@ -883,17 +885,17 @@ struct ChainSubtable
         Insertion           = 5
       };
     
    -  template 
    -  typename context_t::return_t dispatch (context_t *c) const
    +  template 
    +  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
       {
         unsigned int subtable_type = get_type ();
         TRACE_DISPATCH (this, subtable_type);
         switch (subtable_type) {
    -    case Rearrangement:         return_trace (c->dispatch (u.rearrangement));
    -    case Contextual:            return_trace (c->dispatch (u.contextual));
    -    case Ligature:              return_trace (c->dispatch (u.ligature));
    -    case Noncontextual:         return_trace (c->dispatch (u.noncontextual));
    -    case Insertion:             return_trace (c->dispatch (u.insertion));
    +    case Rearrangement:         return_trace (c->dispatch (u.rearrangement, hb_forward (ds)...));
    +    case Contextual:            return_trace (c->dispatch (u.contextual, hb_forward (ds)...));
    +    case Ligature:              return_trace (c->dispatch (u.ligature, hb_forward (ds)...));
    +    case Noncontextual:         return_trace (c->dispatch (u.noncontextual, hb_forward (ds)...));
    +    case Insertion:             return_trace (c->dispatch (u.insertion, hb_forward (ds)...));
         default:                    return_trace (c->default_return_value ());
         }
       }
    @@ -948,8 +950,10 @@ struct Chain
             hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType;
             hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting;
           retry:
    -        const hb_aat_map_builder_t::feature_info_t *info = map->features.bsearch (type);
    -        if (info && info->setting == setting)
    +        // Check whether this type/setting pair was requested in the map, and if so, apply its flags.
    +        // (The search here only looks at the type and setting fields of feature_info_t.)
    +        hb_aat_map_builder_t::feature_info_t info = { type, setting, false, 0 };
    +        if (map->features.bsearch (info))
             {
               flags &= feature.disableFlags;
               flags |= feature.enableFlags;
    @@ -967,9 +971,9 @@ struct Chain
       }
     
       void apply (hb_aat_apply_context_t *c,
    -                     hb_mask_t flags) const
    +              hb_mask_t flags) const
       {
    -    const ChainSubtable *subtable = &StructAfter > (featureZ.as_array (featureCount));
    +    const ChainSubtable *subtable = &StructAfter> (featureZ.as_array (featureCount));
         unsigned int count = subtableCount;
         for (unsigned int i = 0; i < count; i++)
         {
    @@ -1015,7 +1019,7 @@ struct Chain
                     bool (subtable->get_coverage () & ChainSubtable::Backwards) !=
                     HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
     
    -      if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index))
    +      if (!c->buffer->message (c->font, "start chainsubtable %d", c->lookup_index))
             goto skip;
     
           if (reverse)
    @@ -1026,12 +1030,12 @@ struct Chain
           if (reverse)
             c->buffer->reverse ();
     
    -      (void) c->buffer->message (c->font, "end chain subtable %d", c->lookup_index);
    +      (void) c->buffer->message (c->font, "end chainsubtable %d", c->lookup_index);
     
           if (unlikely (!c->buffer->successful)) return;
     
         skip:
    -      subtable = &StructAfter > (*subtable);
    +      subtable = &StructAfter> (*subtable);
           c->set_lookup_index (c->lookup_index + 1);
         }
       }
    @@ -1049,13 +1053,13 @@ struct Chain
         if (!c->check_array (featureZ.arrayZ, featureCount))
           return_trace (false);
     
    -    const ChainSubtable *subtable = &StructAfter > (featureZ.as_array (featureCount));
    +    const ChainSubtable *subtable = &StructAfter> (featureZ.as_array (featureCount));
         unsigned int count = subtableCount;
         for (unsigned int i = 0; i < count; i++)
         {
           if (!subtable->sanitize (c))
             return_trace (false);
    -      subtable = &StructAfter > (*subtable);
    +      subtable = &StructAfter> (*subtable);
         }
     
         return_trace (true);
    @@ -1080,10 +1084,10 @@ struct Chain
      * The 'mort'/'morx' Table
      */
     
    -template 
    +template 
     struct mortmorx
     {
    -  static constexpr hb_tag_t tableTag = HB_AAT_TAG_morx;
    +  static constexpr hb_tag_t tableTag = TAG;
     
       bool has_data () const { return version != 0; }
     
    @@ -1095,7 +1099,7 @@ struct mortmorx
         for (unsigned int i = 0; i < count; i++)
         {
           map->chain_flags.push (chain->compile_flags (mapper));
    -      chain = &StructAfter > (*chain);
    +      chain = &StructAfter> (*chain);
         }
       }
     
    @@ -1109,7 +1113,7 @@ struct mortmorx
         {
           chain->apply (c, c->plan->aat_map.chain_flags[i]);
           if (unlikely (!c->buffer->successful)) return;
    -      chain = &StructAfter > (*chain);
    +      chain = &StructAfter> (*chain);
         }
       }
     
    @@ -1125,7 +1129,7 @@ struct mortmorx
         {
           if (!chain->sanitize (c, version))
             return_trace (false);
    -      chain = &StructAfter > (*chain);
    +      chain = &StructAfter> (*chain);
         }
     
         return_trace (true);
    @@ -1143,14 +1147,8 @@ struct mortmorx
       DEFINE_SIZE_MIN (8);
     };
     
    -struct morx : mortmorx
    -{
    -  static constexpr hb_tag_t tableTag = HB_AAT_TAG_morx;
    -};
    -struct mort : mortmorx
    -{
    -  static constexpr hb_tag_t tableTag = HB_AAT_TAG_mort;
    -};
    +struct morx : mortmorx {};
    +struct mort : mortmorx {};
     
     
     } /* namespace AAT */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh
    new file mode 100644
    index 00000000000..bfd476c77e0
    --- /dev/null
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh
    @@ -0,0 +1,173 @@
    +/*
    + * Copyright © 2019  Ebrahim Byagowi
    + *
    + *  This is part of HarfBuzz, a text shaping library.
    + *
    + * Permission is hereby granted, without written agreement and without
    + * license or royalty fees, to use, copy, modify, and distribute this
    + * software and its documentation for any purpose, provided that the
    + * above copyright notice and the following two paragraphs appear in
    + * all copies of this software.
    + *
    + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
    + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
    + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
    + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
    + * DAMAGE.
    + *
    + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
    + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    + * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
    + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
    + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
    + */
    +
    +#ifndef HB_AAT_LAYOUT_OPBD_TABLE_HH
    +#define HB_AAT_LAYOUT_OPBD_TABLE_HH
    +
    +#include "hb-aat-layout-common.hh"
    +#include "hb-open-type.hh"
    +
    +/*
    + * opbd -- Optical Bounds
    + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html
    + */
    +#define HB_AAT_TAG_opbd HB_TAG('o','p','b','d')
    +
    +
    +namespace AAT {
    +
    +struct OpticalBounds
    +{
    +  bool sanitize (hb_sanitize_context_t *c) const
    +  {
    +    TRACE_SANITIZE (this);
    +    return_trace (likely (c->check_struct (this)));
    +  }
    +
    +  FWORD         leftSide;
    +  FWORD         topSide;
    +  FWORD         rightSide;
    +  FWORD         bottomSide;
    +  public:
    +  DEFINE_SIZE_STATIC (8);
    +};
    +
    +struct opbdFormat0
    +{
    +  bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id,
    +                   hb_glyph_extents_t *extents, const void *base) const
    +  {
    +    const OffsetTo *bounds_offset = lookupTable.get_value (glyph_id, font->face->get_num_glyphs ());
    +    if (!bounds_offset) return false;
    +    const OpticalBounds &bounds = base+*bounds_offset;
    +
    +    if (extents)
    +      *extents = {
    +        font->em_scale_x (bounds.leftSide),
    +        font->em_scale_y (bounds.topSide),
    +        font->em_scale_x (bounds.rightSide),
    +        font->em_scale_y (bounds.bottomSide)
    +      };
    +    return true;
    +  }
    +
    +  bool sanitize (hb_sanitize_context_t *c, const void *base) const
    +  {
    +    TRACE_SANITIZE (this);
    +    return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base)));
    +  }
    +
    +  protected:
    +  Lookup>
    +                lookupTable;    /* Lookup table associating glyphs with the four
    +                                 * int16 values for the left-side, top-side,
    +                                 * right-side, and bottom-side optical bounds. */
    +  public:
    +  DEFINE_SIZE_MIN (2);
    +};
    +
    +struct opbdFormat1
    +{
    +  bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id,
    +                   hb_glyph_extents_t *extents, const void *base) const
    +  {
    +    const OffsetTo *bounds_offset = lookupTable.get_value (glyph_id, font->face->get_num_glyphs ());
    +    if (!bounds_offset) return false;
    +    const OpticalBounds &bounds = base+*bounds_offset;
    +
    +    hb_position_t left = 0, top = 0, right = 0, bottom = 0, ignore;
    +    if (font->get_glyph_contour_point (glyph_id, bounds.leftSide, &left, &ignore) ||
    +        font->get_glyph_contour_point (glyph_id, bounds.topSide, &ignore, &top) ||
    +        font->get_glyph_contour_point (glyph_id, bounds.rightSide, &right, &ignore) ||
    +        font->get_glyph_contour_point (glyph_id, bounds.bottomSide, &ignore, &bottom))
    +    {
    +      if (extents)
    +        *extents = {left, top, right, bottom};
    +      return true;
    +    }
    +    return false;
    +  }
    +
    +  bool sanitize (hb_sanitize_context_t *c, const void *base) const
    +  {
    +    TRACE_SANITIZE (this);
    +    return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base)));
    +  }
    +
    +  protected:
    +  Lookup>
    +                lookupTable;    /* Lookup table associating glyphs with the four
    +                                 * int16 values for the left-side, top-side,
    +                                 * right-side, and bottom-side optical bounds. */
    +  public:
    +  DEFINE_SIZE_MIN (2);
    +};
    +
    +struct opbd
    +{
    +  static constexpr hb_tag_t tableTag = HB_AAT_TAG_opbd;
    +
    +  bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id,
    +                   hb_glyph_extents_t *extents) const
    +  {
    +    switch (format)
    +    {
    +    case 0: return u.format0.get_bounds (font, glyph_id, extents, this);
    +    case 1: return u.format1.get_bounds (font, glyph_id, extents, this);
    +    default:return false;
    +    }
    +  }
    +
    +  bool sanitize (hb_sanitize_context_t *c) const
    +  {
    +    TRACE_SANITIZE (this);
    +    if (unlikely (!c->check_struct (this) || version.major != 1))
    +      return_trace (false);
    +
    +    switch (format)
    +    {
    +    case 0: return_trace (u.format0.sanitize (c, this));
    +    case 1: return_trace (u.format1.sanitize (c, this));
    +    default:return_trace (true);
    +    }
    +  }
    +
    +  protected:
    +  FixedVersion<>version;        /* Version number of the optical bounds
    +                                 * table (0x00010000 for the current version). */
    +  HBUINT16      format;         /* Format of the optical bounds table.
    +                                 * Format 0 indicates distance and Format 1 indicates
    +                                 * control point. */
    +  union {
    +  opbdFormat0   format0;
    +  opbdFormat1   format1;
    +  } u;
    +  public:
    +  DEFINE_SIZE_MIN (8);
    +};
    +
    +} /* namespace AAT */
    +
    +
    +#endif /* HB_AAT_LAYOUT_OPBD_TABLE_HH */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh
    index 469cae5a677..1643e142229 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh
    @@ -62,11 +62,11 @@ struct TrackTableEntry
       }
     
       protected:
    -  Fixed         track;          /* Track value for this record. */
    +  HBFixed       track;          /* Track value for this record. */
       NameID        trackNameID;    /* The 'name' table index for this track.
                                      * (a short word or phrase like "loose"
                                      * or "very tight") */
    -  NNOffsetTo >
    +  NNOffsetTo>
                     valuesZ;        /* Offset from start of tracking table to
                                      * per-size tracking values for this track. */
     
    @@ -82,7 +82,7 @@ struct TrackData
                             const void *base) const
       {
         unsigned int sizes = nSizes;
    -    hb_array_t size_table ((base+sizeTable).arrayZ, sizes);
    +    hb_array_t size_table ((base+sizeTable).arrayZ, sizes);
     
         float s0 = size_table[idx].to_float ();
         float s1 = size_table[idx + 1].to_float ();
    @@ -93,13 +93,6 @@ struct TrackData
     
       int get_tracking (const void *base, float ptem) const
       {
    -    /* CoreText points are CSS pixels (96 per inch),
    -     * NOT typographic points (72 per inch).
    -     *
    -     * https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html
    -     */
    -    float csspx = ptem * 96.f / 72.f;
    -
         /*
          * Choose track.
          */
    @@ -127,14 +120,14 @@ struct TrackData
         if (!sizes) return 0.;
         if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
     
    -    hb_array_t size_table ((base+sizeTable).arrayZ, sizes);
    +    hb_array_t size_table ((base+sizeTable).arrayZ, sizes);
         unsigned int size_index;
         for (size_index = 0; size_index < sizes - 1; size_index++)
    -      if (size_table[size_index].to_float () >= csspx)
    +      if (size_table[size_index].to_float () >= ptem)
             break;
     
    -    return round (interpolate_at (size_index ? size_index - 1 : 0, csspx,
    -                                  *trackTableEntry, base));
    +    return roundf (interpolate_at (size_index ? size_index - 1 : 0, ptem,
    +                                   *trackTableEntry, base));
       }
     
       bool sanitize (hb_sanitize_context_t *c, const void *base) const
    @@ -148,7 +141,7 @@ struct TrackData
       protected:
       HBUINT16      nTracks;        /* Number of separate tracks included in this table. */
       HBUINT16      nSizes;         /* Number of point sizes included in this table. */
    -  LOffsetTo, false>
    +  LNNOffsetTo>
                     sizeTable;      /* Offset from start of the tracking table to
                                      * Array[nSizes] of size values.. */
       UnsizedArrayOf
    @@ -217,7 +210,7 @@ struct trak
     
       protected:
       FixedVersion<>version;        /* Version of the tracking table
    -                                         * (0x00010000u for version 1.0). */
    +                                 * (0x00010000u for version 1.0). */
       HBUINT16      format;         /* Format of the tracking table (set to 0). */
       OffsetTo
                     horizData;      /* Offset from start of tracking table to TrackData
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc
    index a49729dd761..38a5c57519d 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc
    @@ -25,11 +25,9 @@
      * Google Author(s): Behdad Esfahbod
      */
     
    -#include "hb-open-type.hh"
    +#include "hb.hh"
     
    -#include "hb-ot-face.hh"
     #include "hb-aat-layout.hh"
    -#include "hb-aat-fdsc-table.hh" // Just so we compile it; unused otherwise.
     #include "hb-aat-layout-ankr-table.hh"
     #include "hb-aat-layout-bsln-table.hh" // Just so we compile it; unused otherwise.
     #include "hb-aat-layout-feat-table.hh"
    @@ -40,6 +38,41 @@
     #include "hb-aat-ltag-table.hh"
     
     
    +/*
    + * hb_aat_apply_context_t
    + */
    +
    +/* Note: This context is used for kerning, even without AAT, hence the condition. */
    +#if !defined(HB_NO_AAT) || !defined(HB_NO_OT_KERN)
    +
    +AAT::hb_aat_apply_context_t::hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_,
    +                                                     hb_font_t *font_,
    +                                                     hb_buffer_t *buffer_,
    +                                                     hb_blob_t *blob) :
    +                                                       plan (plan_),
    +                                                       font (font_),
    +                                                       face (font->face),
    +                                                       buffer (buffer_),
    +                                                       sanitizer (),
    +                                                       ankr_table (&Null (AAT::ankr)),
    +                                                       lookup_index (0)
    +{
    +  sanitizer.init (blob);
    +  sanitizer.set_num_glyphs (face->get_num_glyphs ());
    +  sanitizer.start_processing ();
    +  sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX);
    +}
    +
    +AAT::hb_aat_apply_context_t::~hb_aat_apply_context_t ()
    +{ sanitizer.end_processing (); }
    +
    +void
    +AAT::hb_aat_apply_context_t::set_ankr_table (const AAT::ankr *ankr_table_)
    +{ ankr_table = ankr_table_; }
    +
    +#endif
    +
    +
     /**
      * SECTION:hb-aat-layout
      * @title: hb-aat-layout
    @@ -50,6 +83,8 @@
      **/
     
     
    +#if !defined(HB_NO_AAT) || defined(HAVE_CORETEXT)
    +
     /* Table data courtesy of Apple.  Converted from mnemonics to integers
      * when moving to this file. */
     static const hb_aat_feature_mapping_t feature_mappings[] =
    @@ -135,44 +170,12 @@ static const hb_aat_feature_mapping_t feature_mappings[] =
     const hb_aat_feature_mapping_t *
     hb_aat_layout_find_feature_mapping (hb_tag_t tag)
     {
    -  return (const hb_aat_feature_mapping_t *) bsearch (&tag,
    -                                                     feature_mappings,
    -                                                     ARRAY_LENGTH (feature_mappings),
    -                                                     sizeof (feature_mappings[0]),
    -                                                     hb_aat_feature_mapping_t::cmp);
    -}
    -
    -
    -/*
    - * hb_aat_apply_context_t
    - */
    -
    -AAT::hb_aat_apply_context_t::hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_,
    -                                                     hb_font_t *font_,
    -                                                     hb_buffer_t *buffer_,
    -                                                     hb_blob_t *blob) :
    -                                                       plan (plan_),
    -                                                       font (font_),
    -                                                       face (font->face),
    -                                                       buffer (buffer_),
    -                                                       sanitizer (),
    -                                                       ankr_table (&Null(AAT::ankr)),
    -                                                       lookup_index (0),
    -                                                       debug_depth (0)
    -{
    -  sanitizer.init (blob);
    -  sanitizer.set_num_glyphs (face->get_num_glyphs ());
    -  sanitizer.start_processing ();
    -  sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX);
    +  return hb_sorted_array (feature_mappings).bsearch (tag);
     }
    +#endif
     
    -AAT::hb_aat_apply_context_t::~hb_aat_apply_context_t ()
    -{ sanitizer.end_processing (); }
    -
    -void
    -AAT::hb_aat_apply_context_t::set_ankr_table (const AAT::ankr *ankr_table_)
    -{ ankr_table = ankr_table_; }
     
    +#ifndef HB_NO_AAT
     
     /*
      * mort/morx/kerx/trak
    @@ -311,14 +314,6 @@ hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
       trak.apply (&c);
     }
     
    -
    -hb_language_t
    -_hb_aat_language_get (hb_face_t *face,
    -                      unsigned int i)
    -{
    -  return face->table.ltag->get_language (i);
    -}
    -
     /**
      * hb_aat_layout_get_feature_types:
      * @face: a face object
    @@ -382,3 +377,6 @@ hb_aat_layout_feature_type_get_selector_infos (hb_face_t
     {
       return face->table.feat->get_selector_infos (feature_type, start_offset, selector_count, selectors, default_index);
     }
    +
    +
    +#endif
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.h b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.h
    index 42540f264e3..977599e6cc4 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.h
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.h
    @@ -85,7 +85,7 @@ typedef enum
       HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE                  = 39,
       HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE             = 103,
     
    -  _HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE= 0x7FFFFFFFu, /*< skip >*/
    +  _HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
     } hb_aat_layout_feature_type_t;
     
     /**
    @@ -424,7 +424,7 @@ typedef enum
       HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_CJK_ROMAN              = 2,
       HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_CJK_ROMAN           = 3,
     
    -  _HB_AAT_LAYOUT_FEATURE_SELECTOR_MAX_VALUE= 0x7FFFFFFFu, /*< skip >*/
    +  _HB_AAT_LAYOUT_FEATURE_SELECTOR_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
     } hb_aat_layout_feature_selector_t;
     
     HB_EXTERN unsigned int
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh
    index 80b6a1d0973..1a95507ccee 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh
    @@ -30,7 +30,7 @@
     #include "hb.hh"
     
     #include "hb-ot-shape.hh"
    -
    +#include "hb-aat-ltag-table.hh"
     
     struct hb_aat_feature_mapping_t
     {
    @@ -39,14 +39,8 @@ struct hb_aat_feature_mapping_t
       hb_aat_layout_feature_selector_t selectorToEnable;
       hb_aat_layout_feature_selector_t selectorToDisable;
     
    -  static int cmp (const void *key_, const void *entry_)
    -  {
    -    hb_tag_t key = * (unsigned int *) key_;
    -    const hb_aat_feature_mapping_t * entry = (const hb_aat_feature_mapping_t *) entry_;
    -    return key < entry->otFeatureTag ? -1 :
    -           key > entry->otFeatureTag ? 1 :
    -           0;
    -  }
    +  int cmp (hb_tag_t key) const
    +  { return key < otFeatureTag ? -1 : key > otFeatureTag ? 1 : 0; }
     };
     
     HB_INTERNAL const hb_aat_feature_mapping_t *
    @@ -77,9 +71,5 @@ hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
                          hb_font_t *font,
                          hb_buffer_t *buffer);
     
    -HB_INTERNAL hb_language_t
    -_hb_aat_language_get (hb_face_t *face,
    -                      unsigned int i);
    -
     
     #endif /* HB_AAT_LAYOUT_HH */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-ltag-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-ltag-table.hh
    index 23649f827ff..f42ca23e0d8 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-ltag-table.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-ltag-table.hh
    @@ -50,7 +50,7 @@ struct FTStringRange
       }
     
       protected:
    -  NNOffsetTo >
    +  NNOffsetTo>
                     tag;            /* Offset from the start of the table to
                                      * the beginning of the string */
       HBUINT16      length;         /* String length (in bytes) */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc
    index c3d078dbd65..ad3eff7935f 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc
    @@ -26,28 +26,55 @@
      * Google Author(s): Behdad Esfahbod
      */
     
    +#include "hb.hh"
    +
    +#ifndef HB_NO_AAT_SHAPE
    +
     #include "hb-aat-map.hh"
     
     #include "hb-aat-layout.hh"
    +#include "hb-aat-layout-feat-table.hh"
     
     
    -void hb_aat_map_builder_t::add_feature (hb_tag_t tag,
    -                                        unsigned int value)
    +void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value)
     {
    +  if (!face->table.feat->has_data ()) return;
    +
       if (tag == HB_TAG ('a','a','l','t'))
       {
    +    if (!face->table.feat->exposes_feature (HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES))
    +      return;
         feature_info_t *info = features.push();
         info->type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES;
         info->setting = (hb_aat_layout_feature_selector_t) value;
    +    info->seq = features.length;
    +    info->is_exclusive = true;
         return;
       }
     
       const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (tag);
       if (!mapping) return;
     
    +  const AAT::FeatureName* feature = &face->table.feat->get_feature (mapping->aatFeatureType);
    +  if (!feature->has_data ())
    +  {
    +    /* Special case: Chain::compile_flags will fall back to the deprecated version of
    +     * small-caps if necessary, so we need to check for that possibility.
    +     * https://github.com/harfbuzz/harfbuzz/issues/2307 */
    +    if (mapping->aatFeatureType == HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE &&
    +        mapping->selectorToEnable == HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS)
    +    {
    +      feature = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE);
    +      if (!feature->has_data ()) return;
    +    }
    +    else return;
    +  }
    +
       feature_info_t *info = features.push();
       info->type = mapping->aatFeatureType;
       info->setting = value ? mapping->selectorToEnable : mapping->selectorToDisable;
    +  info->seq = features.length;
    +  info->is_exclusive = feature->is_exclusive ();
     }
     
     void
    @@ -59,10 +86,17 @@ hb_aat_map_builder_t::compile (hb_aat_map_t  &m)
         features.qsort ();
         unsigned int j = 0;
         for (unsigned int i = 1; i < features.length; i++)
    -      if (features[i].type != features[j].type)
    +      if (features[i].type != features[j].type ||
    +          /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
    +           * respectively, so we mask out the low-order bit when checking for "duplicates"
    +           * (selectors referring to the same feature setting) here. */
    +          (!features[i].is_exclusive && ((features[i].setting & ~1) != (features[j].setting & ~1))))
             features[++j] = features[i];
         features.shrink (j + 1);
       }
     
       hb_aat_layout_compile_map (this, &m);
     }
    +
    +
    +#endif
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.hh
    index 594d48e7cd5..ce30daa6084 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.hh
    @@ -64,19 +64,24 @@ struct hb_aat_map_builder_t
       {
         hb_aat_layout_feature_type_t  type;
         hb_aat_layout_feature_selector_t  setting;
    +    bool is_exclusive;
         unsigned  seq; /* For stable sorting only. */
     
    -    static int cmp (const void *pa, const void *pb)
    +    HB_INTERNAL static int cmp (const void *pa, const void *pb)
         {
           const feature_info_t *a = (const feature_info_t *) pa;
           const feature_info_t *b = (const feature_info_t *) pb;
    -      return (a->type != b->type) ? (a->type < b->type ? -1 : 1) :
    -             (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0);
    +      if (a->type != b->type) return (a->type < b->type ? -1 : 1);
    +      if (!a->is_exclusive &&
    +          (a->setting & ~1) != (b->setting & ~1)) return (a->setting < b->setting ? -1 : 1);
    +            return (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0);
         }
     
    -    int cmp (hb_aat_layout_feature_type_t ty) const
    +    /* compares type & setting only, not is_exclusive flag or seq number */
    +    int cmp (const feature_info_t& f) const
         {
    -      return (type != ty) ? (type < ty ? -1 : 1) : 0;
    +      return (f.type != type) ? (f.type < type ? -1 : 1) :
    +             (f.setting != setting) ? (f.setting < setting ? -1 : 1) : 0;
         }
       };
     
    @@ -84,7 +89,7 @@ struct hb_aat_map_builder_t
       hb_face_t *face;
     
       public:
    -  hb_vector_t features;
    +  hb_sorted_vector_t features;
     };
     
     
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-algs.hh b/src/java.desktop/share/native/libharfbuzz/hb-algs.hh
    new file mode 100644
    index 00000000000..e21cb5429c9
    --- /dev/null
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-algs.hh
    @@ -0,0 +1,1127 @@
    +/*
    + * Copyright © 2017  Google, Inc.
    + * Copyright © 2019  Facebook, Inc.
    + *
    + *  This is part of HarfBuzz, a text shaping library.
    + *
    + * Permission is hereby granted, without written agreement and without
    + * license or royalty fees, to use, copy, modify, and distribute this
    + * software and its documentation for any purpose, provided that the
    + * above copyright notice and the following two paragraphs appear in
    + * all copies of this software.
    + *
    + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
    + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
    + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
    + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
    + * DAMAGE.
    + *
    + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
    + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    + * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
    + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
    + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
    + *
    + * Google Author(s): Behdad Esfahbod
    + * Facebook Author(s): Behdad Esfahbod
    + */
    +
    +#ifndef HB_ALGS_HH
    +#define HB_ALGS_HH
    +
    +#include "hb.hh"
    +#include "hb-meta.hh"
    +#include "hb-null.hh"
    +#include "hb-number.hh"
    +
    +
    +/* Encodes three unsigned integers in one 64-bit number.  If the inputs have more than 21 bits,
    + * values will be truncated / overlap, and might not decode exactly. */
    +#define HB_CODEPOINT_ENCODE3(x,y,z) (((uint64_t) (x) << 42) | ((uint64_t) (y) << 21) | (uint64_t) (z))
    +#define HB_CODEPOINT_DECODE3_1(v) ((hb_codepoint_t) ((v) >> 42))
    +#define HB_CODEPOINT_DECODE3_2(v) ((hb_codepoint_t) ((v) >> 21) & 0x1FFFFFu)
    +#define HB_CODEPOINT_DECODE3_3(v) ((hb_codepoint_t) (v) & 0x1FFFFFu)
    +
    +/* Custom encoding used by hb-ucd. */
    +#define HB_CODEPOINT_ENCODE3_11_7_14(x,y,z) (((uint32_t) ((x) & 0x07FFu) << 21) | (((uint32_t) (y) & 0x007Fu) << 14) | (uint32_t) ((z) & 0x3FFFu))
    +#define HB_CODEPOINT_DECODE3_11_7_14_1(v) ((hb_codepoint_t) ((v) >> 21))
    +#define HB_CODEPOINT_DECODE3_11_7_14_2(v) ((hb_codepoint_t) (((v) >> 14) & 0x007Fu) | 0x0300)
    +#define HB_CODEPOINT_DECODE3_11_7_14_3(v) ((hb_codepoint_t) (v) & 0x3FFFu)
    +
    +struct
    +{
    +  /* Note.  This is dangerous in that if it's passed an rvalue, it returns rvalue-reference. */
    +  template  constexpr auto
    +  operator () (T&& v) const HB_AUTO_RETURN ( hb_forward (v) )
    +}
    +HB_FUNCOBJ (hb_identity);
    +struct
    +{
    +  /* Like identity(), but only retains lvalue-references.  Rvalues are returned as rvalues. */
    +  template  constexpr T&
    +  operator () (T& v) const { return v; }
    +
    +  template  constexpr hb_remove_reference
    +  operator () (T&& v) const { return v; }
    +}
    +HB_FUNCOBJ (hb_lidentity);
    +struct
    +{
    +  /* Like identity(), but always returns rvalue. */
    +  template  constexpr hb_remove_reference
    +  operator () (T&& v) const { return v; }
    +}
    +HB_FUNCOBJ (hb_ridentity);
    +
    +struct
    +{
    +  template  constexpr bool
    +  operator () (T&& v) const { return bool (hb_forward (v)); }
    +}
    +HB_FUNCOBJ (hb_bool);
    +
    +struct
    +{
    +  private:
    +
    +  template  constexpr auto
    +  impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
    +
    +  template  constexpr auto
    +  impl (const T& v, hb_priority<0>) const HB_AUTO_RETURN
    +  (
    +    /* Knuth's multiplicative method: */
    +    (uint32_t) v * 2654435761u
    +  )
    +
    +  public:
    +
    +  template  constexpr auto
    +  operator () (const T& v) const HB_RETURN (uint32_t, impl (v, hb_prioritize))
    +}
    +HB_FUNCOBJ (hb_hash);
    +
    +
    +struct
    +{
    +  private:
    +
    +  /* Pointer-to-member-function. */
    +  template  auto
    +  impl (Appl&& a, hb_priority<2>, T &&v, Ts&&... ds) const HB_AUTO_RETURN
    +  ((hb_deref (hb_forward (v)).*hb_forward (a)) (hb_forward (ds)...))
    +
    +  /* Pointer-to-member. */
    +  template  auto
    +  impl (Appl&& a, hb_priority<1>, T &&v) const HB_AUTO_RETURN
    +  ((hb_deref (hb_forward (v))).*hb_forward (a))
    +
    +  /* Operator(). */
    +  template  auto
    +  impl (Appl&& a, hb_priority<0>, Ts&&... ds) const HB_AUTO_RETURN
    +  (hb_deref (hb_forward (a)) (hb_forward (ds)...))
    +
    +  public:
    +
    +  template  auto
    +  operator () (Appl&& a, Ts&&... ds) const HB_AUTO_RETURN
    +  (
    +    impl (hb_forward (a),
    +          hb_prioritize,
    +          hb_forward (ds)...)
    +  )
    +}
    +HB_FUNCOBJ (hb_invoke);
    +
    +template 
    +struct hb_partial_t
    +{
    +  hb_partial_t (Appl a, V v) : a (a), v (v) {}
    +
    +  static_assert (Pos > 0, "");
    +
    +  template  auto
    +  operator () (Ts&& ...ds) -> decltype (hb_invoke (hb_declval (Appl),
    +                                                   hb_declval (V),
    +                                                   hb_declval (Ts)...))
    +  {
    +    return hb_invoke (hb_forward (a),
    +                      hb_forward (v),
    +                      hb_forward (ds)...);
    +  }
    +  template  auto
    +  operator () (T0&& d0, Ts&& ...ds) -> decltype (hb_invoke (hb_declval (Appl),
    +                                                            hb_declval (T0),
    +                                                            hb_declval (V),
    +                                                            hb_declval (Ts)...))
    +  {
    +    return hb_invoke (hb_forward (a),
    +                      hb_forward (d0),
    +                      hb_forward (v),
    +                      hb_forward (ds)...);
    +  }
    +
    +  private:
    +  hb_reference_wrapper a;
    +  V v;
    +};
    +template 
    +auto hb_partial (Appl&& a, V&& v) HB_AUTO_RETURN
    +(( hb_partial_t (a, v) ))
    +
    +/* The following, HB_PARTIALIZE, macro uses a particular corner-case
    + * of C++11 that is not particularly well-supported by all compilers.
    + * What's happening is that it's using "this" in a trailing return-type
    + * via decltype().  Broken compilers deduce the type of "this" pointer
    + * in that context differently from what it resolves to in the body
    + * of the function.
    + *
    + * One probable cause of this is that at the time of trailing return
    + * type declaration, "this" points to an incomplete type, whereas in
    + * the function body the type is complete.  That doesn't justify the
    + * error in any way, but is probably what's happening.
    + *
    + * In the case of MSVC, we get around this by using C++14 "decltype(auto)"
    + * which deduces the type from the actual return statement.  For gcc 4.8
    + * we use "+this" instead of "this" which produces an rvalue that seems
    + * to be deduced as the same type with this particular compiler, and seem
    + * to be fine as default code path as well.
    + */
    +#ifdef _MSC_VER
    +/* https://github.com/harfbuzz/harfbuzz/issues/1730 */ \
    +#define HB_PARTIALIZE(Pos) \
    +  template  \
    +  decltype(auto) operator () (_T&& _v) const \
    +  { return hb_partial (this, hb_forward<_T> (_v)); } \
    +  static_assert (true, "")
    +#else
    +/* https://github.com/harfbuzz/harfbuzz/issues/1724 */
    +#define HB_PARTIALIZE(Pos) \
    +  template  \
    +  auto operator () (_T&& _v) const HB_AUTO_RETURN \
    +  (hb_partial (+this, hb_forward<_T> (_v))) \
    +  static_assert (true, "")
    +#endif
    +
    +
    +struct
    +{
    +  private:
    +
    +  template  auto
    +  impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN
    +  (hb_deref (hb_forward (p)).has (hb_forward (v)))
    +
    +  template  auto
    +  impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN
    +  (
    +    hb_invoke (hb_forward (p),
    +               hb_forward (v))
    +  )
    +
    +  public:
    +
    +  template  auto
    +  operator () (Pred&& p, Val &&v) const HB_RETURN (bool,
    +    impl (hb_forward (p),
    +          hb_forward (v),
    +          hb_prioritize)
    +  )
    +}
    +HB_FUNCOBJ (hb_has);
    +
    +struct
    +{
    +  private:
    +
    +  template  auto
    +  impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN
    +  (
    +    hb_has (hb_forward (p),
    +            hb_forward (v))
    +  )
    +
    +  template  auto
    +  impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN
    +  (
    +    hb_forward (p) == hb_forward (v)
    +  )
    +
    +  public:
    +
    +  template  auto
    +  operator () (Pred&& p, Val &&v) const HB_RETURN (bool,
    +    impl (hb_forward (p),
    +          hb_forward (v),
    +          hb_prioritize)
    +  )
    +}
    +HB_FUNCOBJ (hb_match);
    +
    +struct
    +{
    +  private:
    +
    +  template  auto
    +  impl (Proj&& f, Val &&v, hb_priority<2>) const HB_AUTO_RETURN
    +  (hb_deref (hb_forward (f)).get (hb_forward (v)))
    +
    +  template  auto
    +  impl (Proj&& f, Val &&v, hb_priority<1>) const HB_AUTO_RETURN
    +  (
    +    hb_invoke (hb_forward (f),
    +               hb_forward (v))
    +  )
    +
    +  template  auto
    +  impl (Proj&& f, Val &&v, hb_priority<0>) const HB_AUTO_RETURN
    +  (
    +    hb_forward (f)[hb_forward (v)]
    +  )
    +
    +  public:
    +
    +  template  auto
    +  operator () (Proj&& f, Val &&v) const HB_AUTO_RETURN
    +  (
    +    impl (hb_forward (f),
    +          hb_forward (v),
    +          hb_prioritize)
    +  )
    +}
    +HB_FUNCOBJ (hb_get);
    +
    +
    +template 
    +struct hb_pair_t
    +{
    +  typedef T1 first_t;
    +  typedef T2 second_t;
    +  typedef hb_pair_t pair_t;
    +
    +  hb_pair_t (T1 a, T2 b) : first (a), second (b) {}
    +
    +  template 
    +  operator hb_pair_t () { return hb_pair_t (first, second); }
    +
    +  hb_pair_t reverse () const
    +  { return hb_pair_t (second, first); }
    +
    +  bool operator == (const pair_t& o) const { return first == o.first && second == o.second; }
    +  bool operator != (const pair_t& o) const { return !(*this == o); }
    +  bool operator < (const pair_t& o) const { return first < o.first || (first == o.first && second < o.second); }
    +  bool operator >= (const pair_t& o) const { return !(*this < o); }
    +  bool operator > (const pair_t& o) const { return first > o.first || (first == o.first && second > o.second); }
    +  bool operator <= (const pair_t& o) const { return !(*this > o); }
    +
    +  T1 first;
    +  T2 second;
    +};
    +#define hb_pair_t(T1,T2) hb_pair_t
    +template  static inline hb_pair_t
    +hb_pair (T1&& a, T2&& b) { return hb_pair_t (a, b); }
    +
    +struct
    +{
    +  template  constexpr typename Pair::first_t
    +  operator () (const Pair& pair) const { return pair.first; }
    +}
    +HB_FUNCOBJ (hb_first);
    +
    +struct
    +{
    +  template  constexpr typename Pair::second_t
    +  operator () (const Pair& pair) const { return pair.second; }
    +}
    +HB_FUNCOBJ (hb_second);
    +
    +/* Note.  In min/max impl, we can use hb_type_identity for second argument.
    + * However, that would silently convert between different-signedness integers.
    + * Instead we accept two different types, such that compiler can err if
    + * comparing integers of different signedness. */
    +struct
    +{
    +  template  constexpr auto
    +  operator () (T&& a, T2&& b) const HB_AUTO_RETURN
    +  (hb_forward (a) <= hb_forward (b) ? hb_forward (a) : hb_forward (b))
    +}
    +HB_FUNCOBJ (hb_min);
    +struct
    +{
    +  template  constexpr auto
    +  operator () (T&& a, T2&& b) const HB_AUTO_RETURN
    +  (hb_forward (a) >= hb_forward (b) ? hb_forward (a) : hb_forward (b))
    +}
    +HB_FUNCOBJ (hb_max);
    +struct
    +{
    +  template  constexpr auto
    +  operator () (T&& x, T2&& min, T3&& max) const HB_AUTO_RETURN
    +  (hb_min (hb_max (hb_forward (x), hb_forward (min)), hb_forward (max)))
    +}
    +HB_FUNCOBJ (hb_clamp);
    +
    +
    +/*
    + * Bithacks.
    + */
    +
    +/* Return the number of 1 bits in v. */
    +template 
    +static inline HB_CONST_FUNC unsigned int
    +hb_popcount (T v)
    +{
    +#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
    +  if (sizeof (T) <= sizeof (unsigned int))
    +    return __builtin_popcount (v);
    +
    +  if (sizeof (T) <= sizeof (unsigned long))
    +    return __builtin_popcountl (v);
    +
    +  if (sizeof (T) <= sizeof (unsigned long long))
    +    return __builtin_popcountll (v);
    +#endif
    +
    +  if (sizeof (T) <= 4)
    +  {
    +    /* "HACKMEM 169" */
    +    uint32_t y;
    +    y = (v >> 1) &033333333333;
    +    y = v - y - ((y >>1) & 033333333333);
    +    return (((y + (y >> 3)) & 030707070707) % 077);
    +  }
    +
    +  if (sizeof (T) == 8)
    +  {
    +    unsigned int shift = 32;
    +    return hb_popcount ((uint32_t) v) + hb_popcount ((uint32_t) (v >> shift));
    +  }
    +
    +  if (sizeof (T) == 16)
    +  {
    +    unsigned int shift = 64;
    +    return hb_popcount ((uint64_t) v) + hb_popcount ((uint64_t) (v >> shift));
    +  }
    +
    +  assert (0);
    +  return 0; /* Shut up stupid compiler. */
    +}
    +
    +/* Returns the number of bits needed to store number */
    +template 
    +static inline HB_CONST_FUNC unsigned int
    +hb_bit_storage (T v)
    +{
    +  if (unlikely (!v)) return 0;
    +
    +#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
    +  if (sizeof (T) <= sizeof (unsigned int))
    +    return sizeof (unsigned int) * 8 - __builtin_clz (v);
    +
    +  if (sizeof (T) <= sizeof (unsigned long))
    +    return sizeof (unsigned long) * 8 - __builtin_clzl (v);
    +
    +  if (sizeof (T) <= sizeof (unsigned long long))
    +    return sizeof (unsigned long long) * 8 - __builtin_clzll (v);
    +#endif
    +
    +#if (defined(_MSC_VER) && _MSC_VER >= 1500) || (defined(__MINGW32__) && (__GNUC__ < 4))
    +  if (sizeof (T) <= sizeof (unsigned int))
    +  {
    +    unsigned long where;
    +    _BitScanReverse (&where, v);
    +    return 1 + where;
    +  }
    +# if defined(_WIN64)
    +  if (sizeof (T) <= 8)
    +  {
    +    unsigned long where;
    +    _BitScanReverse64 (&where, v);
    +    return 1 + where;
    +  }
    +# endif
    +#endif
    +
    +  if (sizeof (T) <= 4)
    +  {
    +    /* "bithacks" */
    +    const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000};
    +    const unsigned int S[] = {1, 2, 4, 8, 16};
    +    unsigned int r = 0;
    +    for (int i = 4; i >= 0; i--)
    +      if (v & b[i])
    +      {
    +        v >>= S[i];
    +        r |= S[i];
    +      }
    +    return r + 1;
    +  }
    +  if (sizeof (T) <= 8)
    +  {
    +    /* "bithacks" */
    +    const uint64_t b[] = {0x2ULL, 0xCULL, 0xF0ULL, 0xFF00ULL, 0xFFFF0000ULL, 0xFFFFFFFF00000000ULL};
    +    const unsigned int S[] = {1, 2, 4, 8, 16, 32};
    +    unsigned int r = 0;
    +    for (int i = 5; i >= 0; i--)
    +      if (v & b[i])
    +      {
    +        v >>= S[i];
    +        r |= S[i];
    +      }
    +    return r + 1;
    +  }
    +  if (sizeof (T) == 16)
    +  {
    +    unsigned int shift = 64;
    +    return (v >> shift) ? hb_bit_storage ((uint64_t) (v >> shift)) + shift :
    +                          hb_bit_storage ((uint64_t) v);
    +  }
    +
    +  assert (0);
    +  return 0; /* Shut up stupid compiler. */
    +}
    +
    +/* Returns the number of zero bits in the least significant side of v */
    +template 
    +static inline HB_CONST_FUNC unsigned int
    +hb_ctz (T v)
    +{
    +  if (unlikely (!v)) return 8 * sizeof (T);
    +
    +#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
    +  if (sizeof (T) <= sizeof (unsigned int))
    +    return __builtin_ctz (v);
    +
    +  if (sizeof (T) <= sizeof (unsigned long))
    +    return __builtin_ctzl (v);
    +
    +  if (sizeof (T) <= sizeof (unsigned long long))
    +    return __builtin_ctzll (v);
    +#endif
    +
    +#if (defined(_MSC_VER) && _MSC_VER >= 1500) || (defined(__MINGW32__) && (__GNUC__ < 4))
    +  if (sizeof (T) <= sizeof (unsigned int))
    +  {
    +    unsigned long where;
    +    _BitScanForward (&where, v);
    +    return where;
    +  }
    +# if defined(_WIN64)
    +  if (sizeof (T) <= 8)
    +  {
    +    unsigned long where;
    +    _BitScanForward64 (&where, v);
    +    return where;
    +  }
    +# endif
    +#endif
    +
    +  if (sizeof (T) <= 4)
    +  {
    +    /* "bithacks" */
    +    unsigned int c = 32;
    +    v &= - (int32_t) v;
    +    if (v) c--;
    +    if (v & 0x0000FFFF) c -= 16;
    +    if (v & 0x00FF00FF) c -= 8;
    +    if (v & 0x0F0F0F0F) c -= 4;
    +    if (v & 0x33333333) c -= 2;
    +    if (v & 0x55555555) c -= 1;
    +    return c;
    +  }
    +  if (sizeof (T) <= 8)
    +  {
    +    /* "bithacks" */
    +    unsigned int c = 64;
    +    v &= - (int64_t) (v);
    +    if (v) c--;
    +    if (v & 0x00000000FFFFFFFFULL) c -= 32;
    +    if (v & 0x0000FFFF0000FFFFULL) c -= 16;
    +    if (v & 0x00FF00FF00FF00FFULL) c -= 8;
    +    if (v & 0x0F0F0F0F0F0F0F0FULL) c -= 4;
    +    if (v & 0x3333333333333333ULL) c -= 2;
    +    if (v & 0x5555555555555555ULL) c -= 1;
    +    return c;
    +  }
    +  if (sizeof (T) == 16)
    +  {
    +    unsigned int shift = 64;
    +    return (uint64_t) v ? hb_bit_storage ((uint64_t) v) :
    +                          hb_bit_storage ((uint64_t) (v >> shift)) + shift;
    +  }
    +
    +  assert (0);
    +  return 0; /* Shut up stupid compiler. */
    +}
    +
    +
    +/*
    + * Tiny stuff.
    + */
    +
    +/* ASCII tag/character handling */
    +static inline bool ISALPHA (unsigned char c)
    +{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
    +static inline bool ISALNUM (unsigned char c)
    +{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); }
    +static inline bool ISSPACE (unsigned char c)
    +{ return c == ' ' || c =='\f'|| c =='\n'|| c =='\r'|| c =='\t'|| c =='\v'; }
    +static inline unsigned char TOUPPER (unsigned char c)
    +{ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; }
    +static inline unsigned char TOLOWER (unsigned char c)
    +{ return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; }
    +static inline bool ISHEX (unsigned char c)
    +{ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); }
    +static inline unsigned char TOHEX (uint8_t c)
    +{ return (c & 0xF) <= 9 ? (c & 0xF) + '0' : (c & 0xF) + 'a' - 10; }
    +static inline uint8_t FROMHEX (unsigned char c)
    +{ return (c >= '0' && c <= '9') ? c - '0' : TOLOWER (c) - 'a' + 10; }
    +
    +static inline unsigned int DIV_CEIL (const unsigned int a, unsigned int b)
    +{ return (a + (b - 1)) / b; }
    +
    +
    +#undef  ARRAY_LENGTH
    +template 
    +static inline unsigned int ARRAY_LENGTH (const Type (&)[n]) { return n; }
    +/* A const version, but does not detect erratically being called on pointers. */
    +#define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0])))
    +
    +
    +static inline int
    +hb_memcmp (const void *a, const void *b, unsigned int len)
    +{
    +  /* It's illegal to pass NULL to memcmp(), even if len is zero.
    +   * So, wrap it.
    +   * https://sourceware.org/bugzilla/show_bug.cgi?id=23878 */
    +  if (unlikely (!len)) return 0;
    +  return memcmp (a, b, len);
    +}
    +
    +static inline void *
    +hb_memset (void *s, int c, unsigned int n)
    +{
    +  /* It's illegal to pass NULL to memset(), even if n is zero. */
    +  if (unlikely (!n)) return 0;
    +  return memset (s, c, n);
    +}
    +
    +static inline unsigned int
    +hb_ceil_to_4 (unsigned int v)
    +{
    +  return ((v - 1) | 3) + 1;
    +}
    +
    +template  static inline bool
    +hb_in_range (T u, T lo, T hi)
    +{
    +  static_assert (!hb_is_signed::value, "");
    +
    +  /* The casts below are important as if T is smaller than int,
    +   * the subtract results will become a signed int! */
    +  return (T)(u - lo) <= (T)(hi - lo);
    +}
    +template  static inline bool
    +hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2)
    +{
    +  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2);
    +}
    +template  static inline bool
    +hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3)
    +{
    +  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3);
    +}
    +
    +
    +/*
    + * Overflow checking.
    + */
    +
    +/* Consider __builtin_mul_overflow use here also */
    +static inline bool
    +hb_unsigned_mul_overflows (unsigned int count, unsigned int size)
    +{
    +  return (size > 0) && (count >= ((unsigned int) -1) / size);
    +}
    +
    +
    +/*
    + * Sort and search.
    + */
    +
    +template 
    +static int
    +_hb_cmp_method (const void *pkey, const void *pval, Ts... ds)
    +{
    +  const K& key = * (const K*) pkey;
    +  const V& val = * (const V*) pval;
    +
    +  return val.cmp (key, ds...);
    +}
    +
    +template 
    +static inline bool
    +hb_bsearch_impl (unsigned *pos, /* Out */
    +                 const K& key,
    +                 V* base, size_t nmemb, size_t stride,
    +                 int (*compar)(const void *_key, const void *_item, Ts... _ds),
    +                 Ts... ds)
    +{
    +  /* This is our *only* bsearch implementation. */
    +
    +  int min = 0, max = (int) nmemb - 1;
    +  while (min <= max)
    +  {
    +    int mid = ((unsigned int) min + (unsigned int) max) / 2;
    +#pragma GCC diagnostic push
    +#pragma GCC diagnostic ignored "-Wcast-align"
    +    V* p = (V*) (((const char *) base) + (mid * stride));
    +#pragma GCC diagnostic pop
    +    int c = compar ((const void *) hb_addressof (key), (const void *) p, ds...);
    +    if (c < 0)
    +      max = mid - 1;
    +    else if (c > 0)
    +      min = mid + 1;
    +    else
    +    {
    +      *pos = mid;
    +      return true;
    +    }
    +  }
    +  *pos = min;
    +  return false;
    +}
    +
    +template 
    +static inline V*
    +hb_bsearch (const K& key, V* base,
    +            size_t nmemb, size_t stride = sizeof (V),
    +            int (*compar)(const void *_key, const void *_item) = _hb_cmp_method)
    +{
    +  unsigned pos;
    +#pragma GCC diagnostic push
    +#pragma GCC diagnostic ignored "-Wcast-align"
    +  return hb_bsearch_impl (&pos, key, base, nmemb, stride, compar) ?
    +         (V*) (((const char *) base) + (pos * stride)) : nullptr;
    +#pragma GCC diagnostic pop
    +}
    +template 
    +static inline V*
    +hb_bsearch (const K& key, V* base,
    +            size_t nmemb, size_t stride,
    +            int (*compar)(const void *_key, const void *_item, Ts... _ds),
    +            Ts... ds)
    +{
    +  unsigned pos;
    +#pragma GCC diagnostic push
    +#pragma GCC diagnostic ignored "-Wcast-align"
    +  return hb_bsearch_impl (&pos, key, base, nmemb, stride, compar, ds...) ?
    +         (V*) (((const char *) base) + (pos * stride)) : nullptr;
    +#pragma GCC diagnostic pop
    +}
    +
    +
    +/* From https://github.com/noporpoise/sort_r
    +   Feb 5, 2019 (c8c65c1e)
    +   Modified to support optional argument using templates */
    +
    +/* Isaac Turner 29 April 2014 Public Domain */
    +
    +/*
    +hb_qsort function to be exported.
    +Parameters:
    +  base is the array to be sorted
    +  nel is the number of elements in the array
    +  width is the size in bytes of each element of the array
    +  compar is the comparison function
    +  arg (optional) is a pointer to be passed to the comparison function
    +
    +void hb_qsort(void *base, size_t nel, size_t width,
    +              int (*compar)(const void *_a, const void *_b, [void *_arg]),
    +              [void *arg]);
    +*/
    +
    +#define SORT_R_SWAP(a,b,tmp) ((tmp) = (a), (a) = (b), (b) = (tmp))
    +
    +/* swap a and b */
    +/* a and b must not be equal! */
    +static inline void sort_r_swap(char *__restrict a, char *__restrict b,
    +                               size_t w)
    +{
    +  char tmp, *end = a+w;
    +  for(; a < end; a++, b++) { SORT_R_SWAP(*a, *b, tmp); }
    +}
    +
    +/* swap a, b iff a>b */
    +/* a and b must not be equal! */
    +/* __restrict is same as restrict but better support on old machines */
    +template 
    +static inline int sort_r_cmpswap(char *__restrict a,
    +                                 char *__restrict b, size_t w,
    +                                 int (*compar)(const void *_a,
    +                                               const void *_b,
    +                                               Ts... _ds),
    +                                 Ts... ds)
    +{
    +  if(compar(a, b, ds...) > 0) {
    +    sort_r_swap(a, b, w);
    +    return 1;
    +  }
    +  return 0;
    +}
    +
    +/*
    +Swap consecutive blocks of bytes of size na and nb starting at memory addr ptr,
    +with the smallest swap so that the blocks are in the opposite order. Blocks may
    +be internally re-ordered e.g.
    +  12345ab  ->   ab34512
    +  123abc   ->   abc123
    +  12abcde  ->   deabc12
    +*/
    +static inline void sort_r_swap_blocks(char *ptr, size_t na, size_t nb)
    +{
    +  if(na > 0 && nb > 0) {
    +    if(na > nb) { sort_r_swap(ptr, ptr+na, nb); }
    +    else { sort_r_swap(ptr, ptr+nb, na); }
    +  }
    +}
    +
    +/* Implement recursive quicksort ourselves */
    +/* Note: quicksort is not stable, equivalent values may be swapped */
    +template 
    +static inline void sort_r_simple(void *base, size_t nel, size_t w,
    +                                 int (*compar)(const void *_a,
    +                                               const void *_b,
    +                                               Ts... _ds),
    +                                 Ts... ds)
    +{
    +  char *b = (char *)base, *end = b + nel*w;
    +
    +  /* for(size_t i=0; i b && sort_r_cmpswap(pj-w,pj,w,compar,ds...); pj -= w) {}
    +    }
    +  }
    +  else
    +  {
    +    /* nel > 9; Quicksort */
    +
    +    int cmp;
    +    char *pl, *ple, *pr, *pre, *pivot;
    +    char *last = b+w*(nel-1), *tmp;
    +
    +    /*
    +    Use median of second, middle and second-last items as pivot.
    +    First and last may have been swapped with pivot and therefore be extreme
    +    */
    +    char *l[3];
    +    l[0] = b + w;
    +    l[1] = b+w*(nel/2);
    +    l[2] = last - w;
    +
    +    /* printf("pivots: %i, %i, %i\n", *(int*)l[0], *(int*)l[1], *(int*)l[2]); */
    +
    +    if(compar(l[0],l[1],ds...) > 0) { SORT_R_SWAP(l[0], l[1], tmp); }
    +    if(compar(l[1],l[2],ds...) > 0) {
    +      SORT_R_SWAP(l[1], l[2], tmp);
    +      if(compar(l[0],l[1],ds...) > 0) { SORT_R_SWAP(l[0], l[1], tmp); }
    +    }
    +
    +    /* swap mid value (l[1]), and last element to put pivot as last element */
    +    if(l[1] != last) { sort_r_swap(l[1], last, w); }
    +
    +    /*
    +    pl is the next item on the left to be compared to the pivot
    +    pr is the last item on the right that was compared to the pivot
    +    ple is the left position to put the next item that equals the pivot
    +    ple is the last right position where we put an item that equals the pivot
    +                                           v- end (beyond the array)
    +      EEEEEELLLLLLLLuuuuuuuuGGGGGGGEEEEEEEE.
    +      ^- b  ^- ple  ^- pl   ^- pr  ^- pre ^- last (where the pivot is)
    +    Pivot comparison key:
    +      E = equal, L = less than, u = unknown, G = greater than, E = equal
    +    */
    +    pivot = last;
    +    ple = pl = b;
    +    pre = pr = last;
    +
    +    /*
    +    Strategy:
    +    Loop into the list from the left and right at the same time to find:
    +    - an item on the left that is greater than the pivot
    +    - an item on the right that is less than the pivot
    +    Once found, they are swapped and the loop continues.
    +    Meanwhile items that are equal to the pivot are moved to the edges of the
    +    array.
    +    */
    +    while(pl < pr) {
    +      /* Move left hand items which are equal to the pivot to the far left.
    +         break when we find an item that is greater than the pivot */
    +      for(; pl < pr; pl += w) {
    +        cmp = compar(pl, pivot, ds...);
    +        if(cmp > 0) { break; }
    +        else if(cmp == 0) {
    +          if(ple < pl) { sort_r_swap(ple, pl, w); }
    +          ple += w;
    +        }
    +      }
    +      /* break if last batch of left hand items were equal to pivot */
    +      if(pl >= pr) { break; }
    +      /* Move right hand items which are equal to the pivot to the far right.
    +         break when we find an item that is less than the pivot */
    +      for(; pl < pr; ) {
    +        pr -= w; /* Move right pointer onto an unprocessed item */
    +        cmp = compar(pr, pivot, ds...);
    +        if(cmp == 0) {
    +          pre -= w;
    +          if(pr < pre) { sort_r_swap(pr, pre, w); }
    +        }
    +        else if(cmp < 0) {
    +          if(pl < pr) { sort_r_swap(pl, pr, w); }
    +          pl += w;
    +          break;
    +        }
    +      }
    +    }
    +
    +    pl = pr; /* pr may have gone below pl */
    +
    +    /*
    +    Now we need to go from: EEELLLGGGGEEEE
    +                        to: LLLEEEEEEEGGGG
    +    Pivot comparison key:
    +      E = equal, L = less than, u = unknown, G = greater than, E = equal
    +    */
    +    sort_r_swap_blocks(b, ple-b, pl-ple);
    +    sort_r_swap_blocks(pr, pre-pr, end-pre);
    +
    +    /*for(size_t i=0; i static inline void
    +hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *), T3 *array2)
    +{
    +  for (unsigned int i = 1; i < len; i++)
    +  {
    +    unsigned int j = i;
    +    while (j && compar (&array[j - 1], &array[i]) > 0)
    +      j--;
    +    if (i == j)
    +      continue;
    +    /* Move item i to occupy place for item j, shift what's in between. */
    +    {
    +      T t = array[i];
    +      memmove (&array[j + 1], &array[j], (i - j) * sizeof (T));
    +      array[j] = t;
    +    }
    +    if (array2)
    +    {
    +      T3 t = array2[i];
    +      memmove (&array2[j + 1], &array2[j], (i - j) * sizeof (T3));
    +      array2[j] = t;
    +    }
    +  }
    +}
    +
    +template  static inline void
    +hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
    +{
    +  hb_stable_sort (array, len, compar, (int *) nullptr);
    +}
    +
    +static inline hb_bool_t
    +hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out)
    +{
    +  unsigned int v;
    +  const char *p = s;
    +  const char *end = p + len;
    +  if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */, base)))
    +    return false;
    +
    +  *out = v;
    +  return true;
    +}
    +
    +
    +/* Operators. */
    +
    +struct hb_bitwise_and
    +{ HB_PARTIALIZE(2);
    +  static constexpr bool passthru_left = false;
    +  static constexpr bool passthru_right = false;
    +  template  constexpr auto
    +  operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & b)
    +}
    +HB_FUNCOBJ (hb_bitwise_and);
    +struct hb_bitwise_or
    +{ HB_PARTIALIZE(2);
    +  static constexpr bool passthru_left = true;
    +  static constexpr bool passthru_right = true;
    +  template  constexpr auto
    +  operator () (const T &a, const T &b) const HB_AUTO_RETURN (a | b)
    +}
    +HB_FUNCOBJ (hb_bitwise_or);
    +struct hb_bitwise_xor
    +{ HB_PARTIALIZE(2);
    +  static constexpr bool passthru_left = true;
    +  static constexpr bool passthru_right = true;
    +  template  constexpr auto
    +  operator () (const T &a, const T &b) const HB_AUTO_RETURN (a ^ b)
    +}
    +HB_FUNCOBJ (hb_bitwise_xor);
    +struct hb_bitwise_sub
    +{ HB_PARTIALIZE(2);
    +  static constexpr bool passthru_left = true;
    +  static constexpr bool passthru_right = false;
    +  template  constexpr auto
    +  operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & ~b)
    +}
    +HB_FUNCOBJ (hb_bitwise_sub);
    +struct
    +{
    +  template  constexpr auto
    +  operator () (const T &a) const HB_AUTO_RETURN (~a)
    +}
    +HB_FUNCOBJ (hb_bitwise_neg);
    +
    +struct
    +{ HB_PARTIALIZE(2);
    +  template  constexpr auto
    +  operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a + b)
    +}
    +HB_FUNCOBJ (hb_add);
    +struct
    +{ HB_PARTIALIZE(2);
    +  template  constexpr auto
    +  operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a - b)
    +}
    +HB_FUNCOBJ (hb_sub);
    +struct
    +{ HB_PARTIALIZE(2);
    +  template  constexpr auto
    +  operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a * b)
    +}
    +HB_FUNCOBJ (hb_mul);
    +struct
    +{ HB_PARTIALIZE(2);
    +  template  constexpr auto
    +  operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a / b)
    +}
    +HB_FUNCOBJ (hb_div);
    +struct
    +{ HB_PARTIALIZE(2);
    +  template  constexpr auto
    +  operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a % b)
    +}
    +HB_FUNCOBJ (hb_mod);
    +struct
    +{
    +  template  constexpr auto
    +  operator () (const T &a) const HB_AUTO_RETURN (+a)
    +}
    +HB_FUNCOBJ (hb_pos);
    +struct
    +{
    +  template  constexpr auto
    +  operator () (const T &a) const HB_AUTO_RETURN (-a)
    +}
    +HB_FUNCOBJ (hb_neg);
    +struct
    +{
    +  template  constexpr auto
    +  operator () (T &a) const HB_AUTO_RETURN (++a)
    +}
    +HB_FUNCOBJ (hb_inc);
    +struct
    +{
    +  template  constexpr auto
    +  operator () (T &a) const HB_AUTO_RETURN (--a)
    +}
    +HB_FUNCOBJ (hb_dec);
    +
    +
    +/* Compiler-assisted vectorization. */
    +
    +/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))),
    + * basically a fixed-size bitset. */
    +template 
    +struct hb_vector_size_t
    +{
    +  elt_t& operator [] (unsigned int i) { return v[i]; }
    +  const elt_t& operator [] (unsigned int i) const { return v[i]; }
    +
    +  void clear (unsigned char v = 0) { memset (this, v, sizeof (*this)); }
    +
    +  template 
    +  hb_vector_size_t process (const Op& op) const
    +  {
    +    hb_vector_size_t r;
    +    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
    +      r.v[i] = op (v[i]);
    +    return r;
    +  }
    +  template 
    +  hb_vector_size_t process (const Op& op, const hb_vector_size_t &o) const
    +  {
    +    hb_vector_size_t r;
    +    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
    +      r.v[i] = op (v[i], o.v[i]);
    +    return r;
    +  }
    +  hb_vector_size_t operator | (const hb_vector_size_t &o) const
    +  { return process (hb_bitwise_or, o); }
    +  hb_vector_size_t operator & (const hb_vector_size_t &o) const
    +  { return process (hb_bitwise_and, o); }
    +  hb_vector_size_t operator ^ (const hb_vector_size_t &o) const
    +  { return process (hb_bitwise_xor, o); }
    +  hb_vector_size_t operator ~ () const
    +  { return process (hb_bitwise_neg); }
    +
    +  private:
    +  static_assert (0 == byte_size % sizeof (elt_t), "");
    +  elt_t v[byte_size / sizeof (elt_t)];
    +};
    +
    +
    +#endif /* HB_ALGS_HH */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-array.hh b/src/java.desktop/share/native/libharfbuzz/hb-array.hh
    index 4034d92903a..c6766e61edd 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-array.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-array.hh
    @@ -28,7 +28,7 @@
     #define HB_ARRAY_HH
     
     #include "hb.hh"
    -#include "hb-dsalgs.hh"
    +#include "hb-algs.hh"
     #include "hb-iter.hh"
     #include "hb-null.hh"
     
    @@ -37,22 +37,31 @@ template 
     struct hb_sorted_array_t;
     
     template 
    -struct hb_array_t :
    -        hb_iter_t, Type>,
    -        hb_iter_mixin_t, Type>
    +struct hb_array_t : hb_iter_with_fallback_t, Type&>
     {
       /*
        * Constructors.
        */
    -  hb_array_t () : arrayZ (nullptr), length (0) {}
    -  hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_) {}
    -  template  hb_array_t (Type (&array_)[length_]) : arrayZ (array_), length (length_) {}
    +  hb_array_t () : arrayZ (nullptr), length (0), backwards_length (0) {}
    +  hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_), backwards_length (0) {}
    +  template 
    +  hb_array_t (Type (&array_)[length_]) : arrayZ (array_), length (length_), backwards_length (0) {}
     
    +  template 
    +  hb_array_t (const hb_array_t &o) :
    +    hb_iter_with_fallback_t (),
    +    arrayZ (o.arrayZ), length (o.length), backwards_length (o.backwards_length) {}
    +  template 
    +  hb_array_t& operator = (const hb_array_t &o)
    +  { arrayZ = o.arrayZ; length = o.length; backwards_length = o.backwards_length; return *this; }
     
       /*
        * Iterator implementation.
        */
    -  typedef Type __item_type__;
    +  typedef Type& __item_t__;
    +  static constexpr bool is_random_access_iterator = true;
       Type& __item_at__ (unsigned i) const
       {
         if (unlikely (i >= length)) return CrapOrNull (Type);
    @@ -63,16 +72,25 @@ struct hb_array_t :
         if (unlikely (n > length))
           n = length;
         length -= n;
    +    backwards_length += n;
         arrayZ += n;
       }
       void __rewind__ (unsigned n)
       {
    -    if (unlikely (n > length))
    -      n = length;
    -    length -= n;
    +    if (unlikely (n > backwards_length))
    +      n = backwards_length;
    +    length += n;
    +    backwards_length -= n;
    +    arrayZ -= n;
       }
       unsigned __len__ () const { return length; }
    -  bool __random_access__ () const { return true; }
    +  /* Ouch. The operator== compares the contents of the array.  For range-based for loops,
    +   * it's best if we can just compare arrayZ, though comparing contents is still fast,
    +   * but also would require that Type has operator==.  As such, we optimize this operator
    +   * for range-based for loop and just compare arrayZ.  No need to compare length, as we
    +   * assume we're only compared to .end(). */
    +  bool operator != (const hb_array_t& o) const
    +  { return arrayZ != o.arrayZ; }
     
       /* Extra operators.
        */
    @@ -80,70 +98,105 @@ struct hb_array_t :
       operator hb_array_t () { return hb_array_t (arrayZ, length); }
       template  operator T * () const { return arrayZ; }
     
    +  HB_INTERNAL bool operator == (const hb_array_t &o) const;
    +
    +  uint32_t hash () const {
    +    uint32_t current = 0;
    +    for (unsigned int i = 0; i < this->length; i++) {
    +      current = current * 31 + hb_hash (this->arrayZ[i]);
    +    }
    +    return current;
    +  }
    +
       /*
        * Compare, Sort, and Search.
        */
     
       /* Note: our compare is NOT lexicographic; it also does NOT call Type::cmp. */
    -  int cmp (const hb_array_t &a) const
    +  int cmp (const hb_array_t &a) const
       {
         if (length != a.length)
           return (int) a.length - (int) length;
         return hb_memcmp (a.arrayZ, arrayZ, get_size ());
       }
    -  static int cmp (const void *pa, const void *pb)
    +  HB_INTERNAL static int cmp (const void *pa, const void *pb)
       {
    -    hb_array_t *a = (hb_array_t *) pa;
    -    hb_array_t *b = (hb_array_t *) pb;
    +    hb_array_t *a = (hb_array_t *) pa;
    +    hb_array_t *b = (hb_array_t *) pb;
         return b->cmp (*a);
       }
     
       template 
       Type *lsearch (const T &x, Type *not_found = nullptr)
       {
    -    unsigned int count = length;
    -    for (unsigned int i = 0; i < count; i++)
    -      if (!this->arrayZ[i].cmp (x))
    -        return &this->arrayZ[i];
    -    return not_found;
    +    unsigned i;
    +    return lfind (x, &i) ? &this->arrayZ[i] : not_found;
       }
       template 
       const Type *lsearch (const T &x, const Type *not_found = nullptr) const
       {
    -    unsigned int count = length;
    -    for (unsigned int i = 0; i < count; i++)
    +    unsigned i;
    +    return lfind (x, &i) ? &this->arrayZ[i] : not_found;
    +  }
    +  template 
    +  bool lfind (const T &x, unsigned *pos = nullptr) const
    +  {
    +    for (unsigned i = 0; i < length; ++i)
           if (!this->arrayZ[i].cmp (x))
    -        return &this->arrayZ[i];
    -    return not_found;
    +      {
    +        if (pos)
    +          *pos = i;
    +        return true;
    +      }
    +
    +    return false;
       }
     
       hb_sorted_array_t qsort (int (*cmp_)(const void*, const void*))
       {
         if (likely (length))
    -      ::qsort (arrayZ, length, this->item_size, cmp_);
    +      hb_qsort (arrayZ, length, this->get_item_size (), cmp_);
         return hb_sorted_array_t (*this);
       }
       hb_sorted_array_t qsort ()
       {
         if (likely (length))
    -      ::qsort (arrayZ, length, this->item_size, Type::cmp);
    +      hb_qsort (arrayZ, length, this->get_item_size (), Type::cmp);
         return hb_sorted_array_t (*this);
       }
       void qsort (unsigned int start, unsigned int end)
       {
    -    end = MIN (end, length);
    +    end = hb_min (end, length);
         assert (start <= end);
         if (likely (start < end))
    -      ::qsort (arrayZ + start, end - start, this->item_size, Type::cmp);
    +      hb_qsort (arrayZ + start, end - start, this->get_item_size (), Type::cmp);
       }
     
       /*
        * Other methods.
        */
     
    -  unsigned int get_size () const { return length * this->item_size; }
    +  unsigned int get_size () const { return length * this->get_item_size (); }
     
    -  hb_array_t sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const
    +  /*
    +   * Reverse the order of items in this array in the range [start, end).
    +   */
    +  void reverse (unsigned start = 0, unsigned end = -1)
    +  {
    +    start = hb_min (start, length);
    +    end = hb_min (end, length);
    +
    +    if (end < start + 2)
    +      return;
    +
    +    for (unsigned lhs = start, rhs = end - 1; lhs < rhs; lhs++, rhs--) {
    +      Type temp = arrayZ[rhs];
    +      arrayZ[rhs] = arrayZ[lhs];
    +      arrayZ[lhs] = temp;
    +    }
    +  }
    +
    +  hb_array_t sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const
       {
         if (!start_offset && !seg_count)
           return *this;
    @@ -154,16 +207,45 @@ struct hb_array_t :
         else
           count -= start_offset;
         if (seg_count)
    -      count = *seg_count = MIN (count, *seg_count);
    -    return hb_array_t (arrayZ + start_offset, count);
    +      count = *seg_count = hb_min (count, *seg_count);
    +    return hb_array_t (arrayZ + start_offset, count);
       }
    -  hb_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
    +  hb_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
       { return sub_array (start_offset, &seg_count); }
     
    +  hb_array_t truncate (unsigned length) const { return sub_array (0, length); }
    +
    +  template 
    +  const T *as () const
    +  { return length < hb_null_size (T) ? &Null (T) : reinterpret_cast (arrayZ); }
    +
    +  template 
    +  bool check_range (const T *p, unsigned int size = T::static_size) const
    +  {
    +    return arrayZ <= ((const char *) p)
    +        && ((const char *) p) <= arrayZ + length
    +        && (unsigned int) (arrayZ + length - (const char *) p) >= size;
    +  }
    +
       /* Only call if you allocated the underlying array using malloc() or similar. */
       void free ()
       { ::free ((void *) arrayZ); arrayZ = nullptr; length = 0; }
     
    +  template 
    +  hb_array_t copy (hb_serialize_context_t *c) const
    +  {
    +    TRACE_SERIALIZE (this);
    +    auto* out = c->start_embed (arrayZ);
    +    if (unlikely (!c->extend_size (out, get_size ()))) return_trace (hb_array_t ());
    +    for (unsigned i = 0; i < length; i++)
    +      out[i] = arrayZ[i]; /* TODO: add version that calls c->copy() */
    +    return_trace (hb_array_t (out, length));
    +  }
    +
       template 
       bool sanitize (hb_sanitize_context_t *c) const
       { return c->check_array (arrayZ, length); }
    @@ -175,6 +257,7 @@ struct hb_array_t :
       public:
       Type *arrayZ;
       unsigned int length;
    +  unsigned int backwards_length;
     };
     template  inline hb_array_t
     hb_array (T *array, unsigned int length)
    @@ -183,7 +266,6 @@ template  inline hb_array_t
     hb_array (T (&array_)[length_])
     { return hb_array_t (array_); }
     
    -
     enum hb_bfind_not_found_t
     {
       HB_BFIND_NOT_FOUND_DONT_STORE,
    @@ -193,20 +275,40 @@ enum hb_bfind_not_found_t
     
     template 
     struct hb_sorted_array_t :
    -        hb_sorted_iter_t, Type>,
    -        hb_array_t,
    -        hb_iter_mixin_t, Type>
    +        hb_iter_t, Type&>,
    +        hb_array_t
     {
    +  typedef hb_iter_t iter_base_t;
    +  HB_ITER_USING (iter_base_t);
    +  static constexpr bool is_random_access_iterator = true;
    +  static constexpr bool is_sorted_iterator = true;
    +
       hb_sorted_array_t () : hb_array_t () {}
    -  hb_sorted_array_t (const hb_array_t &o) : hb_array_t (o) {}
       hb_sorted_array_t (Type *array_, unsigned int length_) : hb_array_t (array_, length_) {}
    -  template  hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t (array_) {}
    +  template 
    +  hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t (array_) {}
    +
    +  template 
    +  hb_sorted_array_t (const hb_array_t &o) :
    +    hb_iter_t (),
    +    hb_array_t (o) {}
    +  template 
    +  hb_sorted_array_t& operator = (const hb_array_t &o)
    +  { hb_array_t (*this) = o; return *this; }
    +
    +  /* Iterator implementation. */
    +  bool operator != (const hb_sorted_array_t& o) const
    +  { return this->arrayZ != o.arrayZ || this->length != o.length; }
     
    -  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const
    -  { return hb_sorted_array_t (((const hb_array_t *) (this))->sub_array (start_offset, seg_count)); }
    -  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
    +  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const
    +  { return hb_sorted_array_t (((const hb_array_t *) (this))->sub_array (start_offset, seg_count)); }
    +  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
       { return sub_array (start_offset, &seg_count); }
     
    +  hb_sorted_array_t truncate (unsigned length) const { return sub_array (0, length); }
    +
       template 
       Type *bsearch (const T &x, Type *not_found = nullptr)
       {
    @@ -221,26 +323,18 @@ struct hb_sorted_array_t :
       }
       template 
       bool bfind (const T &x, unsigned int *i = nullptr,
    -                     hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
    -                     unsigned int to_store = (unsigned int) -1) const
    +              hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
    +              unsigned int to_store = (unsigned int) -1) const
       {
    -    int min = 0, max = (int) this->length - 1;
    -    const Type *array = this->arrayZ;
    -    while (min <= max)
    +    unsigned pos;
    +
    +    if (bsearch_impl (x, &pos))
         {
    -      int mid = ((unsigned int) min + (unsigned int) max) / 2;
    -      int c = array[mid].cmp (x);
    -      if (c < 0)
    -        max = mid - 1;
    -      else if (c > 0)
    -        min = mid + 1;
    -      else
    -      {
    -        if (i)
    -          *i = mid;
    -        return true;
    -      }
    +      if (i)
    +        *i = pos;
    +      return true;
         }
    +
         if (i)
         {
           switch (not_found)
    @@ -253,14 +347,22 @@ struct hb_sorted_array_t :
               break;
     
             case HB_BFIND_NOT_FOUND_STORE_CLOSEST:
    -          if (max < 0 || (max < (int) this->length && array[max].cmp (x) > 0))
    -            max++;
    -          *i = max;
    +          *i = pos;
               break;
           }
         }
         return false;
       }
    +  template 
    +  bool bsearch_impl (const T &x, unsigned *pos) const
    +  {
    +    return hb_bsearch_impl (pos,
    +                            x,
    +                            this->arrayZ,
    +                            this->length,
    +                            sizeof (Type),
    +                            _hb_cmp_method);
    +  }
     };
     template  inline hb_sorted_array_t
     hb_sorted_array (T *array, unsigned int length)
    @@ -269,9 +371,38 @@ template  inline hb_sorted_array_t
     hb_sorted_array (T (&array_)[length_])
     { return hb_sorted_array_t (array_); }
     
    +template 
    +bool hb_array_t::operator == (const hb_array_t &o) const
    +{
    +  if (o.length != this->length) return false;
    +  for (unsigned int i = 0; i < this->length; i++) {
    +    if (this->arrayZ[i] != o.arrayZ[i]) return false;
    +  }
    +  return true;
    +}
    +
    +/* TODO Specialize opeator== for hb_bytes_t and hb_ubytes_t. */
    +
    +template <>
    +inline uint32_t hb_array_t::hash () const {
    +  uint32_t current = 0;
    +  for (unsigned int i = 0; i < this->length; i++)
    +    current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u);
    +  return current;
    +}
    +
    +template <>
    +inline uint32_t hb_array_t::hash () const {
    +  uint32_t current = 0;
    +  for (unsigned int i = 0; i < this->length; i++)
    +    current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u);
    +  return current;
    +}
    +
     
     typedef hb_array_t hb_bytes_t;
     typedef hb_array_t hb_ubytes_t;
     
     
    +
     #endif /* HB_ARRAY_HH */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh b/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh
    index d1ff7a722ef..a6877f8e674 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh
    @@ -33,6 +33,7 @@
     #define HB_ATOMIC_HH
     
     #include "hb.hh"
    +#include "hb-meta.hh"
     
     
     /*
    @@ -85,11 +86,11 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
     #define hb_atomic_int_impl_add(AI, V)           (reinterpret_cast *> (AI)->fetch_add ((V), std::memory_order_acq_rel))
     #define hb_atomic_int_impl_set_relaxed(AI, V)   (reinterpret_cast *> (AI)->store ((V), std::memory_order_relaxed))
     #define hb_atomic_int_impl_set(AI, V)           (reinterpret_cast *> (AI)->store ((V), std::memory_order_release))
    -#define hb_atomic_int_impl_get_relaxed(AI)      (reinterpret_cast *> (AI)->load (std::memory_order_relaxed))
    -#define hb_atomic_int_impl_get(AI)              (reinterpret_cast *> (AI)->load (std::memory_order_acquire))
    +#define hb_atomic_int_impl_get_relaxed(AI)      (reinterpret_cast const *> (AI)->load (std::memory_order_relaxed))
    +#define hb_atomic_int_impl_get(AI)              (reinterpret_cast const *> (AI)->load (std::memory_order_acquire))
     
     #define hb_atomic_ptr_impl_set_relaxed(P, V)    (reinterpret_cast *> (P)->store ((V), std::memory_order_relaxed))
    -#define hb_atomic_ptr_impl_get_relaxed(P)       (reinterpret_cast *> (P)->load (std::memory_order_relaxed))
    +#define hb_atomic_ptr_impl_get_relaxed(P)       (reinterpret_cast const *> (P)->load (std::memory_order_relaxed))
     #define hb_atomic_ptr_impl_get(P)               (reinterpret_cast *> (P)->load (std::memory_order_acquire))
     static inline bool
     _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
    @@ -106,7 +107,7 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
     
     static inline void _hb_memory_barrier ()
     {
    -#if !defined(MemoryBarrier)
    +#if !defined(MemoryBarrier) && !defined(__MINGW32_VERSION)
       /* MinGW has a convoluted history of supporting MemoryBarrier. */
       LONG dummy = 0;
       InterlockedExchange (&dummy, 1);
    @@ -211,25 +212,19 @@ static inline bool _hb_compare_and_swaplp (long *P, long O, long N)
     static_assert ((sizeof (long) == sizeof (void *)), "");
     
     
    -#elif !defined(HB_NO_MT)
    -
    -#define HB_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */
    -
    -#define _hb_memory_barrier()
    +#elif defined(HB_NO_MT)
     
     #define hb_atomic_int_impl_add(AI, V)           ((*(AI) += (V)) - (V))
     
    -#define hb_atomic_ptr_impl_cmpexch(P,O,N)       (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
    -
    -
    -#else /* HB_NO_MT */
    +#define _hb_memory_barrier()                    do {} while (0)
     
    -#define hb_atomic_int_impl_add(AI, V)           ((*(AI) += (V)) - (V))
    +#define hb_atomic_ptr_impl_cmpexch(P,O,N)       (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
     
    -#define _hb_memory_barrier()
     
    -#define hb_atomic_ptr_impl_cmpexch(P,O,N)       (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
    +#else
     
    +#error "Could not find any system to define atomic_int macros."
    +#error "Check hb-atomic.hh for possible resolutions."
     
     #endif
     
    @@ -282,7 +277,7 @@ struct hb_atomic_int_t
     template 
     struct hb_atomic_ptr_t
     {
    -  typedef typename hb_remove_pointer (P) T;
    +  typedef hb_remove_pointer

    T; void init (T* v_ = nullptr) { set_relaxed (v_); } void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh b/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh new file mode 100644 index 00000000000..ff47c6f2476 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh @@ -0,0 +1,166 @@ +/* + * Copyright © 2019 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_BIMAP_HH +#define HB_BIMAP_HH + +#include "hb.hh" +#include "hb-map.hh" + +/* Bi-directional map */ +struct hb_bimap_t +{ + hb_bimap_t () { init (); } + ~hb_bimap_t () { fini (); } + + void init () + { + forw_map.init (); + back_map.init (); + } + + void fini () + { + forw_map.fini (); + back_map.fini (); + } + + void reset () + { + forw_map.reset (); + back_map.reset (); + } + + bool in_error () const { return forw_map.in_error () || back_map.in_error (); } + + void set (hb_codepoint_t lhs, hb_codepoint_t rhs) + { + if (unlikely (lhs == HB_MAP_VALUE_INVALID)) return; + if (unlikely (rhs == HB_MAP_VALUE_INVALID)) { del (lhs); return; } + forw_map.set (lhs, rhs); + back_map.set (rhs, lhs); + } + + hb_codepoint_t get (hb_codepoint_t lhs) const { return forw_map.get (lhs); } + hb_codepoint_t backward (hb_codepoint_t rhs) const { return back_map.get (rhs); } + + hb_codepoint_t operator [] (hb_codepoint_t lhs) const { return get (lhs); } + bool has (hb_codepoint_t lhs, hb_codepoint_t *vp = nullptr) const { return forw_map.has (lhs, vp); } + + void del (hb_codepoint_t lhs) + { + back_map.del (get (lhs)); + forw_map.del (lhs); + } + + void clear () + { + forw_map.clear (); + back_map.clear (); + } + + bool is_empty () const { return get_population () == 0; } + + unsigned int get_population () const { return forw_map.get_population (); } + + protected: + hb_map_t forw_map; + hb_map_t back_map; +}; + +/* Inremental bimap: only lhs is given, rhs is incrementally assigned */ +struct hb_inc_bimap_t : hb_bimap_t +{ + hb_inc_bimap_t () { init (); } + + void init () + { + hb_bimap_t::init (); + next_value = 0; + } + + /* Add a mapping from lhs to rhs with a unique value if lhs is unknown. + * Return the rhs value as the result. + */ + hb_codepoint_t add (hb_codepoint_t lhs) + { + hb_codepoint_t rhs = forw_map[lhs]; + if (rhs == HB_MAP_VALUE_INVALID) + { + rhs = next_value++; + set (lhs, rhs); + } + return rhs; + } + + hb_codepoint_t skip () + { return next_value++; } + + hb_codepoint_t get_next_value () const + { return next_value; } + + void add_set (const hb_set_t *set) + { + hb_codepoint_t i = HB_SET_VALUE_INVALID; + while (hb_set_next (set, &i)) add (i); + } + + /* Create an identity map. */ + bool identity (unsigned int size) + { + clear (); + for (hb_codepoint_t i = 0; i < size; i++) set (i, i); + return !in_error (); + } + + protected: + static int cmp_id (const void* a, const void* b) + { return (int)*(const hb_codepoint_t *)a - (int)*(const hb_codepoint_t *)b; } + + public: + /* Optional: after finished adding all mappings in a random order, + * reassign rhs to lhs so that they are in the same order. */ + void sort () + { + hb_codepoint_t count = get_population (); + hb_vector_t work; + work.resize (count); + + for (hb_codepoint_t rhs = 0; rhs < count; rhs++) + work[rhs] = back_map[rhs]; + + work.qsort (cmp_id); + + clear (); + for (hb_codepoint_t rhs = 0; rhs < count; rhs++) + set (work[rhs], rhs); + } + + protected: + unsigned int next_value; +}; + +#endif /* HB_BIMAP_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-blob.cc b/src/java.desktop/share/native/libharfbuzz/hb-blob.cc index ffeecc2a168..fc18b61c913 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-blob.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-blob.cc @@ -25,18 +25,6 @@ * Red Hat Author(s): Behdad Esfahbod */ - -/* https://github.com/harfbuzz/harfbuzz/issues/1308 - * http://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html - * https://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html - */ -#ifndef _POSIX_C_SOURCE -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-macros" -#define _POSIX_C_SOURCE 200809L -#pragma GCC diagnostic pop -#endif - #include "hb.hh" #include "hb-blob.hh" @@ -48,7 +36,6 @@ #endif /* HAVE_SYS_MMAN_H */ #include -#include #include @@ -155,7 +142,7 @@ hb_blob_create_sub_blob (hb_blob_t *parent, hb_blob_make_immutable (parent); blob = hb_blob_create (parent->data + offset, - MIN (length, parent->length - offset), + hb_min (length, parent->length - offset), HB_MEMORY_MODE_READONLY, hb_blob_reference (parent), _hb_blob_destroy); @@ -202,7 +189,7 @@ hb_blob_copy_writable_or_fail (hb_blob_t *blob) hb_blob_t * hb_blob_get_empty () { - return const_cast (&Null(hb_blob_t)); + return const_cast (&Null (hb_blob_t)); } /** @@ -487,7 +474,11 @@ hb_blob_t::try_make_writable () * Mmap */ +#ifndef HB_NO_OPEN #ifdef HAVE_MMAP +# if !defined(HB_NO_RESOURCE_FORK) && defined(__APPLE__) +# include +# endif # include # include # include @@ -532,6 +523,39 @@ _hb_mapped_file_destroy (void *file_) } #endif +#ifdef _PATH_RSRCFORKSPEC +static int +_open_resource_fork (const char *file_name, hb_mapped_file_t *file) +{ + size_t name_len = strlen (file_name); + size_t len = name_len + sizeof (_PATH_RSRCFORKSPEC); + + char *rsrc_name = (char *) malloc (len); + if (unlikely (!rsrc_name)) return -1; + + strncpy (rsrc_name, file_name, name_len); + strncpy (rsrc_name + name_len, _PATH_RSRCFORKSPEC, + sizeof (_PATH_RSRCFORKSPEC) - 1); + + int fd = open (rsrc_name, O_RDONLY | O_BINARY, 0); + free (rsrc_name); + + if (fd != -1) + { + struct stat st; + if (fstat (fd, &st) != -1) + file->length = (unsigned long) st.st_size; + else + { + close (fd); + fd = -1; + } + } + + return fd; +} +#endif + /** * hb_blob_create_from_file: * @file_name: font filename. @@ -556,6 +580,19 @@ hb_blob_create_from_file (const char *file_name) if (unlikely (fstat (fd, &st) == -1)) goto fail; file->length = (unsigned long) st.st_size; + +#ifdef _PATH_RSRCFORKSPEC + if (unlikely (file->length == 0)) + { + int rfd = _open_resource_fork (file_name, file); + if (rfd != -1) + { + close (fd); + fd = rfd; + } + } +#endif + file->contents = (char *) mmap (nullptr, file->length, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, fd, 0); @@ -579,9 +616,9 @@ hb_blob_create_from_file (const char *file_name) HANDLE fd; unsigned int size = strlen (file_name) + 1; wchar_t * wchar_file_name = (wchar_t *) malloc (sizeof (wchar_t) * size); - if (unlikely (wchar_file_name == nullptr)) goto fail_without_close; + if (unlikely (!wchar_file_name)) goto fail_without_close; mbstowcs (wchar_file_name, file_name, size); -#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) { CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 }; ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); @@ -602,7 +639,7 @@ hb_blob_create_from_file (const char *file_name) if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close; -#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) { LARGE_INTEGER length; GetFileSizeEx (fd, &length); @@ -613,14 +650,14 @@ hb_blob_create_from_file (const char *file_name) file->length = (unsigned long) GetFileSize (fd, nullptr); file->mapping = CreateFileMapping (fd, nullptr, PAGE_READONLY, 0, 0, nullptr); #endif - if (unlikely (file->mapping == nullptr)) goto fail; + if (unlikely (!file->mapping)) goto fail; -#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0); #else file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0); #endif - if (unlikely (file->contents == nullptr)) goto fail; + if (unlikely (!file->contents)) goto fail; CloseHandle (fd); return hb_blob_create (file->contents, file->length, @@ -638,10 +675,10 @@ hb_blob_create_from_file (const char *file_name) It's used as a fallback for systems without mmap or to read from pipes */ unsigned long len = 0, allocated = BUFSIZ * 16; char *data = (char *) malloc (allocated); - if (unlikely (data == nullptr)) return hb_blob_get_empty (); + if (unlikely (!data)) return hb_blob_get_empty (); FILE *fp = fopen (file_name, "rb"); - if (unlikely (fp == nullptr)) goto fread_fail_without_close; + if (unlikely (!fp)) goto fread_fail_without_close; while (!feof (fp)) { @@ -652,7 +689,7 @@ hb_blob_create_from_file (const char *file_name) can cover files like that but lets limit our fallback reader */ if (unlikely (allocated > (2 << 28))) goto fread_fail; char *new_data = (char *) realloc (data, allocated); - if (unlikely (new_data == nullptr)) goto fread_fail; + if (unlikely (!new_data)) goto fread_fail; data = new_data; } @@ -666,6 +703,7 @@ hb_blob_create_from_file (const char *file_name) len += addition; } + fclose (fp); return hb_blob_create (data, len, HB_MEMORY_MODE_WRITABLE, data, (hb_destroy_func_t) free); @@ -676,3 +714,4 @@ hb_blob_create_from_file (const char *file_name) free (data); return hb_blob_get_empty (); } +#endif /* !HB_NO_OPEN */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-blob.h b/src/java.desktop/share/native/libharfbuzz/hb-blob.h index a714fb2057b..ddbcd1a999c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-blob.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-blob.h @@ -71,6 +71,9 @@ hb_blob_create (const char *data, void *user_data, hb_destroy_func_t destroy); +HB_EXTERN hb_blob_t * +hb_blob_create_from_file (const char *file_name); + /* Always creates with MEMORY_MODE_READONLY. * Even if the parent blob is writable, we don't * want the user of the sub-blob to be able to @@ -123,9 +126,6 @@ hb_blob_get_data (hb_blob_t *blob, unsigned int *length); HB_EXTERN char * hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length); -HB_EXTERN hb_blob_t * -hb_blob_create_from_file (const char *file_name); - HB_END_DECLS #endif /* HB_BLOB_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-blob.hh b/src/java.desktop/share/native/libharfbuzz/hb-blob.hh index 4ea13f81370..d85bd823b00 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-blob.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-blob.hh @@ -54,13 +54,9 @@ struct hb_blob_t HB_INTERNAL bool try_make_writable_inplace (); HB_INTERNAL bool try_make_writable_inplace_unix (); + hb_bytes_t as_bytes () const { return hb_bytes_t (data, length); } template - const Type* as () const - { - return length < hb_null_size (Type) ? &Null(Type) : reinterpret_cast (data); - } - hb_bytes_t as_bytes () const - { return hb_bytes_t (data, length); } + const Type* as () const { return as_bytes ().as (); } public: hb_object_header_t header; @@ -81,7 +77,7 @@ struct hb_blob_t template struct hb_blob_ptr_t { - typedef typename hb_remove_pointer (P) T; + typedef hb_remove_pointer

    T; hb_blob_ptr_t (hb_blob_t *b_ = nullptr) : b (b_) {} hb_blob_t * operator = (hb_blob_t *b_) { return b = b_; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc index 14a9a568c7e..52dbb84bb3f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_BUFFER_SERIALIZE + #include "hb-buffer.hh" @@ -85,7 +89,7 @@ hb_buffer_serialize_format_from_string (const char *str, int len) const char * hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format) { - switch (format) + switch ((unsigned) format) { case HB_BUFFER_SERIALIZE_FORMAT_TEXT: return serialize_formats[0]; case HB_BUFFER_SERIALIZE_FORMAT_JSON: return serialize_formats[1]; @@ -138,34 +142,34 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, *p++ = '"'; } else - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster)); } if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) { - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d", + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d", x+pos[i].x_offset, y+pos[i].y_offset)); if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d", + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d", pos[i].x_advance, pos[i].y_advance)); } if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS) { if (info[i].mask & HB_GLYPH_FLAG_DEFINED) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"fl\":%u", info[i].mask & HB_GLYPH_FLAG_DEFINED)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"fl\":%u", info[i].mask & HB_GLYPH_FLAG_DEFINED)); } if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) { hb_glyph_extents_t extents; hb_font_get_glyph_extents(font, info[i].codepoint, &extents); - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", extents.x_bearing, extents.y_bearing)); - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", extents.width, extents.height)); } @@ -224,37 +228,37 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, p += strlen (p); } else - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster)); } if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) { if (x+pos[i].x_offset || y+pos[i].y_offset) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", x+pos[i].x_offset, y+pos[i].y_offset)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", x+pos[i].x_offset, y+pos[i].y_offset)); if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) { *p++ = '+'; - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance)); if (pos[i].y_advance) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance)); } } if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS) { if (info[i].mask & HB_GLYPH_FLAG_DEFINED) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X", info[i].mask &HB_GLYPH_FLAG_DEFINED)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X", info[i].mask &HB_GLYPH_FLAG_DEFINED)); } if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) { hb_glyph_extents_t extents; hb_font_get_glyph_extents(font, info[i].codepoint, &extents); - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height)); } unsigned int l = p - b; @@ -344,8 +348,8 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer, if (buf_size) *buf = '\0'; - assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) || - buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); + assert ((!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)) || + (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS)); if (!buffer->have_positions) flags |= HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS; @@ -375,43 +379,24 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer, } } - -static hb_bool_t -parse_uint (const char *pp, const char *end, uint32_t *pv) +static bool +parse_int (const char *pp, const char *end, int32_t *pv) { - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - pp)); - strncpy (buf, pp, len); - buf[len] = '\0'; - - char *p = buf; - char *pend = p; - uint32_t v; - - errno = 0; - v = strtol (p, &pend, 10); - if (errno || p == pend || pend - p != end - pp) + int v; + const char *p = pp; + if (unlikely (!hb_parse_int (&p, end, &v, true/* whole buffer */))) return false; *pv = v; return true; } -static hb_bool_t -parse_int (const char *pp, const char *end, int32_t *pv) +static bool +parse_uint (const char *pp, const char *end, uint32_t *pv) { - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - pp)); - strncpy (buf, pp, len); - buf[len] = '\0'; - - char *p = buf; - char *pend = p; - int32_t v; - - errno = 0; - v = strtol (p, &pend, 10); - if (errno || p == pend || pend - p != end - pp) + unsigned int v; + const char *p = pp; + if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */))) return false; *pv = v; @@ -449,8 +434,8 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, end_ptr = &end; *end_ptr = buf; - assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) || - buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); + assert ((!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)) || + (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS)); if (buf_len == -1) buf_len = strlen (buf); @@ -484,3 +469,6 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, } } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc index f9a46d12b3c..2da3c486e22 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc @@ -324,7 +324,7 @@ hb_buffer_t::clear_positions () out_len = 0; out_info = info; - memset (pos, 0, sizeof (pos[0]) * len); + hb_memset (pos, 0, sizeof (pos[0]) * len); } void @@ -438,13 +438,6 @@ hb_buffer_t::set_masks (hb_mask_t value, if (!mask) return; - if (cluster_start == 0 && cluster_end == (unsigned int)-1) { - unsigned int count = len; - for (unsigned int i = 0; i < count; i++) - info[i].mask = (info[i].mask & not_mask) | value; - return; - } - unsigned int count = len; for (unsigned int i = 0; i < count; i++) if (cluster_start <= info[i].cluster && info[i].cluster < cluster_end) @@ -455,27 +448,13 @@ void hb_buffer_t::reverse_range (unsigned int start, unsigned int end) { - unsigned int i, j; - if (end - start < 2) return; - for (i = start, j = end - 1; i < j; i++, j--) { - hb_glyph_info_t t; - - t = info[i]; - info[i] = info[j]; - info[j] = t; - } + hb_array_t (info, len).reverse (start, end); if (have_positions) { - for (i = start, j = end - 1; i < j; i++, j--) { - hb_glyph_position_t t; - - t = pos[i]; - pos[i] = pos[j]; - pos[j] = t; - } + hb_array_t (pos, len).reverse (start, end); } } @@ -524,7 +503,7 @@ hb_buffer_t::merge_clusters_impl (unsigned int start, unsigned int cluster = info[start].cluster; for (unsigned int i = start + 1; i < end; i++) - cluster = MIN (cluster, info[i].cluster); + cluster = hb_min (cluster, info[i].cluster); /* Extend end */ while (end < len && info[end - 1].cluster == info[end].cluster) @@ -555,7 +534,7 @@ hb_buffer_t::merge_out_clusters (unsigned int start, unsigned int cluster = out_info[start].cluster; for (unsigned int i = start + 1; i < end; i++) - cluster = MIN (cluster, out_info[i].cluster); + cluster = hb_min (cluster, out_info[i].cluster); /* Extend start */ while (start && out_info[start - 1].cluster == out_info[start].cluster) @@ -612,7 +591,7 @@ hb_buffer_t::delete_glyph () void hb_buffer_t::unsafe_to_break_impl (unsigned int start, unsigned int end) { - unsigned int cluster = (unsigned int) -1; + unsigned int cluster = UINT_MAX; cluster = _unsafe_to_break_find_min_cluster (info, start, end, cluster); _unsafe_to_break_set_mask (info, start, end, cluster); } @@ -628,7 +607,7 @@ hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int en assert (start <= out_len); assert (idx <= end); - unsigned int cluster = (unsigned int) -1; + unsigned int cluster = UINT_MAX; cluster = _unsafe_to_break_find_min_cluster (out_info, start, out_len, cluster); cluster = _unsafe_to_break_find_min_cluster (info, idx, end, cluster); _unsafe_to_break_set_mask (out_info, start, out_len, cluster); @@ -638,8 +617,8 @@ hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int en void hb_buffer_t::guess_segment_properties () { - assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE || - (!len && content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); + assert ((content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) || + (!len && (content_type == HB_BUFFER_CONTENT_TYPE_INVALID))); /* If script is set to INVALID, guess from buffer contents */ if (props.script == HB_SCRIPT_INVALID) { @@ -736,7 +715,7 @@ hb_buffer_create () hb_buffer_t * hb_buffer_get_empty () { - return const_cast (&Null(hb_buffer_t)); + return const_cast (&Null (hb_buffer_t)); } /** @@ -776,8 +755,10 @@ hb_buffer_destroy (hb_buffer_t *buffer) free (buffer->info); free (buffer->pos); +#ifndef HB_NO_BUFFER_MESSAGE if (buffer->message_destroy) buffer->message_destroy (buffer->message_data); +#endif free (buffer); } @@ -956,7 +937,7 @@ hb_buffer_get_direction (hb_buffer_t *buffer) * * You can pass one of the predefined #hb_script_t values, or use * hb_script_from_string() or hb_script_from_iso15924_tag() to get the - * corresponding script from an ISO 15924 script tag. + * corresponding script from an ISO 15924 script tag. * * Since: 0.9.2 **/ @@ -999,7 +980,7 @@ hb_buffer_get_script (hb_buffer_t *buffer) * are orthogonal to the scripts, and though they are related, they are * different concepts and should not be confused with each other. * - * Use hb_language_from_string() to convert from BCP 47 language tags to + * Use hb_language_from_string() to convert from BCP 47 language tags to * #hb_language_t. * * Since: 0.9.2 @@ -1115,8 +1096,8 @@ hb_buffer_get_flags (hb_buffer_t *buffer) * Since: 0.9.42 **/ void -hb_buffer_set_cluster_level (hb_buffer_t *buffer, - hb_buffer_cluster_level_t cluster_level) +hb_buffer_set_cluster_level (hb_buffer_t *buffer, + hb_buffer_cluster_level_t cluster_level) { if (unlikely (hb_object_is_immutable (buffer))) return; @@ -1532,8 +1513,8 @@ hb_buffer_add_utf (hb_buffer_t *buffer, typedef typename utf_t::codepoint_t T; const hb_codepoint_t replacement = buffer->replacement; - assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE || - (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); + assert ((buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) || + (!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID))); if (unlikely (hb_object_is_immutable (buffer))) return; @@ -1736,7 +1717,7 @@ hb_buffer_add_codepoints (hb_buffer_t *buffer, * @buffer: an #hb_buffer_t. * @source: source #hb_buffer_t. * @start: start index into source buffer to copy. Use 0 to copy from start of buffer. - * @end: end index into source buffer to copy. Use (unsigned int) -1 to copy to end of buffer. + * @end: end index into source buffer to copy. Use @HB_FEATURE_GLOBAL_END to copy to end of buffer. * * Append (part of) contents of another buffer to this buffer. * @@ -1853,23 +1834,13 @@ void hb_buffer_normalize_glyphs (hb_buffer_t *buffer) { assert (buffer->have_positions); - assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS || - (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); + assert ((buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS) || + (!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID))); bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); - unsigned int count = buffer->len; - if (unlikely (!count)) return; - hb_glyph_info_t *info = buffer->info; - - unsigned int start = 0; - unsigned int end; - for (end = start + 1; end < count; end++) - if (info[start].cluster != info[end].cluster) { - normalize_glyphs_cluster (buffer, start, end, backward); - start = end; - } - normalize_glyphs_cluster (buffer, start, end, backward); + foreach_cluster (buffer, start, end) + normalize_glyphs_cluster (buffer, start, end, backward); } void @@ -1993,6 +1964,7 @@ hb_buffer_diff (hb_buffer_t *buffer, * Debugging. */ +#ifndef HB_NO_BUFFER_MESSAGE /** * hb_buffer_set_message_func: * @buffer: an #hb_buffer_t. @@ -2022,11 +1994,11 @@ hb_buffer_set_message_func (hb_buffer_t *buffer, buffer->message_destroy = nullptr; } } - bool hb_buffer_t::message_impl (hb_font_t *font, const char *fmt, va_list ap) { char buf[100]; - vsnprintf (buf, sizeof (buf), fmt, ap); + vsnprintf (buf, sizeof (buf), fmt, ap); return (bool) this->message_func (this, font, buf, this->message_data); } +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.h b/src/java.desktop/share/native/libharfbuzz/hb-buffer.h index f5a724cfb43..1a7ca4069e5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.h @@ -284,6 +284,10 @@ hb_buffer_guess_segment_properties (hb_buffer_t *buffer); * space glyph and zeroing the advance width.) * @HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES takes * precedence over this flag. Since: 1.8.0 + * @HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE: + * flag indicating that a dotted circle should + * not be inserted in the rendering of incorrect + * character sequences (such at <0905 093E>). Since: 2.4 * * Since: 0.9.20 */ @@ -292,7 +296,8 @@ typedef enum { /*< flags >*/ HB_BUFFER_FLAG_BOT = 0x00000001u, /* Beginning-of-text */ HB_BUFFER_FLAG_EOT = 0x00000002u, /* End-of-text */ HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES = 0x00000004u, - HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES = 0x00000008u + HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES = 0x00000008u, + HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE = 0x00000010u } hb_buffer_flags_t; HB_EXTERN void diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh index 6416a5328d9..c4ef466b7a7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh @@ -124,9 +124,11 @@ struct hb_buffer_t unsigned int context_len[2]; /* Debugging API */ +#ifndef HB_NO_BUFFER_MESSAGE hb_buffer_message_func_t message_func; void *message_data; hb_destroy_func_t message_destroy; +#endif /* Internal debugging. */ /* The bits here reflect current allocations of the bytes in glyph_info_t's var1 and var2. */ @@ -226,10 +228,10 @@ struct hb_buffer_t /* Makes a copy of the glyph at idx to output and replace glyph_index */ hb_glyph_info_t & output_glyph (hb_codepoint_t glyph_index) { - if (unlikely (!make_room_for (0, 1))) return Crap(hb_glyph_info_t); + if (unlikely (!make_room_for (0, 1))) return Crap (hb_glyph_info_t); if (unlikely (idx == len && !out_len)) - return Crap(hb_glyph_info_t); + return Crap (hb_glyph_info_t); out_info[out_len] = idx < len ? info[idx] : out_info[out_len - 1]; out_info[out_len].codepoint = glyph_index; @@ -316,7 +318,7 @@ struct hb_buffer_t HB_INTERNAL void delete_glyph (); void unsafe_to_break (unsigned int start, - unsigned int end) + unsigned int end) { if (end - start < 2) return; @@ -347,9 +349,19 @@ struct hb_buffer_t HB_INTERNAL void sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *)); - bool messaging () { return unlikely (message_func); } + bool messaging () + { +#ifdef HB_NO_BUFFER_MESSAGE + return false; +#else + return unlikely (message_func); +#endif + } bool message (hb_font_t *font, const char *fmt, ...) HB_PRINTF_FUNC(3, 4) { +#ifdef HB_NO_BUFFER_MESSAGE + return true; +#else if (!messaging ()) return true; va_list ap; @@ -357,6 +369,7 @@ struct hb_buffer_t bool ret = message_impl (font, fmt, ap); va_end (ap); return ret; +#endif } HB_INTERNAL bool message_impl (hb_font_t *font, const char *fmt, va_list ap) HB_PRINTF_FUNC(3, 0); @@ -373,13 +386,13 @@ struct hb_buffer_t inf.cluster = cluster; } - int + unsigned int _unsafe_to_break_find_min_cluster (const hb_glyph_info_t *infos, unsigned int start, unsigned int end, unsigned int cluster) const { for (unsigned int i = start; i < end; i++) - cluster = MIN (cluster, infos[i].cluster); + cluster = hb_min (cluster, infos[i].cluster); return cluster; } void @@ -395,8 +408,7 @@ struct hb_buffer_t } } - void unsafe_to_break_all () - { unsafe_to_break_impl (0, len); } + void unsafe_to_break_all () { unsafe_to_break_impl (0, len); } void safe_to_break_all () { for (unsigned int i = 0; i < len; i++) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh index a013e96dc43..3fcd5575122 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh @@ -220,32 +220,22 @@ struct number_t void init () { set_real (0.0); } void fini () {} - void set_int (int v) { value = (double) v; } - int to_int () const { return (int) value; } + void set_int (int v) { value = v; } + int to_int () const { return value; } void set_fixed (int32_t v) { value = v / 65536.0; } - int32_t to_fixed () const { return (int32_t) (value * 65536.0); } + int32_t to_fixed () const { return value * 65536.0; } - void set_real (double v) { value = v; } + void set_real (double v) { value = v; } double to_real () const { return value; } - int ceil () const { return (int) ::ceil (value); } - int floor () const { return (int) ::floor (value); } - bool in_int_range () const { return ((double) (int16_t) to_int () == value); } - bool operator > (const number_t &n) const - { return value > n.to_real (); } - - bool operator < (const number_t &n) const - { return n > *this; } - - bool operator >= (const number_t &n) const - { return !(*this < n); } - - bool operator <= (const number_t &n) const - { return !(*this > n); } + bool operator > (const number_t &n) const { return value > n.to_real (); } + bool operator < (const number_t &n) const { return n > *this; } + bool operator >= (const number_t &n) const { return !(*this < n); } + bool operator <= (const number_t &n) const { return !(*this > n); } const number_t &operator += (const number_t &n) { @@ -255,37 +245,34 @@ struct number_t } protected: - double value; + double value; }; /* byte string */ struct UnsizedByteStr : UnsizedArrayOf { // encode 2-byte int (Dict/CharString) or 4-byte int (Dict) - template - static bool serialize_int (hb_serialize_context_t *c, op_code_t intOp, int value) + template + static bool serialize_int (hb_serialize_context_t *c, op_code_t intOp, V value) { TRACE_SERIALIZE (this); - if (unlikely ((value < minVal || value > maxVal))) - return_trace (false); - HBUINT8 *p = c->allocate_size (1); - if (unlikely (p == nullptr)) return_trace (false); - p->set (intOp); - - INTTYPE *ip = c->allocate_size (INTTYPE::static_size); - if (unlikely (ip == nullptr)) return_trace (false); - ip->set ((unsigned int)value); + if (unlikely (!p)) return_trace (false); + *p = intOp; - return_trace (true); + T *ip = c->allocate_size (T::static_size); + if (unlikely (!ip)) return_trace (false); + return_trace (c->check_assign (*ip, value)); } - static bool serialize_int4 (hb_serialize_context_t *c, int value) - { return serialize_int (c, OpCode_longintdict, value); } + template + static bool serialize_int4 (hb_serialize_context_t *c, V value) + { return serialize_int (c, OpCode_longintdict, value); } - static bool serialize_int2 (hb_serialize_context_t *c, int value) - { return serialize_int (c, OpCode_shortint, value); } + template + static bool serialize_int2 (hb_serialize_context_t *c, V value) + { return serialize_int (c, OpCode_shortint, value); } /* Defining null_size allows a Null object may be created. Should be safe because: * A descendent struct Dict uses a Null pointer to indicate a missing table, @@ -320,8 +307,7 @@ struct byte_str_t : hb_ubytes_t /* A byte string associated with the current offset and an error condition */ struct byte_str_ref_t { - byte_str_ref_t () - { init (); } + byte_str_ref_t () { init (); } void init () { @@ -343,13 +329,12 @@ struct byte_str_ref_t } const unsigned char& operator [] (int i) { - if (unlikely ((unsigned int)(offset + i) >= str.length)) + if (unlikely ((unsigned int) (offset + i) >= str.length)) { set_error (); - return Null(unsigned char); + return Null (unsigned char); } - else - return str[offset + i]; + return str[offset + i]; } /* Conversion to byte_str_t */ @@ -359,9 +344,7 @@ struct byte_str_ref_t { return str.sub_str (offset_, len_); } bool avail (unsigned int count=1) const - { - return (!in_error () && str.check_limit (offset, count)); - } + { return (!in_error () && str.check_limit (offset, count)); } void inc (unsigned int count=1) { if (likely (!in_error () && (offset <= str.length) && (offset + count <= str.length))) @@ -389,7 +372,7 @@ typedef hb_vector_t byte_str_array_t; /* stack */ template -struct stack_t +struct cff_stack_t { void init () { @@ -400,11 +383,7 @@ struct stack_t for (unsigned int i = 0; i < elements.length; i++) elements[i].init (); } - - void fini () - { - elements.fini_deep (); - } + void fini () { elements.fini_deep (); } ELEM& operator [] (unsigned int i) { @@ -419,7 +398,6 @@ struct stack_t else set_error (); } - ELEM &push () { if (likely (count < elements.length)) @@ -427,7 +405,7 @@ struct stack_t else { set_error (); - return Crap(ELEM); + return Crap (ELEM); } } @@ -438,10 +416,9 @@ struct stack_t else { set_error (); - return Crap(ELEM); + return Crap (ELEM); } } - void pop (unsigned int n) { if (likely (count >= n)) @@ -452,13 +429,12 @@ struct stack_t const ELEM& peek () { - if (likely (count > 0)) - return elements[count-1]; - else + if (unlikely (count < 0)) { set_error (); - return Null(ELEM); + return Null (ELEM); } + return elements[count - 1]; } void unpop () @@ -475,7 +451,7 @@ struct stack_t void set_error () { error = true; } unsigned int get_count () const { return count; } - bool is_empty () const { return count == 0; } + bool is_empty () const { return !count; } static constexpr unsigned kSizeLimit = LIMIT; @@ -487,7 +463,7 @@ struct stack_t /* argument stack */ template -struct arg_stack_t : stack_t +struct arg_stack_t : cff_stack_t { void push_int (int v) { @@ -519,7 +495,7 @@ struct arg_stack_t : stack_t i = 0; S::set_error (); } - return (unsigned)i; + return (unsigned) i; } void push_longint_from_substr (byte_str_ref_t& str_ref) @@ -538,12 +514,10 @@ struct arg_stack_t : stack_t } hb_array_t get_subarray (unsigned int start) const - { - return S::elements.sub_array (start); - } + { return S::elements.sub_array (start); } private: - typedef stack_t S; + typedef cff_stack_t S; }; /* an operator prefixed by its operands in a byte string */ @@ -565,7 +539,7 @@ struct op_serializer_t TRACE_SERIALIZE (this); HBUINT8 *d = c->allocate_size (opstr.str.length); - if (unlikely (d == nullptr)) return_trace (false); + if (unlikely (!d)) return_trace (false); memcpy (d, &opstr.str[0], opstr.str.length); return_trace (true); } @@ -605,7 +579,7 @@ struct parsed_values_t } unsigned get_count () const { return values.length; } - const VAL &get_value (unsigned int i) const { return values[i]; } + const VAL &get_value (unsigned int i) const { return values[i]; } const VAL &operator [] (unsigned int i) const { return get_value (i); } unsigned int opStart; @@ -644,30 +618,19 @@ struct interp_env_t return op; } - const ARG& eval_arg (unsigned int i) - { - return argStack[i]; - } + const ARG& eval_arg (unsigned int i) { return argStack[i]; } - ARG& pop_arg () - { - return argStack.pop (); - } + ARG& pop_arg () { return argStack.pop (); } + void pop_n_args (unsigned int n) { argStack.pop (n); } - void pop_n_args (unsigned int n) - { - argStack.pop (n); - } - - void clear_args () - { - pop_n_args (argStack.get_count ()); - } + void clear_args () { pop_n_args (argStack.get_count ()); } - byte_str_ref_t str_ref; - arg_stack_t argStack; + byte_str_ref_t + str_ref; + arg_stack_t + argStack; protected: - bool error; + bool error; }; typedef interp_env_t<> num_interp_env_t; @@ -691,7 +654,7 @@ struct opset_t case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1: case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3: - env.argStack.push_int ((int16_t)(-(op - OpCode_TwoByteNegInt0) * 256 - env.str_ref[0] - 108)); + env.argStack.push_int ((-(int16_t)(op - OpCode_TwoByteNegInt0) * 256 - env.str_ref[0] - 108)); env.str_ref.inc (); break; @@ -711,8 +674,8 @@ struct opset_t }; template -struct interpreter_t { - +struct interpreter_t +{ ~interpreter_t() { fini (); } void fini () { env.fini (); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-cs-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-cs-common.hh index d6d7f857ecb..1b0d795c7cb 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-cs-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-cs-common.hh @@ -57,14 +57,14 @@ struct call_context_t /* call stack */ const unsigned int kMaxCallLimit = 10; -struct call_stack_t : stack_t {}; +struct call_stack_t : cff_stack_t {}; template struct biased_subrs_t { - void init (const SUBRS &subrs_) + void init (const SUBRS *subrs_) { - subrs = &subrs_; + subrs = subrs_; unsigned int nSubrs = get_count (); if (nSubrs < 1240) bias = 107; @@ -76,13 +76,13 @@ struct biased_subrs_t void fini () {} - unsigned int get_count () const { return (subrs == nullptr)? 0: subrs->count; } - unsigned int get_bias () const { return bias; } + unsigned int get_count () const { return subrs ? subrs->count : 0; } + unsigned int get_bias () const { return bias; } byte_str_t operator [] (unsigned int index) const { - if (unlikely ((subrs == nullptr) || index >= subrs->count)) - return Null(byte_str_t); + if (unlikely (!subrs || index >= subrs->count)) + return Null (byte_str_t); else return (*subrs)[index]; } @@ -118,7 +118,7 @@ struct point_t template struct cs_interp_env_t : interp_env_t { - void init (const byte_str_t &str, const SUBRS &globalSubrs_, const SUBRS &localSubrs_) + void init (const byte_str_t &str, const SUBRS *globalSubrs_, const SUBRS *localSubrs_) { interp_env_t::init (str); @@ -147,8 +147,9 @@ struct cs_interp_env_t : interp_env_t return callStack.in_error () || SUPER::in_error (); } - bool popSubrNum (const biased_subrs_t& biasedSubrs, unsigned int &subr_num) + bool pop_subr_num (const biased_subrs_t& biasedSubrs, unsigned int &subr_num) { + subr_num = 0; int n = SUPER::argStack.pop_int (); n += biasedSubrs.get_bias (); if (unlikely ((n < 0) || ((unsigned int)n >= biasedSubrs.get_count ()))) @@ -158,11 +159,11 @@ struct cs_interp_env_t : interp_env_t return true; } - void callSubr (const biased_subrs_t& biasedSubrs, cs_type_t type) + void call_subr (const biased_subrs_t& biasedSubrs, cs_type_t type) { - unsigned int subr_num; + unsigned int subr_num = 0; - if (unlikely (!popSubrNum (biasedSubrs, subr_num) + if (unlikely (!pop_subr_num (biasedSubrs, subr_num) || callStack.get_count () >= kMaxCallLimit)) { SUPER::set_error (); @@ -175,7 +176,7 @@ struct cs_interp_env_t : interp_env_t SUPER::str_ref = context.str_ref; } - void returnFromSubr () + void return_from_subr () { if (unlikely (SUPER::str_ref.in_error ())) SUPER::set_error (); @@ -246,7 +247,7 @@ struct path_procs_null_t static void flex1 (ENV &env, PARAM& param) {} }; -template > +template > struct cs_opset_t : opset_t { static void process_op (op_code_t op, ENV &env, PARAM& param) @@ -254,7 +255,7 @@ struct cs_opset_t : opset_t switch (op) { case OpCode_return: - env.returnFromSubr (); + env.return_from_subr (); break; case OpCode_endchar: OPSET::check_width (op, env, param); @@ -267,11 +268,11 @@ struct cs_opset_t : opset_t break; case OpCode_callsubr: - env.callSubr (env.localSubrs, CSType_LocalSubr); + env.call_subr (env.localSubrs, CSType_LocalSubr); break; case OpCode_callgsubr: - env.callSubr (env.globalSubrs, CSType_GlobalSubr); + env.call_subr (env.globalSubrs, CSType_GlobalSubr); break; case OpCode_hstem: @@ -550,8 +551,13 @@ struct path_procs_t static void rcurveline (ENV &env, PARAM& param) { + unsigned int arg_count = env.argStack.get_count (); + if (unlikely (arg_count < 8)) + return; + unsigned int i = 0; - for (; i + 6 <= env.argStack.get_count (); i += 6) + unsigned int curve_limit = arg_count - 2; + for (; i + 6 <= curve_limit; i += 6) { point_t pt1 = env.get_pt (); pt1.move (env.eval_arg (i), env.eval_arg (i+1)); @@ -561,34 +567,34 @@ struct path_procs_t pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); PATH::curve (env, param, pt1, pt2, pt3); } - for (; i + 2 <= env.argStack.get_count (); i += 2) - { - point_t pt1 = env.get_pt (); - pt1.move (env.eval_arg (i), env.eval_arg (i+1)); - PATH::line (env, param, pt1); - } + + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + PATH::line (env, param, pt1); } static void rlinecurve (ENV &env, PARAM& param) { + unsigned int arg_count = env.argStack.get_count (); + if (unlikely (arg_count < 8)) + return; + unsigned int i = 0; - unsigned int line_limit = (env.argStack.get_count () % 6); + unsigned int line_limit = arg_count - 6; for (; i + 2 <= line_limit; i += 2) { point_t pt1 = env.get_pt (); pt1.move (env.eval_arg (i), env.eval_arg (i+1)); PATH::line (env, param, pt1); } - for (; i + 6 <= env.argStack.get_count (); i += 6) - { - point_t pt1 = env.get_pt (); - pt1.move (env.eval_arg (i), env.eval_arg (i+1)); - point_t pt2 = pt1; - pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); - point_t pt3 = pt2; - pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); - PATH::curve (env, param, pt1, pt2, pt3); - } + + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); + PATH::curve (env, param, pt1, pt2, pt3); } static void vvcurveto (ENV &env, PARAM& param) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh index 256c96c0462..a0a9429bf9b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh @@ -27,8 +27,6 @@ #define HB_CFF_INTERP_DICT_COMMON_HH #include "hb-cff-interp-common.hh" -#include -#include namespace CFF { @@ -58,19 +56,6 @@ struct top_dict_values_t : dict_values_t } void fini () { dict_values_t::fini (); } - unsigned int calculate_serialized_op_size (const OPSTR& opstr) const - { - switch (opstr.op) - { - case OpCode_CharStrings: - case OpCode_FDArray: - return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op); - - default: - return opstr.str.length; - } - } - unsigned int charStringsOffset; unsigned int FDArrayOffset; }; @@ -94,130 +79,52 @@ struct dict_opset_t : opset_t } } + /* Turns CFF's BCD format into strtod understandable string */ static double parse_bcd (byte_str_ref_t& str_ref) { - bool neg = false; - double int_part = 0; - uint64_t frac_part = 0; - uint32_t frac_count = 0; - bool exp_neg = false; - uint32_t exp_part = 0; - bool exp_overflow = false; - enum Part { INT_PART=0, FRAC_PART, EXP_PART } part = INT_PART; + if (unlikely (str_ref.in_error ())) return .0; + enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END }; - const uint64_t MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 1^52-1 */ - const uint32_t MAX_EXP = 0x7FFu; /* 1^11-1 */ - double value = 0.0; + char buf[32]; unsigned char byte = 0; - for (uint32_t i = 0;; i++) + for (unsigned i = 0, count = 0; count < ARRAY_LENGTH (buf); ++i, ++count) { - char d; - if ((i & 1) == 0) + unsigned nibble; + if (!(i & 1)) { - if (!str_ref.avail ()) - { - str_ref.set_error (); - return 0.0; - } + if (unlikely (!str_ref.avail ())) break; + byte = str_ref[0]; str_ref.inc (); - d = byte >> 4; + nibble = byte >> 4; } else - d = byte & 0x0F; + nibble = byte & 0x0F; - switch (d) + if (unlikely (nibble == RESERVED)) break; + else if (nibble == END) { - case RESERVED: - str_ref.set_error (); - return value; - - case END: - value = (double)(neg? -int_part: int_part); - if (frac_count > 0) - { - double frac = (frac_part / pow (10.0, (double)frac_count)); - if (neg) frac = -frac; - value += frac; - } - if (unlikely (exp_overflow)) - { - if (value == 0.0) - return value; - if (exp_neg) - return neg? -DBL_MIN: DBL_MIN; - else - return neg? -DBL_MAX: DBL_MAX; - } - if (exp_part != 0) - { - if (exp_neg) - value /= pow (10.0, (double)exp_part); - else - value *= pow (10.0, (double)exp_part); - } - return value; - - case NEG: - if (i != 0) - { - str_ref.set_error (); - return 0.0; - } - neg = true; - break; - - case DECIMAL: - if (part != INT_PART) - { - str_ref.set_error (); - return value; - } - part = FRAC_PART; + const char *p = buf; + double pv; + if (unlikely (!hb_parse_double (&p, p + count, &pv, true/* whole buffer */))) break; - - case EXP_NEG: - exp_neg = true; - HB_FALLTHROUGH; - - case EXP_POS: - if (part == EXP_PART) - { - str_ref.set_error (); - return value; - } - part = EXP_PART; - break; - - default: - switch (part) { - default: - case INT_PART: - int_part = (int_part * 10) + d; - break; - - case FRAC_PART: - if (likely (frac_part <= MAX_FRACT / 10)) - { - frac_part = (frac_part * 10) + (unsigned)d; - frac_count++; - } - break; - - case EXP_PART: - if (likely (exp_part * 10 + d <= MAX_EXP)) - { - exp_part = (exp_part * 10) + d; - } - else - exp_overflow = true; - break; - } + return pv; + } + else + { + buf[count] = "0123456789.EE?-?"[nibble]; + if (nibble == EXP_NEG) + { + ++count; + if (unlikely (count == ARRAY_LENGTH (buf))) break; + buf[count] = '-'; + } } } - return value; + str_ref.set_error (); + return .0; } static bool is_hint_op (op_code_t op) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff1-interp-cs.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff1-interp-cs.hh index a8208a3d196..96718f4a698 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff1-interp-cs.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff1-interp-cs.hh @@ -40,7 +40,7 @@ struct cff1_cs_interp_env_t : cs_interp_env_t template void init (const byte_str_t &str, ACC &acc, unsigned int fd) { - SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs); + SUPER::init (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs); processed_width = false; has_width = false; arg_start = 0; @@ -81,7 +81,7 @@ struct cff1_cs_interp_env_t : cs_interp_env_t typedef cs_interp_env_t SUPER; }; -template > +template > struct cff1_cs_opset_t : cs_opset_t { /* PostScript-originated legacy opcodes (OpCode_add etc) are unsupported */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh index 6971c2eec55..f1de1e32981 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh @@ -52,7 +52,7 @@ struct blend_arg_t : number_t void set_real (double v) { reset_blends (); number_t::set_real (v); } void set_blends (unsigned int numValues_, unsigned int valueIndex_, - unsigned int numBlends, hb_array_t blends_) + unsigned int numBlends, hb_array_t blends_) { numValues = numValues_; valueIndex = valueIndex_; @@ -80,9 +80,9 @@ struct cff2_cs_interp_env_t : cs_interp_env_t { template void init (const byte_str_t &str, ACC &acc, unsigned int fd, - const int *coords_=nullptr, unsigned int num_coords_=0) + const int *coords_=nullptr, unsigned int num_coords_=0) { - SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs); + SUPER::init (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs); coords = coords_; num_coords = num_coords_; @@ -90,7 +90,7 @@ struct cff2_cs_interp_env_t : cs_interp_env_t seen_blend = false; seen_vsindex_ = false; scalars.init (); - do_blend = (coords != nullptr) && num_coords && (varStore != &Null(CFF2VariationStore)); + do_blend = num_coords && coords && varStore->size; set_ivs (acc.privateDicts[fd].ivs); } @@ -133,10 +133,11 @@ struct cff2_cs_interp_env_t : cs_interp_env_t region_count = varStore->varStore.get_region_index_count (get_ivs ()); if (do_blend) { - scalars.resize (region_count); - varStore->varStore.get_scalars (get_ivs (), - (int *)coords, num_coords, - &scalars[0], region_count); + if (unlikely (!scalars.resize (region_count))) + set_error (); + else + varStore->varStore.get_scalars (get_ivs (), coords, num_coords, + &scalars[0], region_count); } seen_blend = true; } @@ -193,7 +194,7 @@ struct cff2_cs_interp_env_t : cs_interp_env_t typedef cs_interp_env_t SUPER; }; -template > +template > struct cff2_cs_opset_t : cs_opset_t { static void process_op (op_code_t op, cff2_cs_interp_env_t &env, PARAM& param) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-common.cc b/src/java.desktop/share/native/libharfbuzz/hb-common.cc index 890697cee1b..4dd2479a0e6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-common.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-common.cc @@ -27,14 +27,13 @@ */ #include "hb.hh" - #include "hb-machinery.hh" #include -#ifdef HAVE_XLOCALE_H -#include -#endif +#ifdef HB_NO_SETLOCALE +#define setlocale(Category, Locale) "C" +#endif /** * SECTION:hb-common @@ -67,10 +66,9 @@ _hb_options_init () p = c + strlen (c); #define OPTION(name, symbol) \ - if (0 == strncmp (c, name, p - c) && strlen (name) == p - c) u.opts.symbol = true; + if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast(p - c)) do { u.opts.symbol = true; } while (0) OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible); - OPTION ("aat", aat); #undef OPTION @@ -334,14 +332,14 @@ lang_find_or_insert (const char *key) /** * hb_language_from_string: * @str: (array length=len) (element-type uint8_t): a string representing - * a BCP 47 language tag + * a BCP 47 language tag * @len: length of the @str, or -1 if it is %NULL-terminated. * - * Converts @str representing a BCP 47 language tag to the corresponding + * Converts @str representing a BCP 47 language tag to the corresponding * #hb_language_t. * * Return value: (transfer none): - * The #hb_language_t corresponding to the BCP 47 language tag. + * The #hb_language_t corresponding to the BCP 47 language tag. * * Since: 0.9.2 **/ @@ -356,7 +354,7 @@ hb_language_from_string (const char *str, int len) { /* NUL-terminate it. */ char strbuf[64]; - len = MIN (len, (int) sizeof (strbuf) - 1); + len = hb_min (len, (int) sizeof (strbuf) - 1); memcpy (strbuf, str, len); strbuf[len] = '\0'; item = lang_find_or_insert (strbuf); @@ -382,7 +380,8 @@ hb_language_from_string (const char *str, int len) const char * hb_language_to_string (hb_language_t language) { - /* This is actually nullptr-safe! */ + if (unlikely (!language)) return nullptr; + return language->s; } @@ -422,12 +421,12 @@ hb_language_get_default () /** * hb_script_from_iso15924_tag: - * @tag: an #hb_tag_t representing an ISO 15924 tag. + * @tag: an #hb_tag_t representing an ISO 15924 tag. * - * Converts an ISO 15924 script tag to a corresponding #hb_script_t. + * Converts an ISO 15924 script tag to a corresponding #hb_script_t. * * Return value: - * An #hb_script_t corresponding to the ISO 15924 tag. + * An #hb_script_t corresponding to the ISO 15924 tag. * * Since: 0.9.2 **/ @@ -468,15 +467,15 @@ hb_script_from_iso15924_tag (hb_tag_t tag) /** * hb_script_from_string: * @str: (array length=len) (element-type uint8_t): a string representing an - * ISO 15924 tag. + * ISO 15924 tag. * @len: length of the @str, or -1 if it is %NULL-terminated. * - * Converts a string @str representing an ISO 15924 script tag to a + * Converts a string @str representing an ISO 15924 script tag to a * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then * hb_script_from_iso15924_tag(). * * Return value: - * An #hb_script_t corresponding to the ISO 15924 tag. + * An #hb_script_t corresponding to the ISO 15924 tag. * * Since: 0.9.2 **/ @@ -488,12 +487,12 @@ hb_script_from_string (const char *str, int len) /** * hb_script_to_iso15924_tag: - * @script: an #hb_script_ to convert. + * @script: an #hb_script_t to convert. * * See hb_script_from_iso15924_tag(). * * Return value: - * An #hb_tag_t representing an ISO 15924 script tag. + * An #hb_tag_t representing an ISO 15924 script tag. * * Since: 0.9.2 **/ @@ -575,6 +574,13 @@ hb_script_get_horizontal_direction (hb_script_t script) case HB_SCRIPT_OLD_SOGDIAN: case HB_SCRIPT_SOGDIAN: + /* Unicode-12.0 additions */ + case HB_SCRIPT_ELYMAIC: + + /* Unicode-13.0 additions */ + case HB_SCRIPT_CHORASMIAN: + case HB_SCRIPT_YEZIDI: + return HB_DIRECTION_RTL; @@ -590,38 +596,6 @@ hb_script_get_horizontal_direction (hb_script_t script) } -/* hb_user_data_array_t */ - -bool -hb_user_data_array_t::set (hb_user_data_key_t *key, - void * data, - hb_destroy_func_t destroy, - hb_bool_t replace) -{ - if (!key) - return false; - - if (replace) { - if (!data && !destroy) { - items.remove (key, lock); - return true; - } - } - hb_user_data_item_t item = {key, data, destroy}; - bool ret = !!items.replace_or_insert (item, lock, (bool) replace); - - return ret; -} - -void * -hb_user_data_array_t::get (hb_user_data_key_t *key) -{ - hb_user_data_item_t item = {nullptr, nullptr, nullptr}; - - return items.find (key, &item, lock) ? item.data : nullptr; -} - - /* hb_version */ @@ -719,131 +693,24 @@ parse_char (const char **pp, const char *end, char c) static bool parse_uint (const char **pp, const char *end, unsigned int *pv) { - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); - strncpy (buf, *pp, len); - buf[len] = '\0'; - - char *p = buf; - char *pend = p; - unsigned int v; - - /* Intentionally use strtol instead of strtoul, such that - * -1 turns into "big number"... */ - errno = 0; - v = strtol (p, &pend, 0); - if (errno || p == pend) - return false; + /* Intentionally use hb_parse_int inside instead of hb_parse_uint, + * such that -1 turns into "big number"... */ + int v; + if (unlikely (!hb_parse_int (pp, end, &v))) return false; *pv = v; - *pp += pend - p; return true; } static bool parse_uint32 (const char **pp, const char *end, uint32_t *pv) { - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); - strncpy (buf, *pp, len); - buf[len] = '\0'; - - char *p = buf; - char *pend = p; - unsigned int v; - - /* Intentionally use strtol instead of strtoul, such that - * -1 turns into "big number"... */ - errno = 0; - v = strtol (p, &pend, 0); - if (errno || p == pend) - return false; + /* Intentionally use hb_parse_int inside instead of hb_parse_uint, + * such that -1 turns into "big number"... */ + int v; + if (unlikely (!hb_parse_int (pp, end, &v))) return false; *pv = v; - *pp += pend - p; - return true; -} - -#if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L) -#define USE_XLOCALE 1 -#define HB_LOCALE_T locale_t -#define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr) -#define HB_FREE_LOCALE(loc) freelocale (loc) -#elif defined(_MSC_VER) -#define USE_XLOCALE 1 -#define HB_LOCALE_T _locale_t -#define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName) -#define HB_FREE_LOCALE(loc) _free_locale (loc) -#define strtod_l(a, b, c) _strtod_l ((a), (b), (c)) -#endif - -#ifdef USE_XLOCALE - -#if HB_USE_ATEXIT -static void free_static_C_locale (); -#endif - -static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t -{ - static HB_LOCALE_T create () - { - HB_LOCALE_T C_locale = HB_CREATE_LOCALE ("C"); - -#if HB_USE_ATEXIT - atexit (free_static_C_locale); -#endif - - return C_locale; - } - static void destroy (HB_LOCALE_T p) - { - HB_FREE_LOCALE (p); - } - static HB_LOCALE_T get_null () - { - return nullptr; - } -} static_C_locale; - -#if HB_USE_ATEXIT -static -void free_static_C_locale () -{ - static_C_locale.free_instance (); -} -#endif - -static HB_LOCALE_T -get_C_locale () -{ - return static_C_locale.get_unconst (); -} -#endif /* USE_XLOCALE */ - -static bool -parse_float (const char **pp, const char *end, float *pv) -{ - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); - strncpy (buf, *pp, len); - buf[len] = '\0'; - - char *p = buf; - char *pend = p; - float v; - - errno = 0; -#ifdef USE_XLOCALE - v = strtod_l (p, &pend, get_C_locale ()); -#else - v = strtod (p, &pend); -#endif - if (errno || p == pend) - return false; - - *pv = v; - *pp += pend - p; return true; } @@ -857,9 +724,14 @@ parse_bool (const char **pp, const char *end, uint32_t *pv) (*pp)++; /* CSS allows on/off as aliases 1/0. */ - if (*pp - p == 2 && 0 == strncmp (p, "on", 2)) + if (*pp - p == 2 + && TOLOWER (p[0]) == 'o' + && TOLOWER (p[1]) == 'n') *pv = 1; - else if (*pp - p == 3 && 0 == strncmp (p, "off", 3)) + else if (*pp - p == 3 + && TOLOWER (p[0]) == 'o' + && TOLOWER (p[1]) == 'f' + && TOLOWER (p[2]) == 'f') *pv = 0; else return false; @@ -974,7 +846,41 @@ parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) * * Parses a string into a #hb_feature_t. * - * TODO: document the syntax here. + * The format for specifying feature strings follows. All valid CSS + * font-feature-settings values other than 'normal' and the global values are + * also accepted, though not documented below. CSS string escapes are not + * supported. + * + * The range indices refer to the positions between Unicode characters. The + * position before the first character is always 0. + * + * The format is Python-esque. Here is how it all works: + * + * + * + *

    {@code .level}{@code .level} *
      *
    • If the resulting configuration defines a level for a logger and @@ -1941,7 +1941,7 @@ public void updateConfiguration(Function *
    {@code .useParentHandlers}{@code .useParentHandlers} *
      *
    • If either the resulting or the old value for the useParentHandlers @@ -1955,7 +1955,7 @@ public void updateConfiguration(Function *
    {@code .handlers}{@code .handlers} *
      *
    • If the resulting configuration defines a list of handlers for a @@ -1979,7 +1979,7 @@ public void updateConfiguration(Function *
    {@code .*}{@code .*} *
      *
    • Properties configured/changed on handler classes will only affect @@ -1991,7 +1991,7 @@ public void updateConfiguration(Function *
    {@code config} and any other property{@code config} and any other property *
    ClassDescription
    Package Summary
    PackageDescription
    +
    """); checkOutput("allpackages-index.html", found, """ -
    -
    +
    Package Summary
    +
    """); checkOutput("allclasses-index.html", !found, """ diff --git a/test/langtools/jdk/javadoc/doclet/testNavigation/TestModuleNavigation.java b/test/langtools/jdk/javadoc/doclet/testNavigation/TestModuleNavigation.java index 2b4a367e6d4..f51c89d38c9 100644 --- a/test/langtools/jdk/javadoc/doclet/testNavigation/TestModuleNavigation.java +++ b/test/langtools/jdk/javadoc/doclet/testNavigation/TestModuleNavigation.java @@ -62,7 +62,7 @@ public void checkNavbar(Path base) throws Exception { .uses("p1.A") .uses("p1.B") .exports("p1") - .classes("package p1; public class A {}") + .classes("package p1; @Deprecated public class A {}") .classes("package p1; public class B {}"); mb.write(src); ModuleBuilder mb1 = new ModuleBuilder(tb, "m2") diff --git a/test/langtools/jdk/javadoc/doclet/testNewLanguageFeatures/TestNewLanguageFeatures.java b/test/langtools/jdk/javadoc/doclet/testNewLanguageFeatures/TestNewLanguageFeatures.java index cb5ac433fff..49b80f44dd5 100644 --- a/test/langtools/jdk/javadoc/doclet/testNewLanguageFeatures/TestNewLanguageFeatures.java +++ b/test/langtools/jdk/javadoc/doclet/testNewLanguageFeatures/TestNewLanguageFeatures.java @@ -71,7 +71,7 @@ void checkEnums() {
    public enum Coin
                         extends java.lang.Enum<Coin>
    """, // Check for enum constant section - "
    ", + "
    Enum Constants
    ", // Detail for enum constant """ Dime""", @@ -140,12 +140,15 @@ void checkTypeParameters() { V param2)""", // Method that returns TypeParameters """ -
    - - - """, +
    ClassUseTest1<T extends Foo & Foo2>
    """, """ -
    """, +
    ClassUseTest1.<\ + /span>method​(T t)
    """, """ -
    """, +
    Fields in pkg2\ + declared as ParamTest
    """, """ -
    """, + """, """ - """, +
    ClassUseTest1<T extends Foo & Foo2>
    """, """ -
    """, + """, """ - """ +
    ClassUseTest1.<\ + /span>method​(T t)
    """ ); // ClassUseTest2: > checkOutput("pkg2/class-use/ParamTest.html", true, """ -
    """, + """, """ - """, +
    ClassUseTest2<T extends ParamTest<Foo3>>
    """, """ -
    """, + """, """ - """, +
    ClassUseTest2.<\ + /span>method​(T t)
    """, """ -
    """, +
    Fields in pkg2\ + declared as ParamTest
    """, """ -
    """, + """, """ - """ +
    <T extends ParamTest<Foo3>>
    Pa\ + ramTest<Foo3>
    \ +
    """ ); checkOutput("pkg2/class-use/Foo3.html", true, """ -
    """, +
    ClassUseTest2<T extends ParamTest<Foo3>>
    """, """ -
    """, +
    ClassUseTest2.<\ + /span>method​(T t)
    """, """ -
    """, + """, """ - """ +
    <T extends ParamTest<Foo3>>
    Pa\ + ramTest<Foo3>
    \ +
    """ ); // ClassUseTest3: >> checkOutput("pkg2/class-use/ParamTest2.html", true, """ -
    """, - """ - """, - """ - """, - """ - """, - """ - """ + """, + """ +
    ClassUseTest3<T extends ParamTest2<java.util.Li\ + st<? extends Foo4>>&g\ + t;
    """, + """ + """, + """ +
    ClassUseTest3.<\ + /span>method​(T t)
    """, + """ +
    <T extends ParamTest2<java.util.List<? extends Foo4>>>
    ParamTest2<java.util.List<? extends\ + Foo4>>
    """ ); checkOutput("pkg2/class-use/Foo4.html", true, """ -
    """, - """ - """, - """ - """, - """ - """ + """, + """ +
    ClassUseTest3<T extends ParamTest2<java.util.Li\ + st<? extends Foo4>>&g\ + t;
    """, + """ + """, + """ +
    ClassUseTest3.\ + method​(T t)
    """, + """ + """, + """ +
    <T extends ParamTest2<java.util.List<? extends Foo4>>>
    ParamTest2<java.util.List<? extends Foo4>>
    """ ); // Type parameters in constructor and method args checkOutput("pkg2/class-use/Foo4.html", true, """ -
    - - - - - - - - - - - """, - """ - """ + 4""" ); //================================= @@ -573,32 +576,32 @@ public class AnnotationTypeUsage //================================= checkOutput("pkg/class-use/AnnotationType.html", true, """ - """, +
    Packages with annotations of type AnnotationType
    """, """ -
    """, +
    Classes in pkg\ + with annotations of type AnnotationType
    """, """ -
    """, +
    Fields in pkg\ + with annotations of type AnnotationType
    """, """ -
    """, +
    Methods in pkg\ + with annotations of type AnnotationType
    """, """ -
    """, +
    Method parameters in pkg with annotations of type AnnotationType
    """, """ -
    """, +
    Constructors in pk\ + g with annotations of type AnnotationType
    """, """ -
    """ +
    Constructor parameters in pkg with annotations of type AnnotationType
    """ ); //============================================================== diff --git a/test/langtools/jdk/javadoc/doclet/testOptions/TestOptions.java b/test/langtools/jdk/javadoc/doclet/testOptions/TestOptions.java index a09a3445430..37c64d059b3 100644 --- a/test/langtools/jdk/javadoc/doclet/testOptions/TestOptions.java +++ b/test/langtools/jdk/javadoc/doclet/testOptions/TestOptions.java @@ -44,19 +44,16 @@ public static void main(String... args) throws Exception { } @Test - public void testHeaderFooter() { + public void testHeader() { javadoc("-d", "out-1", "-header", "Test header", - "-footer", "Test footer", "-sourcepath", testSrc, "pkg"); checkExit(Exit.OK); checkOutput("pkg/package-summary.html", true, """ -
    Test header
    """, - """ -
    Test footer
    """); +
    Test header
    """); } @Test diff --git a/test/langtools/jdk/javadoc/doclet/testOverriddenMethods/TestOverrideMethods.java b/test/langtools/jdk/javadoc/doclet/testOverriddenMethods/TestOverrideMethods.java index fbfb4dc3a5e..c157f386ee6 100644 --- a/test/langtools/jdk/javadoc/doclet/testOverriddenMethods/TestOverrideMethods.java +++ b/test/langtools/jdk/javadoc/doclet/testOverriddenMethods/TestOverrideMethods.java @@ -314,24 +314,19 @@ public void testSummary() { // Only m2 should be shown in summary; m1 and m3 should listed as declared in Base checkOutput("pkg6/Sub.html", true, """ -
    Enum Constants
    E[]methodThatReturnsTypeParameterA<\ - /span>​(E[] e)""", - """ +
    E[]
    +
    methodThatReturnsTypeParameterA&#\ + 8203;(E[] e)""", + """
    public E[] methodThatReturnsTypePar\ @@ -153,22 +156,26 @@ void checkTypeParameters() { le="type parameter in TypeParameters">E[] e)
    """, """ -
    <T extends java.lang.Object & java.lang.Compa\ - rable<? super T>>
    T
    methodtThatReturnsTyp\ - eParametersB​(java.util.Collection<? extends T> coll\ - )""", +
    <T extends java.lang.Object & java.lang.C\ + omparable<? super T>>
    T
    +
    methodtThatReturnsTypeParamet\ + ersB​(java.util.Collection<? extends T> coll)""", """
    Returns TypeParameters
    """, // Method takes a TypeVariable """ -
    <X extends java.lang.Throwable>
    E
    orElseThrow​(java.u\ - til.function.Supplier<? extends X> exceptionSupplier)""" +
    <X extends java.lang.Throwable>
    E
    +
    orElseThrow​(java.util.func\ + tion.Supplier<? extends X> exceptionSupplier)""" ); checkOutput("pkg/Wildcards.html", true, @@ -234,211 +241,207 @@ void checkTypeParameters() { // ClassUseTest1: checkOutput("pkg2/class-use/Foo.html", true, """ -
    Classes in pkg2 with type p\ - arameters of type Foo""", +
    Classes in pkg2 with type p\ + arameters of type Foo""", """ -
    ClassUseTest1<T extends Foo & Foo2>
    Methods in pkg2 with type p\ - arameters of type Foo""", + """, """ -
    ClassUseTest1.<\ - /span>method​(T t)
    Fields in pkg2 with type pa\ - rameters of type Foo""", +
    Fields in pkg2\ + with type parameters of type Foo
    """, """ - td class="col-first">Par\ - amTest<Foo>""" +
    ParamTest<Foo&g\ + t;
    """ ); checkOutput("pkg2/class-use/ParamTest.html", true, """ -
    Fields in pkg2 declared as \ - ParamTest
    Pa\ - ramTest<Foo>""" +
    ParamTest<Foo&\ + gt;
    """ ); checkOutput("pkg2/class-use/Foo2.html", true, """ -
    Classes in pkg2 with type p\ - arameters of type Foo2
    ClassUseTest1<T extends Foo & Foo2>
    Methods in pkg2 with type p\ - arameters of type Foo2
    ClassUseTest1.<\ - /span>method​(T t)
    Classes in pkg2 with type p\ - arameters of type ParamTest
    ClassUseTest2<T extends ParamTest<Foo3>>
    Methods in pkg2 with type p\ - arameters of type ParamTest
    ClassUseTest2.<\ - /span>method​(T t)
    Fields in pkg2 declared as \ - ParamTest
    Pa\ - ramTest<Foo>""", +
    ParamTest<Foo&\ + gt;
    """, """ -
    Methods in pkg2 with type p\ - arameters of type ParamTest
    <T extends ParamTest<Foo3>>
    ParamTest<\ - ;Foo3>
    Classes in pkg2 with type p\ - arameters of type Foo3""", + """, """ -
    ClassUseTest2<T extends ParamTest<Foo3>>
    Methods in pkg2 with type p\ - arameters of type Foo3""", + """, """ -
    ClassUseTest2.<\ - /span>method​(T t)
    Methods in pkg2 that return\ - types with arguments of type Foo3<\ - /a>
    <T extends ParamTest<Foo3>>
    ParamTest<\ - ;Foo3>
    Classes in pkg2 with type p\ - arameters of type ParamTest2<\ - /a>
    ClassUseTest3<T extends ParamTest2<java.util.List\ - <? extends Foo4>>><\ - /span>
    Methods in pkg2 with type p\ - arameters of type ParamTest2<\ - /a>
    ClassUseTest3.<\ - /span>method​(T t)<T extends ParamTest2<java.util.List<? extends Foo4>>>
    ParamTest2<java.util.List<? extends Foo4>>
    Classes in pkg2 with type p\ - arameters of type Foo4""", - """ -
    ClassUseTest3<T extends ParamTest2<java.util.List\ - <? extends Foo4>>><\ - /span>
    Methods in pkg2 with type p\ - arameters of type Foo4""", - """ -
    ClassUseTest3.<\ - /span>method​(T t)
    Methods in pkg2 that return\ - types with arguments of type Foo4<\ - /a>
    <T extends ParamTest2<java.util.List<? extends Foo4>>>
    ParamTest2<java.util.List<? extends Foo4>>
    Method parameters in pkg2 w\ - ith type arguments of type Foo4\ -
    Modifier and TypeMethodDescription
    voidClassUseTest3.<\ - /span>method​(java.util.Set<Foo4> p)
    Constructor parameters in pkg2<\ +
    Method parameters in pkg2 with type arguments of type Foo4
    +
    +
    Modifier and Type
    +
    Method
    +
    Description
    +
    void
    +
    ClassUseTest3.<\ + /span>method​(java.util.Set<Foo4> p)
    +
     
    """, + """ +
    Packages with annotations of type AnnotationTypeClasses in pkg with annotat\ - ions of type Annotati\ - onTypeFields in pkg with annotati\ - ons of type Annotatio\ - nTypeMethods in pkg with annotat\ - ions of type Annotati\ - onTypeMethod parameters in pkg wi\ - th annotations of type AnnotationTypeConstructors in pkg with an\ - notations of type Ann\ - otationTypeConstructor parameters in pkg with annotations of type AnnotationType
    - - - - - - - - - - - - - - -
    Modifier and TypeMethodDescription
    java.lang.Stringm2() +
    +
    Modifier and Type
    +
    Method
    +
    Description
    +
    java.lang.String
    + +
    This is Base::m2.
    -
    + + """, """
    diff --git a/test/langtools/jdk/javadoc/doclet/testPackageDeprecation/TestPackageDeprecation.java b/test/langtools/jdk/javadoc/doclet/testPackageDeprecation/TestPackageDeprecation.java index 309281a7973..69b6b4aa9e6 100644 --- a/test/langtools/jdk/javadoc/doclet/testPackageDeprecation/TestPackageDeprecation.java +++ b/test/langtools/jdk/javadoc/doclet/testPackageDeprecation/TestPackageDeprecation.java @@ -73,7 +73,7 @@ public void testNoDeprecated() { "pkg1"); checkOutput("class-use/C2.ModalExclusionType.html", true, """ - <Unnamed>"""); + """); checkFiles(false, "pkg1/package-summary.html", diff --git a/test/langtools/jdk/javadoc/doclet/testPackagePage/TestPackagePage.java b/test/langtools/jdk/javadoc/doclet/testPackagePage/TestPackagePage.java index b6fea647ea2..d7c78f49afd 100644 --- a/test/langtools/jdk/javadoc/doclet/testPackagePage/TestPackagePage.java +++ b/test/langtools/jdk/javadoc/doclet/testPackagePage/TestPackagePage.java @@ -70,10 +70,6 @@ public void testSinglePackage() {
  • Package
  • """); } - private static final String[][] TEST1 = { - }; - - @Test public void testMultiplePackages() { javadoc("-d", "out-2", @@ -90,27 +86,18 @@ public void testMultiplePackages() { "
  • Package
  • "); checkOutput("allclasses-index.html", true, """ -
    - - - - - - - - +
    +
    Class Summary
    +
    +
    Class
    +
    Description
    """); checkOutput("allpackages-index.html", true, """ -
    -
    Class Summary
    ClassDescription
    - - - - - - - +
    Package Summary
    +
    +
    Package
    +
    Description
    """); checkOutput("type-search-index.js", true, """ diff --git a/test/langtools/jdk/javadoc/doclet/testPackagePage/com/pkg/C.java b/test/langtools/jdk/javadoc/doclet/testPackagePage/com/pkg/C.java index 27c7478ed3d..ad2881317fd 100644 --- a/test/langtools/jdk/javadoc/doclet/testPackagePage/com/pkg/C.java +++ b/test/langtools/jdk/javadoc/doclet/testPackagePage/com/pkg/C.java @@ -23,4 +23,5 @@ package com.pkg; +@Deprecated public class C {} diff --git a/test/langtools/jdk/javadoc/doclet/testPackageSummary/TestPackageSummary.java b/test/langtools/jdk/javadoc/doclet/testPackageSummary/TestPackageSummary.java index d1d0370ae3a..27f4f402691 100644 --- a/test/langtools/jdk/javadoc/doclet/testPackageSummary/TestPackageSummary.java +++ b/test/langtools/jdk/javadoc/doclet/testPackageSummary/TestPackageSummary.java @@ -24,7 +24,7 @@ /* * @test - * @bug 8189841 + * @bug 8189841 8253117 * @summary Error in alternate row coloring in package-summary files * @library ../../lib/ * @modules jdk.javadoc/jdk.javadoc.internal.tool @@ -50,28 +50,16 @@ public void testStripes() { checkOutput("pkg/package-summary.html", true, """ -
    - - - - - - - - - - - - - - - - - - - - - + +
     
    + +
     
    + +
     
    + +
     
    + +
     
    """ ); } diff --git a/test/langtools/jdk/javadoc/doclet/testProperty/TestProperty.java b/test/langtools/jdk/javadoc/doclet/testProperty/TestProperty.java index cab8d0b5252..987d854b20e 100644 --- a/test/langtools/jdk/javadoc/doclet/testProperty/TestProperty.java +++ b/test/langtools/jdk/javadoc/doclet/testProperty/TestProperty.java @@ -75,23 +75,24 @@ public void testArrays() { setBad(MyObj[])""", - // id should not be used in the property table + // no tab classes should be used in the property table """ - - - """, + + +
    """, - // id should be used in the method table + // tab classes should be used in the method table """ -
    - - """ + + """ ); checkOutput("pkg/MyClassT.html", true, diff --git a/test/langtools/jdk/javadoc/doclet/testRecordLinks/TestRecordLinks.java b/test/langtools/jdk/javadoc/doclet/testRecordLinks/TestRecordLinks.java index 706ec613f04..d35298134b9 100644 --- a/test/langtools/jdk/javadoc/doclet/testRecordLinks/TestRecordLinks.java +++ b/test/langtools/jdk/javadoc/doclet/testRecordLinks/TestRecordLinks.java @@ -23,12 +23,11 @@ /* * @test - * @bug 8236539 + * @bug 8236539 8246774 * @summary Relative link tags in record javadoc don't resolve * @library /tools/lib ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool * @build toolbox.ToolBox javadoc.tester.* - * @compile TestRecordLinks.java * @run main TestRecordLinks */ @@ -74,7 +73,6 @@ public void bar() { } javadoc("-d", base.resolve("out").toString(), "-sourcepath", src.toString(), - "--enable-preview", "--source", thisRelease, "example"); checkExit(Exit.OK); diff --git a/test/langtools/jdk/javadoc/doclet/testRecordTypes/TestRecordTypes.java b/test/langtools/jdk/javadoc/doclet/testRecordTypes/TestRecordTypes.java index 830bee551d7..2390eb8b383 100644 --- a/test/langtools/jdk/javadoc/doclet/testRecordTypes/TestRecordTypes.java +++ b/test/langtools/jdk/javadoc/doclet/testRecordTypes/TestRecordTypes.java @@ -23,13 +23,12 @@ /* * @test - * @bug 8225055 8239804 + * @bug 8225055 8239804 8246774 * @summary Record types * @library /tools/lib ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool * @build toolbox.ToolBox javadoc.tester.* - * @compile --enable-preview --source ${jdk.version} TestRecordTypes.java - * @run main/othervm --enable-preview TestRecordTypes + * @run main TestRecordTypes */ @@ -69,7 +68,6 @@ public void testRecordKeywordUnnamedPackage(Path base) throws IOException { javadoc("-d", base.resolve("out").toString(), "-quiet", "-noindex", "-sourcepath", src.toString(), - "--enable-preview", "--source", thisRelease, src.resolve("R.java").toString()); checkExit(Exit.OK); @@ -91,7 +89,6 @@ public void testRecordKeywordNamedPackage(Path base) throws IOException { javadoc("-d", base.resolve("out").toString(), "-quiet", "-noindex", "-sourcepath", src.toString(), - "--enable-preview", "--source", thisRelease, "p"); checkExit(Exit.OK); @@ -113,7 +110,6 @@ public void testEmptyRecord(Path base) throws IOException { javadoc("-d", base.resolve("out").toString(), "-quiet", "-noindex", "-sourcepath", src.toString(), - "--enable-preview", "--source", thisRelease, "p"); checkExit(Exit.OK); @@ -139,7 +135,6 @@ public record R(int r1) { }"""); javadoc("-d", base.resolve("out").toString(), "-quiet", "-noindex", "-sourcepath", src.toString(), - "--enable-preview", "--source", thisRelease, "p"); checkExit(Exit.OK); @@ -171,7 +166,6 @@ public record R(int r1) { }"""); javadoc("-d", base.resolve("out").toString(), "-quiet", "-noindex", "-sourcepath", src.toString(), - "--enable-preview", "--source", thisRelease, "p"); checkExit(Exit.OK); @@ -204,7 +198,6 @@ public record R(int r1) { }"""); javadoc("-d", base.resolve("out").toString(), "-quiet", "-noindex", "-sourcepath", src.toString(), - "--enable-preview", "--source", thisRelease, "p"); checkExit(Exit.OK); @@ -268,7 +261,6 @@ public record R(int r1) { }"""); "-quiet", "-noindex", "-sourcepath", src.toString(), "-linkoffline", externalDocs, localDocs, - "--enable-preview", "--source", thisRelease, "p"); checkExit(Exit.OK); @@ -349,7 +341,6 @@ public record R(""" + comps + ") { }"); "-quiet", "-noindex", "--no-platform-links", "-sourcepath", src.toString(), - "--enable-preview", "--source", thisRelease, "p"); checkExit(Exit.OK); @@ -375,7 +366,6 @@ public record R(int r1) { javadoc("-d", base.resolve("out").toString(), "-quiet", "-noindex", "-sourcepath", src.toString(), - "--enable-preview", "--source", thisRelease, "p"); checkExit(Exit.OK); @@ -406,7 +396,6 @@ public void testExamples(Path base) throws IOException { "-quiet", "-noindex", "-sourcepath", testSrc.toString(), "-linksource", - "--enable-preview", "--source", thisRelease, "examples"); checkExit(Exit.OK); @@ -415,13 +404,11 @@ public void testExamples(Path base) throws IOException { "-sourcepath", testSrc.toString(), "-linksource", "-linkoffline", externalDocs, localDocs, - "--enable-preview", "--source", thisRelease, "examples"); checkExit(Exit.OK); } @Test - @SuppressWarnings("preview") public void testAnnotations(Path base) throws IOException { ElementType[] types = { ElementType.FIELD, @@ -468,7 +455,6 @@ void testAnnotations(Path base, Set types) throws IOException { "--no-platform-links", "-sourcepath", src.toString(), "-private", - "--enable-preview", "--source", thisRelease, "p"); checkExit(Exit.OK); diff --git a/test/langtools/jdk/javadoc/doclet/testSearch/TestSearch.java b/test/langtools/jdk/javadoc/doclet/testSearch/TestSearch.java index b8211a45ab0..9bf0cb7aa63 100644 --- a/test/langtools/jdk/javadoc/doclet/testSearch/TestSearch.java +++ b/test/langtools/jdk/javadoc/doclet/testSearch/TestSearch.java @@ -25,7 +25,7 @@ * @test * @bug 8141492 8071982 8141636 8147890 8166175 8168965 8176794 8175218 8147881 * 8181622 8182263 8074407 8187521 8198522 8182765 8199278 8196201 8196202 - * 8184205 8214468 8222548 8223378 8234746 8241219 + * 8184205 8214468 8222548 8223378 8234746 8241219 8254627 * @summary Test the search feature of javadoc. * @library ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool @@ -487,14 +487,14 @@ void checkSingleIndex(boolean expectedOutput, boolean html5) { /a> - Search tag in pkg.AnotherClass.CONSTANT1""", """
    ONE - pkg2.TestEnum
    """, + n> - Enum constant in enum pkg2.TestEnum""", """
    THREE<\ - /span> - pkg2.TestEnum - Enum constant in enum pkg2.TestEnum""", """
    TWO - pkg2.TestEnum
    """); + n> - Enum constant in enum pkg2.TestEnum"""); checkOutput("index-all.html", true, """
    class_test1 passes. Search tag ONE - pkg2.TestEnum - Enum constant in enum pkg2.TestEnum"""); checkOutput("index-files/index-14.html", true, """
    THREE - pkg2.TestEnum - Enum constant in enum pkg2.TestEnum
    """, """
    TWO - pkg2.TestEnum - Enum constant in enum pkg2.TestEnum"""); } @@ -813,45 +813,47 @@ void checkSplitIndexSearchTagDuplication() { void checkAllPkgsAllClasses() { checkOutput("allclasses-index.html", true, """ -
    -
    <\ - button role="tab" aria-selected="false" aria-controls="all-classes-table.tabpane\ - l" tabindex="-1" onkeydown="switchTab(event)" id="t3" class="table-tab" onclick=\ - "show(4);">Enum Summary
    +
    +
    \ + \ + \ + \ + \ + \ + \ + \ +
    -
    Package Summary
    PackageDescription
    C0 
    C1 
    C2 
    C3 
    C4 
    O\ - bjectProperty<MyObj[]>bad
    O\ - bjectProperty<MyObj[]>badProperty()
    - - - - - """, - """ - var data = {"i0":32,"i1":2,"i2":4,"i3":2,"i4":2,"i5":1,"i6":2,"i7":32,"i8":2,"i9\ - ":4,"i10":16,"i11":16,"i12":8,"i13":8,"i14":1,"i15":2};"""); +
    +
    Class
    +
    Description
    """); checkOutput("allpackages-index.html", true, """ -
    -
    ClassDescription
    - - - - - - +
    Package Summary
    +
    +
    Package
    +
    Description
    """); checkOutput("type-search-index.js", true, """ diff --git a/test/langtools/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java b/test/langtools/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java index 2cf4e795a42..f6f636cda7a 100644 --- a/test/langtools/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java +++ b/test/langtools/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java @@ -25,6 +25,7 @@ * @test * @bug 4494033 7028815 7052425 8007338 8023608 8008164 8016549 8072461 8154261 8162363 8160196 8151743 8177417 * 8175218 8176452 8181215 8182263 8183511 8169819 8183037 8185369 8182765 8196201 8184205 8223378 8241544 + * 8253117 * @summary Run tests on doclet stylesheet. * @library /tools/lib ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool @@ -89,10 +90,7 @@ public void test(Path base) { + " list-style-type:disc;\n" + "}", """ - .overview-summary caption, .member-summary caption, .type-summary caption, - .use-summary caption, .constants-summary caption, .deprecated-summary caption, - .requires-summary caption, .packages-summary caption, .provides-summary caption, - .uses-summary caption, .system-properties-summary caption { + .caption { position:relative; text-align:left; background-repeat:no-repeat; @@ -107,10 +105,7 @@ public void test(Path base) { white-space:pre; }""", """ - .overview-summary caption span, .member-summary caption span, .type-summary caption span, - .use-summary caption span, .constants-summary caption span, .deprecated-summary caption span, - .requires-summary caption span, .packages-summary caption span, .provides-summary caption span, - .uses-summary caption span, .system-properties-summary caption span { + .caption span { white-space:nowrap; padding-top:5px; padding-left:12px; @@ -140,23 +135,14 @@ public void test(Path base) { }""", // Test the formatting styles for proper content display in use and constant values pages. """ - .overview-summary td.col-first, .overview-summary th.col-first, - .requires-summary td.col-first, .requires-summary th.col-first, - .packages-summary td.col-first, .packages-summary td.col-second, .packages-summary th.col-first, .packages-summary th, - .uses-summary td.col-first, .uses-summary th.col-first, - .provides-summary td.col-first, .provides-summary th.col-first, - .member-summary td.col-first, .member-summary th.col-first, - .member-summary td.col-second, .member-summary th.col-second, .member-summary th.col-constructor-name, - .type-summary td.col-first, .type-summary th.col-first { + .col-first, .col-second, .col-constructor-name { vertical-align:top; + overflow: auto; }""", """ - .overview-summary td, .member-summary td, .type-summary td, - .use-summary td, .constants-summary td, .deprecated-summary td, - .requires-summary td, .packages-summary td, .provides-summary td, - .uses-summary td, .system-properties-summary td { + .summary-table > div { text-align:left; - padding:0px 0px 12px 10px; + padding: 8px 3px 3px 7px; }""", "@import url('resources/fonts/dejavu.css');", """ @@ -169,15 +155,15 @@ public void test(Path base) { color:#bb7a2a; }""", """ - td.col-first a:link, td.col-first a:visited, - td.col-second a:link, td.col-second a:visited, - th.col-first a:link, th.col-first a:visited, - th.col-second a:link, th.col-second a:visited, - th.col-constructor-name a:link, th.col-constructor-name a:visited, - th.col-deprecated-item-name a:link, th.col-deprecated-item-name a:visited, - .constant-values-container td a:link, .constant-values-container td a:visited, - .all-classes-container td a:link, .all-classes-container td a:visited, - .all-packages-container td a:link, .all-packages-container td a:visited { + .col-first a:link, .col-first a:visited, + .col-second a:link, .col-second a:visited, + .col-first a:link, .col-first a:visited, + .col-second a:link, .col-second a:visited, + .col-constructor-name a:link, .col-constructor-name a:visited, + .col-deprecated-item-name a:link, .col-deprecated-item-name a:visited, + .constant-values-container a:link, .constant-values-container a:visited, + .all-classes-container a:link, .all-classes-container a:visited, + .all-packages-container a:link, .all-packages-container a:visited { font-weight:bold; }""", """ @@ -223,10 +209,10 @@ public void test(Path base) { checkOutput("pkg/package-summary.html", true, """ -
    """); + """); checkOutput("index.html", true, """ diff --git a/test/langtools/jdk/javadoc/doclet/testSystemPropertyPage/TestSystemPropertyPage.java b/test/langtools/jdk/javadoc/doclet/testSystemPropertyPage/TestSystemPropertyPage.java index 7fbabff72db..0cc6c6de4c1 100644 --- a/test/langtools/jdk/javadoc/doclet/testSystemPropertyPage/TestSystemPropertyPage.java +++ b/test/langtools/jdk/javadoc/doclet/testSystemPropertyPage/TestSystemPropertyPage.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8215038 8239487 8240476 + * @bug 8215038 8239487 8240476 8253559 * @summary Add a page that lists all system properties * @library /tools/lib ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool @@ -63,7 +63,7 @@ public void test(Path base) throws Exception { checkOutput("index-all.html", true, """ - System Properties"""); + System Properties"""); checkOutput("system-properties.html", true, """ @@ -78,36 +78,19 @@ public void test(Path base) throws Exception { """, """ -
    Package Summary
    PackageDescription
    +
    Test comment for a class which has an anchor_with_name and an anchor_with_id.
    -
    - - - - - - - - - - - - - - - - - -
    System Properties Summary
    PropertyReferenced In
    user.address - -
    user.name - -
    """); +
    System Properties Summary
    + """); } /* diff --git a/test/langtools/jdk/javadoc/doclet/testTopOption/pkg/Cl.java b/test/langtools/jdk/javadoc/doclet/testTopOption/pkg/Cl.java index 4fe9e4308a8..b2a307e3d26 100644 --- a/test/langtools/jdk/javadoc/doclet/testTopOption/pkg/Cl.java +++ b/test/langtools/jdk/javadoc/doclet/testTopOption/pkg/Cl.java @@ -25,4 +25,5 @@ public class Cl { + public static final int ZERO = 0; } diff --git a/test/langtools/jdk/javadoc/doclet/testTypeAnnotations/TestTypeAnnotations.java b/test/langtools/jdk/javadoc/doclet/testTypeAnnotations/TestTypeAnnotations.java index 2990e2aa1a7..cd1349f88cd 100644 --- a/test/langtools/jdk/javadoc/doclet/testTypeAnnotations/TestTypeAnnotations.java +++ b/test/langtools/jdk/javadoc/doclet/testTypeAnnotations/TestTypeAnnotations.java @@ -550,51 +550,51 @@ class RepeatingAtClassLevel checkOutput("typeannos/RepeatingOnField.html", true, """ - (package private) java.lang.Integer - i1""", - - """ - (package private) @RepTypeUseA @\ - RepTypeUseA @RepT\ - ypeUseB @RepTypeU\ - seB java.lang.Integer - i2""", - - """ - (package private) @RepTypeUseA @\ - RepTypeUseA @RepT\ - ypeUseB @RepTypeU\ - seB java.lang.Integer - i3""", - - """ - (package private) @RepAllContextsA @RepAllContextsA @RepAllContextsB @RepAllContextsB java.lang.Integer - i4""", - - """ - (package private) java.lang.String @RepTypeUseA @RepTypeUseA @RepTypeUseB @RepTypeUseB [] @RepTypeUseA @RepTypeUseA @\ - RepTypeUseB @RepT\ - ypeUseB [] - sa""", - - """ -
    @RepFieldA @RepFieldA - @RepFieldB @RepFieldB +
    (package private) java.lang.Integer
    + """, + + """ +
    (package private) @RepTypeUseA @RepTypeUseA @RepTypeUseB @RepTypeUseB java.lang.Integer
    + +
     
    """, + + """ +
    (package private) @RepTypeUseA @RepTypeUseA @RepTypeUseB @RepTypeUseB java.lang.Integer
    + """, + + """ +
    (package private) @RepAllContextsA @RepAllContextsA @RepAllContextsB @RepAllContextsB java.la\ + ng.Integer
    + """, + + """ + + """, + + """ +
    @RepFieldA @RepFieldA + @RepFieldB @RepFieldB java.lang.Integer i1
    """, """ @@ -646,41 +646,49 @@ class RepeatingAtClassLevel checkOutput("typeannos/RepeatingOnMethod.html", true, """ - (package private) java.lang.String - test1()""", + (package private) java.lang.String
    +
    test1()""", """ (package private) @RepTypeUseA @\ RepTypeUseA @RepT\ ypeUseB @RepTypeU\ - seB java.lang.String - test2()""", + seB java.lang.String
    +
    test2()""", """ (package private) @RepTypeUseA @\ RepTypeUseA @RepT\ ypeUseB @RepTypeU\ - seB java.lang.String - test3()""", + seB java.lang.String
    +
    test3()""", """ (package private) @RepAllContextsA @RepAllContextsA @RepAllContextsB @RepAllContextsB java.lang.String - test4()""", + annos">@RepAllContextsA @RepAllContextsA @RepAllContextsB @RepAllContextsB java.lang.String
    + +
    genericMethod​(T t)""", """ - (package private) <T> java.lang.String - genericMethod2​(@RepTypeUseA @RepTypeUseA @RepTypeUseB @RepTypeUseB T t)""", + (package private) <T> java.lang.String
    +
    genericMethod2​(@RepTypeUseA @RepTypeUseA @RepTypeUseB @RepTypeUseB T t)""", """ - (package private) java.lang.String - test()""", + (package private) java.lang.String
    +
    test()""", """ java.lang.String <W extends java.lang.String,​ - V extends java.util.List>
    java.lang.Object
    """, +
    <W extends java.lang.String,​ + V extends java.util.List>
    java.lang.Object
    """, "<T> java.lang.Object"); checkOutput("pkg/package-summary.html", true, diff --git a/test/langtools/jdk/javadoc/doclet/testUnnamedPackage/TestUnnamedPackage.java b/test/langtools/jdk/javadoc/doclet/testUnnamedPackage/TestUnnamedPackage.java index 32d7fd5f01d..40b92a4ad1b 100644 --- a/test/langtools/jdk/javadoc/doclet/testUnnamedPackage/TestUnnamedPackage.java +++ b/test/langtools/jdk/javadoc/doclet/testUnnamedPackage/TestUnnamedPackage.java @@ -70,45 +70,29 @@ public void test() { checkOutput("allclasses-index.html", true, """ -
    - - - - - - - - - - - - - - -
    Class Summary
    ClassDescription
    C +
    +
    Class Summary
    +
    +
    Class
    +
    Description
    + +
    This is a class in the unnamed package.
    -
    """); +
    +
    """); checkOutput("allpackages-index.html", true, """ -
    - - - - - - - - - - - - - - -
    Package Summary
    PackageDescription
    <Unnamed> +
    Package Summary
    +
    +
    Package
    +
    Description
    + +
    This is a package comment for the unnamed package.
    -
    """); +
    +
    """); checkOutput("type-search-index.js", true, """ diff --git a/test/langtools/jdk/javadoc/doclet/testUseOption/TestUseOption.java b/test/langtools/jdk/javadoc/doclet/testUseOption/TestUseOption.java index 04ef28faa29..a345628d797 100644 --- a/test/langtools/jdk/javadoc/doclet/testUseOption/TestUseOption.java +++ b/test/langtools/jdk/javadoc/doclet/testUseOption/TestUseOption.java @@ -129,20 +129,20 @@ public void test1() { Subinterfaces of UsedI\ nterface in pkg1""", """ - interface  - SubInterface<T>""" +
    interface 
    + """ ); checkOutput("pkg1/class-use/UsedThrowable.html", true, """ Methods in pkg1 that throw UsedThrowable""", """ - void - C1.methodInC1ThrowsThrowable()""" +
    void
    + """ ); } @@ -167,10 +167,10 @@ public void test2() { ); checkOutput("package-use.html", true, """ - UsedInC""", + """, """ - <Unnamed> -  """ + +
     
    """ ); } diff --git a/test/langtools/jdk/javadoc/tool/CheckResourceKeys.java b/test/langtools/jdk/javadoc/tool/CheckResourceKeys.java index 302313a1038..5fe93c25139 100644 --- a/test/langtools/jdk/javadoc/tool/CheckResourceKeys.java +++ b/test/langtools/jdk/javadoc/tool/CheckResourceKeys.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8000612 + * @bug 8000612 8254627 * @summary need test program to validate javadoc resource bundles * @modules jdk.javadoc/jdk.javadoc.internal.tool * jdk.javadoc/jdk.javadoc.internal.doclets.formats.html.resources:open @@ -201,9 +201,9 @@ Set getCodeKeys() throws IOException { } // special handling for code strings synthesized in - // com.sun.tools.doclets.internal.toolkit.util.Util.getTypeName + // jdk.javadoc.internal.doclets.toolkit.util.Utils.getTypeName String[] extras = { - "AnnotationType", "Class", "Enum", "Error", "Exception", "Interface" + "AnnotationType", "Class", "Enum", "Error", "Exception", "Interface", "Record" }; for (String s: extras) { if (results.contains("doclet." + s)) diff --git a/test/langtools/jdk/javadoc/tool/TestScriptInComment.java b/test/langtools/jdk/javadoc/tool/TestScriptInComment.java index 93851691639..3eaa47a36d5 100644 --- a/test/langtools/jdk/javadoc/tool/TestScriptInComment.java +++ b/test/langtools/jdk/javadoc/tool/TestScriptInComment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -103,7 +103,6 @@ enum Template { MTH("package p; public class C { #COMMENT public void m() { } }"), TOP("-top", "lorem #COMMENT ipsum", "package p; public class C { }"), HDR("-header", "lorem #COMMENT ipsum", "package p; public class C { }"), - FTR("-footer", "lorem #COMMENT ipsum", "package p; public class C { }"), BTM("-bottom", "lorem #COMMENT ipsum", "package p; public class C { }"), DTTL("-doctitle", "lorem #COMMENT ipsum", "package p; public class C { }"), PHDR("-packagesheader", "lorem #COMMENT ipsum", "package p; public class C { }"); diff --git a/test/langtools/jdk/javadoc/tool/api/basic/APITest.java b/test/langtools/jdk/javadoc/tool/api/basic/APITest.java index accba262bb4..8c7c9453622 100644 --- a/test/langtools/jdk/javadoc/tool/api/basic/APITest.java +++ b/test/langtools/jdk/javadoc/tool/api/basic/APITest.java @@ -110,7 +110,8 @@ protected File getOutDir(String path) { } protected JavaFileObject createSimpleJavaFileObject() { - return createSimpleJavaFileObject("pkg/C", "package pkg; public class C { }"); + return createSimpleJavaFileObject("pkg/C", + "package pkg; public class C { @Deprecated public static final int ZERO = 0; }"); } protected JavaFileObject createSimpleJavaFileObject(final String binaryName, final String content) { diff --git a/test/langtools/jdk/javadoc/tool/api/basic/GetTask_DiagListenerTest.java b/test/langtools/jdk/javadoc/tool/api/basic/GetTask_DiagListenerTest.java index bbead5a206f..8e51bdbc50e 100644 --- a/test/langtools/jdk/javadoc/tool/api/basic/GetTask_DiagListenerTest.java +++ b/test/langtools/jdk/javadoc/tool/api/basic/GetTask_DiagListenerTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6493690 + * @bug 6493690 8246774 * @summary javadoc should have a javax.tools.Tool service provider * @modules java.compiler * jdk.compiler @@ -78,7 +78,7 @@ public void testDiagListener() throws Exception { } List expect = Arrays.asList( "javadoc.note.msg", // Loading source file - "compiler.err.expected3", // class, interface, enum, or __datum expected + "compiler.err.expected4", // class, interface, enum, or record expected "javadoc.note.msg"); // 1 error if (!diagCodes.equals(expect)) throw new Exception("unexpected diagnostics occurred"); diff --git a/test/langtools/jdk/javadoc/tool/api/basic/pkg/C.java b/test/langtools/jdk/javadoc/tool/api/basic/pkg/C.java index 74383c04f08..6ea7262274e 100644 --- a/test/langtools/jdk/javadoc/tool/api/basic/pkg/C.java +++ b/test/langtools/jdk/javadoc/tool/api/basic/pkg/C.java @@ -23,5 +23,8 @@ package pkg; -public class C { } +public class C { + @Deprecated + public static final int ZERO = 0; +} diff --git a/test/langtools/jdk/javadoc/tool/testLocaleOption/TestLocaleOption.java b/test/langtools/jdk/javadoc/tool/testLocaleOption/TestLocaleOption.java index aa5d783ffc9..b9aa21c5db4 100644 --- a/test/langtools/jdk/javadoc/tool/testLocaleOption/TestLocaleOption.java +++ b/test/langtools/jdk/javadoc/tool/testLocaleOption/TestLocaleOption.java @@ -183,20 +183,20 @@ private void testHelloWorld(Path base, Locale defaultLocale, Locale localeOption checkContains(hw, "

    METHOD SUMMARY

    ", """ - MODIFIER AND TYPE""", +
    MODIFIER AND TYPE
    """, """ - METHOD""", +
    METHOD
    """, """ - DESCRIPTION"""); +
    DESCRIPTION
    """); } else { checkContains(hw, "

    Method Summary

    ", """ - Modifier and Type""", +
    Modifier and Type
    """, """ - Method""", +
    Method
    """, """ - Description"""); +
    Description
    """); } } diff --git a/test/langtools/jdk/jshell/CompletenessTest.java b/test/langtools/jdk/jshell/CompletenessTest.java index 6ff4ce4d55a..97e165abb7a 100644 --- a/test/langtools/jdk/jshell/CompletenessTest.java +++ b/test/langtools/jdk/jshell/CompletenessTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8149524 8131024 8165211 8080071 8130454 8167343 8129559 8114842 8182268 8223782 8235474 + * @bug 8149524 8131024 8165211 8080071 8130454 8167343 8129559 8114842 8182268 8223782 8235474 8246774 * @summary Test SourceCodeAnalysis * @build KullaTesting TestingInputStream * @run testng CompletenessTest @@ -379,10 +379,4 @@ public void testMiscSource() { assertStatus("int[] m = {1, 2}, n = new int[0]; int i;", COMPLETE, "int[] m = {1, 2}, n = new int[0];"); } - - @BeforeMethod - public void setUp() { - setUp(b -> b.compilerOptions("--enable-preview", "-source", String.valueOf(SourceVersion.latest().ordinal()))); - } - } diff --git a/test/langtools/jdk/jshell/HistoryUITest.java b/test/langtools/jdk/jshell/HistoryUITest.java index 307c087409b..04b8e825862 100644 --- a/test/langtools/jdk/jshell/HistoryUITest.java +++ b/test/langtools/jdk/jshell/HistoryUITest.java @@ -23,7 +23,7 @@ /** * @test - * @bug 8178077 + * @bug 8178077 8232856 * @summary Check the UI behavior of editing history. * @modules * jdk.compiler/com.sun.tools.javac.api @@ -77,4 +77,16 @@ public void testPrevNextSnippet() throws Exception { }); } + public void testReRun() throws Exception { + doRunTest((inputSink, out) -> { + inputSink.write("System.err.println(\"RAN\");\n"); + waitOutput(out, "RAN.*" + PROMPT); + inputSink.write("/!\n"); + waitOutput(out, "RAN.*" + PROMPT); + inputSink.write(UP); + inputSink.write("\n"); + waitOutput(out, "RAN.*" + PROMPT); + }); + } + } diff --git a/test/langtools/jdk/jshell/RecordsTest.java b/test/langtools/jdk/jshell/RecordsTest.java index bdca879edde..69aaae8e46f 100644 --- a/test/langtools/jdk/jshell/RecordsTest.java +++ b/test/langtools/jdk/jshell/RecordsTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8235474 8236715 + * @bug 8235474 8236715 8246774 * @summary Tests for evalution of records * @modules jdk.jshell * @build KullaTesting TestingInputStream ExpectedDiagnostic @@ -76,10 +76,4 @@ public void testRecordMethod() { assertEval("record(\"r\")", "\"rr\""); assertEval("record(\"r\").length()", "2"); } - - @BeforeMethod - public void setUp() { - setUp(b -> b.compilerOptions("--enable-preview", "-source", String.valueOf(SourceVersion.latest().ordinal())) - .remoteVMOptions("--enable-preview")); - } } diff --git a/test/langtools/jdk/jshell/ToolSimpleTest.java b/test/langtools/jdk/jshell/ToolSimpleTest.java index 6f38ad6fc0e..6a50d9bc9f1 100644 --- a/test/langtools/jdk/jshell/ToolSimpleTest.java +++ b/test/langtools/jdk/jshell/ToolSimpleTest.java @@ -23,14 +23,14 @@ /* * @test - * @bug 8153716 8143955 8151754 8150382 8153920 8156910 8131024 8160089 8153897 8167128 8154513 8170015 8170368 8172102 8172103 8165405 8173073 8173848 8174041 8173916 8174028 8174262 8174797 8177079 8180508 8177466 8172154 8192979 8191842 8198573 8198801 8210596 8210959 8215099 8199623 8236715 8239536 8247456 + * @bug 8153716 8143955 8151754 8150382 8153920 8156910 8131024 8160089 8153897 8167128 8154513 8170015 8170368 8172102 8172103 8165405 8173073 8173848 8174041 8173916 8174028 8174262 8174797 8177079 8180508 8177466 8172154 8192979 8191842 8198573 8198801 8210596 8210959 8215099 8199623 8236715 8239536 8247456 8246774 * @summary Simple jshell tool tests * @modules jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main * jdk.jdeps/com.sun.tools.javap * jdk.jshell/jdk.internal.jshell.tool * @build KullaTesting TestingInputStream - * @run testng/othervm ToolSimpleTest + * @run testng ToolSimpleTest */ import java.util.ArrayList; @@ -917,7 +917,7 @@ public void testUpdateFalsePositive() { @Test public void testRecords() { - test(new String[] {"--enable-preview"}, + test(new String[] {}, (a) -> assertCommandOutputContains(a, "record R(int i) { public int g() { return j; } }", "| created record R, however, it cannot be instantiated or its methods invoked until variable j is declared"), (a) -> assertCommandOutputContains(a, "new R(0)", diff --git a/test/langtools/tools/javac/Digits.out b/test/langtools/tools/javac/Digits.out index 70113d32c17..941dc175ac2 100644 --- a/test/langtools/tools/javac/Digits.out +++ b/test/langtools/tools/javac/Digits.out @@ -1,2 +1,2 @@ -Digits.java:11:41: compiler.err.illegal.nonascii.digit +Digits.java:11:43: compiler.err.illegal.nonascii.digit 1 error diff --git a/test/langtools/tools/javac/IllegalAnnotation.java b/test/langtools/tools/javac/IllegalAnnotation.java index 164d1786146..79c4194d0c5 100644 --- a/test/langtools/tools/javac/IllegalAnnotation.java +++ b/test/langtools/tools/javac/IllegalAnnotation.java @@ -1,10 +1,9 @@ /** * @test /nodynamiccopyright/ - * @bug 5012028 6384539 8074364 8250741 + * @bug 5012028 6384539 8074364 8250741 8246774 * @summary javac crash when declare an annotation type illegally * * @compile/fail/ref=IllegalAnnotation.out -XDrawDiagnostics IllegalAnnotation.java - * @compile/fail/ref=IllegalAnnotation.out -XDrawDiagnostics --enable-preview -source ${jdk.version} IllegalAnnotation.java */ class IllegalAnnotation { { diff --git a/test/langtools/tools/javac/IllegalAnnotation.out b/test/langtools/tools/javac/IllegalAnnotation.out index 6b99a40d084..00d7fd4e2a3 100644 --- a/test/langtools/tools/javac/IllegalAnnotation.out +++ b/test/langtools/tools/javac/IllegalAnnotation.out @@ -1,2 +1,2 @@ -IllegalAnnotation.java:11:10: compiler.err.annotation.decl.not.allowed.here +IllegalAnnotation.java:10:10: compiler.err.annotation.decl.not.allowed.here 1 error diff --git a/test/langtools/tools/javac/LocalInterface.java b/test/langtools/tools/javac/LocalInterface.java index 13c2d56bdf1..818bb69ceab 100644 --- a/test/langtools/tools/javac/LocalInterface.java +++ b/test/langtools/tools/javac/LocalInterface.java @@ -1,9 +1,9 @@ /** * @test /nodynamiccopyright/ - * @bug 8242478 + * @bug 8242478 8246774 * @summary test for local interfaces - * @compile/fail/ref=LocalInterface.out -XDrawDiagnostics LocalInterface.java - * @compile --enable-preview -source ${jdk.version} LocalInterface.java + * @compile/fail/ref=LocalInterface.out -XDrawDiagnostics -source 15 LocalInterface.java + * @compile LocalInterface.java */ class LocalInterface { void m() { diff --git a/test/langtools/tools/javac/LocalInterface.out b/test/langtools/tools/javac/LocalInterface.out index 2ad6d4011d9..98524edadc0 100644 --- a/test/langtools/tools/javac/LocalInterface.out +++ b/test/langtools/tools/javac/LocalInterface.out @@ -1,2 +1,4 @@ +- compiler.warn.source.no.system.modules.path: 15 LocalInterface.java:10:9: compiler.err.intf.not.allowed.here 1 error +1 warning diff --git a/test/langtools/tools/javac/LocalRecord.java b/test/langtools/tools/javac/LocalRecord.java index 9dda54e04d6..b4053bb1fe3 100644 --- a/test/langtools/tools/javac/LocalRecord.java +++ b/test/langtools/tools/javac/LocalRecord.java @@ -23,9 +23,9 @@ /* * @test - * @bug 8242478 + * @bug 8242478 8246774 * @summary test local records - * @compile --enable-preview -source ${jdk.version} LocalRecord.java + * @compile LocalRecord.java */ class LocalRecord { void m() { diff --git a/src/hotspot/share/adlc/Test/i486.ad b/test/langtools/tools/javac/T4881267-old.out similarity index 100% rename from src/hotspot/share/adlc/Test/i486.ad rename to test/langtools/tools/javac/T4881267-old.out diff --git a/test/langtools/tools/javac/T4881267.out b/test/langtools/tools/javac/T4881267.out index 01a732a612e..ad4e9c84b77 100644 --- a/test/langtools/tools/javac/T4881267.out +++ b/test/langtools/tools/javac/T4881267.out @@ -1,2 +1,2 @@ -T4881267.java:10:34: compiler.err.illegal.generic.type.for.instof +T4881267.java:10:21: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, T 1 error diff --git a/test/langtools/tools/javac/T8254557/T8254557.java b/test/langtools/tools/javac/T8254557/T8254557.java new file mode 100644 index 00000000000..cdae9733849 --- /dev/null +++ b/test/langtools/tools/javac/T8254557/T8254557.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8254557 + * @summary Method Attr.preFlow shouldn't visit class definitions that have not yet been entered and attributed. + * @compile T8254557.java + */ + +import java.util.Iterator; +import java.util.function.Function; + +public class T8254557 { + // test anonymous class in if statement + public void testIf(boolean b) { + test(rs -> { + if (b) { + return new Iterator<>() { + @Override + public boolean hasNext() { + return true; + } + + @Override + public T next() { + return null; + } + }; + } else { + return new Iterator<>() { + @Override + public boolean hasNext() { + return true; + } + + @Override + public T next() { + return null; + } + }; + } + }); + } + + // test anonymous class in while statement + public void testWhile(boolean b) { + test(rs -> { + while (b) { + return new Iterator<>() { + @Override + public boolean hasNext() { + return true; + } + + @Override + public T next() { + return null; + } + }; + } + return null; + }); + } + + // test anonymous class in do while statement + public void testDoWhileLoop(boolean b) { + test(rs -> { + do { + return new Iterator<>() { + @Override + public boolean hasNext() { + return true; + } + + @Override + public T next() { + return null; + } + }; + } while (b); + }); + } + + // test anonymous class in for statement + public void testForLoop(boolean b) { + test(rs -> { + for ( ; ; ) { + return new Iterator<>() { + @Override + public boolean hasNext() { + return true; + } + + @Override + public T next() { + return null; + } + }; + } + }); + } + + private void test(Function function) { } +} \ No newline at end of file diff --git a/test/langtools/tools/javac/annotations/ApplicableAnnotationsOnRecords.java b/test/langtools/tools/javac/annotations/ApplicableAnnotationsOnRecords.java index 05845700903..da5c6746047 100644 --- a/test/langtools/tools/javac/annotations/ApplicableAnnotationsOnRecords.java +++ b/test/langtools/tools/javac/annotations/ApplicableAnnotationsOnRecords.java @@ -24,12 +24,11 @@ /* * @test * @summary test for com.sun.tools.javac.comp.Check::validateAnnotation, com.sun.tools.javac.code.SymbolMetadata::removeDeclarationMetadata and ::removeFromCompoundList - * @bug 8241312 + * @bug 8241312 8246774 * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.util * jdk.jdeps/com.sun.tools.classfile - * @compile --enable-preview -source ${jdk.version} ApplicableAnnotationsOnRecords.java - * @run main/othervm --enable-preview ApplicableAnnotationsOnRecords + * @run main ApplicableAnnotationsOnRecords */ import com.sun.tools.classfile.*; import com.sun.tools.javac.util.Assert; diff --git a/test/langtools/tools/javac/annotations/repeatingAnnotations/combo/TargetAnnoCombo.java b/test/langtools/tools/javac/annotations/repeatingAnnotations/combo/TargetAnnoCombo.java index b22b3d10638..bf03407f754 100644 --- a/test/langtools/tools/javac/annotations/repeatingAnnotations/combo/TargetAnnoCombo.java +++ b/test/langtools/tools/javac/annotations/repeatingAnnotations/combo/TargetAnnoCombo.java @@ -23,7 +23,7 @@ /* * @test - * @bug 7151010 8006547 8007766 8029017 + * @bug 7151010 8006547 8007766 8029017 8246774 * @summary Default test cases for running combinations for Target values * @modules jdk.compiler * @build Helper @@ -204,25 +204,22 @@ public static void main(String args[]) throws Exception { } } - // options to be passed if all targets, including RECORD_COMPONENTS, are to be considered - List previewOptions = List.of( - "--enable-preview", - "-source", Integer.toString(Runtime.version().feature()) - ); + // options to be passed if target RECORD_COMPONENT can't be considered + List source8 = List.of("-source", "8"); private void generate() { // Adding test cases to run. testCases.addAll(Arrays.asList( // No base target against no container target. - new TestCase(noSet, noSet), + /* 0*/ new TestCase(noSet, noSet), // No base target against empty container target. - new TestCase(noSet, empty), + /* 1*/ new TestCase(noSet, empty), // No base target against TYPE_USE only container target. - new TestCase(noSet, less(jdk8, TYPE_PARAMETER)), + new TestCase(noSet, less(jdk8, TYPE_PARAMETER), source8), // No base target against TYPE_PARAMETER only container target. - new TestCase(noSet, less(jdk8, TYPE_USE)), + new TestCase(noSet, less(jdk8, TYPE_USE), source8), // No base target against TYPE_USE + TYPE_PARAMETER only container target. - new TestCase(noSet, jdk8), + new TestCase(noSet, jdk8, source8), // No base target against TYPE_USE + some selection of jdk7 targets. new TestCase(noSet, plus(EnumSet.range(TYPE, LOCAL_VARIABLE), TYPE_USE)), @@ -233,7 +230,7 @@ private void generate() { new TestCase(noSet, plus(empty, TYPE)), new TestCase(noSet, plus(empty, PARAMETER)), new TestCase(noSet, plus(empty, PACKAGE)), - new TestCase(noSet, plus(empty, METHOD)), + /* 10*/ new TestCase(noSet, plus(empty, METHOD)), new TestCase(noSet, plus(empty, LOCAL_VARIABLE)), new TestCase(noSet, plus(empty, FIELD)), new TestCase(noSet, plus(empty, CONSTRUCTOR)), @@ -246,32 +243,32 @@ private void generate() { new TestCase(empty, plus(empty, TYPE)), new TestCase(empty, plus(empty, PARAMETER)), new TestCase(empty, plus(empty, PACKAGE)), - new TestCase(empty, plus(empty, METHOD)), + /* 20*/ new TestCase(empty, plus(empty, METHOD)), new TestCase(empty, plus(empty, LOCAL_VARIABLE)), new TestCase(empty, plus(empty, FIELD)), new TestCase(empty, plus(empty, CONSTRUCTOR)), new TestCase(empty, plus(empty, ANNOTATION_TYPE)), - new TestCase(empty, less(jdk8, TYPE_USE)), - new TestCase(empty, less(jdk8, TYPE_PARAMETER)), + new TestCase(empty, less(jdk8, TYPE_USE), source8), + new TestCase(empty, less(jdk8, TYPE_PARAMETER), source8), // No container target against all all-but one jdk7 targets. - new TestCase(less(jdk7, TYPE), noSet), - new TestCase(less(jdk7, PARAMETER), noSet), - new TestCase(less(jdk7, PACKAGE), noSet), - new TestCase(less(jdk7, METHOD), noSet), - new TestCase(less(jdk7, LOCAL_VARIABLE), noSet), - new TestCase(less(jdk7, FIELD), noSet), - new TestCase(less(jdk7, CONSTRUCTOR), noSet), - new TestCase(less(jdk7, ANNOTATION_TYPE), noSet), + new TestCase(less(jdk7, TYPE), noSet, source8), + new TestCase(less(jdk7, PARAMETER), noSet, source8), + new TestCase(less(jdk7, PACKAGE), noSet, source8), + /* 30*/ new TestCase(less(jdk7, METHOD), noSet, source8), + new TestCase(less(jdk7, LOCAL_VARIABLE), noSet, source8), + new TestCase(less(jdk7, FIELD), noSet, source8), + new TestCase(less(jdk7, CONSTRUCTOR), noSet, source8), + new TestCase(less(jdk7, ANNOTATION_TYPE), noSet, source8), // No container against all but TYPE and ANNOTATION_TYPE new TestCase(less(jdk7, TYPE, ANNOTATION_TYPE), noSet), // No container against jdk7 targets. - new TestCase(jdk7, noSet), + new TestCase(jdk7, noSet, source8), // No container against jdk7 targets plus one or both of TYPE_USE, TYPE_PARAMETER - new TestCase(plus(jdk7, TYPE_USE), noSet), - new TestCase(plus(jdk7, TYPE_PARAMETER), noSet), - new TestCase(allTargets, noSet, previewOptions), + new TestCase(plus(jdk7, TYPE_USE), noSet, source8), + new TestCase(plus(jdk7, TYPE_PARAMETER), noSet, source8), + new TestCase(allTargets, noSet, null), // Empty container target against any lone target. - new TestCase(plus(empty, TYPE), empty), + /* 40*/ new TestCase(plus(empty, TYPE), empty), new TestCase(plus(empty, PARAMETER), empty), new TestCase(plus(empty, PACKAGE), empty), new TestCase(plus(empty, METHOD), empty), @@ -282,34 +279,34 @@ private void generate() { new TestCase(plus(empty, TYPE_USE), empty), new TestCase(plus(empty, TYPE_PARAMETER), empty), // All base targets against all container targets. - new TestCase(allTargets, allTargets, previewOptions), + /* 50*/ new TestCase(allTargets, allTargets), // All base targets against all but one container targets. - new TestCase(allTargets, less(allTargets, TYPE), previewOptions), - new TestCase(allTargets, less(allTargets, PARAMETER), previewOptions), - new TestCase(allTargets, less(allTargets, PACKAGE), previewOptions), - new TestCase(allTargets, less(allTargets, METHOD), previewOptions), - new TestCase(allTargets, less(allTargets, LOCAL_VARIABLE), previewOptions), - new TestCase(allTargets, less(allTargets, FIELD), previewOptions), - new TestCase(allTargets, less(allTargets, CONSTRUCTOR), previewOptions), - new TestCase(allTargets, less(allTargets, ANNOTATION_TYPE), previewOptions), - new TestCase(allTargets, less(allTargets, TYPE_USE), previewOptions), - new TestCase(allTargets, less(allTargets, TYPE_PARAMETER), previewOptions), + new TestCase(allTargets, less(allTargets, TYPE)), + new TestCase(allTargets, less(allTargets, PARAMETER)), + new TestCase(allTargets, less(allTargets, PACKAGE)), + new TestCase(allTargets, less(allTargets, METHOD)), + new TestCase(allTargets, less(allTargets, LOCAL_VARIABLE)), + new TestCase(allTargets, less(allTargets, FIELD)), + new TestCase(allTargets, less(allTargets, CONSTRUCTOR)), + new TestCase(allTargets, less(allTargets, ANNOTATION_TYPE)), + new TestCase(allTargets, less(allTargets, TYPE_USE)), + /* 60*/ new TestCase(allTargets, less(allTargets, TYPE_PARAMETER)), // All container targets against all but one base targets. - new TestCase(less(allTargets, TYPE), allTargets, previewOptions), - new TestCase(less(allTargets, PARAMETER), allTargets, previewOptions), - new TestCase(less(allTargets, PACKAGE), allTargets, previewOptions), - new TestCase(less(allTargets, METHOD), allTargets, previewOptions), - new TestCase(less(allTargets, LOCAL_VARIABLE), allTargets, previewOptions), - new TestCase(less(allTargets, FIELD), allTargets, previewOptions), - new TestCase(less(allTargets, CONSTRUCTOR), allTargets, previewOptions), - new TestCase(less(allTargets, ANNOTATION_TYPE), allTargets, previewOptions), - new TestCase(less(allTargets, TYPE_USE), allTargets, previewOptions), - new TestCase(less(allTargets, TYPE_PARAMETER), allTargets, previewOptions))); + new TestCase(less(allTargets, TYPE), allTargets), + new TestCase(less(allTargets, PARAMETER), allTargets), + new TestCase(less(allTargets, PACKAGE), allTargets), + new TestCase(less(allTargets, METHOD), allTargets), + new TestCase(less(allTargets, LOCAL_VARIABLE), allTargets), + new TestCase(less(allTargets, FIELD), allTargets), + new TestCase(less(allTargets, CONSTRUCTOR), allTargets), + new TestCase(less(allTargets, ANNOTATION_TYPE), allTargets), + new TestCase(less(allTargets, TYPE_USE), allTargets), + /* 70*/ new TestCase(less(allTargets, TYPE_PARAMETER), allTargets))); // Generates 100 test cases for any lone base target contained in Set // allTargets against any lone container target. for (ElementType b : allTargets) { for (ElementType c : allTargets) { - testCases.add(new TestCase(plus(empty, b), plus(empty, c), previewOptions)); + testCases.add(new TestCase(plus(empty, b), plus(empty, c))); } } } @@ -456,7 +453,7 @@ private boolean getCompileResult(String className, boolean shouldCompile, if (allDiagnostics.stream().noneMatch(d -> d.getKind() == javax.tools.Diagnostic.Kind.ERROR)) { ok = true; } else { - errMesg = "Test failed, compiled unexpectedly."; + errMesg = "Test failed, should have compiled successfully."; ok = false; } } else { diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsPositionsOnRecords.java b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsPositionsOnRecords.java index 17b94730e54..551bd981c21 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsPositionsOnRecords.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsPositionsOnRecords.java @@ -23,6 +23,7 @@ /* * @test + * @bug 8246774 * @summary Verify location of type annotations on records * @library /tools/lib * @modules @@ -32,8 +33,7 @@ * jdk.compiler/com.sun.tools.javac.code * jdk.compiler/com.sun.tools.javac.util * @build toolbox.ToolBox toolbox.JavacTask - * @compile --enable-preview -source ${jdk.version} TypeAnnotationsPositionsOnRecords.java - * @run main/othervm --enable-preview TypeAnnotationsPositionsOnRecords + * @run main TypeAnnotationsPositionsOnRecords */ import java.util.List; @@ -105,7 +105,6 @@ void run() throws Exception { void compileTestClass() throws Exception { new JavacTask(tb) .sources(src) - .options("--enable-preview", "-source", Integer.toString(Runtime.version().feature())) .run(); } diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest2.java b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest2.java index 9b33abaeabf..bfeac8f8f55 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest2.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest2.java @@ -399,14 +399,14 @@ String sourceString(String testname, String retentn, String annot2, case src7p: // (repeating) type annotations in use of instanceof with type test pattern /* * class Test10{ - * String data = "test"; + * Object data = "test"; * boolean dataIsString = ( data instanceof @A @B @A @B String str); * } */ source = new String( source + "// " + src.description + "\n" + "class "+ testname + "{\n" + - " String data = \"test\";\n" + + " Object data = \"test\";\n" + " boolean dataIsString = ( data instanceof _As_ _Bs_ String str && str.isEmpty());\n" + "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) + "\n\n"; @@ -455,7 +455,7 @@ String sourceString(String testname, String retentn, String annot2, source = new String( source + "// " + src.description + "\n" + "class "+ testname + "{\n" + - " String data = \"test\";\n" + + " Object data = \"test\";\n" + " Boolean isString() { \n" + " if( data instanceof _As_ _Bs_ String str)\n" + " return true;\n" + diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/AnnotatedImport.out b/test/langtools/tools/javac/annotations/typeAnnotations/failures/AnnotatedImport.out index 91589d9252e..301f7335520 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/failures/AnnotatedImport.out +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/AnnotatedImport.out @@ -1,7 +1,7 @@ AnnotatedImport.java:10:13: compiler.err.expected: token.identifier -AnnotatedImport.java:10:16: compiler.err.expected3: class, interface, enum +AnnotatedImport.java:10:16: compiler.err.expected4: class, interface, enum, record AnnotatedImport.java:11:7: compiler.err.expected: token.identifier -AnnotatedImport.java:11:11: compiler.err.expected3: class, interface, enum +AnnotatedImport.java:11:11: compiler.err.expected4: class, interface, enum, record AnnotatedImport.java:12:18: compiler.err.expected: token.identifier -AnnotatedImport.java:12:21: compiler.err.expected3: class, interface, enum +AnnotatedImport.java:12:21: compiler.err.expected4: class, interface, enum, record 6 errors diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/AnnotatedPackage1.out b/test/langtools/tools/javac/annotations/typeAnnotations/failures/AnnotatedPackage1.out index b2c1770b3b5..cb2e66ca6db 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/failures/AnnotatedPackage1.out +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/AnnotatedPackage1.out @@ -1,3 +1,3 @@ AnnotatedPackage1.java:9:14: compiler.err.expected: token.identifier -AnnotatedPackage1.java:9:17: compiler.err.expected3: class, interface, enum +AnnotatedPackage1.java:9:17: compiler.err.expected4: class, interface, enum, record 2 errors diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/AnnotatedPackage2.out b/test/langtools/tools/javac/annotations/typeAnnotations/failures/AnnotatedPackage2.out index 77b25afc5e0..c511caf6546 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/failures/AnnotatedPackage2.out +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/AnnotatedPackage2.out @@ -1,3 +1,3 @@ AnnotatedPackage2.java:9:8: compiler.err.expected: token.identifier -AnnotatedPackage2.java:9:12: compiler.err.expected3: class, interface, enum +AnnotatedPackage2.java:9:12: compiler.err.expected4: class, interface, enum, record 2 errors diff --git a/test/langtools/tools/javac/api/TestGetScopeResult.java b/test/langtools/tools/javac/api/TestGetScopeResult.java index b2a2e500e03..60913ac0050 100644 --- a/test/langtools/tools/javac/api/TestGetScopeResult.java +++ b/test/langtools/tools/javac/api/TestGetScopeResult.java @@ -23,13 +23,12 @@ /* * @test - * @bug 8205418 8207229 8207230 8230847 8245786 8247334 8248641 8240658 + * @bug 8205418 8207229 8207230 8230847 8245786 8247334 8248641 8240658 8246774 * @summary Test the outcomes from Trees.getScope * @modules jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.comp * jdk.compiler/com.sun.tools.javac.tree * jdk.compiler/com.sun.tools.javac.util - * @compile TestGetScopeResult.java */ import java.io.IOException; @@ -559,9 +558,7 @@ public String getCharContent(boolean ignoreEncodingErrors) { } Context ctx = new Context(); TestAnalyzer.preRegister(ctx); - List options = List.of("--enable-preview", - "-source", System.getProperty("java.specification.version")); - JavacTask t = (JavacTask) c.getTask(null, fm, null, options, null, + JavacTask t = (JavacTask) c.getTask(null, fm, null, null, null, List.of(new MyFileObject()), ctx); CompilationUnitTree cut = t.parse().iterator().next(); t.analyze(); @@ -636,9 +633,7 @@ public String getCharContent(boolean ignoreEncodingErrors) { } Context ctx = new Context(); TestAnalyzer.preRegister(ctx); - List options = List.of("--enable-preview", - "-source", System.getProperty("java.specification.version")); - JavacTask t = (JavacTask) c.getTask(null, fm, null, options, null, + JavacTask t = (JavacTask) c.getTask(null, fm, null, null, null, List.of(new MyFileObject()), ctx); CompilationUnitTree cut = t.parse().iterator().next(); t.analyze(); diff --git a/test/langtools/tools/javac/diags/examples.not-yet.txt b/test/langtools/tools/javac/diags/examples.not-yet.txt index 78b4eafb7e9..0161b2b7d76 100644 --- a/test/langtools/tools/javac/diags/examples.not-yet.txt +++ b/test/langtools/tools/javac/diags/examples.not-yet.txt @@ -44,7 +44,6 @@ compiler.err.stack.sim.error compiler.err.type.var.more.than.once # UNUSED compiler.err.type.var.more.than.once.in.result # UNUSED compiler.err.unexpected.type -compiler.err.unsupported.cross.fp.lit # Scanner: host system dependent compiler.misc.bad.class.signature # bad class file compiler.misc.bad.const.pool.tag # bad class file compiler.misc.bad.const.pool.tag.at # bad class file @@ -162,7 +161,6 @@ compiler.misc.locn.module_source_path # fragment uninter compiler.misc.locn.system_modules # fragment uninteresting in and of itself compiler.misc.locn.upgrade_module_path # fragment uninteresting in and of itself compiler.misc.inferred.do.not.conform.to.eq.bounds # hard to generate, could probably be removed -compiler.err.feature.not.supported.in.source # Generated for using diamond before source 7 # The following are new module-related messages, that need new examples to be created compiler.err.duplicate.module.on.path diff --git a/test/langtools/tools/javac/diags/examples/AccessorCantBeGeneric.java b/test/langtools/tools/javac/diags/examples/AccessorCantBeGeneric.java index 9594b5a561a..8027475b90b 100644 --- a/test/langtools/tools/javac/diags/examples/AccessorCantBeGeneric.java +++ b/test/langtools/tools/javac/diags/examples/AccessorCantBeGeneric.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,9 +23,6 @@ // key: compiler.err.invalid.accessor.method.in.record // key: compiler.misc.accessor.method.must.not.be.generic -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview -source ${jdk.version} record R(int i) { public int i() { diff --git a/test/langtools/tools/javac/diags/examples/AccessorCantThrowException.java b/test/langtools/tools/javac/diags/examples/AccessorCantThrowException.java index f4dcbf305a7..47e2566348e 100644 --- a/test/langtools/tools/javac/diags/examples/AccessorCantThrowException.java +++ b/test/langtools/tools/javac/diags/examples/AccessorCantThrowException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,9 +23,6 @@ // key: compiler.err.invalid.accessor.method.in.record // key: compiler.misc.accessor.method.cant.throw.exception -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview -source ${jdk.version} record R(int i) { public int i() throws ArithmeticException { diff --git a/test/langtools/tools/javac/diags/examples/AccessorMethodCantBeStatic.java b/test/langtools/tools/javac/diags/examples/AccessorMethodCantBeStatic.java index 7299c746207..868a4dd1821 100644 --- a/test/langtools/tools/javac/diags/examples/AccessorMethodCantBeStatic.java +++ b/test/langtools/tools/javac/diags/examples/AccessorMethodCantBeStatic.java @@ -23,9 +23,6 @@ // key: compiler.err.invalid.accessor.method.in.record // key: compiler.misc.accessor.method.must.not.be.static -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview -source ${jdk.version} record R(int x) { static final int j = 0; diff --git a/test/langtools/tools/javac/diags/examples/AccessorReturnTypeDoesntMatch.java b/test/langtools/tools/javac/diags/examples/AccessorReturnTypeDoesntMatch.java index b8af12cc6f9..a11c79b9161 100644 --- a/test/langtools/tools/javac/diags/examples/AccessorReturnTypeDoesntMatch.java +++ b/test/langtools/tools/javac/diags/examples/AccessorReturnTypeDoesntMatch.java @@ -23,9 +23,6 @@ // key: compiler.err.invalid.accessor.method.in.record // key: compiler.misc.accessor.return.type.doesnt.match -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview -source ${jdk.version} import java.util.List; record R(List x) { diff --git a/test/langtools/tools/javac/diags/examples/CanonicalCantHaveStrongerAccessPrivileges.java b/test/langtools/tools/javac/diags/examples/CanonicalCantHaveStrongerAccessPrivileges.java index 7364e698543..2104d3a9287 100644 --- a/test/langtools/tools/javac/diags/examples/CanonicalCantHaveStrongerAccessPrivileges.java +++ b/test/langtools/tools/javac/diags/examples/CanonicalCantHaveStrongerAccessPrivileges.java @@ -23,10 +23,7 @@ // key: compiler.err.invalid.canonical.constructor.in.record // key: compiler.misc.canonical.must.not.have.stronger.access -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile // key: compiler.misc.canonical -// options: --enable-preview -source ${jdk.version} public record CanonicalCantHaveStrongerAccessPrivileges() { private CanonicalCantHaveStrongerAccessPrivileges {} diff --git a/test/langtools/tools/javac/diags/examples/CanonicalCantInvokeOtherConstructor.java b/test/langtools/tools/javac/diags/examples/CanonicalCantInvokeOtherConstructor.java index 89dc304bcfc..1c62e2ede88 100644 --- a/test/langtools/tools/javac/diags/examples/CanonicalCantInvokeOtherConstructor.java +++ b/test/langtools/tools/javac/diags/examples/CanonicalCantInvokeOtherConstructor.java @@ -23,10 +23,7 @@ // key: compiler.err.invalid.canonical.constructor.in.record // key: compiler.misc.canonical.must.not.contain.explicit.constructor.invocation -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile // key: compiler.misc.canonical -// options: --enable-preview -source ${jdk.version} record R(int x) { public R(int x) { super(); this.x = x; } diff --git a/test/langtools/tools/javac/diags/examples/CanonicalConstructorArgumentMismatch.java b/test/langtools/tools/javac/diags/examples/CanonicalConstructorArgumentMismatch.java index 91d7fcb9881..621d26dbdd6 100644 --- a/test/langtools/tools/javac/diags/examples/CanonicalConstructorArgumentMismatch.java +++ b/test/langtools/tools/javac/diags/examples/CanonicalConstructorArgumentMismatch.java @@ -23,10 +23,7 @@ // key: compiler.err.invalid.canonical.constructor.in.record // key: compiler.misc.canonical.with.name.mismatch -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile // key: compiler.misc.canonical -// options: --enable-preview -source ${jdk.version} record R(int x) { public R(int _x) { this.x = _x; } diff --git a/test/langtools/tools/javac/diags/examples/CanonicalConstructorCantHaveReturn.java b/test/langtools/tools/javac/diags/examples/CanonicalConstructorCantHaveReturn.java index 0033ac8761d..6f86a867bd7 100644 --- a/test/langtools/tools/javac/diags/examples/CanonicalConstructorCantHaveReturn.java +++ b/test/langtools/tools/javac/diags/examples/CanonicalConstructorCantHaveReturn.java @@ -23,10 +23,7 @@ // key: compiler.err.invalid.canonical.constructor.in.record // key: compiler.misc.canonical.cant.have.return.statement -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile // key: compiler.misc.compact -// options: --enable-preview -source ${jdk.version} record R() { public R { diff --git a/test/langtools/tools/javac/diags/examples/CanonicalConstructorCantHaveThrowsClause.java b/test/langtools/tools/javac/diags/examples/CanonicalConstructorCantHaveThrowsClause.java index 6c3e165ead9..26003a3a1a7 100644 --- a/test/langtools/tools/javac/diags/examples/CanonicalConstructorCantHaveThrowsClause.java +++ b/test/langtools/tools/javac/diags/examples/CanonicalConstructorCantHaveThrowsClause.java @@ -23,10 +23,7 @@ // key: compiler.err.invalid.canonical.constructor.in.record // key: compiler.misc.throws.clause.not.allowed.for.canonical.constructor -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile // key: compiler.misc.canonical -// options: --enable-preview -source ${jdk.version} record R(int i) { public R(int i) throws Exception { diff --git a/test/langtools/tools/javac/diags/examples/CanonicalMustNotDeclareTypeVariables.java b/test/langtools/tools/javac/diags/examples/CanonicalMustNotDeclareTypeVariables.java index 5ab9185a7ed..1060955d102 100644 --- a/test/langtools/tools/javac/diags/examples/CanonicalMustNotDeclareTypeVariables.java +++ b/test/langtools/tools/javac/diags/examples/CanonicalMustNotDeclareTypeVariables.java @@ -23,10 +23,7 @@ // key: compiler.err.invalid.canonical.constructor.in.record // key: compiler.misc.canonical.must.not.declare.type.variables -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile // key: compiler.misc.canonical -// options: --enable-preview -source ${jdk.version} record R(int i) { public R(int i) { diff --git a/test/langtools/tools/javac/diags/examples/ConstructorWithSameErasureAsCanonical.java b/test/langtools/tools/javac/diags/examples/ConstructorWithSameErasureAsCanonical.java index dcccae65d66..28d485c48ef 100644 --- a/test/langtools/tools/javac/diags/examples/ConstructorWithSameErasureAsCanonical.java +++ b/test/langtools/tools/javac/diags/examples/ConstructorWithSameErasureAsCanonical.java @@ -23,10 +23,7 @@ // key: compiler.err.invalid.canonical.constructor.in.record // key: compiler.misc.type.must.be.identical.to.corresponding.record.component.type -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile // key: compiler.misc.canonical -// options: --enable-preview -source ${jdk.version} import java.util.List; diff --git a/test/langtools/tools/javac/diags/examples/Expected3.java b/test/langtools/tools/javac/diags/examples/Expected3.java index 72fd7d7c43c..2b1ec325618 100644 --- a/test/langtools/tools/javac/diags/examples/Expected3.java +++ b/test/langtools/tools/javac/diags/examples/Expected3.java @@ -22,5 +22,7 @@ */ // key: compiler.err.expected3 +// key: compiler.warn.source.no.system.modules.path +// options: -source 15 int Expected3; diff --git a/test/langtools/tools/javac/diags/examples/ReifiableTypesInstanceof.java b/test/langtools/tools/javac/diags/examples/FeatureReifiableTypesInstanceof.java similarity index 74% rename from test/langtools/tools/javac/diags/examples/ReifiableTypesInstanceof.java rename to test/langtools/tools/javac/diags/examples/FeatureReifiableTypesInstanceof.java index 295b3dc3279..6304c890334 100644 --- a/test/langtools/tools/javac/diags/examples/ReifiableTypesInstanceof.java +++ b/test/langtools/tools/javac/diags/examples/FeatureReifiableTypesInstanceof.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,14 +21,13 @@ * questions. */ +// key: compiler.err.feature.not.supported.in.source.plural // key: compiler.misc.feature.reifiable.types.instanceof -// key: compiler.warn.preview.feature.use.plural -// options: --enable-preview -source ${jdk.version} -Xlint:preview +// options: -source 15 -Xlint:-options -class PatternMatchingInstanceof { - boolean m(I i) { - return i instanceof C; - } - interface I {} - class C implements I {} +import java.util.*; + +class FeatureReifiableTypesInstanceof { + List o; + boolean b = (o instanceof List); } diff --git a/test/langtools/tools/javac/diags/examples/FirstInvocationMustBeAnotherConstructor.java b/test/langtools/tools/javac/diags/examples/FirstInvocationMustBeAnotherConstructor.java index 2fdb1c4f5ae..e2478244cc9 100644 --- a/test/langtools/tools/javac/diags/examples/FirstInvocationMustBeAnotherConstructor.java +++ b/test/langtools/tools/javac/diags/examples/FirstInvocationMustBeAnotherConstructor.java @@ -22,9 +22,6 @@ */ // key: compiler.err.first.statement.must.be.call.to.another.constructor -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview -source ${jdk.version} record R(int x) { public R(int x, int y) { this.x = x; } diff --git a/test/langtools/tools/javac/diags/examples/IllegalRecordComponentName.java b/test/langtools/tools/javac/diags/examples/IllegalRecordComponentName.java index 483ac93a7e7..45a7a153839 100644 --- a/test/langtools/tools/javac/diags/examples/IllegalRecordComponentName.java +++ b/test/langtools/tools/javac/diags/examples/IllegalRecordComponentName.java @@ -22,8 +22,5 @@ */ // key: compiler.err.illegal.record.component.name -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview -source ${jdk.version} record R(int hashCode) {} diff --git a/test/langtools/tools/javac/diags/examples/IncorrectRecordDeclaration.java b/test/langtools/tools/javac/diags/examples/IncorrectRecordDeclaration.java index acaecb37755..dc1dd8af632 100644 --- a/test/langtools/tools/javac/diags/examples/IncorrectRecordDeclaration.java +++ b/test/langtools/tools/javac/diags/examples/IncorrectRecordDeclaration.java @@ -22,8 +22,5 @@ */ // key: compiler.err.record.header.expected -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview -source ${jdk.version} record R {} diff --git a/test/langtools/tools/javac/diags/examples/InstanceInitializersNotAllowedInRecords.java b/test/langtools/tools/javac/diags/examples/InstanceInitializersNotAllowedInRecords.java index 6b57276d865..b12c26ba0a5 100644 --- a/test/langtools/tools/javac/diags/examples/InstanceInitializersNotAllowedInRecords.java +++ b/test/langtools/tools/javac/diags/examples/InstanceInitializersNotAllowedInRecords.java @@ -22,9 +22,6 @@ */ // key: compiler.err.instance.initializer.not.allowed.in.records -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview -source ${jdk.version} record R(int x) { {} diff --git a/test/langtools/tools/javac/diags/examples/InvalidInstanceof.java b/test/langtools/tools/javac/diags/examples/InstanceofPatternNoSubtype.java similarity index 81% rename from test/langtools/tools/javac/diags/examples/InvalidInstanceof.java rename to test/langtools/tools/javac/diags/examples/InstanceofPatternNoSubtype.java index 311af828743..afd4cc92634 100644 --- a/test/langtools/tools/javac/diags/examples/InvalidInstanceof.java +++ b/test/langtools/tools/javac/diags/examples/InstanceofPatternNoSubtype.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,11 +21,10 @@ * questions. */ -// key: compiler.err.illegal.generic.type.for.instof +// key: compiler.err.instanceof.pattern.no.subtype -import java.util.*; - -class IllegalInstanceof { - List o; - boolean b = (o instanceof List); +class InstanceofPatternNoSubtype { + boolean test(Object o) { + return o instanceof Object obj; + } } diff --git a/test/langtools/tools/javac/diags/examples/InstanceofReifiableNotSafe.java b/test/langtools/tools/javac/diags/examples/InstanceofReifiableNotSafe.java index f0c7fb3451f..28c8720aaac 100644 --- a/test/langtools/tools/javac/diags/examples/InstanceofReifiableNotSafe.java +++ b/test/langtools/tools/javac/diags/examples/InstanceofReifiableNotSafe.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,9 +22,6 @@ */ // key: compiler.err.instanceof.reifiable.not.safe -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview -source ${jdk.version} import java.util.List; diff --git a/test/langtools/tools/javac/diags/examples/InterfaceNotAllowed.java b/test/langtools/tools/javac/diags/examples/InterfaceNotAllowed.java index 3c3feb692d4..a72e7ef12bd 100644 --- a/test/langtools/tools/javac/diags/examples/InterfaceNotAllowed.java +++ b/test/langtools/tools/javac/diags/examples/InterfaceNotAllowed.java @@ -22,6 +22,8 @@ */ // key: compiler.err.intf.not.allowed.here +// key: compiler.warn.source.no.system.modules.path +// options: -source 15 class InterfaceNotAllowed { void m() { diff --git a/test/langtools/tools/javac/diags/examples/InvalidSuperTypeRecord.java b/test/langtools/tools/javac/diags/examples/InvalidSuperTypeRecord.java index 8e8b0599c13..d12665681bf 100644 --- a/test/langtools/tools/javac/diags/examples/InvalidSuperTypeRecord.java +++ b/test/langtools/tools/javac/diags/examples/InvalidSuperTypeRecord.java @@ -22,7 +22,6 @@ */ // key: compiler.err.invalid.supertype.record -// options: --enable-preview -source ${jdk.version} @SuppressWarnings("preview") class R extends Record {} diff --git a/test/langtools/tools/javac/diags/examples/KindnameRecord.java b/test/langtools/tools/javac/diags/examples/KindnameRecord.java index 3ee2450e9ed..056e43a0205 100644 --- a/test/langtools/tools/javac/diags/examples/KindnameRecord.java +++ b/test/langtools/tools/javac/diags/examples/KindnameRecord.java @@ -27,10 +27,6 @@ // key: compiler.misc.kindname.method // key: compiler.err.already.defined // key: compiler.err.error -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// key: compiler.note.note // run: backdoor -// options: --enable-preview -source ${jdk.version} record R(int i, int i) {} diff --git a/test/langtools/tools/javac/diags/examples/LocalEnum.java b/test/langtools/tools/javac/diags/examples/LocalEnum.java index 431d6d931e9..fdf875c9697 100644 --- a/test/langtools/tools/javac/diags/examples/LocalEnum.java +++ b/test/langtools/tools/javac/diags/examples/LocalEnum.java @@ -22,6 +22,8 @@ */ // key: compiler.err.local.enum +// key: compiler.warn.source.no.system.modules.path +// options: -source 15 class LocalEnum { void m() { diff --git a/test/langtools/tools/javac/diags/examples/MatchBindingExists.java b/test/langtools/tools/javac/diags/examples/MatchBindingExists.java index 008db333645..03a5a9ecc87 100644 --- a/test/langtools/tools/javac/diags/examples/MatchBindingExists.java +++ b/test/langtools/tools/javac/diags/examples/MatchBindingExists.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,9 +22,6 @@ */ // key: compiler.err.match.binding.exists -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview -source ${jdk.version} class MatchBindingExists { public void test(Object o1, Object o2) { diff --git a/test/langtools/tools/javac/diags/examples/MethodMustBePublic.java b/test/langtools/tools/javac/diags/examples/MethodMustBePublic.java index c104261d361..fb48b1d7578 100644 --- a/test/langtools/tools/javac/diags/examples/MethodMustBePublic.java +++ b/test/langtools/tools/javac/diags/examples/MethodMustBePublic.java @@ -23,9 +23,6 @@ // key: compiler.err.invalid.accessor.method.in.record // key: compiler.misc.method.must.be.public -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview -source ${jdk.version} record R(int x) { private int x() { return x; } diff --git a/test/langtools/tools/javac/diags/examples/PatternMatchingInstanceof.java b/test/langtools/tools/javac/diags/examples/PatternMatchingInstanceof.java index 9c71e4e49ce..6c276721c2e 100644 --- a/test/langtools/tools/javac/diags/examples/PatternMatchingInstanceof.java +++ b/test/langtools/tools/javac/diags/examples/PatternMatchingInstanceof.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ */ // key: compiler.misc.feature.pattern.matching.instanceof -// key: compiler.warn.preview.feature.use -// options: --enable-preview -source ${jdk.version} -Xlint:preview +// key: compiler.err.feature.not.supported.in.source +// options: -source 15 -Xlint:-options class PatternMatchingInstanceof { boolean m(Object o) { diff --git a/test/langtools/tools/javac/diags/examples/Records.java b/test/langtools/tools/javac/diags/examples/Records.java index 65b2c380cd6..9acc44d0f43 100644 --- a/test/langtools/tools/javac/diags/examples/Records.java +++ b/test/langtools/tools/javac/diags/examples/Records.java @@ -22,7 +22,8 @@ */ // key: compiler.misc.feature.records -// key: compiler.warn.preview.feature.use.plural -// options: --enable-preview -source ${jdk.version} -Xlint:preview +// key: compiler.err.feature.not.supported.in.source.plural +// key: compiler.warn.source.no.system.modules.path +// options: -source 15 record R() {} diff --git a/test/langtools/tools/javac/diags/examples/RecordsCanNotDeclareInstanceFields.java b/test/langtools/tools/javac/diags/examples/RecordsCanNotDeclareInstanceFields.java index 20f9ab1ccf0..d98bcae9911 100644 --- a/test/langtools/tools/javac/diags/examples/RecordsCanNotDeclareInstanceFields.java +++ b/test/langtools/tools/javac/diags/examples/RecordsCanNotDeclareInstanceFields.java @@ -22,9 +22,6 @@ */ // key: compiler.err.record.cannot.declare.instance.fields -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview -source ${jdk.version} record R(int i) { private final int y = 0; diff --git a/test/langtools/tools/javac/diags/examples/RecordsCantDeclareComponentModifiers.java b/test/langtools/tools/javac/diags/examples/RecordsCantDeclareComponentModifiers.java index 3fda0742cf4..2bf01b9e7a3 100644 --- a/test/langtools/tools/javac/diags/examples/RecordsCantDeclareComponentModifiers.java +++ b/test/langtools/tools/javac/diags/examples/RecordsCantDeclareComponentModifiers.java @@ -22,8 +22,5 @@ */ // key: compiler.err.record.cant.declare.field.modifiers -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview -source ${jdk.version} record R(final int x) {} diff --git a/test/langtools/tools/javac/diags/examples/RecordsComponentsCanNotDeclareCStyleArrays.java b/test/langtools/tools/javac/diags/examples/RecordsComponentsCanNotDeclareCStyleArrays.java index 6d2d5186e24..dcc06349038 100644 --- a/test/langtools/tools/javac/diags/examples/RecordsComponentsCanNotDeclareCStyleArrays.java +++ b/test/langtools/tools/javac/diags/examples/RecordsComponentsCanNotDeclareCStyleArrays.java @@ -22,8 +22,5 @@ */ // key: compiler.err.record.component.and.old.array.syntax -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview -source ${jdk.version} record R(int i[]) {} diff --git a/test/langtools/tools/javac/diags/examples/RecordsNotAllowedInInnerClasses.java b/test/langtools/tools/javac/diags/examples/RecordsNotAllowedInInnerClasses.java index 99000219c42..1016a874e00 100644 --- a/test/langtools/tools/javac/diags/examples/RecordsNotAllowedInInnerClasses.java +++ b/test/langtools/tools/javac/diags/examples/RecordsNotAllowedInInnerClasses.java @@ -22,9 +22,6 @@ */ // key: compiler.err.static.declaration.not.allowed.in.inner.classes -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview -source ${jdk.version} class Outer { class Inner { diff --git a/test/langtools/tools/javac/enum/FauxEnum3.java b/test/langtools/tools/javac/enum/FauxEnum3.java index 83bb06f80ee..ec53aeb5951 100644 --- a/test/langtools/tools/javac/enum/FauxEnum3.java +++ b/test/langtools/tools/javac/enum/FauxEnum3.java @@ -5,7 +5,6 @@ * @author Joseph D. Darcy * * @compile/fail/ref=FauxEnum3.out -XDrawDiagnostics FauxEnum3.java - * @compile/fail/ref=FauxEnum3.out -XDrawDiagnostics --enable-preview -source ${jdk.version} FauxEnum3.java */ public final class FauxEnum3 extends SpecializedEnum {} diff --git a/test/langtools/tools/javac/enum/FauxEnum3.out b/test/langtools/tools/javac/enum/FauxEnum3.out index aaf4855e215..885154d073a 100644 --- a/test/langtools/tools/javac/enum/FauxEnum3.out +++ b/test/langtools/tools/javac/enum/FauxEnum3.out @@ -1,2 +1,2 @@ -FauxEnum3.java:11:14: compiler.err.enum.types.not.extensible -1 error \ No newline at end of file +FauxEnum3.java:10:14: compiler.err.enum.types.not.extensible +1 error diff --git a/test/langtools/tools/javac/enum/LocalEnum.java b/test/langtools/tools/javac/enum/LocalEnum.java index 0cbb0bf438c..331992b3565 100644 --- a/test/langtools/tools/javac/enum/LocalEnum.java +++ b/test/langtools/tools/javac/enum/LocalEnum.java @@ -1,10 +1,10 @@ /* * @test /nodynamiccopyright/ - * @bug 5019609 + * @bug 5019609 8246774 * @summary javac fails to reject local enums * @author gafter - * @compile/fail/ref=LocalEnum.out -XDrawDiagnostics LocalEnum.java - * @compile --enable-preview -source ${jdk.version} LocalEnum.java + * @compile/fail/ref=LocalEnum.out -XDrawDiagnostics -source 15 LocalEnum.java + * @compile LocalEnum.java */ public class LocalEnum { diff --git a/test/langtools/tools/javac/enum/LocalEnum.out b/test/langtools/tools/javac/enum/LocalEnum.out index ee4cc2d10f5..a01b8555456 100644 --- a/test/langtools/tools/javac/enum/LocalEnum.out +++ b/test/langtools/tools/javac/enum/LocalEnum.out @@ -1,2 +1,4 @@ +- compiler.warn.source.no.system.modules.path: 15 LocalEnum.java:12:9: compiler.err.local.enum 1 error +1 warning diff --git a/test/langtools/tools/javac/generics/InstanceOf2.out b/test/langtools/tools/javac/generics/InstanceOf2.out index b6a1886d6bf..5b1e599e81e 100644 --- a/test/langtools/tools/javac/generics/InstanceOf2.out +++ b/test/langtools/tools/javac/generics/InstanceOf2.out @@ -1,2 +1,2 @@ -InstanceOf2.java:12:48: compiler.err.illegal.generic.type.for.instof +InstanceOf2.java:12:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Class, java.lang.Class 1 error diff --git a/test/langtools/tools/javac/generics/InstanceOf3.java b/test/langtools/tools/javac/generics/InstanceOf3.java index 8a3d327fbc1..f4f5529668e 100644 --- a/test/langtools/tools/javac/generics/InstanceOf3.java +++ b/test/langtools/tools/javac/generics/InstanceOf3.java @@ -4,7 +4,7 @@ * @summary the type in an instanceof expression must be reifiable * @author seligman * - * @compile/fail/ref=InstanceOf3.out -XDrawDiagnostics InstanceOf3.java + * @compile/fail/ref=InstanceOf3.out -XDrawDiagnostics -source 15 -Xlint:-options InstanceOf3.java */ public class InstanceOf3 { diff --git a/test/langtools/tools/javac/generics/InstanceOf3.out b/test/langtools/tools/javac/generics/InstanceOf3.out index a31cc15f569..1056423911b 100644 --- a/test/langtools/tools/javac/generics/InstanceOf3.out +++ b/test/langtools/tools/javac/generics/InstanceOf3.out @@ -1,2 +1,2 @@ -InstanceOf3.java:12:48: compiler.err.illegal.generic.type.for.instof +InstanceOf3.java:12:32: compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.reifiable.types.instanceof), 15, 16 1 error diff --git a/test/langtools/tools/javac/generics/odersky/BadTest-old.out b/test/langtools/tools/javac/generics/odersky/BadTest-old.out new file mode 100644 index 00000000000..9a3385d0fb0 --- /dev/null +++ b/test/langtools/tools/javac/generics/odersky/BadTest-old.out @@ -0,0 +1,5 @@ +BadTest.java:19:50: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: List, List>) +BadTest.java:23:48: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: List, List) +BadTest.java:24:54: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: List, List>) +BadTest.java:26:38: compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.reifiable.types.instanceof), 15, 16 +4 errors diff --git a/test/langtools/tools/javac/generics/odersky/BadTest.java b/test/langtools/tools/javac/generics/odersky/BadTest.java index 3e97080c965..7ab521a2eb4 100644 --- a/test/langtools/tools/javac/generics/odersky/BadTest.java +++ b/test/langtools/tools/javac/generics/odersky/BadTest.java @@ -3,6 +3,7 @@ * @summary Negative regression test from odersky * @author odersky * + * @compile/fail/ref=BadTest-old.out -XDrawDiagnostics -source 15 -Xlint:-options BadTest.java * @compile/fail/ref=BadTest.out -XDrawDiagnostics BadTest.java */ diff --git a/test/langtools/tools/javac/generics/odersky/BadTest.out b/test/langtools/tools/javac/generics/odersky/BadTest.out index bddfb232196..c70a7f81c4e 100644 --- a/test/langtools/tools/javac/generics/odersky/BadTest.out +++ b/test/langtools/tools/javac/generics/odersky/BadTest.out @@ -1,5 +1,5 @@ -BadTest.java:18:50: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: List, List>) -BadTest.java:22:48: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: List, List) -BadTest.java:23:54: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: List, List>) -BadTest.java:25:53: compiler.err.illegal.generic.type.for.instof +BadTest.java:19:50: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: List, List>) +BadTest.java:23:48: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: List, List) +BadTest.java:24:54: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: List, List>) +BadTest.java:26:35: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: List, List) 4 errors diff --git a/test/langtools/tools/javac/launcher/SourceLauncherTest.java b/test/langtools/tools/javac/launcher/SourceLauncherTest.java index 76082210850..1f622e0fefc 100644 --- a/test/langtools/tools/javac/launcher/SourceLauncherTest.java +++ b/test/langtools/tools/javac/launcher/SourceLauncherTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8192920 8204588 + * @bug 8192920 8204588 8246774 * @summary Test source launcher * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.api @@ -299,7 +299,7 @@ public void testHelloWorldWithShebangJava(Path base) throws IOException { file + ":1: error: illegal character: '#'\n" + "#!/usr/bin/java --source " + thisVersion + "\n" + "^\n" + - file + ":1: error: class, interface, or enum expected\n" + + file + ":1: error: class, interface, enum, or record expected\n" + "#!/usr/bin/java --source " + thisVersion + "\n" + " ^\n" + "2 errors\n", @@ -505,7 +505,7 @@ public void testBadShebang(Path base) throws IOException { file + ":1: error: illegal character: '#'\n" + "#/usr/bin/java --source " + thisVersion + "\n" + "^\n" + - file + ":1: error: class, interface, or enum expected\n" + + file + ":1: error: class, interface, enum, or record expected\n" + "#/usr/bin/java --source " + thisVersion + "\n" + " ^\n" + "2 errors\n", diff --git a/test/langtools/tools/javac/lexer/JavaLexerTest.java b/test/langtools/tools/javac/lexer/JavaLexerTest.java index 49c014e3e47..d483ab93d38 100644 --- a/test/langtools/tools/javac/lexer/JavaLexerTest.java +++ b/test/langtools/tools/javac/lexer/JavaLexerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,10 +23,10 @@ /** * @test - * @bug 8056897 + * @bug 8056897 8254073 * @modules jdk.compiler/com.sun.tools.javac.parser * jdk.compiler/com.sun.tools.javac.util - * @summary Proper lexing of integer literals. + * @summary Proper lexing of various token kinds. */ import java.io.IOException; @@ -43,41 +43,130 @@ import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Log; +import static com.sun.tools.javac.parser.Tokens.TokenKind.*; + public class JavaLexerTest { - public static void main(String... args) throws Exception { - new JavaLexerTest().run(); + static final TestTuple[] PASSING_TESTS = { + new TestTuple(FLOATLITERAL, "0.0f"), + new TestTuple(FLOATLITERAL, "0.0F"), + new TestTuple(FLOATLITERAL, ".0F"), + new TestTuple(FLOATLITERAL, "0.F"), + new TestTuple(FLOATLITERAL, "0E0F"), + new TestTuple(FLOATLITERAL, "0E+0F"), + new TestTuple(FLOATLITERAL, "0E-0F"), + + new TestTuple(DOUBLELITERAL, "0.0d"), + new TestTuple(DOUBLELITERAL, "0.0D"), + new TestTuple(DOUBLELITERAL, ".0D"), + new TestTuple(DOUBLELITERAL, "0.D"), + new TestTuple(DOUBLELITERAL, "0E0D"), + new TestTuple(DOUBLELITERAL, "0E+0D"), + new TestTuple(DOUBLELITERAL, "0E-0D"), + new TestTuple(DOUBLELITERAL, "0x0.0p0d"), + new TestTuple(DOUBLELITERAL, "0xff.0p8d"), + + new TestTuple(STRINGLITERAL, "\"\\u2022\""), + new TestTuple(STRINGLITERAL, "\"\\b\\t\\n\\f\\r\\\'\\\"\\\\\""), + + new TestTuple(CHARLITERAL, "\'\\b\'"), + new TestTuple(CHARLITERAL, "\'\\t\'"), + new TestTuple(CHARLITERAL, "\'\\n\'"), + new TestTuple(CHARLITERAL, "\'\\f\'"), + new TestTuple(CHARLITERAL, "\'\\r\'"), + new TestTuple(CHARLITERAL, "\'\\'\'"), + new TestTuple(CHARLITERAL, "\'\\\\'"), + new TestTuple(CHARLITERAL, "\'\\\'\'"), + new TestTuple(CHARLITERAL, "\'\\\"\'"), + + new TestTuple(IDENTIFIER, "abc\\u0005def"), + }; + + static final TestTuple[] FAILING_TESTS = { + new TestTuple(LONGLITERAL, "0bL"), + new TestTuple(LONGLITERAL, "0b20L"), + new TestTuple(LONGLITERAL, "0xL"), + new TestTuple(INTLITERAL, "0xG000L", "0x"), + + new TestTuple(DOUBLELITERAL, "0E*0F", "0E"), + + new TestTuple(DOUBLELITERAL, "0E*0D", "0E"), + new TestTuple(INTLITERAL, "0xp8d", "0x"), + new TestTuple(DOUBLELITERAL, "0x8pd", "0x8pd"), + new TestTuple(INTLITERAL, "0xpd", "0x"), + + new TestTuple(ERROR, "\"\\u20\""), + new TestTuple(ERROR, "\"\\u\""), + new TestTuple(ERROR, "\"\\uG000\""), + new TestTuple(ERROR, "\"\\u \""), + new TestTuple(ERROR, "\"\\q\""), + + new TestTuple(ERROR, "\'\'"), + new TestTuple(ERROR, "\'\\q\'", "\'\\"), + }; + + static class TestTuple { + String input; + TokenKind kind; + String expected; + + TestTuple(TokenKind kind, String input, String expected) { + this.input = input; + this.kind = kind; + this.expected = expected; + } + + TestTuple(TokenKind kind, String input) { + this(kind, input, input); + } } - void run() throws Exception { + void test(TestTuple test, boolean willFail) throws Exception { Context ctx = new Context(); Log log = Log.instance(ctx); - String input = "0bL 0b20L 0xL "; + log.useSource(new SimpleJavaFileObject(new URI("mem://Test.java"), JavaFileObject.Kind.SOURCE) { @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { - return input; + return test.input; } }); - char[] inputArr = input.toCharArray(); - JavaTokenizer tokenizer = new JavaTokenizer(ScannerFactory.instance(ctx), inputArr, inputArr.length) { - }; - - assertKind(input, tokenizer, TokenKind.LONGLITERAL, "0bL"); - assertKind(input, tokenizer, TokenKind.LONGLITERAL, "0b20L"); - assertKind(input, tokenizer, TokenKind.LONGLITERAL, "0xL"); - } - void assertKind(String input, JavaTokenizer tokenizer, TokenKind kind, String expectedText) { + char[] inputArr = test.input.toCharArray(); + JavaTokenizer tokenizer = new JavaTokenizer(ScannerFactory.instance(ctx), inputArr, inputArr.length) {}; Token token = tokenizer.readToken(); + boolean failed = log.nerrors != 0; + boolean normal = failed == willFail; - if (token.kind != kind) { - throw new AssertionError("Unexpected token kind: " + token.kind); + if (!normal) { + System.err.println("input: " + test.input); + String message = willFail ? "Expected to fail: " : "Expected to pass: "; + throw new AssertionError(message + test.input); } - String actualText = input.substring(token.pos, token.endPos); + String actual = test.input.substring(token.pos, token.endPos); - if (!Objects.equals(actualText, expectedText)) { - throw new AssertionError("Unexpected token text: " + actualText); + if (token.kind != test.kind) { + System.err.println("input: " + test.input); + throw new AssertionError("Unexpected token kind: " + token.kind.name()); } + + if (!Objects.equals(test.expected, actual)) { + System.err.println("input: " + test.input); + throw new AssertionError("Unexpected token content: " + actual); + } + } + + void run() throws Exception { + for (TestTuple test : PASSING_TESTS) { + test(test, false); + } + + for (TestTuple test : FAILING_TESTS) { + test(test, true); + } + } + + public static void main(String[] args) throws Exception { + new JavaLexerTest().run(); } } diff --git a/test/langtools/tools/javac/modules/AddLimitMods.java b/test/langtools/tools/javac/modules/AddLimitMods.java index 62144257be1..827af196043 100644 --- a/test/langtools/tools/javac/modules/AddLimitMods.java +++ b/test/langtools/tools/javac/modules/AddLimitMods.java @@ -394,6 +394,7 @@ public void testRuntime2Compile(Path base) throws Exception { try { System.err.println("Running m2x/test.Test:"); output = new JavaTask(tb) + .includeStandardOptions(false) .vmOptions(augmentOptions(options, Collections.emptyList(), "--module-path", modulePath.toString() + File.pathSeparator + out.getParent().toString(), diff --git a/test/langtools/tools/javac/modules/AnnotationProcessing.java b/test/langtools/tools/javac/modules/AnnotationProcessing.java index f134c417c5b..68e0794e8eb 100644 --- a/test/langtools/tools/javac/modules/AnnotationProcessing.java +++ b/test/langtools/tools/javac/modules/AnnotationProcessing.java @@ -23,7 +23,7 @@ /** * @test - * @bug 8133884 8162711 8133896 8172158 8172262 8173636 8175119 8189747 + * @bug 8133884 8162711 8133896 8172158 8172262 8173636 8175119 8189747 8236842 * @summary Verify that annotation processing works. * @library /tools/lib * @modules @@ -46,6 +46,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -53,7 +54,6 @@ import java.util.concurrent.Callable; import java.util.function.Consumer; import java.util.function.Function; -import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.annotation.processing.AbstractProcessor; @@ -80,11 +80,8 @@ import javax.tools.FileObject; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; -import javax.tools.JavaFileManager; -import javax.tools.JavaFileManager.Location; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; -import javax.tools.StandardLocation; import javax.tools.ToolProvider; import toolbox.JavacTask; @@ -1299,6 +1296,440 @@ private static void writeFile(String content, Path base, String... pathElements) } } + @Test + public void testUnboundLookupNew(Path base) throws Exception { + Path moduleSrc = base.resolve("module-src"); + Path m1 = moduleSrc.resolve("m1x"); + Path m2 = moduleSrc.resolve("m2x"); + Path m3 = moduleSrc.resolve("m3x"); + Path m4 = moduleSrc.resolve("m4x"); + + Path src = base.resolve("src"); + + Path classes = base.resolve("classes"); + Path srcClasses = base.resolve("src-classes"); + + Files.createDirectories(classes); + Files.createDirectories(srcClasses); + Files.createDirectories(moduleSrc); + + { + tb.cleanDirectory(classes); + tb.cleanDirectory(moduleSrc); + + tb.writeJavaFiles(m1, + "module m1x { exports api; }", + "package test; public class Test { }", + "package api; public class API {}"); + + tb.writeJavaFiles(m2, + "module m2x { requires m1x; }", + "package test; public class Test { }", + "package api.impl; public class Impl { }"); + + new JavacTask(tb) + .options("--module-source-path", moduleSrc.toString()) + .outdir(classes) + .files(findJavaFiles(moduleSrc)) + .run() + .writeAll() + .getOutputLines(OutputKind.DIRECT); + + List log = new JavacTask(tb) + .options("--module-source-path", moduleSrc.toString(), + "-processorpath", System.getProperty("test.class.path"), + "-processor", UnboundLookupNew.class.getName(), + "-AlookupClass=+test.Test", + "-AlookupPackage=+test,+api", + "-XDrawDiagnostics") + .outdir(classes) + .files(findJavaFiles(m2)) + .run() + .writeAll() + .getOutputLines(OutputKind.DIRECT); + + List expected = Arrays.asList("- compiler.note.proc.messager: test.Test found in module: m2x", + "- compiler.note.proc.messager: test found in module: m2x", + "- compiler.note.proc.messager: api found in module: m1x", + "- compiler.note.proc.messager: test.Test found in module: m2x", + "- compiler.note.proc.messager: test found in module: m2x", + "- compiler.note.proc.messager: api found in module: m1x" + ); + + if (!expected.equals(log)) { + throw new AssertionError("Expected output not found: " + log); + } + } + + { + tb.cleanDirectory(classes); + tb.cleanDirectory(moduleSrc); + + tb.writeJavaFiles(m1, + "module m1x { exports test; }", + "package test; public class Test { }"); + + tb.writeJavaFiles(m2, + "module m2x { requires m1x; }", + "package test; public class Test { }"); + + List log = new JavacTask(tb) + .options("--module-source-path", moduleSrc.toString(), + "-processorpath", System.getProperty("test.class.path"), + "-processor", UnboundLookupNew.class.getName(), + "-AlookupClass=+test.Test", + "-AlookupPackage=+test", + "-XDrawDiagnostics", + "-XDshould-stop.at=FLOW") + .outdir(classes) + .files(findJavaFiles(m2)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(OutputKind.DIRECT); + + List expected = Arrays.asList( + "Test.java:1:1: compiler.err.package.in.other.module: m1x", + "- compiler.note.proc.messager: test.Test found in module: m2x", + "- compiler.note.proc.messager: test found in module: m1x", + "- compiler.note.proc.messager: test.Test found in module: m2x", + "- compiler.note.proc.messager: test found in module: m1x", + "1 error"); + + if (!expected.equals(log)) { + throw new AssertionError("Expected output not found: " + log); + } + } + + { + tb.cleanDirectory(classes); + tb.cleanDirectory(moduleSrc); + + tb.writeJavaFiles(m1, + "module m1x { }", + "package test; public class Test { }"); + + tb.writeJavaFiles(m2, + "module m2x { requires m1x; requires m3x; }", + "package test; public class Test { }"); + + tb.writeJavaFiles(m3, + "module m3x { }", + "package test; public class Test { }"); + + new JavacTask(tb) + .options("--module-source-path", moduleSrc.toString()) + .outdir(classes) + .files(findJavaFiles(moduleSrc)) + .run() + .writeAll() + .getOutputLines(OutputKind.DIRECT); + + List log = new JavacTask(tb) + .options("--module-source-path", moduleSrc.toString(), + "-processorpath", System.getProperty("test.class.path"), + "-processor", UnboundLookupNew.class.getName(), + "-AlookupClass=+test.Test", + "-AlookupPackage=+test", + "-XDrawDiagnostics") + .outdir(classes) + .files(findJavaFiles(m2)) + .run() + .writeAll() + .getOutputLines(OutputKind.DIRECT); + + List expected = Arrays.asList( + "- compiler.note.proc.messager: test.Test found in module: m2x", + "- compiler.note.proc.messager: test found in module: m2x", + "- compiler.note.proc.messager: test.Test found in module: m2x", + "- compiler.note.proc.messager: test found in module: m2x" + ); + + if (!expected.equals(log)) { + throw new AssertionError("Expected output not found: " + log); + } + } + + { + tb.cleanDirectory(classes); + tb.cleanDirectory(moduleSrc); + + tb.writeJavaFiles(m1, + "module m1x { exports test; }", + "package test; public class Test { }"); + + tb.writeJavaFiles(m2, + "module m2x { requires m1x; requires m3x; }"); + + tb.writeJavaFiles(m3, + "module m3x { exports test; }", + "package test; public class Test { }"); + + tb.writeJavaFiles(m4, + "module m4x { }", + "package test; public class Test { }"); + + { + List log = new JavacTask(tb) + .options("--module-source-path", moduleSrc.toString(), + "-processorpath", System.getProperty("test.class.path"), + "-processor", UnboundLookupNew.class.getName(), + "-AlookupClass=+test.Test", + "-AlookupPackage=+test", + "-XDrawDiagnostics", + "-XDshould-stop.at=FLOW") + .outdir(classes) + .files(findJavaFiles(m2)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(OutputKind.DIRECT); + + List> expected = Arrays.asList( + variants("module-info.java:1:1: compiler.err.package.clash.from.requires: m2x, test, m1x, m3x"), + variants("- compiler.note.proc.messager: test.Test found in module: m1x"), + variants("- compiler.note.proc.messager: test found in module: m1x"), + variants("- compiler.note.proc.messager: test.Test found in module: m1x"), + variants("- compiler.note.proc.messager: test found in module: m1x"), + variants("1 error")); + + assertErrorsWithVariants(expected, log); + } + + { + List log = new JavacTask(tb) + .options("--module-source-path", moduleSrc.toString(), + "-processorpath", System.getProperty("test.class.path"), + "-processor", UnboundLookupNew.class.getName(), + "-AlookupClass=-test.Test", + "-AlookupPackage=-test", + "-XDrawDiagnostics", + "-XDshould-stop.at=FLOW") + .outdir(classes) + .files(findJavaFiles(m2, m4)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(OutputKind.DIRECT); + + List expected = Arrays.asList( + "module-info.java:1:1: compiler.err.package.clash.from.requires: m2x, test, m1x, m3x", + "- compiler.note.multiple.elements: getTypeElement, test.Test, m1x, m4x", + "- compiler.note.multiple.elements: getPackageElement, test, m1x, m4x", + "1 error"); + + if (!expected.equals(log)) { + throw new AssertionError("Expected output not found: " + log); + } + } + } + + { + tb.cleanDirectory(classes); + tb.cleanDirectory(moduleSrc); + + tb.writeJavaFiles(m1, + "module m1x { exports test; }", + "package test; public class Test { }"); + + tb.writeJavaFiles(m2, + "module m2x { requires m1x; }"); + + tb.writeJavaFiles(src, + "package test; public class Test { }"); + + new JavacTask(tb) + .options("--module-source-path", moduleSrc.toString()) + .outdir(classes) + .files(findJavaFiles(moduleSrc)) + .run() + .writeAll() + .getOutputLines(OutputKind.DIRECT); + + List log = new JavacTask(tb) + .options("--module-source-path", moduleSrc.toString(), + "--source-path", src.toString(), + "-processorpath", System.getProperty("test.class.path"), + "-processor", UnboundLookupNew.class.getName(), + "-AlookupClass=+test.Test", + "-AlookupPackage=+test", + "-XDrawDiagnostics") + .outdir(classes) + .files(findJavaFiles(m2)) + .run() + .writeAll() + .getOutputLines(OutputKind.DIRECT); + + List expected = Arrays.asList( + "- compiler.note.proc.messager: test.Test found in module: m1x", + "- compiler.note.proc.messager: test found in module: m1x", + "- compiler.note.proc.messager: test.Test found in module: m1x", + "- compiler.note.proc.messager: test found in module: m1x" + ); + + if (!expected.equals(log)) { + throw new AssertionError("Expected output not found: " + log); + } + } + + { + tb.cleanDirectory(classes); + tb.cleanDirectory(moduleSrc); + + tb.writeJavaFiles(m1, + "module m1x { exports test; }", + "package test; public class Test { }"); + + tb.writeJavaFiles(m2, + "module m2x { requires m1x; }"); + + tb.writeJavaFiles(src, + "package test; public class Test { }"); + + new JavacTask(tb) + .options("--module-source-path", moduleSrc.toString()) + .outdir(classes) + .files(findJavaFiles(moduleSrc)) + .run() + .writeAll() + .getOutputLines(OutputKind.DIRECT); + + List log = new JavacTask(tb) + .options("--module-source-path", moduleSrc.toString(), + "--source-path", src.toString(), + "--add-reads=m2x=ALL-UNNAMED", + "-processorpath", System.getProperty("test.class.path"), + "-processor", UnboundLookupNew.class.getName(), + "-AlookupClass=+test.Test", + "-AlookupPackage=+test", + "-XDrawDiagnostics") + .outdir(classes) + .files(findJavaFiles(m2)) + .run() + .writeAll() + .getOutputLines(OutputKind.DIRECT); + + List expected = Arrays.asList( + "- compiler.note.proc.messager: test.Test found in module: m1x", + "- compiler.note.proc.messager: test found in module: m1x", + "- compiler.note.proc.messager: test.Test found in module: m1x", + "- compiler.note.proc.messager: test found in module: m1x" + ); + + if (!expected.equals(log)) { + throw new AssertionError("Expected output not found: " + log); + } + } + + { + tb.cleanDirectory(classes); + tb.cleanDirectory(srcClasses); + tb.cleanDirectory(moduleSrc); + + tb.writeJavaFiles(m1, + "module m1x { exports test; }", + "package test; public class Test { }"); + + tb.writeJavaFiles(m2, + "module m2x { requires m1x; }"); + + tb.writeJavaFiles(src, + "package test; public class Test { }"); + + new JavacTask(tb) + .options("--module-source-path", moduleSrc.toString()) + .outdir(classes) + .files(findJavaFiles(moduleSrc)) + .run() + .writeAll() + .getOutputLines(OutputKind.DIRECT); + + List log = new JavacTask(tb) + .options("--module-path", classes.toString(), + "--source-path", src.toString(), + "-processorpath", System.getProperty("test.class.path"), + "-processor", UnboundLookupNew.class.getName(), + "-AlookupClass=+test.Test", + "-AlookupPackage=+test", + "--add-modules=m2x", + "-XDshould-stop.at=ATTR", + "-XDrawDiagnostics") + .outdir(srcClasses) + .files(findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(OutputKind.DIRECT); + + List expected = Arrays.asList("Test.java:1:1: compiler.err.package.in.other.module: m1x", + "- compiler.note.proc.messager: test.Test found in module: unnamed module", + "- compiler.note.proc.messager: test found in module: m1x", + "- compiler.note.proc.messager: test.Test found in module: unnamed module", + "- compiler.note.proc.messager: test found in module: m1x", + "1 error" + ); + + if (!expected.equals(log)) { + throw new AssertionError("Expected output not found: " + log); + } + } + } + + private Set variants(String... expected) { + return new HashSet<>(Arrays.asList(expected)); + } + + private void assertErrorsWithVariants(List> expectedVariants, List actual) { + assertEquals(expectedVariants.size(), actual.size()); + Iterator> expIt = expectedVariants.iterator(); + Iterator actIt = actual.iterator(); + + while (expIt.hasNext() && actIt.hasNext()) { + Set exp = expIt.next(); + String act = actIt.next(); + + if (!exp.contains(act)) { + throw new AssertionError("Expected: " + exp + ", actual: " + act); + } + } + } + + @SupportedAnnotationTypes("*") + @SupportedOptions({"lookupClass", "lookupPackage"}) + public static final class UnboundLookupNew extends AbstractProcessor { + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + performLookup("lookupClass", processingEnv.getElementUtils()::getTypeElement); + performLookup("lookupPackage", processingEnv.getElementUtils()::getPackageElement); + + return false; + } + + private void performLookup(String optionName, Function name2Element) { + String[] lookupList = processingEnv.getOptions().get(optionName).split(","); + for (String lookup : lookupList) { + boolean shouldExists = lookup.charAt(0) == '+'; + String name = lookup.substring(1); + Element type = name2Element.apply(name); + + if (shouldExists) { + if (type == null) { + throw new AssertionError("Did not find the expected type."); + } else { + processingEnv.getMessager().printMessage(Kind.NOTE, name + " found in module: " + processingEnv.getElementUtils().getModuleOf(type)); + } + } else { + if (type != null) { + throw new AssertionError("Found the unexpected type."); + } + } + } + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + } + @Test public void testUnboundLookup(Path base) throws Exception { Path src = base.resolve("src"); @@ -1333,7 +1764,7 @@ public void testUnboundLookup(Path base) throws Exception { "package nested.pack; public class Impl { }"); //from source: - String log = new JavacTask(tb) + new JavacTask(tb) .options("--module-source-path", moduleSrc.toString(), "--source-path", src.toString(), "-processorpath", System.getProperty("test.class.path"), @@ -1345,20 +1776,6 @@ public void testUnboundLookup(Path base) throws Exception { .writeAll() .getOutput(OutputKind.DIRECT); - String moduleImplConflictString = - "- compiler.note.multiple.elements: getTypeElement, impl.conflict.module.Impl, m2x, m1x"; - String srcConflictString = - "- compiler.note.multiple.elements: getTypeElement, impl.conflict.src.Impl, m1x, unnamed module"; - - if (!log.contains(moduleImplConflictString) || - !log.contains(srcConflictString)) { - throw new AssertionError("Expected output not found: " + log); - } - - if (log.split(Pattern.quote(moduleImplConflictString)).length > 2) { - throw new AssertionError("Too many warnings in: " + log); - } - new JavacTask(tb) .options("--source-path", src.toString()) .outdir(cpClasses) @@ -1373,7 +1790,8 @@ public void testUnboundLookup(Path base) throws Exception { "--add-modules", "m1x,m2x", "-processorpath", System.getProperty("test.class.path"), "-processor", UnboundLookup.class.getName(), - "-proc:only") + "-proc:only", + "-Aunnamedmodule") .classes("java.lang.Object") .run() .writeAll(); @@ -1405,6 +1823,7 @@ public void testUnboundLookup(Path base) throws Exception { } @SupportedAnnotationTypes("*") + @SupportedOptions("unnamedmodule") public static final class UnboundLookup extends AbstractProcessor { @Override @@ -1422,8 +1841,8 @@ public boolean process(Set annotations, RoundEnvironment assertTypeElementNotFound("impl.conflict.module.Impl"); assertTypeElementNotFound("impl.conflict.module.Impl"); //check that the warning/note is produced only once assertPackageElementNotFound("impl.conflict.module"); - assertTypeElementNotFound("impl.conflict.src.Impl"); - assertPackageElementNotFound("impl.conflict.src"); + assertTypeElementExists("impl.conflict.src.Impl", processingEnv.getOptions().containsKey("unnamedmodule") ? "" : "m1x"); + assertPackageElementExists("impl.conflict.src", processingEnv.getOptions().containsKey("unnamedmodule") ? "" : "m1x"); assertTypeElementNotFound("impl.conflict.clazz.pkg"); assertPackageElementNotFound("unique"); //do not return packages without members in module mode assertTypeElementNotFound("nested"); //cannot distinguish between m1x and m2x diff --git a/test/langtools/tools/javac/parser/JavacParserTest.java b/test/langtools/tools/javac/parser/JavacParserTest.java index cb7b8d01382..e12e40ce4c4 100644 --- a/test/langtools/tools/javac/parser/JavacParserTest.java +++ b/test/langtools/tools/javac/parser/JavacParserTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 7073631 7159445 7156633 8028235 8065753 8205418 8205913 8228451 8237041 8253584 + * @bug 7073631 7159445 7156633 8028235 8065753 8205418 8205913 8228451 8237041 8253584 8246774 * @summary tests error and diagnostics positions * @author Jan Lahoda * @modules jdk.compiler/com.sun.tools.javac.api @@ -999,7 +999,7 @@ void testVoidLambdaParameter() throws IOException { @Test //JDK-8065753 void testWrongFirstToken() throws IOException { String code = "<"; - String expectedErrors = "Test.java:1:1: compiler.err.expected3: class, interface, enum\n" + + String expectedErrors = "Test.java:1:1: compiler.err.expected4: class, interface, enum, record\n" + "1 error\n"; StringWriter out = new StringWriter(); JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(out, fm, null, diff --git a/test/langtools/tools/javac/parser/SingleCommaAnnotationValueFail.out b/test/langtools/tools/javac/parser/SingleCommaAnnotationValueFail.out index 8c8f0566d15..3748ccfeb97 100644 --- a/test/langtools/tools/javac/parser/SingleCommaAnnotationValueFail.out +++ b/test/langtools/tools/javac/parser/SingleCommaAnnotationValueFail.out @@ -1,3 +1,3 @@ SingleCommaAnnotationValueFail.java:34:12: compiler.err.expected: '}' -SingleCommaAnnotationValueFail.java:34:14: compiler.err.expected3: class, interface, enum +SingleCommaAnnotationValueFail.java:34:14: compiler.err.expected4: class, interface, enum, record 2 errors diff --git a/test/langtools/tools/javac/patterns/BindingsExistTest.java b/test/langtools/tools/javac/patterns/BindingsExistTest.java index 27aa66d409f..deca70f7344 100644 --- a/test/langtools/tools/javac/patterns/BindingsExistTest.java +++ b/test/langtools/tools/javac/patterns/BindingsExistTest.java @@ -2,7 +2,7 @@ * @test /nodynamiccopyright/ * @bug 8231827 * @summary Clashing bindings are reported correctly - * @compile/fail/ref=BindingsExistTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} BindingsExistTest.java + * @compile/fail/ref=BindingsExistTest.out -XDrawDiagnostics BindingsExistTest.java */ public class BindingsExistTest { public void t(Object o1, Object o2) { diff --git a/test/langtools/tools/javac/patterns/BindingsExistTest.out b/test/langtools/tools/javac/patterns/BindingsExistTest.out index 28ed647f5c5..fa7bff1476e 100644 --- a/test/langtools/tools/javac/patterns/BindingsExistTest.out +++ b/test/langtools/tools/javac/patterns/BindingsExistTest.out @@ -4,6 +4,4 @@ BindingsExistTest.java:16:35: compiler.err.already.defined: kindname.variable, k BindingsExistTest.java:19:34: compiler.err.already.defined: kindname.variable, s2, kindname.method, t(java.lang.Object,java.lang.Object) BindingsExistTest.java:22:20: compiler.err.already.defined: kindname.variable, s3, kindname.method, t(java.lang.Object,java.lang.Object) BindingsExistTest.java:28:16: compiler.err.already.defined: kindname.variable, s4, kindname.method, t(java.lang.Object,java.lang.Object) -- compiler.note.preview.filename: BindingsExistTest.java -- compiler.note.preview.recompile 6 errors diff --git a/test/langtools/tools/javac/patterns/BindingsTest1.java b/test/langtools/tools/javac/patterns/BindingsTest1.java index d34e5cd792c..018084a98e7 100644 --- a/test/langtools/tools/javac/patterns/BindingsTest1.java +++ b/test/langtools/tools/javac/patterns/BindingsTest1.java @@ -25,8 +25,8 @@ * @test * @bug 8231827 * @summary Basic tests for bindings from instanceof - * @compile --enable-preview -source ${jdk.version} BindingsTest1.java - * @run main/othervm --enable-preview BindingsTest1 + * @compile BindingsTest1.java + * @run main BindingsTest1 */ public class BindingsTest1 { diff --git a/test/langtools/tools/javac/patterns/BindingsTest1Merging.java b/test/langtools/tools/javac/patterns/BindingsTest1Merging.java index 5458a1dee80..f6e1f6dd201 100644 --- a/test/langtools/tools/javac/patterns/BindingsTest1Merging.java +++ b/test/langtools/tools/javac/patterns/BindingsTest1Merging.java @@ -2,7 +2,7 @@ * @test /nodynamiccopyright/ * @bug 8231827 * @summary Basic tests for bindings from instanceof - tests for merging pattern variables - * @compile/fail/ref=BindingsTest1Merging.out -XDrawDiagnostics --enable-preview -source ${jdk.version} BindingsTest1Merging.java + * @compile/fail/ref=BindingsTest1Merging.out -XDrawDiagnostics BindingsTest1Merging.java */ public class BindingsTest1Merging { diff --git a/test/langtools/tools/javac/patterns/BindingsTest1Merging.out b/test/langtools/tools/javac/patterns/BindingsTest1Merging.out index 77f01a12367..28374ebc04f 100644 --- a/test/langtools/tools/javac/patterns/BindingsTest1Merging.out +++ b/test/langtools/tools/javac/patterns/BindingsTest1Merging.out @@ -7,6 +7,4 @@ BindingsTest1Merging.java:44:21: compiler.err.match.binding.exists BindingsTest1Merging.java:50:36: compiler.err.match.binding.exists BindingsTest1Merging.java:56:39: compiler.err.match.binding.exists BindingsTest1Merging.java:62:42: compiler.err.match.binding.exists -- compiler.note.preview.filename: BindingsTest1Merging.java -- compiler.note.preview.recompile 9 errors diff --git a/test/langtools/tools/javac/patterns/BindingsTest2.java b/test/langtools/tools/javac/patterns/BindingsTest2.java index be0af3458a9..4bbd5f3f532 100644 --- a/test/langtools/tools/javac/patterns/BindingsTest2.java +++ b/test/langtools/tools/javac/patterns/BindingsTest2.java @@ -2,7 +2,7 @@ * @test /nodynamiccopyright/ * @bug 8231827 * @summary Ensure that scopes arising from conditionalExpressions are handled corrected. - * @compile/fail/ref=BindingsTest2.out -XDrawDiagnostics -XDshould-stop.at=FLOW --enable-preview -source ${jdk.version} BindingsTest2.java + * @compile/fail/ref=BindingsTest2.out -XDrawDiagnostics -XDshould-stop.at=FLOW BindingsTest2.java */ public class BindingsTest2 { public static boolean Ktrue() { return true; } diff --git a/test/langtools/tools/javac/patterns/BindingsTest2.out b/test/langtools/tools/javac/patterns/BindingsTest2.out index c9c179e1f97..e52e9db2342 100644 --- a/test/langtools/tools/javac/patterns/BindingsTest2.out +++ b/test/langtools/tools/javac/patterns/BindingsTest2.out @@ -49,6 +49,4 @@ BindingsTest2.java:241:17: compiler.err.cant.resolve.location: kindname.variable BindingsTest2.java:135:17: compiler.err.unreachable.stmt BindingsTest2.java:160:17: compiler.err.unreachable.stmt BindingsTest2.java:185:17: compiler.err.unreachable.stmt -- compiler.note.preview.filename: BindingsTest2.java -- compiler.note.preview.recompile 51 errors \ No newline at end of file diff --git a/test/langtools/tools/javac/patterns/BreakAndLoops.java b/test/langtools/tools/javac/patterns/BreakAndLoops.java index 01d3bb70540..3d43fb504b2 100644 --- a/test/langtools/tools/javac/patterns/BreakAndLoops.java +++ b/test/langtools/tools/javac/patterns/BreakAndLoops.java @@ -95,10 +95,7 @@ protected void doWork() throws Throwable { case "NESTED" -> brk; case "BODY" -> innerLabel; default -> throw new UnsupportedOperationException(pname); - }) - .withOption("--enable-preview") - .withOption("-source") - .withOption(String.valueOf(Runtime.version().feature())); + }); task.generate(result -> { boolean shouldPass; diff --git a/test/langtools/tools/javac/patterns/CastConversionMatch.java b/test/langtools/tools/javac/patterns/CastConversionMatch.java index 0878d7c6c73..1300bf09d16 100644 --- a/test/langtools/tools/javac/patterns/CastConversionMatch.java +++ b/test/langtools/tools/javac/patterns/CastConversionMatch.java @@ -2,7 +2,7 @@ * @test /nodynamicopyright/ * @bug 8231827 * @summary Match which involves a cast conversion - * @compile/fail/ref=CastConversionMatch.out -XDrawDiagnostics --enable-preview -source ${jdk.version} CastConversionMatch.java + * @compile/fail/ref=CastConversionMatch.out -XDrawDiagnostics CastConversionMatch.java */ public class CastConversionMatch { diff --git a/test/langtools/tools/javac/patterns/CastConversionMatch.out b/test/langtools/tools/javac/patterns/CastConversionMatch.out index 528ef8e817a..8f747810aff 100644 --- a/test/langtools/tools/javac/patterns/CastConversionMatch.out +++ b/test/langtools/tools/javac/patterns/CastConversionMatch.out @@ -1,4 +1,2 @@ CastConversionMatch.java:11:26: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array) -- compiler.note.preview.filename: CastConversionMatch.java -- compiler.note.preview.recompile 1 error diff --git a/test/langtools/tools/javac/patterns/ConditionalTest.java b/test/langtools/tools/javac/patterns/ConditionalTest.java index 828fabfb828..16f2ceb0084 100644 --- a/test/langtools/tools/javac/patterns/ConditionalTest.java +++ b/test/langtools/tools/javac/patterns/ConditionalTest.java @@ -85,10 +85,7 @@ protected void doWork() throws Throwable { case "TRUE" -> trueSec; case "FALSE" -> falseSec; default -> throw new UnsupportedOperationException(pname); - }) - .withOption("--enable-preview") - .withOption("-source") - .withOption(String.valueOf(Runtime.version().feature())); + }); task.analyze(result -> { boolean shouldPass; diff --git a/test/langtools/tools/javac/patterns/DuplicateBindingTest.java b/test/langtools/tools/javac/patterns/DuplicateBindingTest.java index 2f9afff625c..e6112fd1bb4 100644 --- a/test/langtools/tools/javac/patterns/DuplicateBindingTest.java +++ b/test/langtools/tools/javac/patterns/DuplicateBindingTest.java @@ -2,7 +2,7 @@ * @test /nodynamiccopyright/ * @bug 8231827 * @summary Basic pattern bindings scope test - * @compile/fail/ref=DuplicateBindingTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} DuplicateBindingTest.java + * @compile/fail/ref=DuplicateBindingTest.out -XDrawDiagnostics DuplicateBindingTest.java */ public class DuplicateBindingTest { @@ -10,17 +10,17 @@ public class DuplicateBindingTest { int f; public static boolean main(String[] args) { + Object o1 = ""; + Object o2 = ""; if (args != null) { int s; - if (args[0] instanceof String s) { // NOT OK. Redef same scope. + if (o1 instanceof String s) { // NOT OK. Redef same scope. } - if (args[0] instanceof String f) { // OK to redef field. + if (o1 instanceof String f) { // OK to redef field. } } - Object o1 = ""; - Object o2 = ""; if (o1 instanceof String s && o2 instanceof String s) {} //error - already in scope on RHS (in scope due to LHS when true) if (o1 instanceof String s && !(o2 instanceof String s)) {} //error - already in scope on RHS (in scope due to LHS when true) diff --git a/test/langtools/tools/javac/patterns/DuplicateBindingTest.out b/test/langtools/tools/javac/patterns/DuplicateBindingTest.out index 1d41169eb15..cb0d37d9b28 100644 --- a/test/langtools/tools/javac/patterns/DuplicateBindingTest.out +++ b/test/langtools/tools/javac/patterns/DuplicateBindingTest.out @@ -1,4 +1,4 @@ -DuplicateBindingTest.java:16:43: compiler.err.already.defined: kindname.variable, s, kindname.method, main(java.lang.String[]) +DuplicateBindingTest.java:18:38: compiler.err.already.defined: kindname.variable, s, kindname.method, main(java.lang.String[]) DuplicateBindingTest.java:25:60: compiler.err.match.binding.exists DuplicateBindingTest.java:26:62: compiler.err.match.binding.exists DuplicateBindingTest.java:27:39: compiler.err.match.binding.exists @@ -22,6 +22,4 @@ DuplicateBindingTest.java:50:102: compiler.err.match.binding.exists DuplicateBindingTest.java:51:73: compiler.err.match.binding.exists DuplicateBindingTest.java:54:69: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, DuplicateBindingTest, null) DuplicateBindingTest.java:55:44: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, DuplicateBindingTest, null) -- compiler.note.preview.filename: DuplicateBindingTest.java -- compiler.note.preview.recompile 24 errors diff --git a/test/langtools/tools/javac/patterns/EnsureTypesOrderTest.java b/test/langtools/tools/javac/patterns/EnsureTypesOrderTest.java index 14d56b416d4..5c18fd750ca 100644 --- a/test/langtools/tools/javac/patterns/EnsureTypesOrderTest.java +++ b/test/langtools/tools/javac/patterns/EnsureTypesOrderTest.java @@ -2,7 +2,7 @@ * @test /nodynamiccopyright/ * @bug 8187420 8231827 * @summary Error message mentions relevant types transposed - * @compile/fail/ref=EnsureTypesOrderTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} EnsureTypesOrderTest.java + * @compile/fail/ref=EnsureTypesOrderTest.out -XDrawDiagnostics EnsureTypesOrderTest.java */ public class EnsureTypesOrderTest { public static void main(String [] args) { diff --git a/test/langtools/tools/javac/patterns/EnsureTypesOrderTest.out b/test/langtools/tools/javac/patterns/EnsureTypesOrderTest.out index ca05e4638dc..5047b38fe1a 100644 --- a/test/langtools/tools/javac/patterns/EnsureTypesOrderTest.out +++ b/test/langtools/tools/javac/patterns/EnsureTypesOrderTest.out @@ -1,4 +1,2 @@ EnsureTypesOrderTest.java:9:13: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String[], java.lang.String) -- compiler.note.preview.filename: EnsureTypesOrderTest.java -- compiler.note.preview.recompile 1 error \ No newline at end of file diff --git a/test/langtools/tools/javac/patterns/ExamplesFromProposal.java b/test/langtools/tools/javac/patterns/ExamplesFromProposal.java index 3612c7e618a..2d1fdf29113 100644 --- a/test/langtools/tools/javac/patterns/ExamplesFromProposal.java +++ b/test/langtools/tools/javac/patterns/ExamplesFromProposal.java @@ -25,8 +25,8 @@ * @test * @bug 8231827 * @summary All example code from "Pattern Matching for Java" document, released April 2017, adjusted to current state (no switches, etc) - * @compile --enable-preview -source ${jdk.version} ExamplesFromProposal.java - * @run main/othervm --enable-preview ExamplesFromProposal + * @compile ExamplesFromProposal.java + * @run main ExamplesFromProposal */ interface Node { diff --git a/test/langtools/tools/javac/patterns/ImpossibleTypeTest.java b/test/langtools/tools/javac/patterns/ImpossibleTypeTest.java index 0e564869362..2c4ba47adeb 100644 --- a/test/langtools/tools/javac/patterns/ImpossibleTypeTest.java +++ b/test/langtools/tools/javac/patterns/ImpossibleTypeTest.java @@ -2,7 +2,7 @@ * @test /nodynamiccopyright/ * @bug 8231827 * @summary Ensure that in type test patterns, the predicate is not trivially provable false. - * @compile/fail/ref=ImpossibleTypeTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} ImpossibleTypeTest.java + * @compile/fail/ref=ImpossibleTypeTest.out -XDrawDiagnostics ImpossibleTypeTest.java */ public class ImpossibleTypeTest { diff --git a/test/langtools/tools/javac/patterns/ImpossibleTypeTest.out b/test/langtools/tools/javac/patterns/ImpossibleTypeTest.out index 6003b006d91..78331a727dd 100644 --- a/test/langtools/tools/javac/patterns/ImpossibleTypeTest.out +++ b/test/langtools/tools/javac/patterns/ImpossibleTypeTest.out @@ -1,5 +1,3 @@ ImpossibleTypeTest.java:14:13: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Integer, java.lang.String) ImpossibleTypeTest.java:17:26: compiler.err.cant.resolve.location: kindname.class, Undefined, , , (compiler.misc.location: kindname.class, ImpossibleTypeTest, null) -- compiler.note.preview.filename: ImpossibleTypeTest.java -- compiler.note.preview.recompile 2 errors diff --git a/test/langtools/tools/javac/patterns/LocalVariableTable.java b/test/langtools/tools/javac/patterns/LocalVariableTable.java index ac4bb0cc27a..ee03b16b927 100644 --- a/test/langtools/tools/javac/patterns/LocalVariableTable.java +++ b/test/langtools/tools/javac/patterns/LocalVariableTable.java @@ -26,8 +26,8 @@ * @bug 8231827 * @summary Ensure the LV table entries are generated for bindings * @modules jdk.jdeps/com.sun.tools.classfile - * @compile -g --enable-preview -source ${jdk.version} LocalVariableTable.java - * @run main/othervm --enable-preview LocalVariableTable + * @compile -g LocalVariableTable.java + * @run main LocalVariableTable */ import java.io.*; diff --git a/test/langtools/tools/javac/patterns/MatchBindingScopeTest.java b/test/langtools/tools/javac/patterns/MatchBindingScopeTest.java index 50fbf0a7bfc..2e32428a67d 100644 --- a/test/langtools/tools/javac/patterns/MatchBindingScopeTest.java +++ b/test/langtools/tools/javac/patterns/MatchBindingScopeTest.java @@ -2,7 +2,7 @@ * @test /nodynamiccopyright/ * @bug 8231827 * @summary Basic pattern bindings scope test - * @compile/fail/ref=MatchBindingScopeTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} MatchBindingScopeTest.java + * @compile/fail/ref=MatchBindingScopeTest.out -XDrawDiagnostics MatchBindingScopeTest.java */ public class MatchBindingScopeTest { diff --git a/test/langtools/tools/javac/patterns/MatchBindingScopeTest.out b/test/langtools/tools/javac/patterns/MatchBindingScopeTest.out index 6f21aca174e..f4424d36149 100644 --- a/test/langtools/tools/javac/patterns/MatchBindingScopeTest.out +++ b/test/langtools/tools/javac/patterns/MatchBindingScopeTest.out @@ -11,6 +11,4 @@ MatchBindingScopeTest.java:56:48: compiler.err.cant.resolve.location: kindname.v MatchBindingScopeTest.java:57:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) MatchBindingScopeTest.java:62:23: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) MatchBindingScopeTest.java:65:23: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) -- compiler.note.preview.filename: MatchBindingScopeTest.java -- compiler.note.preview.recompile 13 errors \ No newline at end of file diff --git a/test/langtools/tools/javac/patterns/NoSubtypeCheck.java b/test/langtools/tools/javac/patterns/NoSubtypeCheck.java new file mode 100644 index 00000000000..973ffb5a6ec --- /dev/null +++ b/test/langtools/tools/javac/patterns/NoSubtypeCheck.java @@ -0,0 +1,22 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8250625 + * @summary Verify pattern matching test which is always true produces an error + * @compile/fail/ref=NoSubtypeCheck.out -XDrawDiagnostics NoSubtypeCheck.java + */ +public class NoSubtypeCheck { + + public static void main(Object o, String s, List l) { + boolean b1 = o instanceof Object v1; + boolean b2 = o instanceof String v2; + boolean b3 = s instanceof Object v3; + boolean b4 = s instanceof String v4; + boolean b5 = l instanceof List v5; + boolean b6 = l instanceof List2 v6; + boolean b7 = undef instanceof String v7; + boolean b8 = o instanceof Undef v7; + } + + public interface List {} + public interface List2 extends List {} +} diff --git a/test/langtools/tools/javac/patterns/NoSubtypeCheck.out b/test/langtools/tools/javac/patterns/NoSubtypeCheck.out new file mode 100644 index 00000000000..fc95883838a --- /dev/null +++ b/test/langtools/tools/javac/patterns/NoSubtypeCheck.out @@ -0,0 +1,7 @@ +NoSubtypeCheck.java:10:24: compiler.err.instanceof.pattern.no.subtype: java.lang.Object, java.lang.Object +NoSubtypeCheck.java:12:24: compiler.err.instanceof.pattern.no.subtype: java.lang.Object, java.lang.String +NoSubtypeCheck.java:13:24: compiler.err.instanceof.pattern.no.subtype: java.lang.String, java.lang.String +NoSubtypeCheck.java:14:24: compiler.err.instanceof.pattern.no.subtype: NoSubtypeCheck.List, NoSubtypeCheck.List +NoSubtypeCheck.java:16:22: compiler.err.cant.resolve.location: kindname.variable, undef, , , (compiler.misc.location: kindname.class, NoSubtypeCheck, null) +NoSubtypeCheck.java:17:35: compiler.err.cant.resolve.location: kindname.class, Undef, , , (compiler.misc.location: kindname.class, NoSubtypeCheck, null) +6 errors diff --git a/test/langtools/tools/javac/patterns/NoUnnecessaryCast.java b/test/langtools/tools/javac/patterns/NoUnnecessaryCast.java index 02527f7a84e..c58e42a8f5e 100644 --- a/test/langtools/tools/javac/patterns/NoUnnecessaryCast.java +++ b/test/langtools/tools/javac/patterns/NoUnnecessaryCast.java @@ -27,8 +27,8 @@ * @summary Verify there are no unnecessary checkcasts and conditions generated * for the pattern matching in instanceof. * @modules jdk.jdeps/com.sun.tools.classfile - * @compile --enable-preview -source ${jdk.version} NoUnnecessaryCast.java - * @run main/othervm --enable-preview NoUnnecessaryCast + * @compile NoUnnecessaryCast.java + * @run main NoUnnecessaryCast */ import java.io.File; diff --git a/test/langtools/tools/javac/patterns/NullsInPatterns.java b/test/langtools/tools/javac/patterns/NullsInPatterns.java index 87f042c96bc..e9f6221d153 100644 --- a/test/langtools/tools/javac/patterns/NullsInPatterns.java +++ b/test/langtools/tools/javac/patterns/NullsInPatterns.java @@ -25,8 +25,7 @@ * @test * @bug 8231827 * @summary Testing pattern matching against the null constant - * @compile --enable-preview -source ${jdk.version} NullsInPatterns.java - * @run main/othervm --enable-preview NullsInPatterns + * @compile/fail/ref=NullsInPatterns.out -XDrawDiagnostics NullsInPatterns.java */ import java.util.List; diff --git a/test/langtools/tools/javac/patterns/NullsInPatterns.out b/test/langtools/tools/javac/patterns/NullsInPatterns.out new file mode 100644 index 00000000000..14b5e56f825 --- /dev/null +++ b/test/langtools/tools/javac/patterns/NullsInPatterns.out @@ -0,0 +1,3 @@ +NullsInPatterns.java:35:18: compiler.err.instanceof.pattern.no.subtype: java.util.List, compiler.misc.type.null +NullsInPatterns.java:46:18: compiler.err.instanceof.pattern.no.subtype: java.util.List, compiler.misc.type.null +2 errors diff --git a/test/langtools/tools/javac/patterns/PatternMatchPosTest.java b/test/langtools/tools/javac/patterns/PatternMatchPosTest.java index 9e532a69f79..4b20ffe948f 100644 --- a/test/langtools/tools/javac/patterns/PatternMatchPosTest.java +++ b/test/langtools/tools/javac/patterns/PatternMatchPosTest.java @@ -26,7 +26,7 @@ * @bug 8231827 * @summary Check proper positions. * @build PatternMatchPosTest - * @compile/ref=PatternMatchPosTest.out -processor PatternMatchPosTest -Xlint:unchecked -XDrawDiagnostics --enable-preview -source ${jdk.version} PatternMatchPosTestData.java + * @compile/ref=PatternMatchPosTest.out -processor PatternMatchPosTest -Xlint:unchecked -XDrawDiagnostics PatternMatchPosTestData.java */ import java.io.IOException; @@ -85,8 +85,10 @@ public Void scan(Tree tree, Void p) { if (print) { int start = (int) sp.getStartPosition(dataPath.getCompilationUnit(), tree); int end = (int) sp.getEndPosition(dataPath.getCompilationUnit(), tree); - processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, - text.substring(start, end)); + if (start != (-1) || end != (-1)) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, + text.substring(start, end)); + } } return super.scan(tree, p); } diff --git a/test/langtools/tools/javac/patterns/PatternMatchPosTest.out b/test/langtools/tools/javac/patterns/PatternMatchPosTest.out index 0d5d809ef1e..71eb6d7186d 100644 --- a/test/langtools/tools/javac/patterns/PatternMatchPosTest.out +++ b/test/langtools/tools/javac/patterns/PatternMatchPosTest.out @@ -2,13 +2,13 @@ - compiler.note.proc.messager: o instanceof String s - compiler.note.proc.messager: o - compiler.note.proc.messager: String s +- compiler.note.proc.messager: String s - compiler.note.proc.messager: String - compiler.note.proc.messager: (o instanceof java.lang.String s) - compiler.note.proc.messager: o instanceof java.lang.String s - compiler.note.proc.messager: o - compiler.note.proc.messager: java.lang.String s +- compiler.note.proc.messager: java.lang.String s - compiler.note.proc.messager: java.lang.String - compiler.note.proc.messager: java.lang -- compiler.note.proc.messager: java -- compiler.note.preview.filename: PatternMatchPosTestData.java -- compiler.note.preview.recompile +- compiler.note.proc.messager: java \ No newline at end of file diff --git a/test/langtools/tools/javac/patterns/PatternTypeTest2.java b/test/langtools/tools/javac/patterns/PatternTypeTest2.java index 736396db792..06c1fe6156c 100644 --- a/test/langtools/tools/javac/patterns/PatternTypeTest2.java +++ b/test/langtools/tools/javac/patterns/PatternTypeTest2.java @@ -25,8 +25,8 @@ * @test * @bug 8231827 * @summary Basic pattern test - * @compile --enable-preview -source ${jdk.version} PatternTypeTest2.java - * @run main/othervm --enable-preview PatternTypeTest2 + * @compile PatternTypeTest2.java + * @run main PatternTypeTest2 */ public class PatternTypeTest2 { diff --git a/test/langtools/tools/javac/patterns/PatternVariablesAreFinal.java b/test/langtools/tools/javac/patterns/PatternVariablesAreFinal.java deleted file mode 100644 index e5c9f0ca422..00000000000 --- a/test/langtools/tools/javac/patterns/PatternVariablesAreFinal.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * @test /nodynamiccopyright/ - * @bug 8231827 - * @summary Ensure that in type test patterns, the predicate is not trivially provable false. - * @compile/fail/ref=PatternVariablesAreFinal.out -XDrawDiagnostics --enable-preview -source ${jdk.version} PatternVariablesAreFinal.java - */ -public class PatternVariablesAreFinal { - public static void main(String[] args) { - Object o = 32; - if (o instanceof String s) { - s = "hello again"; - System.out.println(s); - } - System.out.println("test complete"); - } -} diff --git a/test/langtools/tools/javac/patterns/PatternVariablesAreFinal.out b/test/langtools/tools/javac/patterns/PatternVariablesAreFinal.out deleted file mode 100644 index bf266e311f3..00000000000 --- a/test/langtools/tools/javac/patterns/PatternVariablesAreFinal.out +++ /dev/null @@ -1,4 +0,0 @@ -PatternVariablesAreFinal.java:11:13: compiler.err.pattern.binding.may.not.be.assigned: s -- compiler.note.preview.filename: PatternVariablesAreFinal.java -- compiler.note.preview.recompile -1 error diff --git a/test/langtools/tools/javac/patterns/PatternVariablesAreNonFinal.java b/test/langtools/tools/javac/patterns/PatternVariablesAreNonFinal.java new file mode 100644 index 00000000000..6ee3adf30b8 --- /dev/null +++ b/test/langtools/tools/javac/patterns/PatternVariablesAreNonFinal.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8231827 + * @summary Pattern variables are non-final. + * @compile/fail/ref=PatternVariablesAreNonFinal.out -XDrawDiagnostics PatternVariablesAreNonFinal.java + */ +public class PatternVariablesAreNonFinal { + public static void main(String[] args) { + Object o = 32; + if (o instanceof String s) { + s = "hello again"; + new Runnable() { + @Override + public void run() { + System.err.println(s); + } + }; + } + System.out.println("test complete"); + } +} diff --git a/test/langtools/tools/javac/patterns/PatternVariablesAreNonFinal.out b/test/langtools/tools/javac/patterns/PatternVariablesAreNonFinal.out new file mode 100644 index 00000000000..c2f3f410a7f --- /dev/null +++ b/test/langtools/tools/javac/patterns/PatternVariablesAreNonFinal.out @@ -0,0 +1,2 @@ +PatternVariablesAreNonFinal.java:38:40: compiler.err.cant.ref.non.effectively.final.var: s, (compiler.misc.inner.cls) +1 error diff --git a/test/langtools/tools/javac/patterns/PatternVariablesAreFinal2.java b/test/langtools/tools/javac/patterns/PatternVariablesAreNonFinal2.java similarity index 74% rename from test/langtools/tools/javac/patterns/PatternVariablesAreFinal2.java rename to test/langtools/tools/javac/patterns/PatternVariablesAreNonFinal2.java index 4925fbd2dc4..2381d4465bc 100644 --- a/test/langtools/tools/javac/patterns/PatternVariablesAreFinal2.java +++ b/test/langtools/tools/javac/patterns/PatternVariablesAreNonFinal2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,11 +24,11 @@ /* * @test * @bug 8231827 - * @summary Pattern variables are final so should be allowed to be referenced in an inner class - * @compile --enable-preview -source ${jdk.version} PatternVariablesAreFinal2.java - * @run main/othervm --enable-preview PatternVariablesAreFinal2 + * @summary Pattern variables can be effectivelly final so should be allowed to be referenced in an inner class + * @compile PatternVariablesAreNonFinal2.java + * @run main PatternVariablesAreNonFinal2 */ -public class PatternVariablesAreFinal2 { +public class PatternVariablesAreNonFinal2 { public static void main(String[] args) { Object o = "42"; if (o instanceof String s) { @@ -36,5 +36,9 @@ public static void main(String[] args) { void run() { System.err.println(s); } }.run(); } + if (o instanceof String s) { + s = "hello again"; + System.out.println(s); + } } } diff --git a/test/langtools/tools/javac/patterns/PatternsSimpleVisitorTest.java b/test/langtools/tools/javac/patterns/PatternsSimpleVisitorTest.java index dec7610f4bf..9d02f17bd25 100644 --- a/test/langtools/tools/javac/patterns/PatternsSimpleVisitorTest.java +++ b/test/langtools/tools/javac/patterns/PatternsSimpleVisitorTest.java @@ -105,7 +105,7 @@ private CompilationUnitTree parse(String code) throws IOException { StringWriter out = new StringWriter(); JavacTask ct = (JavacTask) tool.getTask(out, null, noErrors, - List.of("--enable-preview", "-source", Integer.toString(Runtime.version().feature())), null, + null, null, Arrays.asList(new MyFileObject(code))); return ct.parse().iterator().next(); } diff --git a/test/langtools/tools/javac/patterns/Reifiable.java b/test/langtools/tools/javac/patterns/Reifiable.java index 968138a0ada..966d8b964f9 100644 --- a/test/langtools/tools/javac/patterns/Reifiable.java +++ b/test/langtools/tools/javac/patterns/Reifiable.java @@ -2,7 +2,7 @@ * @test /nodynamiccopyright/ * @bug 8231827 * @summary Verify behavior w.r.t. non-reifiable types and type test patterns in instanceof - * @compile/fail/ref=Reifiable.out --enable-preview -source ${jdk.version} -XDrawDiagnostics Reifiable.java + * @compile/fail/ref=Reifiable.out -XDrawDiagnostics Reifiable.java */ public class Reifiable implements ReifiableI { diff --git a/test/langtools/tools/javac/patterns/Reifiable.out b/test/langtools/tools/javac/patterns/Reifiable.out index 7d4461c9fc6..03d9688a035 100644 --- a/test/langtools/tools/javac/patterns/Reifiable.out +++ b/test/langtools/tools/javac/patterns/Reifiable.out @@ -2,6 +2,4 @@ Reifiable.java:10:16: compiler.err.instanceof.reifiable.not.safe: java.lang.Obje Reifiable.java:12:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: Reifiable.List, Reifiable.ListImpl) Reifiable.java:13:39: compiler.err.not.within.bounds: java.lang.String, T Reifiable.java:14:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: Reifiable.List, Reifiable.Unrelated) -- compiler.note.preview.filename: Reifiable.java -- compiler.note.preview.recompile 4 errors diff --git a/test/langtools/tools/javac/patterns/ReifiableOld-old.out b/test/langtools/tools/javac/patterns/ReifiableOld-old.out index 85ea8714e0a..ac56bfa3e1d 100644 --- a/test/langtools/tools/javac/patterns/ReifiableOld-old.out +++ b/test/langtools/tools/javac/patterns/ReifiableOld-old.out @@ -1,7 +1,5 @@ -ReifiableOld.java:12:37: compiler.err.illegal.generic.type.for.instof -ReifiableOld.java:13:38: compiler.err.illegal.generic.type.for.instof -ReifiableOld.java:14:38: compiler.err.illegal.generic.type.for.instof -ReifiableOld.java:15:38: compiler.err.illegal.generic.type.for.instof -ReifiableOld.java:15:39: compiler.err.not.within.bounds: java.lang.String, T -ReifiableOld.java:16:39: compiler.err.illegal.generic.type.for.instof -6 errors +ReifiableOld.java:11:18: compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.reifiable.types.instanceof), 15, 16 +ReifiableOld.java:13:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: ReifiableOld.List, ReifiableOld.ListImpl) +ReifiableOld.java:14:39: compiler.err.not.within.bounds: java.lang.String, T +ReifiableOld.java:15:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: ReifiableOld.List, ReifiableOld.Unrelated) +4 errors diff --git a/test/langtools/tools/javac/patterns/ReifiableOld.java b/test/langtools/tools/javac/patterns/ReifiableOld.java index 4ea4bd17919..1cdbd5a8554 100644 --- a/test/langtools/tools/javac/patterns/ReifiableOld.java +++ b/test/langtools/tools/javac/patterns/ReifiableOld.java @@ -2,9 +2,8 @@ * @test /nodynamiccopyright/ * @bug 8231827 * @summary Verify behavior w.r.t. non-reifiable types in instanceof - * @compile/fail/ref=ReifiableOld-old.out -source 13 -Xlint:-options -XDrawDiagnostics ReifiableOld.java - * @compile/fail/ref=ReifiableOld-old.out -source ${jdk.version} -XDrawDiagnostics ReifiableOld.java - * @compile/fail/ref=ReifiableOld.out --enable-preview -source ${jdk.version} -XDrawDiagnostics ReifiableOld.java + * @compile/fail/ref=ReifiableOld-old.out -source 15 -Xlint:-options -XDrawDiagnostics ReifiableOld.java + * @compile/fail/ref=ReifiableOld.out -XDrawDiagnostics ReifiableOld.java */ public class ReifiableOld implements ReifiableOldI { diff --git a/test/langtools/tools/javac/patterns/ReifiableOld.out b/test/langtools/tools/javac/patterns/ReifiableOld.out index 04d84f10201..d43038027f4 100644 --- a/test/langtools/tools/javac/patterns/ReifiableOld.out +++ b/test/langtools/tools/javac/patterns/ReifiableOld.out @@ -1,7 +1,5 @@ -ReifiableOld.java:12:16: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, ReifiableOld.ListImpl -ReifiableOld.java:14:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: ReifiableOld.List, ReifiableOld.ListImpl) -ReifiableOld.java:15:39: compiler.err.not.within.bounds: java.lang.String, T -ReifiableOld.java:16:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: ReifiableOld.List, ReifiableOld.Unrelated) -- compiler.note.preview.filename: ReifiableOld.java -- compiler.note.preview.recompile +ReifiableOld.java:11:16: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, ReifiableOld.ListImpl +ReifiableOld.java:13:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: ReifiableOld.List, ReifiableOld.ListImpl) +ReifiableOld.java:14:39: compiler.err.not.within.bounds: java.lang.String, T +ReifiableOld.java:15:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: ReifiableOld.List, ReifiableOld.Unrelated) 4 errors diff --git a/test/langtools/tools/javac/patterns/UncheckedWarningOnMatchesTest.java b/test/langtools/tools/javac/patterns/UncheckedWarningOnMatchesTest.java index 5ada0240827..db0a58f5c41 100644 --- a/test/langtools/tools/javac/patterns/UncheckedWarningOnMatchesTest.java +++ b/test/langtools/tools/javac/patterns/UncheckedWarningOnMatchesTest.java @@ -2,7 +2,7 @@ * @test /nodynamiccopyright/ * @bug 8187429 8231827 * @summary Missing unchecked conversion warning - * @compile/fail/ref=UncheckedWarningOnMatchesTest.out -Xlint:unchecked -Werror -XDrawDiagnostics --enable-preview -source ${jdk.version} UncheckedWarningOnMatchesTest.java + * @compile/fail/ref=UncheckedWarningOnMatchesTest.out -Xlint:unchecked -Werror -XDrawDiagnostics UncheckedWarningOnMatchesTest.java */ import java.util.ArrayList; diff --git a/test/langtools/tools/javac/patterns/UncheckedWarningOnMatchesTest.out b/test/langtools/tools/javac/patterns/UncheckedWarningOnMatchesTest.out index 447cfff51b3..179bdcf8883 100644 --- a/test/langtools/tools/javac/patterns/UncheckedWarningOnMatchesTest.out +++ b/test/langtools/tools/javac/patterns/UncheckedWarningOnMatchesTest.out @@ -1,4 +1,2 @@ UncheckedWarningOnMatchesTest.java:14:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, java.util.ArrayList -- compiler.note.preview.filename: UncheckedWarningOnMatchesTest.java -- compiler.note.preview.recompile 1 error diff --git a/test/langtools/tools/javac/patterns/scope/ScopeTest.java b/test/langtools/tools/javac/patterns/scope/ScopeTest.java index 82548fa62c8..1f10fb7dea5 100644 --- a/test/langtools/tools/javac/patterns/scope/ScopeTest.java +++ b/test/langtools/tools/javac/patterns/scope/ScopeTest.java @@ -98,10 +98,7 @@ private void program(String block) { } private void assertOK(String block) { - String sourceVersion = Integer.toString(Runtime.version().feature()); - reset(); - addCompileOptions("--enable-preview", "-source", sourceVersion); program(block); try { compile(); @@ -113,10 +110,7 @@ private void assertOK(String block) { } private void assertFail(String expectedDiag, String block) { - String sourceVersion = Integer.toString(Runtime.version().feature()); - reset(); - addCompileOptions("--enable-preview", "-source", sourceVersion); program(block); try { compile(); diff --git a/test/langtools/tools/javac/preview/classReaderTest/TooNewMajorVersionTest.java b/test/langtools/tools/javac/preview/classReaderTest/TooNewMajorVersionTest.java new file mode 100644 index 00000000000..c3fd0e1d376 --- /dev/null +++ b/test/langtools/tools/javac/preview/classReaderTest/TooNewMajorVersionTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8231599 + * @summary Verify javac does not crash on preview classfiles from the future + Java versions. + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.jvm + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run main TooNewMajorVersionTest + */ + +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.TestRunner; +import toolbox.ToolBox; + +import com.sun.tools.javac.jvm.ClassFile.Version; + +import java.io.RandomAccessFile; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +public class TooNewMajorVersionTest extends TestRunner { + + protected ToolBox tb; + + TooNewMajorVersionTest() { + super(System.err); + tb = new ToolBox(); + } + + public static void main(String... args) throws Exception { + TooNewMajorVersionTest t = new TooNewMajorVersionTest(); + t.runTests(); + } + + protected void runTests() throws Exception { + runTests(m -> new Object[] { Paths.get(m.getName()) }); + } + + @Test + public void brokenMajorVersionWithPreview(Path base) throws Exception { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, """ + class C { + private Object o = null; + private boolean b = o instanceof String s; + } + """); + Path classes = base.resolve("classes"); + + Files.createDirectories(classes); + + for (int upgrade = 1; upgrade < 3; upgrade++) { + new JavacTask(tb) + .outdir(classes) + .options("-XDforcePreview", + "--enable-preview", + "--release", String.valueOf(Runtime.version().feature())) + .files(tb.findJavaFiles(src)) + .run() + .writeAll(); + + Path classfile = classes.resolve("C.class"); + int wrongMajor; + + try (RandomAccessFile f = new RandomAccessFile(classfile.toFile(), "rw")) { + f.readInt(); + short minor = f.readShort(); + if (minor != (-1)) { + throw new AssertionError("Unexpected minor version: " + minor); + } + long point = f.getFilePointer(); + short major = f.readShort(); + f.seek(point); + f.writeShort(wrongMajor = major + upgrade); + } + + Path test = base.resolve("test"); + tb.writeJavaFiles(test, "class Test extends C {}"); + Path testClasses = base.resolve("classes"); + + Files.createDirectories(testClasses); + + for (String extraOption : new String[] {"-XDignored", "--enable-preview"}) { + List log = new JavacTask(tb) + .outdir(testClasses) + .options("-XDrawDiagnostics", + "-classpath", classes.toString(), + "--release", String.valueOf(Runtime.version().feature()), + extraOption) + .files(tb.findJavaFiles(test)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + List expected = List.of( + "Test.java:1:20: compiler.err.cant.access: C, (compiler.misc.bad.class.file.header: C.class, (compiler.misc.wrong.version: " + wrongMajor + ", 65535, " + Version.MAX().major + ", 0))", + "1 error" + ); + if (!log.equals(expected)) + throw new Exception("expected output not found" + log); + } + } + } +} diff --git a/test/langtools/tools/javac/processing/model/element/JavaxLangModelForRecords.java b/test/langtools/tools/javac/processing/model/element/JavaxLangModelForRecords.java index 85fa3b90435..9bc06ce6d42 100644 --- a/test/langtools/tools/javac/processing/model/element/JavaxLangModelForRecords.java +++ b/test/langtools/tools/javac/processing/model/element/JavaxLangModelForRecords.java @@ -23,6 +23,7 @@ /* * @test + * @bug 8246774 * @summary Verify that annotation processing works for records * @library /tools/lib /tools/javac/lib * @modules @@ -67,7 +68,6 @@ public class JavaxLangModelForRecords extends TestRunner { } public static void main(String... args) throws Exception { - System.out.println(System.getProperties()); new JavaxLangModelForRecords().runTests(); } @@ -105,17 +105,13 @@ public void testQualifiedClassForProcessing(Path base) throws Exception { tb.writeJavaFiles(r, "record R(int i) {}"); - List expected = List.of("Note: field: i", - "Note: record component: i", - "Note: testQualifiedClassForProcessing" + File.separator + "src" + File.separator - + "R" + File.separator + "R.java uses preview language features.", - "Note: Recompile with -Xlint:preview for details."); + List expected = List.of( + "Note: field: i", + "Note: record component: i"); for (Mode mode : new Mode[] {Mode.API}) { List log = new JavacTask(tb, mode) - .options("-processor", QualifiedClassForProcessing.class.getName(), - "--enable-preview", - "-source", Integer.toString(Runtime.version().feature())) + .options("-processor", QualifiedClassForProcessing.class.getName()) .files(findJavaFiles(src)) .outdir(classes) .run() diff --git a/test/langtools/tools/javac/processing/model/element/TestBindingVariable.java b/test/langtools/tools/javac/processing/model/element/TestBindingVariable.java index 7abbf700518..46eecd6ae2a 100644 --- a/test/langtools/tools/javac/processing/model/element/TestBindingVariable.java +++ b/test/langtools/tools/javac/processing/model/element/TestBindingVariable.java @@ -105,20 +105,20 @@ public BindingVariableScanner(Trees trees) { } @Override public Void visitBindingPattern(BindingPatternTree node, Void p) { - handleCurrentTreeAsBindingVar(); + handleTreeAsBindingVar(new TreePath(getCurrentPath(), node.getVariable())); return super.visitBindingPattern(node, p); } @Override public Void visitIdentifier(IdentifierTree node, Void p) { if (node.getName().contentEquals("bindingVar")) { - handleCurrentTreeAsBindingVar(); + handleTreeAsBindingVar(getCurrentPath()); } return super.visitIdentifier(node, p); } - private void handleCurrentTreeAsBindingVar() { - Element element = trees.getElement(getCurrentPath()); + private void handleTreeAsBindingVar(TreePath tp) { + Element element = trees.getElement(tp); System.out.println("Name: " + element.getSimpleName() + "\tKind: " + element.getKind()); diff --git a/test/langtools/tools/javac/records/ElementFilterRecordComponentTest.java b/test/langtools/tools/javac/records/ElementFilterRecordComponentTest.java index 062de5b5392..3ca306ed34f 100644 --- a/test/langtools/tools/javac/records/ElementFilterRecordComponentTest.java +++ b/test/langtools/tools/javac/records/ElementFilterRecordComponentTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8241312 + * @bug 8241312 8246774 * @summary test for javax.lang.model.util.ElementFilter::recordComponentsIn * @modules jdk.compiler/com.sun.tools.javac.util */ @@ -47,8 +47,7 @@ public class ElementFilterRecordComponentTest { public static void main(String... args) throws IOException { JavaCompiler c = ToolProvider.getSystemJavaCompiler(); - JavacTask t = (JavacTask) c.getTask(null, null, null, - List.of("--enable-preview", "-source", Integer.toString(Runtime.version().feature())), null, + JavacTask t = (JavacTask) c.getTask(null, null, null, null, null, List.of(new SimpleJavaFileObject(URI.create("TestClass.java"), JavaFileObject.Kind.SOURCE) { @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { diff --git a/test/langtools/tools/javac/records/LocalStaticDeclarations.java b/test/langtools/tools/javac/records/LocalStaticDeclarations.java index 1b8519bc7b9..53dd9854231 100644 --- a/test/langtools/tools/javac/records/LocalStaticDeclarations.java +++ b/test/langtools/tools/javac/records/LocalStaticDeclarations.java @@ -23,15 +23,14 @@ /* * @test - * @bug 8242293 + * @bug 8242293 8246774 * @summary allow for local interfaces and enums plus nested records, interfaces and enums * @library /tools/javac/lib * @modules jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.file * jdk.compiler/com.sun.tools.javac.util * @build combo.ComboTestHelper - * @compile --enable-preview -source ${jdk.version} LocalStaticDeclarations.java - * @run main/othervm --enable-preview LocalStaticDeclarations + * @run main LocalStaticDeclarations */ import javax.lang.model.element.Element; @@ -175,7 +174,6 @@ public static void main(String... args) throws Exception { @Override public void doWork() throws Throwable { newCompilationTask() - .withOptions(new String[]{"--enable-preview", "-source", Integer.toString(Runtime.version().feature())}) .withSourceFromTemplate("Test", sourceTemplate) .generate(this::check); } diff --git a/test/langtools/tools/javac/records/MapAccessorToComponent.java b/test/langtools/tools/javac/records/MapAccessorToComponent.java index e8f2bcac492..9927d0ddb76 100644 --- a/test/langtools/tools/javac/records/MapAccessorToComponent.java +++ b/test/langtools/tools/javac/records/MapAccessorToComponent.java @@ -23,6 +23,7 @@ /* * @test + * @bug 8246774 * @summary test for javax.lang.model.util.Elements::recordComponentFor * @modules jdk.compiler */ @@ -51,8 +52,7 @@ public static void main(String... args) throws IOException { public void run() throws IOException { String code = "record R(int val1, int val2) {}"; JavaCompiler c = ToolProvider.getSystemJavaCompiler(); - JavacTask t = (JavacTask) c.getTask(null, null, null, - List.of("--enable-preview", "-source", Integer.toString(Runtime.version().feature())), null, + JavacTask t = (JavacTask) c.getTask(null, null, null, null, null, List.of(new MyFileObject(code))); TypeElement record = (TypeElement) t.analyze().iterator().next(); for (RecordComponentElement rce : ElementFilter.recordComponentsIn(record.getEnclosedElements())) { diff --git a/test/langtools/tools/javac/records/RecordCompilationTests.java b/test/langtools/tools/javac/records/RecordCompilationTests.java index 279ded7443d..bc3be98c72d 100644 --- a/test/langtools/tools/javac/records/RecordCompilationTests.java +++ b/test/langtools/tools/javac/records/RecordCompilationTests.java @@ -26,7 +26,8 @@ /** * RecordCompilationTests * - * @test 8250629 8252307 8247352 8241151 + * @test + * @bug 8250629 8252307 8247352 8241151 8246774 * @summary Negative compilation tests, and positive compilation (smoke) tests for records * @library /lib/combo /tools/lib /tools/javac/lib * @modules @@ -35,9 +36,8 @@ * jdk.compiler/com.sun.tools.javac.util * jdk.jdeps/com.sun.tools.classfile * @build JavacTestingAbstractProcessor - * @compile --enable-preview -source ${jdk.version} RecordCompilationTests.java - * @run testng/othervm -DuseAP=false --enable-preview RecordCompilationTests - * @run testng/othervm -DuseAP=true --enable-preview RecordCompilationTests + * @run testng/othervm -DuseAP=false RecordCompilationTests + * @run testng/othervm -DuseAP=true RecordCompilationTests */ import java.io.File; @@ -119,17 +119,7 @@ @Test public class RecordCompilationTests extends CompilationTestCase { - // @@@ When records become a permanent feature, we don't need these any more - private static String[] PREVIEW_OPTIONS = { - "--enable-preview", - "-source", Integer.toString(Runtime.version().feature()) - }; - - private static String[] PREVIEW_OPTIONS_WITH_AP = { - "--enable-preview", - "-source", Integer.toString(Runtime.version().feature()), - "-processor", SimplestAP.class.getName() - }; + private static String[] OPTIONS_WITH_AP = {"-processor", SimplestAP.class.getName()}; private static final List BAD_COMPONENT_NAMES = List.of( "clone", "finalize", "getClass", "hashCode", @@ -150,7 +140,9 @@ public boolean process(Set annotations, RoundEnvironment public RecordCompilationTests() { useAP = System.getProperty("useAP", "false").equals("true"); setDefaultFilename("R.java"); - setCompileOptions(useAP ? PREVIEW_OPTIONS_WITH_AP : PREVIEW_OPTIONS); + if (useAP) { + setCompileOptions(OPTIONS_WITH_AP); + } System.out.println(useAP ? "running all tests using an annotation processor" : "running all tests without annotation processor"); } @@ -1010,7 +1002,7 @@ public void testAcceptRecordId() { String[] previousOptions = getCompileOptions(); String[] testOptions = {/* no options */}; setCompileOptions(testOptions); - assertOKWithWarning("compiler.warn.restricted.type.not.allowed.preview", + assertFail("compiler.err.illegal.start.of.type", "class R {\n" + " record RR(int i) {\n" + " return null;\n" + @@ -1062,8 +1054,6 @@ record R(@Anno String s) {} ); String[] generalOptions = { - "--enable-preview", - "-source", Integer.toString(Runtime.version().feature()), "-processor", Processor.class.getName(), "-Atargets=" }; @@ -1682,4 +1672,39 @@ record R() implements java.io.Serializable {} removeLastCompileOptions(2); } } + + public void testAnnotationsOnVarargsRecComp() { + assertOK( + """ + import java.lang.annotation.*; + + @Target({ElementType.TYPE_USE}) + @interface Simple {} + + record R(@Simple int... val) { + static void test() { + R rec = new R(10, 20); + } + } + """ + ); + assertOK( + """ + import java.lang.annotation.*; + + @Target({ElementType.TYPE_USE}) + @interface SimpleContainer{ Simple[] value(); } + + @Repeatable(SimpleContainer.class) + @Target({ElementType.TYPE_USE}) + @interface Simple {} + + record R(@Simple int... val) { + static void test() { + R rec = new R(10, 20); + } + } + """ + ); + } } diff --git a/test/langtools/tools/javac/records/RecordMemberTests.java b/test/langtools/tools/javac/records/RecordMemberTests.java index 85d9434ffb6..a754f294723 100644 --- a/test/langtools/tools/javac/records/RecordMemberTests.java +++ b/test/langtools/tools/javac/records/RecordMemberTests.java @@ -24,11 +24,10 @@ */ /** - * RecordMemberTests - * * @test - * @compile --enable-preview -source ${jdk.version} RecordMemberTests.java - * @run testng/othervm --enable-preview RecordMemberTests + * @bug 8246774 + * @summary test several assertions on record classes members + * @run testng RecordMemberTests */ import java.lang.reflect.Constructor; diff --git a/test/langtools/tools/javac/records/RecordsBinaryCompatibilityTests.java b/test/langtools/tools/javac/records/RecordsBinaryCompatibilityTests.java new file mode 100644 index 00000000000..d526dbc4ef2 --- /dev/null +++ b/test/langtools/tools/javac/records/RecordsBinaryCompatibilityTests.java @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary test binary compatibility rules for record classes + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.util + * jdk.compiler/com.sun.tools.javac.code + * jdk.jdeps/com.sun.tools.classfile + * @build toolbox.ToolBox toolbox.JavacTask + * @run main RecordsBinaryCompatibilityTests + */ + +import java.util.*; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.stream.IntStream; + +import com.sun.tools.classfile.*; +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.util.Assert; +import toolbox.TestRunner; +import toolbox.ToolBox; +import toolbox.JavaTask; +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.Task.OutputKind; + +public class RecordsBinaryCompatibilityTests extends TestRunner { + ToolBox tb; + + RecordsBinaryCompatibilityTests() { + super(System.err); + tb = new ToolBox(); + } + + protected void runTests() throws Exception { + runTests(m -> new Object[]{Paths.get(m.getName())}); + } + + public static void main(String... args) throws Exception { + RecordsBinaryCompatibilityTests t = new RecordsBinaryCompatibilityTests(); + t.runTests(); + } + + Path[] findJavaFiles(Path... paths) throws IOException { + return tb.findJavaFiles(paths); + } + + @Test + public void testCompatibilityAfterAddingRecordComponent(Path base) throws Exception { + testCompatibilityAfterModifyingRecord( + base, + """ + package pkg; + public record R(String i) {} + """, + """ + package pkg; + public record R(String i, String j) {} + """, + """ + package pkg; + public class Client { + public static void main(String... args) { + R r = new R("Hello World!"); + System.out.println(r.i()); + } + } + """, + true, + NoSuchMethodError.class + ); + } + + @Test + public void testCompatibilityAfterDeletingRecordComponent(Path base) throws Exception { + testCompatibilityAfterModifyingRecord( + base, + """ + package pkg; + public record R(String i, String j) { + public R(String j) { + this("Hello World!", j); + } + } + """, + """ + package pkg; + public record R(String j) {} + """, + """ + package pkg; + public class Client { + public static void main(String... args) { + R r = new R("Hi"); + System.out.println(r.i()); + } + } + """, + true, + NoSuchMethodError.class + ); + } + + @Test + public void testCompatibilityAfterChangingRecordComponent(Path base) throws Exception { + testCompatibilityAfterModifyingRecord( + base, + """ + package pkg; + public record R(String i, double j) {} + """, + """ + package pkg; + public record R(String i, int j) {} + """, + """ + package pkg; + public class Client { + public static void main(String... args) { + R r = new R("Hello World!", 1); + System.out.println(r.i()); + } + } + """, + true, + NoSuchMethodError.class + ); + } + + @Test + public void testCompatibilityAfterReorderingRecordComponents(Path base) throws Exception { + testCompatibilityAfterModifyingRecord( + base, + """ + package pkg; + public record R(String i, int j) {} + """, + """ + package pkg; + public record R(int j, String i) {} + """, + """ + package pkg; + public class Client { + public static void main(String... args) { + R r = new R("Hello World!", 1); + System.out.println(r.i()); + } + } + """, + true, + NoSuchMethodError.class + ); + } + + @Test + public void testCompatibilityAfterChangingRecordComponent2(Path base) throws Exception { + testCompatibilityAfterModifyingRecord( + base, + """ + package pkg; + public record R(String j) { + public static String i() { return "Hello World!"; } + } + """, + """ + package pkg; + public record R(String i) {} + """, + """ + package pkg; + public class Client { + public static void main(String... args) { + R r = new R("Hello World!"); + System.out.println(r.i()); + } + } + """, + true, + IncompatibleClassChangeError.class + ); + } + + @Test + public void testCompatibilityAfterChangingRecordComponent3(Path base) throws Exception { + testCompatibilityAfterModifyingRecord( + base, + """ + package pkg; + public record R(String i) { + } + """, + """ + package pkg; + public record R(String j) {} + """, + """ + package pkg; + public class Client { + public static void main(String... args) { + R r = new R("Hello World!"); + System.out.println(r.i()); + } + } + """, + true, + NoSuchMethodError.class + ); + } + + /* 1- compiles the first version of the record class source code along with the client source code + * 2- executes the client class just to make sure that it works + * 3- compiles the second version of the record class + * 4- executes the client class and makes sure that the VM throws the expected error or not + * depending on the shouldFail argument + */ + private void testCompatibilityAfterModifyingRecord( + Path base, + String recordCode1, + String recordCode2, + String clientCode, + boolean shouldFail, + Class expectedError) throws Exception { + Path src = base.resolve("src"); + Path pkg = src.resolve("pkg"); + Path recordSrc = pkg.resolve("R"); + Path client = pkg.resolve("Client"); + + tb.writeJavaFiles(recordSrc, recordCode1); + tb.writeJavaFiles(client, clientCode); + + Path out = base.resolve("out"); + Files.createDirectories(out); + + new JavacTask(tb) + .outdir(out) + .files(findJavaFiles(pkg)) + .run(); + + // let's execute to check that it's working + String output = new JavaTask(tb) + .classpath(out.toString()) + .classArgs("pkg.Client") + .run() + .writeAll() + .getOutput(Task.OutputKind.STDOUT); + + // let's first check that it runs wo issues + if (!output.contains("Hello World!")) { + throw new AssertionError("execution of Client didn't finish"); + } + + // now lets change the record class + tb.writeJavaFiles(recordSrc, recordCode2); + + new JavacTask(tb) + .outdir(out) + .files(findJavaFiles(recordSrc)) + .run(); + + if (shouldFail) { + // let's now check that we get the expected error + output = new JavaTask(tb) + .classpath(out.toString()) + .classArgs("pkg.Client") + .run(Task.Expect.FAIL) + .writeAll() + .getOutput(Task.OutputKind.STDERR); + if (!output.startsWith("Exception in thread \"main\" " + expectedError.getName())) { + throw new AssertionError(expectedError.getName() + " expected"); + } + } else { + new JavaTask(tb) + .classpath(out.toString()) + .classArgs("pkg.Client") + .run(Task.Expect.SUCCESS); + } + } +} diff --git a/test/langtools/tools/javac/records/VarargsRecordsTest.java b/test/langtools/tools/javac/records/VarargsRecordsTest.java index 2151db278ac..2e60ec810ea 100644 --- a/test/langtools/tools/javac/records/VarargsRecordsTest.java +++ b/test/langtools/tools/javac/records/VarargsRecordsTest.java @@ -33,11 +33,10 @@ import static org.testng.Assert.*; /** - * VarargsRecordsTest - * * @test - * @compile --enable-preview -source ${jdk.version} VarargsRecordsTest.java - * @run testng/othervm --enable-preview VarargsRecordsTest + * @bug 8246774 + * @summary test for varargs record components + * @run testng VarargsRecordsTest */ @Test public class VarargsRecordsTest { diff --git a/test/langtools/tools/javac/records/mandated_members/CheckRecordMembers.java b/test/langtools/tools/javac/records/mandated_members/CheckRecordMembers.java index e862ec42538..f0aab5985d5 100644 --- a/test/langtools/tools/javac/records/mandated_members/CheckRecordMembers.java +++ b/test/langtools/tools/javac/records/mandated_members/CheckRecordMembers.java @@ -25,6 +25,7 @@ * CheckRecordMembers * * @test + * @bug 8246774 * @summary check that the accessors, equals, hashCode and toString methods * work as expected * @library /tools/javac/lib @@ -32,7 +33,7 @@ * jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.util * @build combo.ComboTestHelper - * @run main/othervm --enable-preview CheckRecordMembers + * @run main CheckRecordMembers */ import java.lang.reflect.Constructor; @@ -102,7 +103,6 @@ public static void main(String... args) throws Exception { @Override public void doWork() throws Throwable { newCompilationTask() - .withOptions(new String[]{"--enable-preview", "-source", Integer.toString(Runtime.version().feature())}) .withSourceFromTemplate("Data", sourceTemplate) .generate(this::check); } diff --git a/test/langtools/tools/javac/records/writeread/WriteReadTest.java b/test/langtools/tools/javac/records/writeread/WriteReadTest.java index 2d6430ee7ae..e7d75a852f4 100644 --- a/test/langtools/tools/javac/records/writeread/WriteReadTest.java +++ b/test/langtools/tools/javac/records/writeread/WriteReadTest.java @@ -23,9 +23,10 @@ /* * @test + * @bug 8246774 * @summary Verify javac can read record classfiles it writes - * @compile --enable-preview -source ${jdk.version} Record.java - * @compile --enable-preview -source ${jdk.version} WriteReadTest.java + * @compile Record.java + * @compile WriteReadTest.java */ public class WriteReadTest { Record1 r1; diff --git a/test/langtools/tools/javac/switchexpr/ExpressionSwitchInfer.java b/test/langtools/tools/javac/switchexpr/ExpressionSwitchInfer.java index ff48ef133b5..a796411fc66 100644 --- a/test/langtools/tools/javac/switchexpr/ExpressionSwitchInfer.java +++ b/test/langtools/tools/javac/switchexpr/ExpressionSwitchInfer.java @@ -1,6 +1,6 @@ /* * @test /nodynamiccopyright/ - * @bug 8206986 + * @bug 8206986 8254286 * @summary Check types inferred for switch expressions. * @compile/fail/ref=ExpressionSwitchInfer.out -XDrawDiagnostics ExpressionSwitchInfer.java */ @@ -34,4 +34,53 @@ private T test(List l, Class c, String param) { return null; } + void bug8254286(I1 i1, I2 i2, int s) { + var t1 = switch (s) { + case 1 -> i1; + case 2 -> null; + default -> i2; + }; + t1.m(); + var t2 = switch (s) { + case 2 -> null; + case 1 -> i1; + default -> i2; + }; + t2.m(); + var t3 = switch (s) { + case 1 -> i1; + default -> i2; + case 2 -> null; + }; + t3.m(); + var t4 = switch (s) { + case 1 -> i1; + default -> null; + }; + t4.m(); + var t5 = switch (s) { + default -> null; + case 1 -> i1; + }; + t5.m(); + var t6 = switch (s) { + default -> null; + }; + var t7 = switch (s) { + case 1 -> null; + default -> null; + }; + var t8 = switch (s) { + case 1 -> null; + case 2 -> null; + default -> null; + }; + } + + interface I { + void m(); + } + interface I1 extends I {} + interface I2 extends I {} + } diff --git a/test/langtools/tools/javac/switchexpr/ExpressionSwitchInfer.out b/test/langtools/tools/javac/switchexpr/ExpressionSwitchInfer.out index b0b4a738c9c..5359db37da3 100644 --- a/test/langtools/tools/javac/switchexpr/ExpressionSwitchInfer.out +++ b/test/langtools/tools/javac/switchexpr/ExpressionSwitchInfer.out @@ -1,4 +1,7 @@ ExpressionSwitchInfer.java:17:95: compiler.err.cant.resolve.location.args: kindname.method, substring, , int, (compiler.misc.location: kindname.interface, java.lang.CharSequence, null) ExpressionSwitchInfer.java:26:38: compiler.err.cant.resolve.location.args: kindname.method, substring, , int, (compiler.misc.location: kindname.interface, java.lang.CharSequence, null) ExpressionSwitchInfer.java:30:23: compiler.err.prob.found.req: (compiler.misc.incompatible.type.in.switch.expression: (compiler.misc.inconvertible.types: int, java.lang.String)) -3 errors +ExpressionSwitchInfer.java:66:13: compiler.err.cant.infer.local.var.type: t6, (compiler.misc.local.cant.infer.null) +ExpressionSwitchInfer.java:69:13: compiler.err.cant.infer.local.var.type: t7, (compiler.misc.local.cant.infer.null) +ExpressionSwitchInfer.java:73:13: compiler.err.cant.infer.local.var.type: t8, (compiler.misc.local.cant.infer.null) +6 errors diff --git a/test/langtools/tools/javac/tree/TreePosTest.java b/test/langtools/tools/javac/tree/TreePosTest.java index ba5876bb9d9..add1db72e29 100644 --- a/test/langtools/tools/javac/tree/TreePosTest.java +++ b/test/langtools/tools/javac/tree/TreePosTest.java @@ -79,6 +79,7 @@ import com.sun.tools.javac.tree.JCTree.JCAnnotatedType; import com.sun.tools.javac.tree.JCTree.JCCase; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCNewClass; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.TreeInfo; @@ -419,6 +420,14 @@ private boolean isAnnotatedArray(JCTree tree) { ((JCAnnotatedType)tree).underlyingType.hasTag(TYPEARRAY); } + @Override + public void visitMethodDef(JCMethodDecl tree) { + // ignore compact record constructors + if ((tree.mods.flags & Flags.COMPACT_RECORD_CONSTRUCTOR) == 0) { + super.visitMethodDef(tree); + } + } + @Override public void visitVarDef(JCVariableDecl tree) { // enum member declarations are desugared in the parser and have diff --git a/test/langtools/tools/javac/unicode/NonasciiDigit.out b/test/langtools/tools/javac/unicode/NonasciiDigit.out index 6b68e496d45..b5b062d8976 100644 --- a/test/langtools/tools/javac/unicode/NonasciiDigit.out +++ b/test/langtools/tools/javac/unicode/NonasciiDigit.out @@ -1,10 +1,10 @@ -NonasciiDigit.java:12:24: compiler.err.illegal.nonascii.digit +NonasciiDigit.java:12:18: compiler.err.illegal.nonascii.digit NonasciiDigit.java:13:19: compiler.err.illegal.nonascii.digit -NonasciiDigit.java:14:24: compiler.err.illegal.nonascii.digit -NonasciiDigit.java:16:27: compiler.err.illegal.nonascii.digit -NonasciiDigit.java:17:22: compiler.err.illegal.nonascii.digit -NonasciiDigit.java:18:22: compiler.err.illegal.nonascii.digit -NonasciiDigit.java:19:22: compiler.err.illegal.nonascii.digit +NonasciiDigit.java:14:18: compiler.err.illegal.nonascii.digit +NonasciiDigit.java:16:21: compiler.err.illegal.nonascii.digit +NonasciiDigit.java:17:23: compiler.err.illegal.nonascii.digit +NonasciiDigit.java:18:25: compiler.err.illegal.nonascii.digit +NonasciiDigit.java:19:23: compiler.err.illegal.nonascii.digit NonasciiDigit.java:20:22: compiler.err.illegal.nonascii.digit -NonasciiDigit.java:21:27: compiler.err.illegal.nonascii.digit +NonasciiDigit.java:21:21: compiler.err.illegal.nonascii.digit 9 errors diff --git a/test/langtools/tools/javac/unicode/SubChar.java b/test/langtools/tools/javac/unicode/SubChar.java index 00072133964..66b092bb374 100644 --- a/test/langtools/tools/javac/unicode/SubChar.java +++ b/test/langtools/tools/javac/unicode/SubChar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,24 +25,48 @@ * @test * @bug 4330479 * @summary ASCII SUB character is rejected in multi-line comments - * @author gafter - * - * @compile SubChar.java + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run main SubChar */ +import toolbox.JavacTask; +import toolbox.JavaTask; +import toolbox.Task; +import toolbox.ToolBox; -/* -Note: this source file has been crafted very carefully to end with the -unicode escape sequence for the control-Z character without a -following newline. The scanner is specified to allow control-Z there. -If you edit this source file, please make sure that your editor does -not insert a newline after that trailing line. -*/ -/** \u001A */ -class SubChar { - public static void main(String args[]) { - return; - } +public class SubChar { + private static final ToolBox TOOLBOX = new ToolBox(); + + private static final String SOURCE = """ + /* + Note: this source file has been crafted very carefully to end with the + unicode escape sequence for the control-Z character without a + following newline. The scanner is specified to allow control-Z there. + If you edit this source file, please make sure that your editor does + not insert a newline after that trailing line. + */ + + /** \\u001A */ + class ControlZTest { + public static void main(String args[]) { + return; + } + } + /* \\u001A */\ + """; + + public static void main(String... args) { + String output = new JavacTask(TOOLBOX) + .sources(SOURCE) + .classpath(".") + .options("-encoding", "utf8") + .run() + .writeAll() + .getOutput(Task.OutputKind.DIRECT); + System.out.println(output); + } } -/* \u001A */ diff --git a/test/langtools/tools/javac/unicode/SupplementaryJavaID2.out b/test/langtools/tools/javac/unicode/SupplementaryJavaID2.out index faf0f8cf356..1f1380ded8c 100644 --- a/test/langtools/tools/javac/unicode/SupplementaryJavaID2.out +++ b/test/langtools/tools/javac/unicode/SupplementaryJavaID2.out @@ -1,4 +1,4 @@ -SupplementaryJavaID2.java:12:14: compiler.err.illegal.char: \ud801 -SupplementaryJavaID2.java:12:20: compiler.err.illegal.char: \ud801 +SupplementaryJavaID2.java:12:9: compiler.err.illegal.char: \ud801 +SupplementaryJavaID2.java:12:15: compiler.err.illegal.char: \ud801 SupplementaryJavaID2.java:12:24: compiler.err.expected: token.identifier 3 errors diff --git a/test/langtools/tools/javac/unicode/SupplementaryJavaID3.out b/test/langtools/tools/javac/unicode/SupplementaryJavaID3.out index c6d56646a5d..64c9e6c0a3a 100644 --- a/test/langtools/tools/javac/unicode/SupplementaryJavaID3.out +++ b/test/langtools/tools/javac/unicode/SupplementaryJavaID3.out @@ -1,3 +1,3 @@ -SupplementaryJavaID3.java:12:17: compiler.err.illegal.char: \ud801 -SupplementaryJavaID3.java:12:23: compiler.err.illegal.char: \ud801 +SupplementaryJavaID3.java:12:12: compiler.err.illegal.char: \ud801 +SupplementaryJavaID3.java:12:18: compiler.err.illegal.char: \ud801 2 errors diff --git a/test/langtools/tools/javac/unicode/SupplementaryJavaID4.out b/test/langtools/tools/javac/unicode/SupplementaryJavaID4.out index 43a7437fc2f..ea07f072cf4 100644 --- a/test/langtools/tools/javac/unicode/SupplementaryJavaID4.out +++ b/test/langtools/tools/javac/unicode/SupplementaryJavaID4.out @@ -1,2 +1,2 @@ -SupplementaryJavaID4.java:14:14: compiler.err.illegal.char: \ud834\udd7b +SupplementaryJavaID4.java:14:9: compiler.err.illegal.char: \ud834\udd7b 1 error diff --git a/test/langtools/tools/javac/unicode/SupplementaryJavaID5.out b/test/langtools/tools/javac/unicode/SupplementaryJavaID5.out index 986454a7dd9..91b6472dfc3 100644 --- a/test/langtools/tools/javac/unicode/SupplementaryJavaID5.out +++ b/test/langtools/tools/javac/unicode/SupplementaryJavaID5.out @@ -1,2 +1,2 @@ -SupplementaryJavaID5.java:14:17: compiler.err.illegal.char: \ud834\udd00 +SupplementaryJavaID5.java:14:12: compiler.err.illegal.char: \ud834\udd00 1 error diff --git a/test/langtools/tools/sjavac/PubApisTest.java b/test/langtools/tools/sjavac/PubApisTest.java index 2192aeee531..c62166fb793 100644 --- a/test/langtools/tools/sjavac/PubApisTest.java +++ b/test/langtools/tools/sjavac/PubApisTest.java @@ -23,13 +23,12 @@ /* * @test - * @bug 8241312 + * @bug 8241312 8246774 * @summary test for com.sun.tools.sjavac.comp.PubAPIs and com.sun.tools.sjavac.comp.PubapiVisitor * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.util * jdk.compiler/com.sun.tools.sjavac.comp * jdk.compiler/com.sun.tools.sjavac.pubapi - * @run main PubApisTest */ import com.sun.source.util.JavacTask; import com.sun.tools.javac.util.Context; @@ -48,8 +47,7 @@ public class PubApisTest { public static void main(String[] args) throws Throwable { javax.tools.JavaCompiler c = ToolProvider.getSystemJavaCompiler(); - JavacTask t = (JavacTask) c.getTask(null, null, null, - List.of("--enable-preview", "-source", Integer.toString(Runtime.version().feature())), null, + JavacTask t = (JavacTask) c.getTask(null, null, null, null, null, List.of(new SimpleJavaFileObject(URI.create("TestClass.java"), JavaFileObject.Kind.SOURCE) { @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { diff --git a/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java b/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java new file mode 100644 index 00000000000..7b7dfc086f1 --- /dev/null +++ b/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.format; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertEquals; + +/* + * @test + * @summary Check ArrayDiff formatting + * @library /test/lib + * @run testng jdk.test.lib.format.ArrayDiffTest + */ +public class ArrayDiffTest { + + @Test + public void testEqualArrays() { + char[] first = new char[] {'a', 'b', 'c', 'd', 'e', 'f', 'g'}; + char[] second = new char[] {'a', 'b', 'c', 'd', 'e', 'f', 'g'}; + + assertTrue(ArrayDiff.of(first, second).areEqual()); + } + + @Test + public void testOutputFitsWidth() { + new AssertBuilder() + .withDefaultParams() + .withArrays( + new byte[] {7, 8, 9, 10, 11, 12, 13}, + new byte[] {7, 8, 9, 10, 125, 12, 13}) + .thatResultIs(false) + .thatFormattedValuesAre( + 4, + "[7, 8, 9, 10, 11, 12, 13]", + "[7, 8, 9, 10, 125, 12, 13]", + " ^^^^") + .assertTwoWay(); + } + + @Test + public void testIntegers() { + new AssertBuilder() + .withDefaultParams() + .withArrays( + new int[] {7, 8, 10, 11, 12}, + new int[] {7, 8, 9, 10, 11, 12, 13}) + .thatResultIs(false) + .thatFormattedValuesAre( + 2, + "[7, 8, 10, 11, 12]", + "[7, 8, 9, 10, 11, 12, 13]", + " ^^^") + .assertTwoWay(); + } + + @Test + public void testLongs() { + new AssertBuilder() + .withDefaultParams() + .withArrays( + new long[] {1, 2, 3, 4}, + new long[] {1, 2, 3, 10}) + .thatResultIs(false) + .thatFormattedValuesAre( + 3, + "[1, 2, 3, 4]", + "[1, 2, 3, 10]", + " ^^^") + .assertTwoWay(); + } + + @Test + public void testFirstElementIsWrong() { + new AssertBuilder() + .withDefaultParams() + .withArrays( + new byte[] {122}, + new byte[] {7, 8, 9, 10, 125, 12, 13}) + .thatResultIs(false) + .thatFormattedValuesAre( + 0, + "[122]", + "[ 7, 8, 9, 10, 125, 12, 13]", + " ^^^") + .assertTwoWay(); + } + + @Test + public void testOneElementIsEmpty() { + new AssertBuilder() + .withDefaultParams() + .withArrays( + new byte[] {7, 8, 9, 10, 125, 12, 13}, + new byte[] {}) + .thatResultIs(false) + .thatFormattedValuesAre( + 0, + "[7, 8, 9, 10, 125, 12, 13]", + "[]", + " ^") + .assertTwoWay(); + } + + @Test + public void testOutputDoesntFitWidth() { + new AssertBuilder() + .withParams(20, Integer.MAX_VALUE) + .withArrays( + new char[] {'1', '2', '3', '4', '5', '6', '7'}, + new char[] {'1', 'F', '3', '4', '5', '6', '7'}) + .thatResultIs(false) + .thatFormattedValuesAre( + 1, + "[1, 2, 3, 4, 5, ...", + "[1, F, 3, 4, 5, ...", + " ^^") + .assertTwoWay(); + } + + @Test + public void testVariableElementWidthOutputDoesntFitWidth() { + new AssertBuilder() + .withParams(20, Integer.MAX_VALUE) + .withArrays( + new byte[] {1, 2, 3, 4, 5, 6, 7}, + new byte[] {1, 112, 3, 4, 5, 6, 7}) + .thatResultIs(false) + .thatFormattedValuesAre( + 1, + "[1, 2, 3, 4, 5, ...", + "[1, 112, 3, 4, 5, ...", + " ^^^^") + .assertTwoWay(); + } + + @Test + public void testContextBefore() { + new AssertBuilder() + .withParams(20, 2) + .withArrays( + new char[] {'1', '2', '3', '4', '5', '6', '7'}, + new char[] {'1', '2', '3', '4', 'F', '6', '7'}) + .thatResultIs(false) + .thatFormattedValuesAre( + 4, + "... 3, 4, 5, 6, 7]", + "... 3, 4, F, 6, 7]", + " ^^") + .assertTwoWay(); + } + + @Test + public void testBoundedBytesWithDifferentWidth() { + new AssertBuilder() + .withParams(24, 2) + .withArrays( + new byte[] {0, 1, 2, 3, 125, 5, 6, 7}, + new byte[] {0, 1, 2, 3, 4, 5, 6, 7}) + .thatResultIs(false) + .thatFormattedValuesAre( + 4, + "... 2, 3, 125, 5, 6, 7]", + "... 2, 3, 4, 5, 6, 7]", + " ^^^^") + .assertTwoWay(); + } + + @Test + public void testBoundedFirstElementIsWrong() { + new AssertBuilder() + .withParams(25, 2) + .withArrays( + new byte[] {101, 102, 103, 104, 105, 110}, + new byte[] {2}) + .thatResultIs(false) + .thatFormattedValuesAre( + 0, + "[101, 102, 103, 104, ...", + "[ 2]", + " ^^^") + .assertTwoWay(); + } + + @Test + public void testBoundedOneArchiveIsEmpty() { + new AssertBuilder() + .withParams(10, 2) + .withArrays( + new char[] {'a', 'b', 'c', 'd', 'e'}, + new char[] {}) + .thatResultIs(false) + .thatFormattedValuesAre( + 0, + "[a, b, ...", + "[]", + " ^") + .assertTwoWay(); + } + + @Test + public void testUnboundedOneArchiveIsEmpty() { + new AssertBuilder() + .withDefaultParams() + .withArrays( + new char[] {'a', 'b', 'c', 'd', 'e'}, + new char[] {}) + .thatResultIs(false) + .thatFormattedValuesAre( + 0, + "[a, b, c, d, e]", + "[]", + " ^") + .assertTwoWay(); + } + + @Test + public void testUnprintableCharFormatting() { + new AssertBuilder() + .withDefaultParams() + .withArrays( + new char[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + new char[] {0, 1, 2, 3, 4, 5, 6, 125, 8, 9, 10, 11, 12, 13, 14, 15, 16}) + .thatResultIs(false) + .thatFormattedValuesAre( + 7, + "... \\u0005, \\u0006, \\u0007, \\u0008, \\u0009, \\n, \\u000B, \\u000C, \\r, \\u000E, ...", + "... \\u0005, \\u0006, }, \\u0008, \\u0009, \\n, \\u000B, \\u000C, \\r, \\u000E, ...", + " ^^^^^^^") + .assertTwoWay(); + } + + @Test + public void testStringElements() { + new AssertBuilder() + .withDefaultParams() + .withArrays( + new String[] {"first", "second", "third", "u\nprintable"}, + new String[] {"first", "second", "incorrect", "u\nprintable"}) + .thatResultIs(false) + .thatFormattedValuesAre( + 2, + "[\"first\", \"second\", \"third\", \"u\\nprintable\"]", + "[\"first\", \"second\", \"incorrect\", \"u\\nprintable\"]", + " ^^^^^^^^^^^^") + .assertTwoWay(); + } + + @Test + public void testToStringableObjects() { + class StrObj { + private final String value; + public boolean equals(Object another) { return ((StrObj)another).value.equals(value); } + public StrObj(String value) { this.value = value; } + public String toString() { return value; } + } + + new AssertBuilder() + .withDefaultParams() + .withArrays( + new StrObj[] {new StrObj("1"), new StrObj("Unp\rintable"), new StrObj("5")}, + new StrObj[] {new StrObj("1"), new StrObj("2"), new StrObj("5")}) + .thatResultIs(false) + .thatFormattedValuesAre( + 1, + "[1, Unp\\rintable, 5]", + "[1, 2, 5]", + " ^^^^^^^^^^^^^") + .assertTwoWay(); + } + + @Test + public void testNullElements() { + new AssertBuilder() + .withDefaultParams() + .withArrays( + new String[] {"Anna", null, "Bill", "Julia"}, + new String[] {"Anna", "null", "William", "Julia"}) + .thatResultIs(false) + .thatFormattedValuesAre( + 1, + "[\"Anna\", null, \"Bill\", \"Julia\"]", + "[\"Anna\", \"null\", \"William\", \"Julia\"]", + " ^^^^^^^") + .assertTwoWay(); + } + + @Test (expectedExceptions = NullPointerException.class) + public void testFirstArrayIsNull() { + var diff = ArrayDiff.of(null, new String[] {"a", "b"}); + } + + @Test (expectedExceptions = NullPointerException.class) + public void testSecondArrayIsNull() { + var diff = ArrayDiff.of(null, new String[] {"a", "b"}); + } + + class AssertBuilder { + private boolean defaultParameters; + private int width; + private int contextBefore; + private Object firstArray; + private Object secondArray; + private boolean expectedResult; + private int expectedIndex; + private String firstFormattedArray; + private String secondFormattedArray; + private String failureMark; + + public AssertBuilder withDefaultParams() { + defaultParameters = true; + return this; + } + + public AssertBuilder withParams(int width, int contextBefore) { + defaultParameters = false; + this.width = width; + this.contextBefore = contextBefore; + return this; + } + + public AssertBuilder withArrays(Object first, Object second) { + firstArray = first; + secondArray = second; + return this; + } + + public AssertBuilder thatResultIs(boolean result) { + expectedResult = result; + return this; + } + + public AssertBuilder thatFormattedValuesAre( + int idx, String first, String second, String mark) { + expectedIndex = idx; + firstFormattedArray = first; + secondFormattedArray = second; + failureMark = mark; + return this; + } + + public void assertTwoWay() { + ArrayDiff diff; + + // Direct + if (defaultParameters) { + diff = ArrayDiff.of(firstArray, secondArray); + } else { + diff = ArrayDiff.of(firstArray, secondArray, width, contextBefore); + } + + if (expectedResult == true) { + assertTrue(diff.areEqual()); + } else { + String expected = String.format( + "Arrays differ starting from [index: %d]:%n" + + "%s%n" + "%s%n" + "%s", + expectedIndex, firstFormattedArray, secondFormattedArray, failureMark); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + + // Reversed + if (defaultParameters) { + diff = ArrayDiff.of(secondArray, firstArray); + } else { + diff = ArrayDiff.of(secondArray, firstArray, width, contextBefore); + } + + if (expectedResult == true) { + assertTrue(diff.areEqual()); + } else { + String expected = String.format( + "Arrays differ starting from [index: %d]:%n" + + "%s%n" + "%s%n" + "%s", + expectedIndex, secondFormattedArray, firstFormattedArray, failureMark); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + } + + } + +} diff --git a/test/lib-test/jdk/test/lib/hexdump/ASN1FormatterTest.java b/test/lib-test/jdk/test/lib/hexdump/ASN1FormatterTest.java index 01887fa8baa..c7622d4b9b4 100644 --- a/test/lib-test/jdk/test/lib/hexdump/ASN1FormatterTest.java +++ b/test/lib-test/jdk/test/lib/hexdump/ASN1FormatterTest.java @@ -39,6 +39,7 @@ /* * @test * @summary ASN.1 formatting + * @modules java.base/sun.security.util * @library /test/lib * @compile ASN1FormatterTest.java * @run testng jdk.test.lib.hexdump.ASN1FormatterTest diff --git a/test/lib/jdk/test/lib/Platform.java b/test/lib/jdk/test/lib/Platform.java index 351ad6a2570..a2a8e23c23e 100644 --- a/test/lib/jdk/test/lib/Platform.java +++ b/test/lib/jdk/test/lib/Platform.java @@ -109,6 +109,18 @@ public static boolean isLinux() { return isOs("linux"); } + public static boolean isBusybox(String tool) { + try { + Path toolpath = Paths.get(tool); + return !isWindows() + && Files.isSymbolicLink(toolpath) + && Paths.get("/bin/busybox") + .equals(Files.readSymbolicLink(toolpath)); + } catch (IOException ignore) { + return false; + } + } + public static boolean isOSX() { return isOs("mac"); } diff --git a/test/lib/jdk/test/lib/Utils.java b/test/lib/jdk/test/lib/Utils.java index cf85c50af67..59558802bc5 100644 --- a/test/lib/jdk/test/lib/Utils.java +++ b/test/lib/jdk/test/lib/Utils.java @@ -246,6 +246,8 @@ public static String[] addTestJavaOpts(String... userArgs) { return prependTestJavaOpts(userArgs); } + private static final Pattern useGcPattern = Pattern.compile( + "(?:\\-XX\\:[\\+\\-]Use.+GC)"); /** * Removes any options specifying which GC to use, for example "-XX:+UseG1GC". * Removes any options matching: -XX:(+/-)Use*GC @@ -253,8 +255,6 @@ public static String[] addTestJavaOpts(String... userArgs) { * GC specified by the framework must first be removed. * @return A copy of given opts with all GC options removed. */ - private static final Pattern useGcPattern = Pattern.compile( - "(?:\\-XX\\:[\\+\\-]Use.+GC)"); public static List removeGcOpts(List opts) { List optsWithoutGC = new ArrayList(); for (String opt : opts) { @@ -564,7 +564,7 @@ public static T getRandomElement(T[] array) /** * Wait for condition to be true * - * @param condition, a condition to wait for + * @param condition a condition to wait for */ public static final void waitForCondition(BooleanSupplier condition) { waitForCondition(condition, -1L, 100L); @@ -573,7 +573,7 @@ public static final void waitForCondition(BooleanSupplier condition) { /** * Wait until timeout for condition to be true * - * @param condition, a condition to wait for + * @param condition a condition to wait for * @param timeout a time in milliseconds to wait for condition to be true * specifying -1 will wait forever * @return condition value, to determine if wait was successful @@ -586,7 +586,7 @@ public static final boolean waitForCondition(BooleanSupplier condition, /** * Wait until timeout for condition to be true for specified time * - * @param condition, a condition to wait for + * @param condition a condition to wait for * @param timeout a time in milliseconds to wait for condition to be true, * specifying -1 will wait forever * @param sleepTime a time to sleep value in milliseconds @@ -619,13 +619,13 @@ public static interface ThrowingRunnable { * Filters out an exception that may be thrown by the given * test according to the given filter. * - * @param test - method that is invoked and checked for exception. - * @param filter - function that checks if the thrown exception matches - * criteria given in the filter's implementation. - * @return - exception that matches the filter if it has been thrown or - * {@code null} otherwise. - * @throws Throwable - if test has thrown an exception that does not - * match the filter. + * @param test method that is invoked and checked for exception. + * @param filter function that checks if the thrown exception matches + * criteria given in the filter's implementation. + * @return exception that matches the filter if it has been thrown or + * {@code null} otherwise. + * @throws Throwable if test has thrown an exception that does not + * match the filter. */ public static Throwable filterException(ThrowingRunnable test, Function filter) throws Throwable { @@ -786,42 +786,44 @@ public static OutputAnalyzer uname(String... args) throws Throwable { /** * Creates an empty file in "user.dir" if the property set. *

    - * This method is meant as a replacement for {@code Files#createTempFile(String, String, FileAttribute...)} + * This method is meant as a replacement for {@link Files#createTempFile(String, String, FileAttribute...)} * that doesn't leave files behind in /tmp directory of the test machine *

    * If the property "user.dir" is not set, "." will be used. * - * @param prefix - * @param suffix - * @param attrs + * @param prefix the prefix string to be used in generating the file's name; + * may be null + * @param suffix the suffix string to be used in generating the file's name; + * may be null, in which case ".tmp" is used + * @param attrs an optional list of file attributes to set atomically when creating the file * @return the path to the newly created file that did not exist before this * method was invoked - * @throws IOException + * @throws IOException if an I/O error occurs or dir does not exist * - * @see {@link Files#createTempFile(String, String, FileAttribute...)} + * @see Files#createTempFile(String, String, FileAttribute...) */ public static Path createTempFile(String prefix, String suffix, FileAttribute... attrs) throws IOException { Path dir = Paths.get(System.getProperty("user.dir", ".")); - return Files.createTempFile(dir, prefix, suffix); + return Files.createTempFile(dir, prefix, suffix, attrs); } /** * Creates an empty directory in "user.dir" or "." *

    - * This method is meant as a replacement for {@code Files#createTempDirectory(String, String, FileAttribute...)} + * This method is meant as a replacement for {@link Files#createTempDirectory(Path, String, FileAttribute...)} * that doesn't leave files behind in /tmp directory of the test machine *

    * If the property "user.dir" is not set, "." will be used. * - * @param prefix - * @param attrs + * @param prefix the prefix string to be used in generating the directory's name; may be null + * @param attrs an optional list of file attributes to set atomically when creating the directory * @return the path to the newly created directory - * @throws IOException + * @throws IOException if an I/O error occurs or dir does not exist * - * @see {@link Files#createTempDirectory(String, String, FileAttribute...)} + * @see Files#createTempDirectory(Path, String, FileAttribute...) */ public static Path createTempDirectory(String prefix, FileAttribute... attrs) throws IOException { Path dir = Paths.get(System.getProperty("user.dir", ".")); - return Files.createTempDirectory(dir, prefix); + return Files.createTempDirectory(dir, prefix, attrs); } } diff --git a/test/lib/jdk/test/lib/artifacts/ArtifactResolverException.java b/test/lib/jdk/test/lib/artifacts/ArtifactResolverException.java index 28ba3096b36..f3c2179191f 100644 --- a/test/lib/jdk/test/lib/artifacts/ArtifactResolverException.java +++ b/test/lib/jdk/test/lib/artifacts/ArtifactResolverException.java @@ -12,4 +12,24 @@ public ArtifactResolverException(String message) { public ArtifactResolverException(String message, Throwable cause) { super(message, cause); } + + public String toString() { + Throwable root = getRootCause(); + if (root != null) { + return super.toString() + ": " + root.toString(); + } else { + return super.toString(); + } + } + + public Throwable getRootCause() { + Throwable root = getCause(); + if (root == null) { + return null; + } + while (root.getCause() != null && root.getCause() != root) { + root = root.getCause(); + } + return root; + } } diff --git a/test/lib/jdk/test/lib/format/ArrayCodec.java b/test/lib/jdk/test/lib/format/ArrayCodec.java new file mode 100644 index 00000000000..0e34e7cd811 --- /dev/null +++ b/test/lib/jdk/test/lib/format/ArrayCodec.java @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.format; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * A codec helping representing arrays in a string form. + * + * Encoding can be done in a controllable fashion (allowing the user to encode two + * or more arrays in a time) or as a single operation. + */ +public class ArrayCodec { + private static final String ELLIPSIS = "..."; + + private boolean exhausted; + private StringBuilder encoded; + + private List source; + private String element; + + private boolean bounded = false; + private int maxWidth; + private int idx; + + private ArrayCodec(List source) { + this.source = source; + } + + /** + * Creates a codec for a char array + * + * @param array source array + * @return an ArrayCodec for the provided array + */ + public static ArrayCodec of(char[] array) { + var source = new ArrayList(array.length); + for (char value: array) { + source.add(value); + } + return new ArrayCodec<>(source); + } + + /** + * Creates a codec for a byte array + * + * @param array source array + * @return an ArrayCodec for the provided array + */ + public static ArrayCodec of(byte[] array) { + var source = new ArrayList(array.length); + for (byte value: array) { + source.add(value); + } + return new ArrayCodec<>(source); + } + + /** + * Creates a codec for an int array + * + * @param array source array + * @return an ArrayCodec for the provided array + */ + public static ArrayCodec of(int[] array) { + var source = new ArrayList(array.length); + for (int value: array) { + source.add(value); + } + return new ArrayCodec<>(source); + } + + /** + * Creates a codec for a long array + * + * @param array source array + * @return an ArrayCodec for the provided array + */ + public static ArrayCodec of(long[] array) { + var source = new ArrayList(array.length); + for (long value: array) { + source.add(value); + } + return new ArrayCodec<>(source); + } + + /** + * Creates a codec for a String array + * + * @param array source array + * @return an ArrayCodec for the provided array + */ + public static ArrayCodec of(String[] array) { + var source = new ArrayList(array.length); + for (String value: array) { + source.add(value); + } + return new ArrayCodec<>(source); + } + + /** + * Creates a codec for a generic Object array + * + * @param array source array + * @return an ArrayCodec for the provided array + */ + public static ArrayCodec of(Object[] array) { + var source = new ArrayList(array.length); + for (Object value: array) { + source.add(value); + } + return new ArrayCodec(source); + } + + /** + * Creates a codec for a generic array, trying to recognize its component type + * + * @param array source array + * @throws IllegalArgumentException if {@code array}'s component type is not supported + * @return an ArrayCodec for the provided array + */ + public static ArrayCodec of(Object array) { + var type = array.getClass().getComponentType(); + if (type == byte.class) { + return ArrayCodec.of((byte[])array); + } else if (type == int.class) { + return ArrayCodec.of((int[])array); + } else if (type == long.class) { + return ArrayCodec.of((long[])array); + } else if (type == char.class) { + return ArrayCodec.of((char[])array); + } else if (type == String.class) { + return ArrayCodec.of((String[])array); + } else if (!type.isPrimitive() && !type.isArray()) { + return ArrayCodec.of((Object[])array); + } + + throw new IllegalArgumentException("Unsupported array component type: " + type); + } + + /** + * Formats an array at-once. + * The array is enclosed in brackets, its elements are separated with + * commas. String elements are additionally surrounded by double quotes. + * Unprintable symbols are C-stye escaped. + * + *

    Sample outputs: + * + *

    +     *   [0, 1, 2, 3, 4]
    +     *   ["one", "first", "tree"]
    +     *   [object1, object2, object3]
    +     *   [a, b, \n, \u0002/, c]
    +     * 
    + * + * @throws IllegalArgumentException if {@code array}'s component type is not supported + * @return an ArrayCodec for the provided array + */ + public static String format(Object array) { + var codec = ArrayCodec.of(array); + codec.startFormatting(0, -1); + while (!codec.isExhausted()) { + codec.formatNext(); + codec.appendFormatted(); + } + return codec.getEncoded(); + } + + /** + * Starts formatting with the given parameters. + * + * @param startIdx first element's index to start formattig with + * @param maxWidth maximum allowed formatting width (in characters). + * @return an ArrayCodec for the provided array + */ + public void startFormatting(int startIdx, int maxWidth) { + encoded = new StringBuilder(startIdx == 0 ? "[" : ELLIPSIS); + exhausted = false; + this.maxWidth = maxWidth; + bounded = (maxWidth > 0); + idx = startIdx; + } + + /** + * Format next element, store it in the internal element storage. + */ + public void formatNext() { + int limit = source.size(); + + String prefix = idx == 0 || idx >= limit ? "" : " "; + String suffix = (idx + 1 == limit) || (source.isEmpty() && idx == 0) + ? "]" + : idx >= limit ? "" : ","; + element = prefix + + (idx >= limit ? "" : Format.asLiteral(source.get(idx))) + + suffix; + } + + /** + * Append formatted element to internal StringBuilder. + * + * The formatted-so-far string can be accessed via {@link #getEncoded} + * no elements in array left the method silently does nothing. + */ + public void appendFormatted() { + if (exhausted) { + return; + } + + boolean isLast = idx == source.size() - 1; + if (isLast || source.isEmpty()) { + exhausted = true; + } + + if (bounded && encoded.length() + element.length() > maxWidth - ELLIPSIS.length()) { + encoded.append(isLast ? element : " " + ELLIPSIS); + exhausted = true; + } else { + encoded.append(element); + } + idx++; + } + + /** + * Aligns the element by another codec. + * + * If another codec's last encoded element string is longer than this + * codec's, widens this codec's encoded element with spaces so the + * two strings have the same length; + * + * @param another Another codec to compare encoded element width with + */ + public void alignBy(ArrayCodec another) { + if (!element.equals("") && !element.equals("]")) { + int delta = another.element.length() - element.length(); + if (delta > 0) { + element = Format.paddingForWidth(delta) + element; + } + } + } + + /** + * Indicates if there are no elements left in the source array + * + * @return {@code true} if there are no elements left, {@code false} otherwise + */ + public boolean isExhausted() { + return exhausted; + } + + /** + * Returns the string encoded-so-far + * + * @return the string encoded-so-far + */ + public String getEncoded() { + return encoded.toString(); + } + + /** + * Returns the length of the string encoded-so-far + * + * @return the length of the string encoded-so-far + */ + public int getEncodedLength() { + return encoded.length(); + } + + /** + * Returns the length of the last encoded element + * + * @return the length of the last encoded element + */ + public int getElementLength() { + return element.length(); + } + + /** + * Finds and returns the first mismatch index in another codec + * + * @param another a codec mismatch with whom is to be found + * @return the first mismatched element's index or -1 if arrays are identical + */ + public int findMismatchIndex(ArrayCodec another) { + int result = 0; + while ((source.size() > result) && (another.source.size() > result)) { + Object first = source.get(result); + Object second = another.source.get(result); + + if (first == null || second == null) { + if (first == null && second == null) { + continue; // Both elements are null (i.e. equal) + } else { + return result; // Only one element is null, here's the failure index + } + } + + if (!first.equals(second)) { + return result; + } + + result++; + } + + return source.size() != another.source.size() + ? result // Lengths are different, but the shorter arrays is a preffix to the longer array. + : -1; // Arrays are identical, there's no mismatch index + } + + /** + * Indicates whether source array for another codec is equal to this codec's array + * + * @return {@code true} if source arrays are equal, {@code false} otherwise + */ + public boolean equals(ArrayCodec another) { + return source.equals(another.source); + } +} diff --git a/test/lib/jdk/test/lib/format/ArrayDiff.java b/test/lib/jdk/test/lib/format/ArrayDiff.java new file mode 100644 index 00000000000..6755a693b7c --- /dev/null +++ b/test/lib/jdk/test/lib/format/ArrayDiff.java @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.format; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +/** + * A difference between two arrays, which can be pretty formatted. + * For the calculated difference, user can request if the two arrays + * are equal (in terms of {@link Object#equals Object.equals()} for their + * elements). For the arrays that differ, a human-readable difference can + * be provided. + * + *

    The difference is represented as a four-line text block, comprising of the + * first different element index, arrays printouts in the difference area, + * and a difference mark. For Primitive and Object elements in the source + * arrays their C-style escaped {@link String#valueOf String.valueOf()} are + * printed, element in String[] arrays are additionally surrounded with quotes. + * Additional formatting parameters, like maximum allowed width and number of + * elements printed before difference, can be specified. + * + *

    Output examples: + * + *

    two int arrays:

    + *
    + * Arrays differ starting from [index: 4]:
    + *         ... 3, 4,   5, 6, 7]
    + *         ... 3, 4, 225, 6, 7]
    + *                  ^^^^
    + * 
    + *

    two String arrays:

    + *
    + * Arrays differ starting from [index: 2]:
    + *         ["first", "second",     "third", "u\nprintable"]
    + *         ["first", "second", "incorrect", "u\nprintable"]
    + *                            ^^^^^^^^^^^^
    + * 
    + *

    two char arrays arrays:

    + *
    + * Arrays differ starting from [index: 7]:
    + *         ... \u0001, \u0002, \u0007, a, b, \n, ...
    + *         ... \u0001, \u0002,      }, a, b, \n, ...
    + *                            ^^^^^^^
    + * 
    + */ +public class ArrayDiff implements Diff { + + private int failureIdx; + private final int maxWidth; + private final int contextBefore; + + private final ArrayCodec first; + private final ArrayCodec second; + + private ArrayDiff(ArrayCodec first, ArrayCodec second, + int width, int getContextBefore) { + this.first = first; + this.second = second; + this.maxWidth = width; + this.contextBefore = getContextBefore; + failureIdx = first.findMismatchIndex(second); + } + + /** + * Creates an ArrayDiff fom two arrays and default limits. The given arguments must be of the same + * component type. + * + * @param first the first array + * @param second the second array + * @return an ArrayDiff instance for the two arrays + */ + public static ArrayDiff of(Object first, Object second) { + return ArrayDiff.of(first, second, Diff.Defaults.WIDTH, Diff.Defaults.CONTEXT_BEFORE); + } + + /** + * Creates an ArrayDiff fom two arrays with the given limits. The given arguments must be of the same + * component type. + * + * @param first the first array + * @param second the second array + * @param width the maximum allowed width in characters for the formatting + * @param contextBefore maximum number of elements to print before those that differ + * @throws IllegalArgumentException if component types of arrays is not supported or are not the same + * @throws NullPointerException if at least one of the arrays is null + * @return an ArrayDiff instance for the two arrays and formatting parameters provided + */ + public static ArrayDiff of(Object first, Object second, int width, int contextBefore) { + Objects.requireNonNull(first); + Objects.requireNonNull(second); + + boolean bothAreArrays = first.getClass().isArray() && second.getClass().isArray(); + boolean componentTypesAreSame = + first.getClass().getComponentType() == second.getClass().getComponentType(); + + if (!bothAreArrays || !componentTypesAreSame) { + throw new IllegalArgumentException("Both arguments should be arrays of the same type"); + } + + return new ArrayDiff( + ArrayCodec.of(first), + ArrayCodec.of(second), + width, contextBefore); + } + + /** + * Formats the given diff. + * + * @return formatted difference representation. + */ + @Override + public String format() { + if (areEqual()) { + return ""; + } + + return format(false) + .orElseGet(() -> format(true).get()); + } + + /** + * Indicates whether the two source arrays are equal + * + * @return {@code true} if the arrays are different, {@code false} otherwise + */ + @Override + public boolean areEqual() { + return first.equals(second); + } + + private void extractAndAlignElements() { + first.formatNext(); + second.formatNext(); + + first.alignBy(second); + second.alignBy(first); + } + + private static String failureMarkForWidth(int width) { + return new String("^").repeat(width); + } + + private Optional format(boolean bounded) { + int idx = bounded ? Math.max(0, failureIdx - contextBefore) : 0; + + first.startFormatting(idx, bounded ? maxWidth : -1); + second.startFormatting(idx, bounded ? maxWidth : -1); + StringBuilder failureMark = new StringBuilder( + Format.paddingForWidth(first.getEncodedLength())); + + for (; !(first.isExhausted() && second.isExhausted()); idx++) { + extractAndAlignElements(); + + first.appendFormatted(); + second.appendFormatted(); + + { // Process failure mark + if (idx < failureIdx) { + failureMark.append(Format.paddingForWidth(first.getElementLength())); + } else if (idx == failureIdx) { + int markLength = Math.max(first.getElementLength(), second.getElementLength()) - 1; + failureMark.append(failureMarkForWidth(markLength)); + } + } + + final int maxEncodedLength = Math.max( + first.getEncodedLength(), + second.getEncodedLength()); + if (!bounded && maxEncodedLength > maxWidth) { + return Optional.empty(); + } + } + + return Optional.of(String.format( + "Arrays differ starting from [index: %d]:%n%s%n%s%n%s", + failureIdx, + first.getEncoded(), + second.getEncoded(), + failureMark.toString())); + } + +} + diff --git a/test/lib/jdk/test/lib/format/Diff.java b/test/lib/jdk/test/lib/format/Diff.java new file mode 100644 index 00000000000..02ed3430e5d --- /dev/null +++ b/test/lib/jdk/test/lib/format/Diff.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Represents a possible difference between two objects. + */ +package jdk.test.lib.format; + +/** + * An abstraction representing formattable difference between two or more objects + */ +public interface Diff { + + /** + * Default limits for formatters + */ + public static class Defaults { + private Defaults() { } // This class should not be instantiated + public final static int WIDTH = 80; + public final static int CONTEXT_BEFORE = 2; + } + + /** + * Formats the given diff. Different implementations can provide different + * result and formatting style. + * + * @return formatted difference representation. + */ + String format(); + + /** + * Indicates whether the two source arrays are equal. Different + * implementations can treat this notion differently. + * + * @return {@code true} if the source objects are different, {@code false} otherwise + */ + boolean areEqual(); +} diff --git a/test/lib/jdk/test/lib/format/Format.java b/test/lib/jdk/test/lib/format/Format.java new file mode 100644 index 00000000000..9770e33c232 --- /dev/null +++ b/test/lib/jdk/test/lib/format/Format.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.format; + +/** + * A collection of formatting utilities + */ +public class Format { + + /** + * Formats character as literal, using C-style escaping for unprintable symbols + * + * @param c character to format + * @return formatted string representation of the character + */ + public static String asLiteral(char c) { + StringBuilder sb = new StringBuilder(); + appendCharToSb(c, sb); + return sb.toString(); + } + + /** + * Escapes String in C-style + * + * @param src source string + * @return C-style escaped source string + */ + public static String escapeString(String src) { + StringBuilder sb = new StringBuilder(); + src.chars().forEachOrdered( + (c) -> appendCharToSb((char) c, sb)); + return sb.toString(); + } + + /** + * Formats Object as literal, using its String representation C-style escaped. + * + * @param o object to format + * @return C-style escaped String representation of the object + */ + public static String asLiteral(Object o) { + if (o instanceof String) { + return '"' + escapeString((String)o) + '"'; + } else if (o instanceof Character) { + return asLiteral((char) o); + } else if (o instanceof Byte) { + return String.valueOf(o); + } else { + return escapeString(String.valueOf(o)); + } + } + + /** + * Formats a difference between two arrays with index of the first mismatch element, + * and slices of arrays necessary to understand the problem, along with a failure mark. + * + * @param first first array to compare + * @param second second array to compare + * @return the difference, generated by the {@link ArrayDiff ArrayDiff} + */ + public static String arrayDiff(Object first, Object second) { + return ArrayDiff.of(first, second).format(); + } + + /** + * Formats a difference between two arrays with index of the first mismatch element, + * and slices of arrays necessary to understand the problem, along with a failure mark. + * Takes into account maximum allowed width and context (in elements) before the mismatch. + * + * @param first first array to compare + * @param second second array to compare + * @param width the maximum allowed width in characters for the formatting + * @param contextBefore maximum number of elements to print before those that differ + * @return the difference, generated by the {@link ArrayDiff ArrayDiff} + */ + public static String arrayDiff(Object first, Object second, int width, int contextBefore) { + return ArrayDiff.of(first, second, width, contextBefore).format(); + } + + + /** + * Returns a string of spaces with length specified. + * + * @param width number of spaces in the resulting string + * @return Padding string of spaces + */ + public static String paddingForWidth(int width) { + return " ".repeat(width); + } + + private static void appendCharToSb(char c, StringBuilder sb) { + if (c == 10) { + sb.append("\\n"); + } else if (c == 13) { + sb.append("\\r"); + } else if (c == 92) { + sb.append("\\\\"); + } else if (c == 34) { + sb.append("\\\""); + } else if (c < 32 || c > 126) { + sb.append("\\u" + String.format("%04X", (int) c)); + } else { + sb.append(c); + } + } +} diff --git a/test/lib/jdk/test/lib/hexdump/ASN1Formatter.java b/test/lib/jdk/test/lib/hexdump/ASN1Formatter.java index fb9486c3f95..086aa81e471 100644 --- a/test/lib/jdk/test/lib/hexdump/ASN1Formatter.java +++ b/test/lib/jdk/test/lib/hexdump/ASN1Formatter.java @@ -412,7 +412,6 @@ private static String oidName(byte[] bytes) { // Look up the OID; if the class is not accessible just return the numeric form Class cl = Class.forName("sun.security.util.KnownOIDs"); Method findMatch = cl.getDeclaredMethod("findMatch", String.class); - findMatch.setAccessible(true); Object oid = findMatch.invoke(null, noid); return (oid == null) ? noid : noid + " (" + oid.toString() + ")"; } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { diff --git a/test/lib/jdk/test/lib/security/DerUtils.java b/test/lib/jdk/test/lib/security/DerUtils.java index c2efaf91503..06e6ece738f 100644 --- a/test/lib/jdk/test/lib/security/DerUtils.java +++ b/test/lib/jdk/test/lib/security/DerUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ import jdk.test.lib.Asserts; import sun.security.util.DerInputStream; import sun.security.util.DerValue; +import sun.security.util.KnownOIDs; import sun.security.util.ObjectIdentifier; import java.io.IOException; @@ -95,8 +96,18 @@ public static DerValue innerDerValue(byte[] data, String location) * Ensures that the inner DerValue is the expected ObjectIdentifier. */ public static void checkAlg(byte[] der, String location, - ObjectIdentifier expected) throws Exception { - Asserts.assertEQ(innerDerValue(der, location).getOID(), expected); + Object expected) throws Exception { + ObjectIdentifier oid; + if (expected instanceof ObjectIdentifier) { + oid = (ObjectIdentifier)expected; + } else if (expected instanceof KnownOIDs) { + oid = ObjectIdentifier.of((KnownOIDs) expected); + } else if (expected instanceof String) { + oid = ObjectIdentifier.of(KnownOIDs.findMatch((String)expected)); + } else { + throw new IllegalArgumentException(expected.toString()); + } + Asserts.assertEQ(innerDerValue(der, location).getOID(), oid); } /** diff --git a/test/lib/jdk/test/lib/security/timestamp/TsaHandler.java b/test/lib/jdk/test/lib/security/timestamp/TsaHandler.java index 1ed6d6d7392..b8ec2f2ab9d 100644 --- a/test/lib/jdk/test/lib/security/timestamp/TsaHandler.java +++ b/test/lib/jdk/test/lib/security/timestamp/TsaHandler.java @@ -33,15 +33,13 @@ import java.time.LocalDate; import java.time.ZoneId; import java.time.format.DateTimeFormatter; -import java.util.Arrays; -import java.util.Date; -import java.util.Enumeration; -import java.util.List; +import java.util.*; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import sun.security.util.KnownOIDs; +import sun.security.util.SignatureUtil; import sun.security.x509.AlgorithmId; /** @@ -208,9 +206,18 @@ protected TsaParam getParam(URI uri) { protected String getSignerAlias(String alias, String sigAlgo) throws Exception { if (alias == null) { - String keyAlgo = sigAlgo == null - ? null - : AlgorithmId.getEncAlgFromSigAlg(sigAlgo); + String keyAlgo; + if (sigAlgo == null) { + keyAlgo = null; + } else { + String lower = sigAlgo.toLowerCase(Locale.ROOT); + int pos = lower.indexOf("with"); + if (pos < 0) { + keyAlgo = sigAlgo; + } else { + keyAlgo = sigAlgo.substring(pos + 4); + } + } Enumeration aliases = keyStore.aliases(); while(aliases.hasMoreElements()) { String bufAlias = aliases.nextElement(); diff --git a/test/lib/jdk/test/lib/security/timestamp/TsaSigner.java b/test/lib/jdk/test/lib/security/timestamp/TsaSigner.java index 401ad0f98fc..d0238e1aa5d 100644 --- a/test/lib/jdk/test/lib/security/timestamp/TsaSigner.java +++ b/test/lib/jdk/test/lib/security/timestamp/TsaSigner.java @@ -34,10 +34,7 @@ import sun.security.pkcs.ContentInfo; import sun.security.pkcs.PKCS7; import sun.security.pkcs.SignerInfo; -import sun.security.util.DerOutputStream; -import sun.security.util.DerValue; -import sun.security.util.KnownOIDs; -import sun.security.util.ObjectIdentifier; +import sun.security.util.*; import sun.security.x509.AlgorithmId; import sun.security.x509.X500Name; @@ -209,7 +206,7 @@ private byte[] createResponse(TsaParam requestParam) throws Exception { ObjectIdentifier.of(KnownOIDs.TimeStampTokenInfo), new DerValue(eContentOut.toByteArray())); - String defaultSigAlgo = AlgorithmId.getDefaultSigAlgForKey( + String defaultSigAlgo = SignatureUtil.getDefaultSigAlgForKey( signerEntry.privateKey); String sigAlgo = interceptor.getSigAlgo(defaultSigAlgo); Signature signature = Signature.getInstance(sigAlgo); @@ -221,8 +218,8 @@ private byte[] createResponse(TsaParam requestParam) throws Exception { SignerInfo signerInfo = new SignerInfo( new X500Name(issuerName), signerEntry.cert.getSerialNumber(), - AlgorithmId.get( - AlgorithmId.getDigAlgFromSigAlg(sigAlgo)), + SignatureUtil.getDigestAlgInPkcs7SignerInfo( + signature, sigAlgo, signerEntry.privateKey, false), AlgorithmId.get(sigAlgo), signature.sign()); diff --git a/test/lib/jdk/test/lib/serial/SerialObjectBuilder.java b/test/lib/jdk/test/lib/serial/SerialObjectBuilder.java new file mode 100644 index 00000000000..44daed8d89d --- /dev/null +++ b/test/lib/jdk/test/lib/serial/SerialObjectBuilder.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.serial; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.UncheckedIOException; +import java.util.LinkedHashMap; +import java.util.Map; +import static java.io.ObjectStreamConstants.*; + +/** + * A basic builder of a serial object. + */ +public class SerialObjectBuilder { + + private final ObjectOutputStream objectOutputStream; + private final ByteArrayOutputStream byteArrayOutputStream; + + private record NameAndType(String name, Classtype) { } + + private String className; + private long suid; + private SerialObjectBuilder superClass; + private final LinkedHashMap, Object> primFields = new LinkedHashMap<>(); + private final LinkedHashMap, Object> objectFields = new LinkedHashMap<>(); + + private SerialObjectBuilder() { + try { + byteArrayOutputStream = new ByteArrayOutputStream(); + objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public static SerialObjectBuilder newBuilder(String className) { + return (new SerialObjectBuilder()).className(className); + } + + private SerialObjectBuilder className(String className) { + this.className = className; + return this; + } + + public SerialObjectBuilder suid(long suid) { + this.suid = suid; + return this; + } + + public SerialObjectBuilder superClass(SerialObjectBuilder superClass) { + this.superClass = superClass; + return this; + } + + public SerialObjectBuilder addPrimitiveField(String name, Class type, T value) { + if (!type.isPrimitive()) + throw new IllegalArgumentException("Unexpected non-primitive field: " + type); + primFields.put(new NameAndType<>(name, type), value); + return this; + } + + public SerialObjectBuilder addField(String name, Class type, T value) { + if (type.isPrimitive()) + throw new IllegalArgumentException("Unexpected primitive field: " + type); + objectFields.put(new NameAndType<>(name, type), value); + return this; + } + + private static void writeUTF(DataOutputStream out, String str) throws IOException { + assert str.codePoints().noneMatch(cp -> cp > 127); // only ASCII for now + int utflen = str.length(); + assert utflen <= 0xFFFF; // only small strings for now + out.writeShort(utflen); + out.writeBytes(str); + } + + private void writePrimFieldsDesc(DataOutputStream out) throws IOException { + for (Map.Entry, Object> entry : primFields.entrySet()) { + Class primClass = entry.getKey().type(); + assert primClass.isPrimitive(); + assert primClass != void.class; + out.writeByte(primClass.descriptorString().getBytes()[0]); // prim_typecode + out.writeUTF(entry.getKey().name()); // fieldName + } + } + + private void writePrimFieldsValues(DataOutputStream out) throws IOException { + for (Map.Entry, Object> entry : primFields.entrySet()) { + Class cl = entry.getKey().type(); + Object value = entry.getValue(); + if (cl == Integer.TYPE) out.writeInt((int) value); + else if (cl == Byte.TYPE) out.writeByte((byte) value); + else if (cl == Long.TYPE) out.writeLong((long) value); + else if (cl == Float.TYPE) out.writeFloat((float) value); + else if (cl == Double.TYPE) out.writeDouble((double) value); + else if (cl == Short.TYPE) out.writeShort((short) value); + else if (cl == Character.TYPE) out.writeChar((char) value); + else if (cl == Boolean.TYPE) out.writeBoolean((boolean) value); + else throw new InternalError(); + } + } + + private void writeObjectFieldDesc(DataOutputStream out) throws IOException { + for (Map.Entry, Object> entry : objectFields.entrySet()) { + Class cl = entry.getKey().type(); + assert !cl.isPrimitive(); + // obj_typecode + if (cl.isArray()) { + out.writeByte('['); + } else { + out.writeByte('L'); + } + writeUTF(out, entry.getKey().name()); + out.writeByte(TC_STRING); + writeUTF(out, cl.descriptorString()); + } + } + + private void writeObject(DataOutputStream out, Object value) throws IOException { + objectOutputStream.reset(); + byteArrayOutputStream.reset(); + objectOutputStream.writeUnshared(value); + out.write(byteArrayOutputStream.toByteArray()); + } + + private void writeObjectFieldValues(DataOutputStream out) throws IOException { + for (Map.Entry, Object> entry : objectFields.entrySet()) { + Class cl = entry.getKey().type(); + assert !cl.isPrimitive(); + if (cl == String.class) { + out.writeByte(TC_STRING); + writeUTF(out, (String) entry.getValue()); + } else { + writeObject(out, entry.getValue()); + } + } + } + + private int numFields() { + return primFields.size() + objectFields.size(); + } + + private static void writeClassDesc(DataOutputStream dos, + SerialObjectBuilder sb) + throws IOException + { + dos.writeByte(TC_CLASSDESC); + dos.writeUTF(sb.className); + dos.writeLong(sb.suid); + dos.writeByte(SC_SERIALIZABLE); + dos.writeShort(sb.numFields()); // number of fields + sb.writePrimFieldsDesc(dos); + sb.writeObjectFieldDesc(dos); + dos.writeByte(TC_ENDBLOCKDATA); // no annotations + if (sb.superClass == null) { + dos.writeByte(TC_NULL); // no superclasses + } else { + writeClassDesc(dos, sb.superClass); + } + } + + public byte[] build() { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + dos.writeShort(STREAM_MAGIC); + dos.writeShort(STREAM_VERSION); + dos.writeByte(TC_OBJECT); + writeClassDesc(dos, this); + if (superClass != null) { + superClass.writePrimFieldsValues(dos); + superClass.writeObjectFieldValues(dos); + } + writePrimFieldsValues(dos); + writeObjectFieldValues(dos); + dos.close(); + return baos.toByteArray(); + } catch (IOException unexpected) { + throw new AssertionError(unexpected); + } + } +} diff --git a/test/lib/sun/hotspot/WhiteBox.java b/test/lib/sun/hotspot/WhiteBox.java index b34f29585ea..5cba6ffa806 100644 --- a/test/lib/sun/hotspot/WhiteBox.java +++ b/test/lib/sun/hotspot/WhiteBox.java @@ -240,6 +240,7 @@ public Object[] parseCommandLine(String commandline, char delim, Dia public native int matchesInline(Executable method, String pattern); public native boolean shouldPrintAssembly(Executable method, int comp_level); public native int deoptimizeFrames(boolean makeNotEntrant); + public native boolean isFrameDeoptimized(int depth); public native void deoptimizeAll(); public boolean isMethodCompiled(Executable method) { @@ -396,11 +397,24 @@ public void clearInlineCaches(boolean preserve_static_stubs) { // Memory public native void readReservedMemory(); public native long allocateMetaspace(ClassLoader classLoader, long size); - public native void freeMetaspace(ClassLoader classLoader, long addr, long size); public native long incMetaspaceCapacityUntilGC(long increment); public native long metaspaceCapacityUntilGC(); public native long metaspaceReserveAlignment(); + // Metaspace Arena Tests + public native long createMetaspaceTestContext(long commit_limit, long reserve_limit); + public native void destroyMetaspaceTestContext(long context); + public native void purgeMetaspaceTestContext(long context); + public native void printMetaspaceTestContext(long context); + public native long getTotalCommittedWordsInMetaspaceTestContext(long context); + public native long getTotalUsedWordsInMetaspaceTestContext(long context); + public native long createArenaInTestContext(long context, boolean is_micro); + public native void destroyMetaspaceTestArena(long arena); + public native long allocateFromMetaspaceTestArena(long arena, long word_size); + public native void deallocateToMetaspaceTestArena(long arena, long p, long word_size); + + public native long maxMetaspaceAllocationSize(); + // Don't use these methods directly // Use sun.hotspot.gc.GC class instead. public native boolean isGCSupported(int name); @@ -616,6 +630,9 @@ public native int validateCgroup(String procCgroups, // ThreadSMR GC safety check for threadObj public native void checkThreadObjOfTerminatingThread(Thread target); + // libc name + public native String getLibcName(); + public native boolean isJVMTIIncluded(); public native void waitUnsafe(int time_ms); diff --git a/test/micro/org/openjdk/bench/java/io/DataOutputStreamTest.java b/test/micro/org/openjdk/bench/java/io/DataOutputStreamTest.java new file mode 100644 index 00000000000..2e63268f12d --- /dev/null +++ b/test/micro/org/openjdk/bench/java/io/DataOutputStreamTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.java.io; + +import org.openjdk.jmh.annotations.*; + +import java.io.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@Fork(value = 1, warmups = 0) +@Measurement(iterations = 6, time = 1) +@Warmup(iterations=2, time = 2) +@State(Scope.Benchmark) +public class DataOutputStreamTest { + + public enum BasicType {CHAR, SHORT, INT, STRING} + @Param({"CHAR", "SHORT", "INT", /* "STRING"*/}) BasicType basicType; + + @Param({"4096"}) int size; + final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(size); + File f; + String outputString; + FileOutputStream fileOutputStream; + DataOutput bufferedFileStream, rawFileStream, byteArrayStream; + + @Setup(Level.Trial) + public void setup() throws Exception { + f = File.createTempFile("DataOutputStreamTest","out"); + fileOutputStream = new FileOutputStream(f); + byteArrayStream = new DataOutputStream(byteArrayOutputStream); + rawFileStream = new DataOutputStream(fileOutputStream); + bufferedFileStream = new DataOutputStream(new BufferedOutputStream(fileOutputStream)); + outputString = new String(new byte[size]); + } + + public void writeChars(DataOutput dataOutput) + throws Exception { + for (int i = 0; i < size; i += 2) { + dataOutput.writeChar(i); + } + } + + public void writeShorts(DataOutput dataOutput) + throws Exception { + for (int i = 0; i < size; i += 2) { + dataOutput.writeShort(i); + } + } + + public void writeInts(DataOutput dataOutput) + throws Exception { + for (int i = 0; i < size; i += 4) { + dataOutput.writeInt(i); + } + } + + public void writeString(DataOutput dataOutput) + throws Exception { + dataOutput.writeChars(outputString); + } + + public void write(DataOutput dataOutput) + throws Exception { + switch (basicType) { + case CHAR: + writeChars(dataOutput); + break; + case SHORT: + writeShorts(dataOutput); + break; + case INT: + writeInts(dataOutput); + break; + case STRING: + writeString(dataOutput); + break; + } + } + + @Benchmark + public void dataOutputStreamOverByteArray() throws Exception { + byteArrayOutputStream.reset(); + write(byteArrayStream); + byteArrayOutputStream.flush(); + } + + @Benchmark + public void dataOutputStreamOverRawFileStream() throws Exception { + fileOutputStream.getChannel().position(0); + write(rawFileStream); + fileOutputStream.flush(); + } + + @Benchmark + public void dataOutputStreamOverBufferedFileStream() throws Exception{ + fileOutputStream.getChannel().position(0); + write(bufferedFileStream); + fileOutputStream.flush(); + } +} diff --git a/test/micro/org/openjdk/bench/java/io/RecordDeserialization.java b/test/micro/org/openjdk/bench/java/io/RecordDeserialization.java index b2dbefe3c05..f119ce37b2b 100644 --- a/test/micro/org/openjdk/bench/java/io/RecordDeserialization.java +++ b/test/micro/org/openjdk/bench/java/io/RecordDeserialization.java @@ -82,7 +82,7 @@ @Measurement(iterations = 10, time = 1) @OutputTimeUnit(TimeUnit.MICROSECONDS) @State(Scope.Thread) -@Fork(value = 1, warmups = 0, jvmArgsAppend = "--enable-preview") +@Fork(value = 1, warmups = 0) public class RecordDeserialization { public record PointR(int x, int y) implements Serializable {} diff --git a/test/micro/org/openjdk/bench/java/lang/ArrayCopyObject.java b/test/micro/org/openjdk/bench/java/lang/ArrayCopyObject.java new file mode 100644 index 00000000000..ff33d451fdb --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/ArrayCopyObject.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Arm Limited. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.vm.compiler; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.results.Result; +import org.openjdk.jmh.results.RunResult; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.TimeValue; + + + + +import java.util.concurrent.TimeUnit; +import java.util.Arrays; + +class MyClass { + public int field1; + public int field2; + public int field3; + + public MyClass(int val) { + field1 = val; + field2 = val; + field3 = val; + } +} + +@State(Scope.Benchmark) +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class ArrayCopyObject { + @Param({"31", "63", "127" , "2047" , "4095", "8191"}) private int size; + + private MyClass [] src; + private MyClass [] dst; + + @Setup + public void setup() { + src = new MyClass[size]; + dst = new MyClass[size]; + for (int i = 0; i < src.length ; i++) { + src[i] = new MyClass(i); + dst[i] = new MyClass(0); + } + } + + @Benchmark + public void disjoint_micro() { + System.arraycopy(src, 0 , dst, 0 , size); + } + + @Benchmark + public void conjoint_micro() { + System.arraycopy(src, 0 , src, 10 , size - 10 ); + } + + public static void main(String[] args) throws RunnerException { + String [] base_opts = + { "-XX:+UnlockDiagnosticVMOptions ", + "-XX:+IgnoreUnrecognizedVMOptions ", + "-XX:UseAVX=3" }; + String [] opts_str1 = {"-XX:-UseCompressedOops "}; + String [] opts_str2 = {"-XX:+UseCompressedOops "}; + + Options baseOpts = new OptionsBuilder() + .include(ArrayCopyObject.class.getName()) + .warmupTime(TimeValue.seconds(30)) + .measurementTime(TimeValue.seconds(10)) + .warmupIterations(1) + .measurementIterations(2) + .jvmArgs(base_opts) + .forks(1) + .build(); + + RunResult r1 = new Runner(new OptionsBuilder() + .parent(baseOpts) + .jvmArgs(opts_str1) + .build()).runSingle(); + + RunResult r2 = new Runner(new OptionsBuilder() + .parent(baseOpts) + .jvmArgs(opts_str2) + .build()).runSingle(); + + System.out.println(r1.getPrimaryResult().getScore() + r2.getPrimaryResult().getScore()); + } +} + diff --git a/test/micro/org/openjdk/bench/java/lang/StringIndexOfChar.java b/test/micro/org/openjdk/bench/java/lang/StringIndexOfChar.java new file mode 100644 index 00000000000..adc3c296460 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/StringIndexOfChar.java @@ -0,0 +1,218 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang; + +import java.util.Random; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.infra.Blackhole; + +import java.util.concurrent.TimeUnit; + +/** + * This benchmark can be used to measure performance between StringLatin1 and StringUTF16 in terms of + * performance of the indexOf(char) and indexOf(String) methods which are intrinsified. + * On x86 the behaviour of the indexOf method is contingent upon the length of the string + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +public class StringIndexOfChar { + @Param("100000") + private int loops; + + @Param("1000") + private int pathCnt; + + @Param("1999") + private int rngSeed; + + private Random rng; + private String[] latn1_short; + private String[] latn1_sse4; + private String[] latn1_avx2; + private String[] latn1_mixedLength; + private String[] utf16_short; + private String[] utf16_sse4; + private String[] utf16_avx2; + private String[] utf16_mixedLength; + + @Setup + public void setup() { + rng = new Random(rngSeed); + latn1_short = new String[pathCnt]; + latn1_sse4 = new String[pathCnt]; + latn1_avx2 = new String[pathCnt]; + latn1_mixedLength = new String[pathCnt]; + utf16_short = new String[pathCnt]; + utf16_sse4 = new String[pathCnt]; + utf16_avx2 = new String[pathCnt]; + utf16_mixedLength = new String[pathCnt]; + + for (int i = 0; i < pathCnt; i++) { + latn1_short[i] = makeRndString(false, 15); + latn1_sse4[i] = makeRndString(false, 16); + latn1_avx2[i] = makeRndString(false, 32); + utf16_short[i] = makeRndString(true, 7); + utf16_sse4[i] = makeRndString(true, 8); + utf16_avx2[i] = makeRndString(true, 16); + latn1_mixedLength[i] = makeRndString(false, rng.nextInt(65)); + utf16_mixedLength[i] = makeRndString(true, rng.nextInt(65)); + } + } + + private String makeRndString(boolean isUtf16, int length) { + StringBuilder sb = new StringBuilder(length); + if(length > 0){ + sb.append(isUtf16?'\u2026':'b'); // ... + + for (int i = 1; i < length-1; i++) { + sb.append((char)('b' + rng.nextInt(26))); + } + + sb.append(rng.nextInt(3) >= 1?'a':'b');//66.6% of time 'a' is in string + } + return sb.toString(); + } + + + @Benchmark + public void latin1_mixed_char(Blackhole bh) { + for (String what : latn1_mixedLength) { + bh.consume(what.indexOf('a')); + } + } + + @Benchmark + public void utf16_mixed_char(Blackhole bh) { + for (String what : utf16_mixedLength) { + bh.consume(what.indexOf('a')); + } + } + + @Benchmark + public void latin1_mixed_String(Blackhole bh) { + for (String what : latn1_mixedLength) { + bh.consume(what.indexOf("a")); + } + } + + @Benchmark + public void utf16_mixed_String(Blackhole bh) { + for (String what : utf16_mixedLength) { + bh.consume(what.indexOf("a")); + } + } + + ////////// more detailed code path dependent tests ////////// + + @Benchmark + public void latin1_Short_char(Blackhole bh) { + for (String what : latn1_short) { + bh.consume(what.indexOf('a')); + } + } + + @Benchmark + public void latin1_SSE4_char(Blackhole bh) { + for (String what : latn1_sse4) { + bh.consume(what.indexOf('a')); + } + } + + @Benchmark + public void latin1_AVX2_char(Blackhole bh) { + for (String what : latn1_avx2) { + bh.consume(what.indexOf('a')); + } + } + + @Benchmark + public void utf16_Short_char(Blackhole bh) { + for (String what : utf16_short) { + bh.consume(what.indexOf('a')); + } + } + + @Benchmark + public void utf16_SSE4_char(Blackhole bh) { + for (String what : utf16_sse4) { + bh.consume(what.indexOf('a')); + } + } + + @Benchmark + public void utf16_AVX2_char(Blackhole bh) { + for (String what : utf16_avx2) { + bh.consume(what.indexOf('a')); + } + } + + @Benchmark + public void latin1_Short_String(Blackhole bh) { + for (String what : latn1_short) { + bh.consume(what.indexOf("a")); + } + } + + @Benchmark + public void latin1_SSE4_String(Blackhole bh) { + for (String what : latn1_sse4) { + bh.consume(what.indexOf("a")); + } + } + + @Benchmark + public void latin1_AVX2_String(Blackhole bh) { + for (String what : latn1_avx2) { + bh.consume(what.indexOf("a")); + } + } + + @Benchmark + public void utf16_Short_String(Blackhole bh) { + for (String what : utf16_short) { + bh.consume(what.indexOf("a")); + } + } + + @Benchmark + public void utf16_SSE4_String(Blackhole bh) { + for (String what : utf16_sse4) { + bh.consume(what.indexOf("a")); + } + } + + @Benchmark + public void utf16_AVX2_String(Blackhole bh) { + for (String what : utf16_avx2) { + bh.consume(what.indexOf("a")); + } + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/invoke/VarHandleExact.java b/test/micro/org/openjdk/bench/java/lang/invoke/VarHandleExact.java new file mode 100644 index 00000000000..1b5e4f77601 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/invoke/VarHandleExact.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang.invoke; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@State(org.openjdk.jmh.annotations.Scope.Thread) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Fork(3) +public class VarHandleExact { + + static final VarHandle exact; + static final VarHandle generic; + + static { + try { + generic = MethodHandles.lookup().findVarHandle(Data.class, "longField", long.class); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + exact = generic.withInvokeExactBehavior(); + } + + Data data; + + static class Data { + long longField; + } + + @Setup + public void setup() { + data = new Data(); + } + + @Benchmark + public void exact_exactInvocation() { + exact.set(data, (long) 42); + } + + @Benchmark + public void generic_genericInvocation() { + generic.set(data, 42); + } + + @Benchmark + public void generic_exactInvocation() { + generic.set(data, (long) 42); + } +} diff --git a/test/micro/org/openjdk/bench/java/math/BigIntegers.java b/test/micro/org/openjdk/bench/java/math/BigIntegers.java index 8b26b47cf1c..d3a20a3ee5e 100644 --- a/test/micro/org/openjdk/bench/java/math/BigIntegers.java +++ b/test/micro/org/openjdk/bench/java/math/BigIntegers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.infra.Blackhole; import java.math.BigInteger; @@ -45,11 +46,14 @@ @State(Scope.Thread) public class BigIntegers { - private BigInteger[] hugeArray, largeArray, smallArray, shiftArray; + private BigInteger[] hugeArray, largeArray, smallArray, shiftArray, smallShiftArray; public String[] dummyStringArray; public Object[] dummyArr; private static final int TESTSIZE = 1000; + @Param({"32", "64", "96", "128", "160", "192", "224", "256"}) + private int maxNumbits; + @Setup public void setup() { Random r = new Random(1123); @@ -72,6 +76,9 @@ public void setup() { * Each array entry is atmost 16k bits * in size */ + smallShiftArray = new BigInteger[TESTSIZE]; /* + * Small numbers, bits count in range [maxNumbits - 31, maxNumbits] + */ dummyStringArray = new String[TESTSIZE]; dummyArr = new Object[TESTSIZE]; @@ -84,6 +91,7 @@ public void setup() { largeArray[i] = new BigInteger("" + ((long) value + (long) Integer.MAX_VALUE)); smallArray[i] = new BigInteger("" + ((long) value / 1000)); shiftArray[i] = new BigInteger(numbits, r); + smallShiftArray[i] = new BigInteger(Math.max(maxNumbits - value % 32, 0), r); } } @@ -177,4 +185,30 @@ public void testRightShift(Blackhole bh) { } bh.consume(tmp); } + + /** Invokes the shiftLeft method of small BigInteger with different values. */ + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testSmallLeftShift(Blackhole bh) { + Random rand = new Random(); + int shift = rand.nextInt(30) + 1; + BigInteger tmp = null; + for (BigInteger s : smallShiftArray) { + tmp = s.shiftLeft(shift); + bh.consume(tmp); + } + } + + /** Invokes the shiftRight method of small BigInteger with different values. */ + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testSmallRightShift(Blackhole bh) { + Random rand = new Random(); + int shift = rand.nextInt(30) + 1; + BigInteger tmp = null; + for (BigInteger s : smallShiftArray) { + tmp = s.shiftRight(shift); + bh.consume(tmp); + } + } } diff --git a/test/micro/org/openjdk/bench/java/net/SocketChannelCompare.java b/test/micro/org/openjdk/bench/java/net/SocketChannelCompare.java new file mode 100644 index 00000000000..2d67bed5f23 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/net/SocketChannelCompare.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.net; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.StandardProtocolFamily; +import java.net.UnixDomainSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.file.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * Tests sending a 128 byte message on a second, to a thread which + * echo's it back and received by the original thread. + * Benchmark is performed for "inet" channels over TCP/IP + * and "unix" domain channels. + */ +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Thread) +public class SocketChannelCompare { + + static final int BUFSIZE = 128; // message size sent and received + private ServerSocketChannel ssc; + private SocketChannel s1, s2; + private EchoThread rt; + private ByteBuffer bb = ByteBuffer.allocate(BUFSIZE); + + private static volatile String tempDir; + private static final AtomicInteger count = new AtomicInteger(0); + private volatile Path socket; + + @Param({"inet", "unix"}) + private volatile String family; + + static { + try { + Path p = Files.createTempDirectory("readWriteTest"); + tempDir = p.toString(); + } catch (IOException e) { + tempDir = null; + } + } + + private ServerSocketChannel getServerSocketChannel() throws IOException { + if (family.equals("inet")) + return getInetServerSocketChannel(); + else if (family.equals("unix")) + return getUnixServerSocketChannel(); + throw new InternalError(); + } + + + private ServerSocketChannel getInetServerSocketChannel() throws IOException { + InetAddress iaddr = InetAddress.getLoopbackAddress(); + return ServerSocketChannel.open().bind(null); + } + + private ServerSocketChannel getUnixServerSocketChannel() throws IOException { + int next = count.incrementAndGet(); + socket = Paths.get(tempDir, Integer.toString(next)); + UnixDomainSocketAddress addr = UnixDomainSocketAddress.of(socket); + return ServerSocketChannel.open(StandardProtocolFamily.UNIX).bind(addr); + } + + @Setup(Level.Trial) + public void beforeRun() throws IOException { + ssc = getServerSocketChannel(); + s1 = SocketChannel.open(ssc.getLocalAddress()); + s2 = ssc.accept(); + + rt = new EchoThread(s2); + rt.start(); + } + + @TearDown(Level.Trial) + public void afterRun() throws IOException, InterruptedException { + s1.close(); + s2.close(); + ssc.close(); + if (family.equals("unix")) { + Files.delete(socket); + Files.delete(Path.of(tempDir)); + } + rt.join(); + } + + @Benchmark + public void test() throws IOException { + bb.position(0).limit(BUFSIZE); + s1.write(bb); + bb.clear(); + readFully(s1, bb); + } + + // read until buf is full, or EOF. Always returns number of bytes read + + static int readFully(SocketChannel chan, ByteBuffer buf) throws IOException { + int n = buf.remaining(); + int count = 0; + while (n > 0) { + int c = chan.read(buf); + if (c == -1) + return count; + n -= c; + count += c; + } + return count; + } + + static class EchoThread extends Thread { + private SocketChannel sc; + + public EchoThread(SocketChannel s2) { + this.sc = s2; + } + + public void run() { + try { + ByteBuffer bb = ByteBuffer.allocate(BUFSIZE); + while (true) { + bb.clear(); + int c = readFully(sc, bb); + if (c == 0) { + sc.close(); + return; + } + bb.flip(); + sc.write(bb); + } + } catch (ClosedChannelException ex) { + // shutdown time + } catch (IOException ioex) { + ioex.printStackTrace(); + } + } + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(org.openjdk.bench.java.net.SocketChannelCompare.class.getSimpleName()) + .warmupForks(1) + .warmupIterations(2) + .measurementIterations(2) + .forks(2) + .build(); + + new Runner(opt).run(); + + opt = new OptionsBuilder() + .include(org.openjdk.bench.java.net.SocketChannelCompare.class.getSimpleName()) + .warmupForks(1) + .warmupIterations(2) + .measurementIterations(2) + .jvmArgsPrepend("-Djdk.net.useFastTcpLoopback=true") + .forks(3) + .build(); + + new Runner(opt).run(); + } +} diff --git a/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java b/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java new file mode 100644 index 00000000000..965c8d41c79 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.net; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.StandardProtocolFamily; +import java.net.UnixDomainSocketAddress; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.file.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * Measures connection setup times + */ +@BenchmarkMode(Mode.SingleShotTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Thread) +public class SocketChannelConnectionSetup { + + private ServerSocketChannel ssc; + private SocketChannel s1, s2; + + private static volatile String tempDir; + private static final AtomicInteger count = new AtomicInteger(0); + private volatile Path socket; + + @Param({"inet", "unix"}) + private volatile String family; + + static { + try { + Path p = Files.createTempDirectory("readWriteTest"); + tempDir = p.toString(); + } catch (IOException e) { + tempDir = null; + } + } + + private ServerSocketChannel getServerSocketChannel() throws IOException { + if (family.equals("inet")) + return getInetServerSocketChannel(); + else if (family.equals("unix")) + return getUnixServerSocketChannel(); + throw new InternalError(); + } + + + private ServerSocketChannel getInetServerSocketChannel() throws IOException { + InetAddress iaddr = InetAddress.getLoopbackAddress(); + return ServerSocketChannel.open().bind(null); + } + + private ServerSocketChannel getUnixServerSocketChannel() throws IOException { + int next = count.incrementAndGet(); + socket = Paths.get(tempDir, Integer.toString(next)); + UnixDomainSocketAddress addr = UnixDomainSocketAddress.of(socket); + return ServerSocketChannel.open(StandardProtocolFamily.UNIX).bind(addr); + } + + @Setup(Level.Trial) + public void beforeRun() throws IOException { + ssc = getServerSocketChannel(); + } + + @TearDown(Level.Trial) + public void afterRun() throws IOException, InterruptedException { + ssc.close(); + if (family.equals("unix")) { + Files.delete(socket); + Files.delete(Path.of(tempDir)); + } + } + + @Benchmark + @Measurement(iterations = 5, batchSize=200) + public void test() throws IOException { + s1 = SocketChannel.open(ssc.getLocalAddress()); + s2 = ssc.accept(); + s1.close(); + s2.close(); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(org.openjdk.bench.java.net.SocketChannelConnectionSetup.class.getSimpleName()) + .warmupForks(1) + .forks(2) + .build(); + + new Runner(opt).run(); + + opt = new OptionsBuilder() + .include(org.openjdk.bench.java.net.SocketChannelConnectionSetup.class.getSimpleName()) + .jvmArgsPrepend("-Djdk.net.useFastTcpLoopback=true") + .warmupForks(1) + .forks(2) + .build(); + + new Runner(opt).run(); + } +} diff --git a/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java b/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java new file mode 100644 index 00000000000..9e75b0d556c --- /dev/null +++ b/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.net; + +import java.io.IOException; +import java.net.StandardProtocolFamily; +import java.net.UnixDomainSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.file.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.openjdk.jmh.annotations.*; + +/** + * Tests the overheads of I/O API. + * This test is known to depend heavily on network conditions and paltform. + */ +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Thread) +public class UnixSocketChannelReadWrite { + + private ServerSocketChannel ssc; + private SocketChannel s1, s2; + private ReadThread rt; + private ByteBuffer bb = ByteBuffer.allocate(1); + + private static volatile String tempDir; + private static final AtomicInteger count = new AtomicInteger(0); + private volatile Path socket; + + static { + try { + Path p = Files.createTempDirectory("readWriteTest"); + tempDir = p.toString(); + } catch (IOException e) { + tempDir = null; + } + } + + private ServerSocketChannel getServerSocketChannel() throws IOException { + int next = count.incrementAndGet(); + socket = Paths.get(tempDir, Integer.toString(next)); + UnixDomainSocketAddress addr = UnixDomainSocketAddress.of(socket); + ServerSocketChannel c = ServerSocketChannel.open(StandardProtocolFamily.UNIX); + c.bind(addr); + return c; + } + + @Setup(Level.Trial) + public void beforeRun() throws IOException { + ssc = getServerSocketChannel(); + s1 = SocketChannel.open(ssc.getLocalAddress()); + s2 = ssc.accept(); + + rt = new ReadThread(s2); + rt.start(); + + bb.put((byte) 47); + bb.flip(); + } + + @TearDown(Level.Trial) + public void afterRun() throws IOException, InterruptedException { + s1.close(); + s2.close(); + ssc.close(); + Files.delete(socket); + Files.delete(Path.of(tempDir)); + rt.join(); + } + + @Benchmark + public void test() throws IOException { + s1.write(bb); + bb.flip(); + } + + static class ReadThread extends Thread { + private SocketChannel sc; + + public ReadThread(SocketChannel s2) { + this.sc = s2; + } + + public void run() { + try { + ByteBuffer bb = ByteBuffer.allocate(1); + while (sc.read(bb) > 0) { + bb.flip(); + } + } catch (ClosedChannelException ex) { + // shutdown time + } catch (IOException e) { + e.printStackTrace(); + } + } + } + +} diff --git a/test/micro/org/openjdk/bench/java/security/MessageDigests.java b/test/micro/org/openjdk/bench/java/security/MessageDigests.java index 8b77785c37e..6b25f6dae35 100644 --- a/test/micro/org/openjdk/bench/java/security/MessageDigests.java +++ b/test/micro/org/openjdk/bench/java/security/MessageDigests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,7 +53,7 @@ public class MessageDigests { @Param({"64", "1024", "16384"}) private int length; - @Param({"md2", "md5", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"}) + @Param({"md2", "md5", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512", "SHA3-224", "SHA3-256", "SHA3-384", "SHA3-512"}) private String digesterName; @Param({"DEFAULT", "SUN"}) diff --git a/test/micro/org/openjdk/bench/java/util/Base64Encode.java b/test/micro/org/openjdk/bench/java/util/Base64Encode.java new file mode 100644 index 00000000000..297f216eb49 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/util/Base64Encode.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020, Huawei Technologies Co. Ltd. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.micro.bench.java.util; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; + +import java.util.Base64; +import java.util.Random; +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +public class Base64Encode { + + private Base64.Encoder encoder; + private ArrayList unencoded; + private byte[] encoded; + + private static final int TESTSIZE = 1000; + + @Param({"1", "2", "3", "6", "7", "9", "10", "48", "512", "1000", "20000"}) + private int maxNumBytes; + + @Setup + public void setup() { + Random r = new Random(1123); + + int dstLen = ((maxNumBytes + 16) / 3) * 4; + + encoder = Base64.getEncoder(); + unencoded = new ArrayList (); + encoded = new byte[dstLen]; + + for (int i = 0; i < TESTSIZE; i++) { + int srcLen = 1 + r.nextInt(maxNumBytes); + byte[] src = new byte[srcLen]; + r.nextBytes(src); + unencoded.add(src); + } + } + + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testBase64Encode(Blackhole bh) { + for (byte[] s : unencoded) { + encoder.encode(s, encoded); + bh.consume(encoded); + } + } +} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/VarHandleExact.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/VarHandleExact.java new file mode 100644 index 00000000000..ec920b90ae8 --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/VarHandleExact.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.jdk.incubator.foreign; + +import jdk.incubator.foreign.MemoryHandles; +import jdk.incubator.foreign.MemorySegment; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; + +import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; +import java.util.concurrent.TimeUnit; + +import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; + +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@State(org.openjdk.jmh.annotations.Scope.Thread) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Fork(value = 3, jvmArgsAppend = { "--add-modules", "jdk.incubator.foreign" }) +public class VarHandleExact { + + static final VarHandle exact; + static final VarHandle generic; + + static { + generic = MemoryHandles.withStride(MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()), 4); + exact = generic.withInvokeExactBehavior(); + } + + MemorySegment data; + + @Setup + public void setup() { + data = MemorySegment.allocateNative(JAVA_INT); + } + + @TearDown + public void tearDown() { + data.close(); + } + + @Benchmark + public void exact_exactInvocation() { + exact.set(data.baseAddress(), (long) 0, 42); + } + + @Benchmark + public void generic_genericInvocation() { + generic.set(data.baseAddress(), 0, 42); + } + + @Benchmark + public void generic_exactInvocation() { + generic.set(data.baseAddress(), (long) 0, 42); + } +} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/points/support/libJNIPoint.c b/test/micro/org/openjdk/bench/jdk/incubator/foreign/points/support/libJNIPoint.c index 569e0e3d25b..eb7a38a2845 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/points/support/libJNIPoint.c +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/points/support/libJNIPoint.c @@ -22,40 +22,41 @@ */ #include #include +#include "jlong.h" #include "points.h" JNIEXPORT jlong JNICALL Java_org_openjdk_bench_jdk_incubator_foreign_points_support_JNIPoint_allocate (JNIEnv *env, jclass nativePointClass) { Point* p = malloc(sizeof *p); - return (jlong) p; + return ptr_to_jlong(p); } JNIEXPORT void JNICALL Java_org_openjdk_bench_jdk_incubator_foreign_points_support_JNIPoint_free (JNIEnv *env, jclass cls, jlong thisPoint) { - free((Point*) thisPoint); + free(jlong_to_ptr(thisPoint)); } JNIEXPORT jint JNICALL Java_org_openjdk_bench_jdk_incubator_foreign_points_support_JNIPoint_getX (JNIEnv *env, jclass cls, jlong thisPoint) { - Point* point = (Point*) thisPoint; + Point* point = jlong_to_ptr(thisPoint); return point->x; } JNIEXPORT void JNICALL Java_org_openjdk_bench_jdk_incubator_foreign_points_support_JNIPoint_setX (JNIEnv *env, jclass cls, jlong thisPoint, jint value) { - Point* point = (Point*) thisPoint; + Point* point = jlong_to_ptr(thisPoint); point->x = value; } JNIEXPORT jint JNICALL Java_org_openjdk_bench_jdk_incubator_foreign_points_support_JNIPoint_getY (JNIEnv *env, jclass cls, jlong thisPoint) { - Point* point = (Point*) thisPoint; + Point* point = jlong_to_ptr(thisPoint); return point->y; } JNIEXPORT void JNICALL Java_org_openjdk_bench_jdk_incubator_foreign_points_support_JNIPoint_setY (JNIEnv *env, jclass cls, jlong thisPoint, jint value) { - Point* point = (Point*) thisPoint; + Point* point = jlong_to_ptr(thisPoint); point->y = value; } diff --git a/test/micro/org/openjdk/bench/vm/compiler/VectorShiftAccumulate.java b/test/micro/org/openjdk/bench/vm/compiler/VectorShiftAccumulate.java new file mode 100644 index 00000000000..d9c729f56a0 --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/VectorShiftAccumulate.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2020, Huawei Technologies Co. Ltd. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.vm.compiler; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.*; + +import java.util.concurrent.TimeUnit; +import java.util.Random; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +public class VectorShiftAccumulate { + @Param({"1028"}) + public int count; + + private byte[] bytesA, bytesB, bytesD; + private short[] shortsA, shortsB, shortsD; + private char[] charsA, charsB, charsD; + private int[] intsA, intsB, intsD; + private long[] longsA, longsB, longsD; + + @Param("0") + private int seed; + private Random r = new Random(seed); + + @Setup + public void init() { + bytesA = new byte[count]; + shortsA = new short[count]; + charsA = new char[count]; + intsA = new int[count]; + longsA = new long[count]; + + bytesB = new byte[count]; + shortsB = new short[count]; + charsB = new char[count]; + intsB = new int[count]; + longsB = new long[count]; + + bytesD = new byte[count]; + shortsD = new short[count]; + charsD = new char[count]; + intsD = new int[count]; + longsD = new long[count]; + + for (int i = 0; i < count; i++) { + bytesA[i] = (byte) r.nextInt(); + shortsA[i] = (short) r.nextInt(); + intsA[i] = r.nextInt(); + longsA[i] = r.nextLong(); + + bytesB[i] = (byte) r.nextInt(); + shortsB[i] = (short) r.nextInt(); + intsB[i] = r.nextInt(); + longsB[i] = r.nextLong(); + } + } + + @Benchmark + public void shiftRightAccumulateByte() { + for (int i = 0; i < count; i++) { + bytesD[i] = (byte) (bytesA[i] + (bytesB[i] >> 1)); + } + } + + @Benchmark + public void shiftURightAccumulateByte() { + for (int i = 0; i < count; i++) { + bytesD[i] = (byte) (bytesA[i] + (((byte) (bytesB[i] >>> 3)))); + } + } + + @Benchmark + public void shiftRightAccumulateShort() { + for (int i = 0; i < count; i++) { + shortsD[i] = (short) (shortsA[i] + (shortsB[i] >> 5)); + } + } + + @Benchmark + public void shiftURightAccumulateChar() { + for (int i = 0; i < count; i++) { + charsD[i] = (char) (charsA[i] + (charsB[i] >>> 4)); + } + } + + @Benchmark + public void shiftRightAccumulateInt() { + for (int i = 0; i < count; i++) { + intsD[i] = intsA[i] + (intsB[i] >> 2); + } + } + + @Benchmark + public void shiftURightAccumulateInt() { + for (int i = 0; i < count; i++) { + intsD[i] = (intsB[i] >>> 2) + intsA[i]; + } + } + + @Benchmark + public void shiftRightAccumulateLong() { + for (int i = 0; i < count; i++) { + longsD[i] = longsA[i] + (longsB[i] >> 5); + } + } + + @Benchmark + public void shiftURightAccumulateLong() { + for (int i = 0; i < count; i++) { + longsD[i] = (longsB[i] >>> 2) + longsA[i]; + } + } +} + diff --git a/test/micro/org/openjdk/bench/vm/compiler/overhead/SimpleRepeatCompilation.java b/test/micro/org/openjdk/bench/vm/compiler/overhead/SimpleRepeatCompilation.java new file mode 100644 index 00000000000..0605050ed2a --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/overhead/SimpleRepeatCompilation.java @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.vm.compiler.overhead; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; + +import java.util.concurrent.TimeUnit; +import java.util.Arrays; + +/** + * The purpose of these microbenchmarks is to use RepeatCompilation + * to produce a benchmark that focuses on the overhead of various JIT + * compilations themselves. + */ + +@State(Scope.Benchmark) +@BenchmarkMode(Mode.SingleShotTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Fork(value = 10, warmups = 1) +public class SimpleRepeatCompilation { + + public static final String MIXHASH_METHOD + = "-XX:CompileCommand=option,org/openjdk/bench/vm/compiler/overhead/SimpleRepeatCompilation.mixHashCode,intx,RepeatCompilation,500"; + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch", MIXHASH_METHOD}) + public int mixHashCode_repeat() { + return loop_hashCode(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch", "-XX:-TieredCompilation", MIXHASH_METHOD}) + public int mixHashCode_repeat_c2() { + return loop_hashCode(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch", "-XX:TieredStopAtLevel=1", MIXHASH_METHOD}) + public int mixHashCode_repeat_c1() { + return loop_hashCode(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch"}) + public int mixHashCode_baseline() { + return loop_hashCode(); + } + + public int loop_hashCode() { + int value = 0; + for (int i = 0; i < 1_000_000; i++) { + value += mixHashCode("simple_string"); + } + return value; + } + + public int mixHashCode(String value) { + int h = value.hashCode(); + for (int i = 0; i < value.length(); i++) { + h = value.charAt(i) ^ h; + } + return h; + } + + public static final String TRIVIAL_MATH_METHOD + = "-XX:CompileCommand=option,org/openjdk/bench/vm/compiler/overhead/SimpleRepeatCompilation.trivialMath,intx,RepeatCompilation,2000"; + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch",TRIVIAL_MATH_METHOD}) + public int trivialMath_repeat() { + return loop_trivialMath(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch", "-XX:-TieredCompilation", TRIVIAL_MATH_METHOD}) + public int trivialMath_repeat_c2() { + return loop_trivialMath(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch", "-XX:TieredStopAtLevel=1", TRIVIAL_MATH_METHOD}) + public int trivialMath_repeat_c1() { + return loop_trivialMath(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch"}) + public int trivialMath_baseline() { + return loop_trivialMath(); + } + + public int loop_trivialMath() { + int value = 0; + for (int i = 0; i < 1_000_000; i++) { + value += trivialMath(i, i - 1); + } + return value; + } + + public int trivialMath(int a, int b) { + return a * b + a; + } + + + public static final String LARGE_METHOD + = "-XX:CompileCommand=option,org/openjdk/bench/vm/compiler/overhead/SimpleRepeatCompilation.largeMethod,intx,RepeatCompilation,100"; + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch",LARGE_METHOD}) + public int largeMethod_repeat() { + return loop_largeMethod(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch", "-XX:-TieredCompilation", LARGE_METHOD}) + public int largeMethod_repeat_c2() { + return loop_largeMethod(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch", "-XX:TieredStopAtLevel=1", LARGE_METHOD}) + public int largeMethod_repeat_c1() { + return loop_largeMethod(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch"}) + public int largeMethod_baseline() { + return loop_largeMethod(); + } + + public int loop_largeMethod() { + int value = 0; + for (int i = 0; i < 50_000; i++) { + value += largeMethod(i, i - 1); + } + return value; + } + + // Meaningless but largish method with plenty of locals + // to put more stress on register allocation + public int largeMethod(int a, int b) { + int c = b + 17; + int d = a + b + 6; + int e = c + d + 99; + int f = d + e + 919; + long val = 0; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + a -= b; + b += c; + c -= d; + d += e; + e -= a; + val = a - b + c - d + e - f; + } + } + } + int g = b; + int h = a; + int l = c; + int m = d; + int n = d; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + g += b; + h -= c; + l += d; + m -= e; + e += a; + val = g + h + l + m + n; + } + } + } + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + a -= b; + b += c; + c -= d; + d += e; + e -= a; + val = a - b - c - d - e - f; + } + } + } + int o = b; + int p = a; + int q = c; + int r = d; + int s = d; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + o += b; + p += c; + q += d; + r += e; + s += a; + val = o + p + q + r + s; + } + } + } + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + g += b; + h -= c; + l += d; + m -= e; + e += a; + val = g + h + l + m + n; + } + } + } + return (int)(val ^ (val >> 32L)); + } +}